|
1 | 1 | from typing import List, Optional |
2 | 2 | from uuid import UUID |
3 | 3 |
|
4 | | -from sqlalchemy import delete as sa_delete, select |
| 4 | +from sqlalchemy import and_, delete as sa_delete, or_, select |
| 5 | +from sqlalchemy import update as sa_update |
5 | 6 | from sqlalchemy.exc import IntegrityError |
6 | 7 |
|
7 | 8 | from oss.src.core.sessions.turns.dtos import ( |
8 | 9 | HarnessKind, |
9 | 10 | SessionTurn, |
| 11 | + SessionTurnComplete, |
10 | 12 | SessionTurnCreate, |
11 | 13 | SessionTurnQuery, |
12 | 14 | ) |
|
23 | 25 | TransactionsEngine, |
24 | 26 | get_transactions_engine, |
25 | 27 | ) |
26 | | -from oss.src.dbs.postgres.shared.utils import apply_windowing |
27 | 28 |
|
28 | 29 |
|
29 | 30 | class SessionTurnsDAO(SessionTurnsDAOInterface): |
@@ -68,6 +69,48 @@ async def append( |
68 | 69 | raise |
69 | 70 | return map_turn_dbe_to_dto(turn_dbe=dbe) |
70 | 71 |
|
| 72 | + async def complete( |
| 73 | + self, |
| 74 | + *, |
| 75 | + project_id: UUID, |
| 76 | + # |
| 77 | + turn: SessionTurnComplete, |
| 78 | + ) -> Optional[SessionTurn]: |
| 79 | + values = {"end_time": turn.end_time} |
| 80 | + if turn.agent_session_id is not None: |
| 81 | + values["agent_session_id"] = turn.agent_session_id |
| 82 | + |
| 83 | + async with self.engine.session() as session: |
| 84 | + stmt = ( |
| 85 | + sa_update(SessionTurnDBE) |
| 86 | + .where( |
| 87 | + SessionTurnDBE.project_id == project_id, |
| 88 | + SessionTurnDBE.session_id == turn.session_id, |
| 89 | + SessionTurnDBE.turn_index == turn.turn_index, |
| 90 | + SessionTurnDBE.end_time.is_(None), |
| 91 | + ) |
| 92 | + .values(**values) |
| 93 | + .returning(SessionTurnDBE) |
| 94 | + ) |
| 95 | + result = await session.execute(stmt) |
| 96 | + dbe = result.scalar_one_or_none() |
| 97 | + if dbe is not None: |
| 98 | + await session.commit() |
| 99 | + await session.refresh(dbe) |
| 100 | + return map_turn_dbe_to_dto(turn_dbe=dbe) |
| 101 | + |
| 102 | + stmt = select(SessionTurnDBE).where( |
| 103 | + SessionTurnDBE.project_id == project_id, |
| 104 | + SessionTurnDBE.session_id == turn.session_id, |
| 105 | + SessionTurnDBE.turn_index == turn.turn_index, |
| 106 | + ) |
| 107 | + result = await session.execute(stmt) |
| 108 | + dbe = result.scalar_one_or_none() |
| 109 | + |
| 110 | + if dbe is None: |
| 111 | + return None |
| 112 | + return map_turn_dbe_to_dto(turn_dbe=dbe) |
| 113 | + |
71 | 114 | async def fetch_turn( |
72 | 115 | self, |
73 | 116 | *, |
@@ -119,16 +162,67 @@ async def query_turns( |
119 | 162 | SessionTurnDBE.references.contains(turn_references), |
120 | 163 | ) |
121 | 164 |
|
| 165 | + descending = windowing is None or windowing.order != "ascending" |
| 166 | + time_attribute = SessionTurnDBE.start_time |
| 167 | + |
122 | 168 | if windowing: |
123 | | - stmt = apply_windowing( |
124 | | - stmt=stmt, |
125 | | - DBE=SessionTurnDBE, |
126 | | - attribute="id", |
127 | | - order="descending", |
128 | | - windowing=windowing, |
| 169 | + if windowing.newest is not None: |
| 170 | + if descending and windowing.next is None: |
| 171 | + stmt = stmt.where(time_attribute < windowing.newest) |
| 172 | + else: |
| 173 | + stmt = stmt.where(time_attribute <= windowing.newest) |
| 174 | + if windowing.oldest is not None: |
| 175 | + if not descending and windowing.next is None: |
| 176 | + stmt = stmt.where(time_attribute > windowing.oldest) |
| 177 | + else: |
| 178 | + stmt = stmt.where(time_attribute >= windowing.oldest) |
| 179 | + |
| 180 | + if windowing.next is not None: |
| 181 | + cursor_stmt = select( |
| 182 | + SessionTurnDBE.turn_index, |
| 183 | + SessionTurnDBE.id, |
| 184 | + ).where( |
| 185 | + SessionTurnDBE.project_id == project_id, |
| 186 | + SessionTurnDBE.id == windowing.next, |
| 187 | + ) |
| 188 | + cursor_result = await session.execute(cursor_stmt) |
| 189 | + cursor = cursor_result.one_or_none() |
| 190 | + if cursor is not None: |
| 191 | + cursor_index, cursor_id = cursor |
| 192 | + if descending: |
| 193 | + stmt = stmt.where( |
| 194 | + or_( |
| 195 | + SessionTurnDBE.turn_index < cursor_index, |
| 196 | + and_( |
| 197 | + SessionTurnDBE.turn_index == cursor_index, |
| 198 | + SessionTurnDBE.id < cursor_id, |
| 199 | + ), |
| 200 | + ) |
| 201 | + ) |
| 202 | + else: |
| 203 | + stmt = stmt.where( |
| 204 | + or_( |
| 205 | + SessionTurnDBE.turn_index > cursor_index, |
| 206 | + and_( |
| 207 | + SessionTurnDBE.turn_index == cursor_index, |
| 208 | + SessionTurnDBE.id > cursor_id, |
| 209 | + ), |
| 210 | + ) |
| 211 | + ) |
| 212 | + |
| 213 | + if descending: |
| 214 | + stmt = stmt.order_by( |
| 215 | + SessionTurnDBE.turn_index.desc(), |
| 216 | + SessionTurnDBE.id.desc(), |
129 | 217 | ) |
130 | 218 | else: |
131 | | - stmt = stmt.order_by(SessionTurnDBE.created_at.desc()) |
| 219 | + stmt = stmt.order_by( |
| 220 | + SessionTurnDBE.turn_index.asc(), |
| 221 | + SessionTurnDBE.id.asc(), |
| 222 | + ) |
| 223 | + |
| 224 | + if windowing and windowing.limit: |
| 225 | + stmt = stmt.limit(windowing.limit) |
132 | 226 |
|
133 | 227 | result = await session.execute(stmt) |
134 | 228 | return [map_turn_dbe_to_dto(turn_dbe=dbe) for dbe in result.scalars().all()] |
|
0 commit comments