@pikku/kysely 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/src/index.d.ts +9 -1
  3. package/dist/src/index.js +6 -1
  4. package/dist/src/kysely-agent-run-service.d.ts +19 -0
  5. package/dist/src/kysely-agent-run-service.js +171 -0
  6. package/dist/src/kysely-ai-storage-service.d.ts +37 -0
  7. package/dist/src/kysely-ai-storage-service.js +586 -0
  8. package/dist/src/kysely-channel-store.d.ts +7 -3
  9. package/dist/src/kysely-channel-store.js +61 -24
  10. package/dist/src/kysely-deployment-service.d.ts +17 -0
  11. package/dist/src/kysely-deployment-service.js +128 -0
  12. package/dist/src/kysely-eventhub-store.d.ts +7 -3
  13. package/dist/src/kysely-eventhub-store.js +34 -18
  14. package/dist/src/kysely-json.d.ts +1 -0
  15. package/dist/src/kysely-json.js +7 -0
  16. package/dist/src/kysely-tables.d.ts +136 -0
  17. package/dist/src/kysely-tables.js +1 -0
  18. package/dist/src/kysely-workflow-run-service.d.ts +29 -0
  19. package/dist/src/kysely-workflow-run-service.js +194 -0
  20. package/dist/src/kysely-workflow-service.d.ts +47 -0
  21. package/dist/src/kysely-workflow-service.js +485 -0
  22. package/dist/src/pikku-kysely.d.ts +3 -2
  23. package/dist/src/pikku-kysely.js +25 -5
  24. package/dist/tsconfig.tsbuildinfo +1 -1
  25. package/package.json +12 -6
  26. package/src/index.ts +11 -1
  27. package/src/kysely-agent-run-service.ts +205 -0
  28. package/src/kysely-ai-storage-service.ts +713 -0
  29. package/src/kysely-channel-store.ts +82 -26
  30. package/src/kysely-deployment-service.ts +171 -0
  31. package/src/kysely-eventhub-store.ts +38 -17
  32. package/src/kysely-json.ts +5 -0
  33. package/src/kysely-services.test.ts +800 -0
  34. package/src/kysely-tables.ts +150 -0
  35. package/src/kysely-workflow-run-service.ts +242 -0
  36. package/src/kysely-workflow-service.ts +642 -0
  37. package/src/pikku-kysely.ts +28 -6
  38. package/sql/serverless-tables.sql +0 -16
@@ -0,0 +1,205 @@
1
+ import type {
2
+ AIThread,
3
+ AIMessage,
4
+ AgentRunRow,
5
+ AgentRunService,
6
+ } from '@pikku/core/ai-agent'
7
+ import { Kysely } from 'kysely'
8
+ import type { KyselyPikkuDB } from './kysely-tables.js'
9
+ import { parseJson } from './kysely-json.js'
10
+
11
+ export class KyselyAgentRunService implements AgentRunService {
12
+ constructor(private db: Kysely<KyselyPikkuDB>) {}
13
+
14
+ async listThreads(options?: {
15
+ agentName?: string
16
+ limit?: number
17
+ offset?: number
18
+ }): Promise<AIThread[]> {
19
+ const { agentName, limit = 50, offset = 0 } = options ?? {}
20
+
21
+ let query = this.db
22
+ .selectFrom('ai_threads as t')
23
+ .select([
24
+ 't.id',
25
+ 't.resource_id',
26
+ 't.title',
27
+ 't.metadata',
28
+ 't.created_at',
29
+ 't.updated_at',
30
+ ])
31
+
32
+ if (agentName) {
33
+ query = query.where(
34
+ 't.id',
35
+ 'in',
36
+ this.db
37
+ .selectFrom('ai_run')
38
+ .select('thread_id')
39
+ .where('agent_name', '=', agentName)
40
+ .distinct()
41
+ )
42
+ }
43
+
44
+ const result = await query
45
+ .orderBy('t.updated_at', 'desc')
46
+ .limit(limit)
47
+ .offset(offset)
48
+ .execute()
49
+
50
+ return result.map((row) => this.mapThreadRow(row))
51
+ }
52
+
53
+ async getThread(threadId: string): Promise<AIThread | null> {
54
+ const row = await this.db
55
+ .selectFrom('ai_threads')
56
+ .select([
57
+ 'id',
58
+ 'resource_id',
59
+ 'title',
60
+ 'metadata',
61
+ 'created_at',
62
+ 'updated_at',
63
+ ])
64
+ .where('id', '=', threadId)
65
+ .executeTakeFirst()
66
+
67
+ if (!row) return null
68
+ return this.mapThreadRow(row)
69
+ }
70
+
71
+ async getThreadMessages(threadId: string): Promise<AIMessage[]> {
72
+ const [msgResult, tcResult] = await Promise.all([
73
+ this.db
74
+ .selectFrom('ai_message')
75
+ .select(['id', 'role', 'content', 'created_at'])
76
+ .where('thread_id', '=', threadId)
77
+ .orderBy('created_at', 'asc')
78
+ .execute(),
79
+ this.db
80
+ .selectFrom('ai_tool_call')
81
+ .select(['id', 'message_id', 'tool_name', 'args', 'result'])
82
+ .where('thread_id', '=', threadId)
83
+ .orderBy('created_at', 'asc')
84
+ .execute(),
85
+ ])
86
+
87
+ const tcByMessage = new Map<string, (typeof tcResult)[number][]>()
88
+ for (const tc of tcResult) {
89
+ const msgId = tc.message_id
90
+ if (!tcByMessage.has(msgId)) tcByMessage.set(msgId, [])
91
+ tcByMessage.get(msgId)!.push(tc)
92
+ }
93
+
94
+ const messages: AIMessage[] = []
95
+ for (const row of msgResult) {
96
+ const msg: AIMessage = {
97
+ id: row.id,
98
+ role: row.role as AIMessage['role'],
99
+ content: row.content ?? undefined,
100
+ createdAt: new Date(row.created_at as unknown as string),
101
+ }
102
+
103
+ const tcs = tcByMessage.get(msg.id)
104
+ if (tcs?.length) {
105
+ msg.toolCalls = tcs.map((tc) => ({
106
+ id: tc.id,
107
+ name: tc.tool_name,
108
+ args: parseJson(tc.args) as Record<string, unknown>,
109
+ }))
110
+
111
+ const completed = tcs.filter((tc) => tc.result != null)
112
+ if (completed.length) {
113
+ messages.push(msg)
114
+ messages.push({
115
+ id: `tool-results-${msg.id}`,
116
+ role: 'tool',
117
+ toolResults: completed.map((tc) => ({
118
+ id: tc.id,
119
+ name: tc.tool_name,
120
+ result: tc.result!,
121
+ })),
122
+ createdAt: msg.createdAt,
123
+ })
124
+ continue
125
+ }
126
+ }
127
+
128
+ messages.push(msg)
129
+ }
130
+
131
+ return messages
132
+ }
133
+
134
+ async getThreadRuns(threadId: string): Promise<AgentRunRow[]> {
135
+ const result = await this.db
136
+ .selectFrom('ai_run')
137
+ .select([
138
+ 'run_id',
139
+ 'agent_name',
140
+ 'thread_id',
141
+ 'resource_id',
142
+ 'status',
143
+ 'suspend_reason',
144
+ 'missing_rpcs',
145
+ 'usage_input_tokens',
146
+ 'usage_output_tokens',
147
+ 'usage_model',
148
+ 'created_at',
149
+ 'updated_at',
150
+ ])
151
+ .where('thread_id', '=', threadId)
152
+ .orderBy('created_at', 'desc')
153
+ .execute()
154
+
155
+ return result.map((row) => this.mapRunRow(row))
156
+ }
157
+
158
+ async deleteThread(threadId: string): Promise<boolean> {
159
+ const result = await this.db
160
+ .deleteFrom('ai_threads')
161
+ .where('id', '=', threadId)
162
+ .executeTakeFirst()
163
+
164
+ return BigInt(result.numDeletedRows) > 0n
165
+ }
166
+
167
+ async getDistinctAgentNames(): Promise<string[]> {
168
+ const result = await this.db
169
+ .selectFrom('ai_run')
170
+ .select('agent_name')
171
+ .distinct()
172
+ .orderBy('agent_name')
173
+ .execute()
174
+
175
+ return result.map((row) => row.agent_name)
176
+ }
177
+
178
+ private mapThreadRow(row: any): AIThread {
179
+ return {
180
+ id: row.id as string,
181
+ resourceId: row.resource_id as string,
182
+ title: (row.title as string) ?? undefined,
183
+ metadata: parseJson(row.metadata),
184
+ createdAt: new Date(row.created_at as string),
185
+ updatedAt: new Date(row.updated_at as string),
186
+ }
187
+ }
188
+
189
+ private mapRunRow(row: any): AgentRunRow {
190
+ return {
191
+ runId: row.run_id as string,
192
+ agentName: row.agent_name as string,
193
+ threadId: row.thread_id as string,
194
+ resourceId: row.resource_id as string,
195
+ status: row.status as string,
196
+ suspendReason: (row.suspend_reason as string) ?? undefined,
197
+ missingRpcs: parseJson(row.missing_rpcs),
198
+ usageInputTokens: Number(row.usage_input_tokens),
199
+ usageOutputTokens: Number(row.usage_output_tokens),
200
+ usageModel: row.usage_model as string,
201
+ createdAt: new Date(row.created_at as string),
202
+ updatedAt: new Date(row.updated_at as string),
203
+ }
204
+ }
205
+ }