History

Move provider event SQL access into Effect repository

Cursor Agent committed 8 months ago

Commit bce0df01 parent d6e0cd82 files · +231 −62Browse files

Unified · Split

2 files
  1. apps/server/src/persistence/providerEventsRepo.ts +124 −0
  2. apps/server/src/persistenceService.ts +107 −62
apps/server/src/persistence/providerEventsRepo.ts+124−0
@@ -0,0 +1,124 @@
1+import * as Effect from "effect/Effect";
2+import * as SqlClient from "effect/unstable/sql/SqlClient";
3+
4+function toSafeInteger(value: unknown, fallback = 0): number {
5+ if (typeof value === "number" && Number.isFinite(value)) {
6+ return Math.trunc(value);
7+ }
8+ if (typeof value === "bigint") {
9+ return Number(value);
10+ }
11+ return fallback;
12+}
13+
14+export interface CompletedProviderItemRow {
15+ item_id: string | null;
16+ payload_json: string | null;
17+}
18+
19+export const insertProviderEvent = (input: {
20+ id: string;
21+ sessionId: string;
22+ provider: string;
23+ kind: string;
24+ method: string;
25+ runtimeThreadId: string | null;
26+ turnId: string | null;
27+ itemId: string | null;
28+ requestId: string | null;
29+ requestKind: string | null;
30+ textDelta: string | null;
31+ message: string | null;
32+ payloadJson: string | null;
33+ createdAt: string;
34+}): Effect.Effect<number, unknown, SqlClient.SqlClient> =>
35+ Effect.gen(function*() {
36+ const sql = yield* SqlClient.SqlClient;
37+ const rawResult = (yield* sql
38+ .unsafe<{ changes?: number | bigint }>(
39+ `INSERT OR IGNORE INTO provider_events (
40+ id,
41+ session_id,
42+ provider,
43+ kind,
44+ method,
45+ thread_id,
46+ turn_id,
47+ item_id,
48+ request_id,
49+ request_kind,
50+ text_delta,
51+ message,
52+ payload_json,
53+ created_at
54+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`,
55+ [
56+ input.id,
57+ input.sessionId,
58+ input.provider,
59+ input.kind,
60+ input.method,
61+ input.runtimeThreadId,
62+ input.turnId,
63+ input.itemId,
64+ input.requestId,
65+ input.requestKind,
66+ input.textDelta,
67+ input.message,
68+ input.payloadJson,
69+ input.createdAt,
70+ ],
71+ )
72+ .raw) as { changes?: number | bigint };
73+ return toSafeInteger(rawResult.changes, 0);
74+ });
75+
76+export const listCompletedItemEventsBySessionTurn = (input: {
77+ sessionId: string;
78+ turnId: string;
79+}): Effect.Effect<CompletedProviderItemRow[], unknown, SqlClient.SqlClient> =>
80+ Effect.gen(function*() {
81+ const sql = yield* SqlClient.SqlClient;
82+ return (yield* sql
83+ .unsafe<CompletedProviderItemRow>(
84+ `SELECT item_id, payload_json
85+ FROM provider_events
86+ WHERE session_id = ? AND turn_id = ? AND method = 'item/completed'
87+ ORDER BY created_at DESC;`,
88+ [input.sessionId, input.turnId],
89+ )
90+ .unprepared) as CompletedProviderItemRow[];
91+ });
92+
93+export const listCompletedItemEventsByThreadTurn = (input: {
94+ runtimeThreadId: string;
95+ turnId: string;
96+}): Effect.Effect<CompletedProviderItemRow[], unknown, SqlClient.SqlClient> =>
97+ Effect.gen(function*() {
98+ const sql = yield* SqlClient.SqlClient;
99+ return (yield* sql
100+ .unsafe<CompletedProviderItemRow>(
101+ `SELECT item_id, payload_json
102+ FROM provider_events
103+ WHERE thread_id = ? AND turn_id = ? AND method = 'item/completed'
104+ ORDER BY created_at DESC;`,
105+ [input.runtimeThreadId, input.turnId],
106+ )
107+ .unprepared) as CompletedProviderItemRow[];
108+ });
109+
110+export const listCompletedItemEventsByTurn = (
111+ turnId: string,
112+): Effect.Effect<CompletedProviderItemRow[], unknown, SqlClient.SqlClient> =>
113+ Effect.gen(function*() {
114+ const sql = yield* SqlClient.SqlClient;
115+ return (yield* sql
116+ .unsafe<CompletedProviderItemRow>(
117+ `SELECT item_id, payload_json
118+ FROM provider_events
119+ WHERE turn_id = ? AND method = 'item/completed'
120+ ORDER BY created_at DESC;`,
121+ [turnId],
122+ )
123+ .unprepared) as CompletedProviderItemRow[];
124+ });
apps/server/src/persistenceService.ts+107−62
@@ -73,6 +73,13 @@ import { · 72 unmodified lines
7373 upsertDocument as upsertDocumentEffect,
7474 type DocumentRow,
7575 } from "./persistence/documentsRepo";
76+import {
77+ insertProviderEvent as insertProviderEventEffect,
78+ listCompletedItemEventsBySessionTurn as listCompletedItemEventsBySessionTurnEffect,
79+ listCompletedItemEventsByThreadTurn as listCompletedItemEventsByThreadTurnEffect,
80+ listCompletedItemEventsByTurn as listCompletedItemEventsByTurnEffect,
81+ type CompletedProviderItemRow,
82+} from "./persistence/providerEventsRepo";
7683 import {
7784 appendStateEvent as appendStateEventEffect,
7885 listStateEventsAfterSeq as listStateEventsAfterSeqEffect,
@@ -1570,38 +1577,55 @@ export class PersistenceService extends EventEmitter<PersistenceServiceEvents> { · 1491 unmodified lines
15701577 sessionId?: string;
15711578 runtimeThreadId?: string | null;
15721579 }): string | undefined {
1573− const queries: Array<{ sql: string; params: unknown[] }> = [];
1580+ const rowGroups: CompletedProviderItemRow[][] = [];
15741581 if (input.sessionId) {
1575− queries.push({
1576− sql: `SELECT item_id, payload_json
1577− FROM provider_events
1578− WHERE session_id = ? AND turn_id = ? AND method = 'item/completed'
1579− ORDER BY created_at DESC;`,
1580− params: [input.sessionId, input.turnId],
1581− });
1582+ rowGroups.push(
1583+ this.runWithEffectSql(
1584+ listCompletedItemEventsBySessionTurnEffect({
1585+ sessionId: input.sessionId,
1586+ turnId: input.turnId,
1587+ }),
1588+ () =>
1589+ this.db.prepare(
1590+ `SELECT item_id, payload_json
1591+ FROM provider_events
1592+ WHERE session_id = ? AND turn_id = ? AND method = 'item/completed'
1593+ ORDER BY created_at DESC;`,
1594+ ).all(input.sessionId, input.turnId) as CompletedProviderItemRow[],
1595+ ),
1596+ );
15821597 }
15831598 if (input.runtimeThreadId) {
1584− queries.push({
1585− sql: `SELECT item_id, payload_json
1586− FROM provider_events
1587− WHERE thread_id = ? AND turn_id = ? AND method = 'item/completed'
1588− ORDER BY created_at DESC;`,
1589− params: [input.runtimeThreadId, input.turnId],
1590− });
1599+ rowGroups.push(
1600+ this.runWithEffectSql(
1601+ listCompletedItemEventsByThreadTurnEffect({
1602+ runtimeThreadId: input.runtimeThreadId,
1603+ turnId: input.turnId,
1604+ }),
1605+ () =>
1606+ this.db.prepare(
1607+ `SELECT item_id, payload_json
1608+ FROM provider_events
1609+ WHERE thread_id = ? AND turn_id = ? AND method = 'item/completed'
1610+ ORDER BY created_at DESC;`,
1611+ ).all(input.runtimeThreadId, input.turnId) as CompletedProviderItemRow[],
1612+ ),
1613+ );
15911614 }
1592− queries.push({
1593− sql: `SELECT item_id, payload_json
1594− FROM provider_events
1595− WHERE turn_id = ? AND method = 'item/completed'
1596− ORDER BY created_at DESC;`,
1597− params: [input.turnId],
1598− });
1615+ rowGroups.push(
1616+ this.runWithEffectSql(
1617+ listCompletedItemEventsByTurnEffect(input.turnId),
1618+ () =>
1619+ this.db.prepare(
1620+ `SELECT item_id, payload_json
1621+ FROM provider_events
1622+ WHERE turn_id = ? AND method = 'item/completed'
1623+ ORDER BY created_at DESC;`,
1624+ ).all(input.turnId) as CompletedProviderItemRow[],
1625+ ),
1626+ );
15991627
1600− for (const query of queries) {
1601− const rows = this.db.prepare(query.sql).all(...query.params) as Array<{
1602− item_id: string | null;
1603− payload_json: string | null;
1604− }>;
1628+ for (const rows of rowGroups) {
16051629 for (const row of rows) {
16061630 const payload = row.payload_json ? this.tryParseJson(row.payload_json) : null;
16071631 const item = asObject(asObject(payload)?.item);
@@ -1774,43 +1798,64 @@ export class PersistenceService extends EventEmitter<PersistenceServiceEvents> { · 166 unmodified lines
17741798 private insertProviderEvent(event: ProviderEvent): ProviderEventInsertResult {
17751799 const runtimeThreadId = event.threadId ?? parseThreadIdFromEventPayload(event.payload);
17761800 const payloadJson = event.payload === undefined ? null : JSON.stringify(event.payload);
1777− const result = this.db
1778− .prepare(
1779− `INSERT OR IGNORE INTO provider_events (
1780− id,
1781− session_id,
1782− provider,
1783− kind,
1784− method,
1785− thread_id,
1786− turn_id,
1787− item_id,
1788− request_id,
1789− request_kind,
1790− text_delta,
1791− message,
1792− payload_json,
1793− created_at
1794− ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`,
1795− )
1796− .run(
1797− event.id,
1798− event.sessionId,
1799− event.provider,
1800− event.kind,
1801− event.method,
1802− runtimeThreadId,
1803− event.turnId ?? null,
1804− event.itemId ?? null,
1805− event.requestId ?? null,
1806− event.requestKind ?? null,
1807− event.textDelta ?? null,
1808− event.message ?? null,
1801+ const changes = this.runWithEffectSql(
1802+ insertProviderEventEffect({
1803+ id: event.id,
1804+ sessionId: event.sessionId,
1805+ provider: event.provider,
1806+ kind: event.kind,
1807+ method: event.method,
1808+ runtimeThreadId: runtimeThreadId ?? null,
1809+ turnId: event.turnId ?? null,
1810+ itemId: event.itemId ?? null,
1811+ requestId: event.requestId ?? null,
1812+ requestKind: event.requestKind ?? null,
1813+ textDelta: event.textDelta ?? null,
1814+ message: event.message ?? null,
18091815 payloadJson,
1810− event.createdAt,
1811− ) as { changes?: number | bigint };
1816+ createdAt: event.createdAt,
1817+ }),
1818+ () => {
1819+ const result = this.db
1820+ .prepare(
1821+ `INSERT OR IGNORE INTO provider_events (
1822+ id,
1823+ session_id,
1824+ provider,
1825+ kind,
1826+ method,
1827+ thread_id,
1828+ turn_id,
1829+ item_id,
1830+ request_id,
1831+ request_kind,
1832+ text_delta,
1833+ message,
1834+ payload_json,
1835+ created_at
1836+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`,
1837+ )
1838+ .run(
1839+ event.id,
1840+ event.sessionId,
1841+ event.provider,
1842+ event.kind,
1843+ event.method,
1844+ runtimeThreadId,
1845+ event.turnId ?? null,
1846+ event.itemId ?? null,
1847+ event.requestId ?? null,
1848+ event.requestKind ?? null,
1849+ event.textDelta ?? null,
1850+ event.message ?? null,
1851+ payloadJson,
1852+ event.createdAt,
1853+ ) as { changes?: number | bigint };
1854+ return toSafeInteger(result.changes, 0);
1855+ },
1856+ );
18121857 return {
1813− inserted: toSafeInteger(result.changes, 0) > 0,
1858+ inserted: changes > 0,
18141859 runtimeThreadId: runtimeThreadId ?? null,
18151860 };
18161861 }

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t