@pikku/kysely 0.12.0 → 0.12.2
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.
- package/CHANGELOG.md +29 -0
- package/dist/src/kysely-agent-run-service.d.ts +1 -1
- package/dist/src/kysely-ai-storage-service.d.ts +2 -1
- package/dist/src/kysely-ai-storage-service.js +65 -47
- package/dist/src/kysely-channel-store.d.ts +4 -3
- package/dist/src/kysely-deployment-service.d.ts +1 -1
- package/dist/src/kysely-eventhub-store.d.ts +1 -1
- package/dist/src/kysely-workflow-run-service.d.ts +1 -1
- package/dist/src/kysely-workflow-service.d.ts +1 -1
- package/package.json +3 -3
- package/src/kysely-agent-run-service.ts +1 -1
- package/src/kysely-ai-storage-service.ts +90 -58
- package/src/kysely-channel-store.ts +5 -3
- package/src/kysely-deployment-service.ts +2 -1
- package/src/kysely-eventhub-store.ts +1 -1
- package/src/kysely-workflow-run-service.ts +1 -1
- package/src/kysely-workflow-service.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
## 0.12.0
|
|
2
2
|
|
|
3
|
+
## 0.12.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ce961b5: fix: improve MySQL compatibility in AI storage service by using varchar columns with explicit lengths instead of text for primary keys, foreign keys, and indexed columns, and handle duplicate index errors gracefully
|
|
8
|
+
- 3e04565: chore: update dependencies to latest minor/patch versions
|
|
9
|
+
- Updated dependencies [cc4c9e9]
|
|
10
|
+
- Updated dependencies [3e04565]
|
|
11
|
+
- @pikku/core@0.12.2
|
|
12
|
+
|
|
13
|
+
## 0.12.1
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- e04531f: Code quality improvements: resolve oxlint warnings and apply autofixes across the codebase (unused bindings, unnecessary constructors, prefer `const` over `let`, etc.). No behaviour changes.
|
|
18
|
+
- Updated dependencies [62a8725]
|
|
19
|
+
- Updated dependencies [a3bdb0d]
|
|
20
|
+
- Updated dependencies [e0349ff]
|
|
21
|
+
- Updated dependencies [62a8725]
|
|
22
|
+
- Updated dependencies [e04531f]
|
|
23
|
+
- Updated dependencies [62a8725]
|
|
24
|
+
- Updated dependencies [a83efb8]
|
|
25
|
+
- Updated dependencies [8eed717]
|
|
26
|
+
- Updated dependencies [62a8725]
|
|
27
|
+
- Updated dependencies [62a8725]
|
|
28
|
+
- Updated dependencies [62a8725]
|
|
29
|
+
|
|
30
|
+
- @pikku/core@0.12.1
|
|
31
|
+
|
|
3
32
|
- Updated dependencies
|
|
4
33
|
|
|
5
34
|
## 0.11.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AIThread, AIMessage, AgentRunRow, AgentRunService } from '@pikku/core/ai-agent';
|
|
2
|
-
import { Kysely } from 'kysely';
|
|
2
|
+
import type { Kysely } from 'kysely';
|
|
3
3
|
import type { KyselyPikkuDB } from './kysely-tables.js';
|
|
4
4
|
export declare class KyselyAgentRunService implements AgentRunService {
|
|
5
5
|
private db;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { AIStorageService, AIRunStateService, CreateRunInput } from '@pikku/core/services';
|
|
2
2
|
import type { AIThread, AIMessage, AgentRunState } from '@pikku/core/ai-agent';
|
|
3
|
-
import { Kysely } from 'kysely';
|
|
3
|
+
import type { Kysely } from 'kysely';
|
|
4
4
|
import type { KyselyPikkuDB } from './kysely-tables.js';
|
|
5
5
|
export declare class KyselyAIStorageService implements AIStorageService, AIRunStateService {
|
|
6
6
|
private db;
|
|
7
7
|
private initialized;
|
|
8
8
|
constructor(db: Kysely<KyselyPikkuDB>);
|
|
9
|
+
private createIndexSafe;
|
|
9
10
|
init(): Promise<void>;
|
|
10
11
|
createThread(resourceId: string, options?: {
|
|
11
12
|
threadId?: string;
|
|
@@ -6,6 +6,17 @@ export class KyselyAIStorageService {
|
|
|
6
6
|
constructor(db) {
|
|
7
7
|
this.db = db;
|
|
8
8
|
}
|
|
9
|
+
async createIndexSafe(builder) {
|
|
10
|
+
try {
|
|
11
|
+
await builder.execute();
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
// Ignore "index already exists" errors (MySQL doesn't support IF NOT EXISTS for indexes)
|
|
15
|
+
if (e?.code === 'ER_DUP_KEYNAME' || e?.errno === 1061)
|
|
16
|
+
return;
|
|
17
|
+
throw e;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
9
20
|
async init() {
|
|
10
21
|
if (this.initialized) {
|
|
11
22
|
return;
|
|
@@ -13,68 +24,60 @@ export class KyselyAIStorageService {
|
|
|
13
24
|
await this.db.schema
|
|
14
25
|
.createTable('ai_threads')
|
|
15
26
|
.ifNotExists()
|
|
16
|
-
.addColumn('id', '
|
|
17
|
-
.addColumn('resource_id', '
|
|
27
|
+
.addColumn('id', 'varchar(36)', (col) => col.primaryKey())
|
|
28
|
+
.addColumn('resource_id', 'varchar(255)', (col) => col.notNull())
|
|
18
29
|
.addColumn('title', 'text')
|
|
19
30
|
.addColumn('metadata', 'text')
|
|
20
31
|
.addColumn('created_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
|
|
21
32
|
.addColumn('updated_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
|
|
22
33
|
.execute();
|
|
23
|
-
await this.db.schema
|
|
34
|
+
await this.createIndexSafe(this.db.schema
|
|
24
35
|
.createIndex('idx_ai_threads_resource')
|
|
25
|
-
.ifNotExists()
|
|
26
36
|
.on('ai_threads')
|
|
27
|
-
.column('resource_id')
|
|
28
|
-
.execute();
|
|
37
|
+
.column('resource_id'));
|
|
29
38
|
await this.db.schema
|
|
30
39
|
.createTable('ai_message')
|
|
31
40
|
.ifNotExists()
|
|
32
|
-
.addColumn('id', '
|
|
33
|
-
.addColumn('thread_id', '
|
|
34
|
-
.addColumn('role', '
|
|
41
|
+
.addColumn('id', 'varchar(36)', (col) => col.primaryKey())
|
|
42
|
+
.addColumn('thread_id', 'varchar(36)', (col) => col.notNull().references('ai_threads.id').onDelete('cascade'))
|
|
43
|
+
.addColumn('role', 'varchar(50)', (col) => col.notNull())
|
|
35
44
|
.addColumn('content', 'text')
|
|
36
45
|
.addColumn('created_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
|
|
37
46
|
.execute();
|
|
38
|
-
await this.db.schema
|
|
47
|
+
await this.createIndexSafe(this.db.schema
|
|
39
48
|
.createIndex('idx_ai_message_thread')
|
|
40
|
-
.ifNotExists()
|
|
41
49
|
.on('ai_message')
|
|
42
|
-
.columns(['thread_id', 'created_at'])
|
|
43
|
-
.execute();
|
|
50
|
+
.columns(['thread_id', 'created_at']));
|
|
44
51
|
await this.db.schema
|
|
45
52
|
.createTable('ai_tool_call')
|
|
46
53
|
.ifNotExists()
|
|
47
|
-
.addColumn('id', '
|
|
48
|
-
.addColumn('thread_id', '
|
|
49
|
-
.addColumn('message_id', '
|
|
50
|
-
.addColumn('run_id', '
|
|
51
|
-
.addColumn('tool_name', '
|
|
52
|
-
.addColumn('args', 'text', (col) => col.notNull()
|
|
54
|
+
.addColumn('id', 'varchar(36)', (col) => col.primaryKey())
|
|
55
|
+
.addColumn('thread_id', 'varchar(36)', (col) => col.notNull().references('ai_threads.id').onDelete('cascade'))
|
|
56
|
+
.addColumn('message_id', 'varchar(36)', (col) => col.notNull().references('ai_message.id').onDelete('cascade'))
|
|
57
|
+
.addColumn('run_id', 'varchar(36)')
|
|
58
|
+
.addColumn('tool_name', 'varchar(255)', (col) => col.notNull())
|
|
59
|
+
.addColumn('args', 'text', (col) => col.notNull())
|
|
53
60
|
.addColumn('result', 'text')
|
|
54
|
-
.addColumn('approval_status', '
|
|
55
|
-
.addColumn('approval_type', '
|
|
56
|
-
.addColumn('agent_run_id', '
|
|
57
|
-
.addColumn('display_tool_name', '
|
|
61
|
+
.addColumn('approval_status', 'varchar(50)')
|
|
62
|
+
.addColumn('approval_type', 'varchar(50)')
|
|
63
|
+
.addColumn('agent_run_id', 'varchar(36)')
|
|
64
|
+
.addColumn('display_tool_name', 'varchar(255)')
|
|
58
65
|
.addColumn('display_args', 'text')
|
|
59
66
|
.addColumn('created_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
|
|
60
67
|
.execute();
|
|
61
|
-
await this.db.schema
|
|
68
|
+
await this.createIndexSafe(this.db.schema
|
|
62
69
|
.createIndex('idx_ai_tool_call_thread')
|
|
63
|
-
.ifNotExists()
|
|
64
70
|
.on('ai_tool_call')
|
|
65
|
-
.column('thread_id')
|
|
66
|
-
|
|
67
|
-
await this.db.schema
|
|
71
|
+
.column('thread_id'));
|
|
72
|
+
await this.createIndexSafe(this.db.schema
|
|
68
73
|
.createIndex('idx_ai_tool_call_message')
|
|
69
|
-
.ifNotExists()
|
|
70
74
|
.on('ai_tool_call')
|
|
71
|
-
.column('message_id')
|
|
72
|
-
.execute();
|
|
75
|
+
.column('message_id'));
|
|
73
76
|
await this.db.schema
|
|
74
77
|
.createTable('ai_working_memory')
|
|
75
78
|
.ifNotExists()
|
|
76
|
-
.addColumn('id', '
|
|
77
|
-
.addColumn('scope', '
|
|
79
|
+
.addColumn('id', 'varchar(255)', (col) => col.notNull())
|
|
80
|
+
.addColumn('scope', 'varchar(50)', (col) => col.notNull())
|
|
78
81
|
.addColumn('data', 'text', (col) => col.notNull())
|
|
79
82
|
.addColumn('updated_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
|
|
80
83
|
.addPrimaryKeyConstraint('ai_working_memory_pk', ['id', 'scope'])
|
|
@@ -82,25 +85,23 @@ export class KyselyAIStorageService {
|
|
|
82
85
|
await this.db.schema
|
|
83
86
|
.createTable('ai_run')
|
|
84
87
|
.ifNotExists()
|
|
85
|
-
.addColumn('run_id', '
|
|
86
|
-
.addColumn('agent_name', '
|
|
87
|
-
.addColumn('thread_id', '
|
|
88
|
-
.addColumn('resource_id', '
|
|
89
|
-
.addColumn('status', '
|
|
88
|
+
.addColumn('run_id', 'varchar(36)', (col) => col.primaryKey())
|
|
89
|
+
.addColumn('agent_name', 'varchar(255)', (col) => col.notNull())
|
|
90
|
+
.addColumn('thread_id', 'varchar(36)', (col) => col.notNull().references('ai_threads.id').onDelete('cascade'))
|
|
91
|
+
.addColumn('resource_id', 'varchar(255)', (col) => col.notNull())
|
|
92
|
+
.addColumn('status', 'varchar(50)', (col) => col.notNull().defaultTo('running'))
|
|
90
93
|
.addColumn('suspend_reason', 'text')
|
|
91
94
|
.addColumn('missing_rpcs', 'text')
|
|
92
95
|
.addColumn('usage_input_tokens', 'integer', (col) => col.notNull().defaultTo(0))
|
|
93
96
|
.addColumn('usage_output_tokens', 'integer', (col) => col.notNull().defaultTo(0))
|
|
94
|
-
.addColumn('usage_model', '
|
|
97
|
+
.addColumn('usage_model', 'varchar(255)', (col) => col.notNull().defaultTo(''))
|
|
95
98
|
.addColumn('created_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
|
|
96
99
|
.addColumn('updated_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
|
|
97
100
|
.execute();
|
|
98
|
-
await this.db.schema
|
|
101
|
+
await this.createIndexSafe(this.db.schema
|
|
99
102
|
.createIndex('idx_ai_run_thread')
|
|
100
|
-
.ifNotExists()
|
|
101
103
|
.on('ai_run')
|
|
102
|
-
.columns(['thread_id', 'created_at'])
|
|
103
|
-
.execute();
|
|
104
|
+
.columns(['thread_id', 'created_at']));
|
|
104
105
|
this.initialized = true;
|
|
105
106
|
}
|
|
106
107
|
async createThread(resourceId, options) {
|
|
@@ -192,16 +193,17 @@ export class KyselyAIStorageService {
|
|
|
192
193
|
msgQuery = msgQuery.where('created_at', '<', cursorRow.created_at);
|
|
193
194
|
}
|
|
194
195
|
}
|
|
196
|
+
let msgResult;
|
|
195
197
|
if (options?.cursor || options?.lastN) {
|
|
196
198
|
const innerResult = await msgQuery
|
|
197
199
|
.orderBy('created_at', 'desc')
|
|
198
200
|
.limit(options?.lastN ?? 50)
|
|
199
201
|
.execute();
|
|
200
202
|
innerResult.reverse();
|
|
201
|
-
|
|
203
|
+
msgResult = innerResult;
|
|
202
204
|
}
|
|
203
205
|
else {
|
|
204
|
-
|
|
206
|
+
msgResult = await msgQuery.orderBy('created_at', 'asc').execute();
|
|
205
207
|
}
|
|
206
208
|
const tcResult = await this.db
|
|
207
209
|
.selectFrom('ai_tool_call')
|
|
@@ -218,10 +220,26 @@ export class KyselyAIStorageService {
|
|
|
218
220
|
}
|
|
219
221
|
const messages = [];
|
|
220
222
|
for (const row of msgResult) {
|
|
223
|
+
const rawContent = row.content;
|
|
224
|
+
let parsedContent = rawContent ?? undefined;
|
|
225
|
+
if (rawContent) {
|
|
226
|
+
try {
|
|
227
|
+
const parsed = JSON.parse(rawContent);
|
|
228
|
+
if (Array.isArray(parsed)) {
|
|
229
|
+
parsedContent = parsed;
|
|
230
|
+
}
|
|
231
|
+
else if (typeof parsed === 'string') {
|
|
232
|
+
parsedContent = parsed;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
// Not JSON, use raw string
|
|
237
|
+
}
|
|
238
|
+
}
|
|
221
239
|
const msg = {
|
|
222
240
|
id: row.id,
|
|
223
241
|
role: row.role,
|
|
224
|
-
content:
|
|
242
|
+
content: parsedContent,
|
|
225
243
|
createdAt: new Date(row.created_at),
|
|
226
244
|
};
|
|
227
245
|
const tcs = tcByMessage.get(msg.id);
|
|
@@ -263,7 +281,7 @@ export class KyselyAIStorageService {
|
|
|
263
281
|
id: msg.id,
|
|
264
282
|
thread_id: threadId,
|
|
265
283
|
role: msg.role,
|
|
266
|
-
content: msg.content
|
|
284
|
+
content: msg.content != null ? JSON.stringify(msg.content) : null,
|
|
267
285
|
created_at: msg.createdAt ?? new Date(),
|
|
268
286
|
})))
|
|
269
287
|
.execute();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { CoreUserSession } from '@pikku/core';
|
|
2
|
-
import { Channel
|
|
3
|
-
import {
|
|
1
|
+
import type { CoreUserSession } from '@pikku/core';
|
|
2
|
+
import type { Channel } from '@pikku/core/channel';
|
|
3
|
+
import { ChannelStore } from '@pikku/core/channel';
|
|
4
|
+
import type { Kysely } from 'kysely';
|
|
4
5
|
import type { KyselyPikkuDB } from './kysely-tables.js';
|
|
5
6
|
export declare class KyselyChannelStore extends ChannelStore {
|
|
6
7
|
private db;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DeploymentService, DeploymentServiceConfig, DeploymentConfig, DeploymentInfo } from '@pikku/core/services';
|
|
2
|
-
import { Kysely } from 'kysely';
|
|
2
|
+
import type { Kysely } from 'kysely';
|
|
3
3
|
import type { KyselyPikkuDB } from './kysely-tables.js';
|
|
4
4
|
export declare class KyselyDeploymentService implements DeploymentService {
|
|
5
5
|
private db;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WorkflowRun, StepState, WorkflowRunService } from '@pikku/core/workflow';
|
|
2
|
-
import { Kysely } from 'kysely';
|
|
2
|
+
import type { Kysely } from 'kysely';
|
|
3
3
|
import type { KyselyPikkuDB } from './kysely-tables.js';
|
|
4
4
|
export declare class KyselyWorkflowRunService implements WorkflowRunService {
|
|
5
5
|
private db;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SerializedError } from '@pikku/core';
|
|
2
2
|
import { PikkuWorkflowService, type WorkflowRun, type WorkflowRunWire, type StepState, type WorkflowStatus } from '@pikku/core/workflow';
|
|
3
|
-
import { Kysely } from 'kysely';
|
|
3
|
+
import type { Kysely } from 'kysely';
|
|
4
4
|
import type { KyselyPikkuDB } from './kysely-tables.js';
|
|
5
5
|
export declare class KyselyWorkflowService extends PikkuWorkflowService {
|
|
6
6
|
private db;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/kysely",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"module": "dist/src/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"release": "npm run build && npm test"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"@pikku/core": "^0.12.
|
|
22
|
+
"@pikku/core": "^0.12.2"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"kysely": "^0.28.11",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@types/better-sqlite3": "^7.6.13",
|
|
31
31
|
"better-sqlite3": "^12.6.2",
|
|
32
32
|
"kysely": "^0.28.11",
|
|
33
|
-
"kysely-codegen": "^0.
|
|
33
|
+
"kysely-codegen": "^0.20.0",
|
|
34
34
|
"kysely-plugin-serialize": "^0.8.2",
|
|
35
35
|
"kysely-postgres-js": "^3.0.0",
|
|
36
36
|
"postgres": "^3.4.8",
|
|
@@ -4,7 +4,8 @@ import type {
|
|
|
4
4
|
CreateRunInput,
|
|
5
5
|
} from '@pikku/core/services'
|
|
6
6
|
import type { AIThread, AIMessage, AgentRunState } from '@pikku/core/ai-agent'
|
|
7
|
-
import { Kysely
|
|
7
|
+
import type { Kysely } from 'kysely'
|
|
8
|
+
import { sql } from 'kysely'
|
|
8
9
|
import type { KyselyPikkuDB } from './kysely-tables.js'
|
|
9
10
|
import { parseJson } from './kysely-json.js'
|
|
10
11
|
|
|
@@ -15,6 +16,18 @@ export class KyselyAIStorageService
|
|
|
15
16
|
|
|
16
17
|
constructor(private db: Kysely<KyselyPikkuDB>) {}
|
|
17
18
|
|
|
19
|
+
private async createIndexSafe(builder: {
|
|
20
|
+
execute(): Promise<void>
|
|
21
|
+
}): Promise<void> {
|
|
22
|
+
try {
|
|
23
|
+
await builder.execute()
|
|
24
|
+
} catch (e: any) {
|
|
25
|
+
// Ignore "index already exists" errors (MySQL doesn't support IF NOT EXISTS for indexes)
|
|
26
|
+
if (e?.code === 'ER_DUP_KEYNAME' || e?.errno === 1061) return
|
|
27
|
+
throw e
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
public async init(): Promise<void> {
|
|
19
32
|
if (this.initialized) {
|
|
20
33
|
return
|
|
@@ -23,8 +36,8 @@ export class KyselyAIStorageService
|
|
|
23
36
|
await this.db.schema
|
|
24
37
|
.createTable('ai_threads')
|
|
25
38
|
.ifNotExists()
|
|
26
|
-
.addColumn('id', '
|
|
27
|
-
.addColumn('resource_id', '
|
|
39
|
+
.addColumn('id', 'varchar(36)', (col) => col.primaryKey())
|
|
40
|
+
.addColumn('resource_id', 'varchar(255)', (col) => col.notNull())
|
|
28
41
|
.addColumn('title', 'text')
|
|
29
42
|
.addColumn('metadata', 'text')
|
|
30
43
|
.addColumn('created_at', 'timestamp', (col) =>
|
|
@@ -35,77 +48,77 @@ export class KyselyAIStorageService
|
|
|
35
48
|
)
|
|
36
49
|
.execute()
|
|
37
50
|
|
|
38
|
-
await this.
|
|
39
|
-
.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
await this.createIndexSafe(
|
|
52
|
+
this.db.schema
|
|
53
|
+
.createIndex('idx_ai_threads_resource')
|
|
54
|
+
.on('ai_threads')
|
|
55
|
+
.column('resource_id')
|
|
56
|
+
)
|
|
44
57
|
|
|
45
58
|
await this.db.schema
|
|
46
59
|
.createTable('ai_message')
|
|
47
60
|
.ifNotExists()
|
|
48
|
-
.addColumn('id', '
|
|
49
|
-
.addColumn('thread_id', '
|
|
61
|
+
.addColumn('id', 'varchar(36)', (col) => col.primaryKey())
|
|
62
|
+
.addColumn('thread_id', 'varchar(36)', (col) =>
|
|
50
63
|
col.notNull().references('ai_threads.id').onDelete('cascade')
|
|
51
64
|
)
|
|
52
|
-
.addColumn('role', '
|
|
65
|
+
.addColumn('role', 'varchar(50)', (col) => col.notNull())
|
|
53
66
|
.addColumn('content', 'text')
|
|
54
67
|
.addColumn('created_at', 'timestamp', (col) =>
|
|
55
68
|
col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
|
|
56
69
|
)
|
|
57
70
|
.execute()
|
|
58
71
|
|
|
59
|
-
await this.
|
|
60
|
-
.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
await this.createIndexSafe(
|
|
73
|
+
this.db.schema
|
|
74
|
+
.createIndex('idx_ai_message_thread')
|
|
75
|
+
.on('ai_message')
|
|
76
|
+
.columns(['thread_id', 'created_at'])
|
|
77
|
+
)
|
|
65
78
|
|
|
66
79
|
await this.db.schema
|
|
67
80
|
.createTable('ai_tool_call')
|
|
68
81
|
.ifNotExists()
|
|
69
|
-
.addColumn('id', '
|
|
70
|
-
.addColumn('thread_id', '
|
|
82
|
+
.addColumn('id', 'varchar(36)', (col) => col.primaryKey())
|
|
83
|
+
.addColumn('thread_id', 'varchar(36)', (col) =>
|
|
71
84
|
col.notNull().references('ai_threads.id').onDelete('cascade')
|
|
72
85
|
)
|
|
73
|
-
.addColumn('message_id', '
|
|
86
|
+
.addColumn('message_id', 'varchar(36)', (col) =>
|
|
74
87
|
col.notNull().references('ai_message.id').onDelete('cascade')
|
|
75
88
|
)
|
|
76
|
-
.addColumn('run_id', '
|
|
77
|
-
.addColumn('tool_name', '
|
|
78
|
-
.addColumn('args', 'text', (col) => col.notNull()
|
|
89
|
+
.addColumn('run_id', 'varchar(36)')
|
|
90
|
+
.addColumn('tool_name', 'varchar(255)', (col) => col.notNull())
|
|
91
|
+
.addColumn('args', 'text', (col) => col.notNull())
|
|
79
92
|
.addColumn('result', 'text')
|
|
80
|
-
.addColumn('approval_status', '
|
|
81
|
-
.addColumn('approval_type', '
|
|
82
|
-
.addColumn('agent_run_id', '
|
|
83
|
-
.addColumn('display_tool_name', '
|
|
93
|
+
.addColumn('approval_status', 'varchar(50)')
|
|
94
|
+
.addColumn('approval_type', 'varchar(50)')
|
|
95
|
+
.addColumn('agent_run_id', 'varchar(36)')
|
|
96
|
+
.addColumn('display_tool_name', 'varchar(255)')
|
|
84
97
|
.addColumn('display_args', 'text')
|
|
85
98
|
.addColumn('created_at', 'timestamp', (col) =>
|
|
86
99
|
col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
|
|
87
100
|
)
|
|
88
101
|
.execute()
|
|
89
102
|
|
|
90
|
-
await this.
|
|
91
|
-
.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
await this.createIndexSafe(
|
|
104
|
+
this.db.schema
|
|
105
|
+
.createIndex('idx_ai_tool_call_thread')
|
|
106
|
+
.on('ai_tool_call')
|
|
107
|
+
.column('thread_id')
|
|
108
|
+
)
|
|
96
109
|
|
|
97
|
-
await this.
|
|
98
|
-
.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
await this.createIndexSafe(
|
|
111
|
+
this.db.schema
|
|
112
|
+
.createIndex('idx_ai_tool_call_message')
|
|
113
|
+
.on('ai_tool_call')
|
|
114
|
+
.column('message_id')
|
|
115
|
+
)
|
|
103
116
|
|
|
104
117
|
await this.db.schema
|
|
105
118
|
.createTable('ai_working_memory')
|
|
106
119
|
.ifNotExists()
|
|
107
|
-
.addColumn('id', '
|
|
108
|
-
.addColumn('scope', '
|
|
120
|
+
.addColumn('id', 'varchar(255)', (col) => col.notNull())
|
|
121
|
+
.addColumn('scope', 'varchar(50)', (col) => col.notNull())
|
|
109
122
|
.addColumn('data', 'text', (col) => col.notNull())
|
|
110
123
|
.addColumn('updated_at', 'timestamp', (col) =>
|
|
111
124
|
col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
|
|
@@ -116,13 +129,15 @@ export class KyselyAIStorageService
|
|
|
116
129
|
await this.db.schema
|
|
117
130
|
.createTable('ai_run')
|
|
118
131
|
.ifNotExists()
|
|
119
|
-
.addColumn('run_id', '
|
|
120
|
-
.addColumn('agent_name', '
|
|
121
|
-
.addColumn('thread_id', '
|
|
132
|
+
.addColumn('run_id', 'varchar(36)', (col) => col.primaryKey())
|
|
133
|
+
.addColumn('agent_name', 'varchar(255)', (col) => col.notNull())
|
|
134
|
+
.addColumn('thread_id', 'varchar(36)', (col) =>
|
|
122
135
|
col.notNull().references('ai_threads.id').onDelete('cascade')
|
|
123
136
|
)
|
|
124
|
-
.addColumn('resource_id', '
|
|
125
|
-
.addColumn('status', '
|
|
137
|
+
.addColumn('resource_id', 'varchar(255)', (col) => col.notNull())
|
|
138
|
+
.addColumn('status', 'varchar(50)', (col) =>
|
|
139
|
+
col.notNull().defaultTo('running')
|
|
140
|
+
)
|
|
126
141
|
.addColumn('suspend_reason', 'text')
|
|
127
142
|
.addColumn('missing_rpcs', 'text')
|
|
128
143
|
.addColumn('usage_input_tokens', 'integer', (col) =>
|
|
@@ -131,7 +146,9 @@ export class KyselyAIStorageService
|
|
|
131
146
|
.addColumn('usage_output_tokens', 'integer', (col) =>
|
|
132
147
|
col.notNull().defaultTo(0)
|
|
133
148
|
)
|
|
134
|
-
.addColumn('usage_model', '
|
|
149
|
+
.addColumn('usage_model', 'varchar(255)', (col) =>
|
|
150
|
+
col.notNull().defaultTo('')
|
|
151
|
+
)
|
|
135
152
|
.addColumn('created_at', 'timestamp', (col) =>
|
|
136
153
|
col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
|
|
137
154
|
)
|
|
@@ -140,12 +157,12 @@ export class KyselyAIStorageService
|
|
|
140
157
|
)
|
|
141
158
|
.execute()
|
|
142
159
|
|
|
143
|
-
await this.
|
|
144
|
-
.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
160
|
+
await this.createIndexSafe(
|
|
161
|
+
this.db.schema
|
|
162
|
+
.createIndex('idx_ai_run_thread')
|
|
163
|
+
.on('ai_run')
|
|
164
|
+
.columns(['thread_id', 'created_at'])
|
|
165
|
+
)
|
|
149
166
|
|
|
150
167
|
this.initialized = true
|
|
151
168
|
}
|
|
@@ -261,15 +278,16 @@ export class KyselyAIStorageService
|
|
|
261
278
|
}
|
|
262
279
|
}
|
|
263
280
|
|
|
281
|
+
let msgResult
|
|
264
282
|
if (options?.cursor || options?.lastN) {
|
|
265
283
|
const innerResult = await msgQuery
|
|
266
284
|
.orderBy('created_at', 'desc')
|
|
267
285
|
.limit(options?.lastN ?? 50)
|
|
268
286
|
.execute()
|
|
269
287
|
innerResult.reverse()
|
|
270
|
-
|
|
288
|
+
msgResult = innerResult
|
|
271
289
|
} else {
|
|
272
|
-
|
|
290
|
+
msgResult = await msgQuery.orderBy('created_at', 'asc').execute()
|
|
273
291
|
}
|
|
274
292
|
|
|
275
293
|
const tcResult = await this.db
|
|
@@ -288,10 +306,24 @@ export class KyselyAIStorageService
|
|
|
288
306
|
|
|
289
307
|
const messages: AIMessage[] = []
|
|
290
308
|
for (const row of msgResult) {
|
|
309
|
+
const rawContent = row.content as string | undefined
|
|
310
|
+
let parsedContent: AIMessage['content'] = rawContent ?? undefined
|
|
311
|
+
if (rawContent) {
|
|
312
|
+
try {
|
|
313
|
+
const parsed = JSON.parse(rawContent)
|
|
314
|
+
if (Array.isArray(parsed)) {
|
|
315
|
+
parsedContent = parsed
|
|
316
|
+
} else if (typeof parsed === 'string') {
|
|
317
|
+
parsedContent = parsed
|
|
318
|
+
}
|
|
319
|
+
} catch {
|
|
320
|
+
// Not JSON, use raw string
|
|
321
|
+
}
|
|
322
|
+
}
|
|
291
323
|
const msg: AIMessage = {
|
|
292
324
|
id: row.id,
|
|
293
325
|
role: row.role as AIMessage['role'],
|
|
294
|
-
content:
|
|
326
|
+
content: parsedContent,
|
|
295
327
|
createdAt: new Date(row.created_at),
|
|
296
328
|
}
|
|
297
329
|
|
|
@@ -340,7 +372,7 @@ export class KyselyAIStorageService
|
|
|
340
372
|
id: msg.id,
|
|
341
373
|
thread_id: threadId,
|
|
342
374
|
role: msg.role,
|
|
343
|
-
content: msg.content
|
|
375
|
+
content: msg.content != null ? JSON.stringify(msg.content) : null,
|
|
344
376
|
created_at: msg.createdAt ?? new Date(),
|
|
345
377
|
}))
|
|
346
378
|
)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { CoreUserSession } from '@pikku/core'
|
|
2
|
-
import { Channel
|
|
3
|
-
import {
|
|
1
|
+
import type { CoreUserSession } from '@pikku/core'
|
|
2
|
+
import type { Channel } from '@pikku/core/channel'
|
|
3
|
+
import { ChannelStore } from '@pikku/core/channel'
|
|
4
|
+
import type { Kysely } from 'kysely'
|
|
5
|
+
import { sql } from 'kysely'
|
|
4
6
|
import type { KyselyPikkuDB } from './kysely-tables.js'
|
|
5
7
|
import { parseJson } from './kysely-json.js'
|
|
6
8
|
|
|
@@ -5,7 +5,8 @@ import type {
|
|
|
5
5
|
DeploymentInfo,
|
|
6
6
|
} from '@pikku/core/services'
|
|
7
7
|
import { getAllFunctionNames } from '@pikku/core/function'
|
|
8
|
-
import { Kysely
|
|
8
|
+
import type { Kysely } from 'kysely'
|
|
9
|
+
import { sql } from 'kysely'
|
|
9
10
|
import type { KyselyPikkuDB } from './kysely-tables.js'
|
|
10
11
|
|
|
11
12
|
export class KyselyDeploymentService implements DeploymentService {
|
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
type StepState,
|
|
7
7
|
type WorkflowStatus,
|
|
8
8
|
} from '@pikku/core/workflow'
|
|
9
|
-
import { Kysely
|
|
9
|
+
import type { Kysely } from 'kysely'
|
|
10
|
+
import { sql } from 'kysely'
|
|
10
11
|
import type { KyselyPikkuDB } from './kysely-tables.js'
|
|
11
12
|
import { KyselyWorkflowRunService } from './kysely-workflow-run-service.js'
|
|
12
13
|
import { parseJson } from './kysely-json.js'
|