@pikku/kysely 0.12.1 → 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
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
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
|
+
|
|
3
13
|
## 0.12.1
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
|
@@ -6,6 +6,7 @@ export declare class KyselyAIStorageService implements AIStorageService, AIRunSt
|
|
|
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) {
|
|
@@ -219,10 +220,26 @@ export class KyselyAIStorageService {
|
|
|
219
220
|
}
|
|
220
221
|
const messages = [];
|
|
221
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
|
+
}
|
|
222
239
|
const msg = {
|
|
223
240
|
id: row.id,
|
|
224
241
|
role: row.role,
|
|
225
|
-
content:
|
|
242
|
+
content: parsedContent,
|
|
226
243
|
createdAt: new Date(row.created_at),
|
|
227
244
|
};
|
|
228
245
|
const tcs = tcByMessage.get(msg.id);
|
|
@@ -264,7 +281,7 @@ export class KyselyAIStorageService {
|
|
|
264
281
|
id: msg.id,
|
|
265
282
|
thread_id: threadId,
|
|
266
283
|
role: msg.role,
|
|
267
|
-
content: msg.content
|
|
284
|
+
content: msg.content != null ? JSON.stringify(msg.content) : null,
|
|
268
285
|
created_at: msg.createdAt ?? new Date(),
|
|
269
286
|
})))
|
|
270
287
|
.execute();
|
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",
|
|
@@ -16,6 +16,18 @@ export class KyselyAIStorageService
|
|
|
16
16
|
|
|
17
17
|
constructor(private db: Kysely<KyselyPikkuDB>) {}
|
|
18
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
|
+
|
|
19
31
|
public async init(): Promise<void> {
|
|
20
32
|
if (this.initialized) {
|
|
21
33
|
return
|
|
@@ -24,8 +36,8 @@ export class KyselyAIStorageService
|
|
|
24
36
|
await this.db.schema
|
|
25
37
|
.createTable('ai_threads')
|
|
26
38
|
.ifNotExists()
|
|
27
|
-
.addColumn('id', '
|
|
28
|
-
.addColumn('resource_id', '
|
|
39
|
+
.addColumn('id', 'varchar(36)', (col) => col.primaryKey())
|
|
40
|
+
.addColumn('resource_id', 'varchar(255)', (col) => col.notNull())
|
|
29
41
|
.addColumn('title', 'text')
|
|
30
42
|
.addColumn('metadata', 'text')
|
|
31
43
|
.addColumn('created_at', 'timestamp', (col) =>
|
|
@@ -36,77 +48,77 @@ export class KyselyAIStorageService
|
|
|
36
48
|
)
|
|
37
49
|
.execute()
|
|
38
50
|
|
|
39
|
-
await this.
|
|
40
|
-
.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
await this.createIndexSafe(
|
|
52
|
+
this.db.schema
|
|
53
|
+
.createIndex('idx_ai_threads_resource')
|
|
54
|
+
.on('ai_threads')
|
|
55
|
+
.column('resource_id')
|
|
56
|
+
)
|
|
45
57
|
|
|
46
58
|
await this.db.schema
|
|
47
59
|
.createTable('ai_message')
|
|
48
60
|
.ifNotExists()
|
|
49
|
-
.addColumn('id', '
|
|
50
|
-
.addColumn('thread_id', '
|
|
61
|
+
.addColumn('id', 'varchar(36)', (col) => col.primaryKey())
|
|
62
|
+
.addColumn('thread_id', 'varchar(36)', (col) =>
|
|
51
63
|
col.notNull().references('ai_threads.id').onDelete('cascade')
|
|
52
64
|
)
|
|
53
|
-
.addColumn('role', '
|
|
65
|
+
.addColumn('role', 'varchar(50)', (col) => col.notNull())
|
|
54
66
|
.addColumn('content', 'text')
|
|
55
67
|
.addColumn('created_at', 'timestamp', (col) =>
|
|
56
68
|
col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
|
|
57
69
|
)
|
|
58
70
|
.execute()
|
|
59
71
|
|
|
60
|
-
await this.
|
|
61
|
-
.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
+
)
|
|
66
78
|
|
|
67
79
|
await this.db.schema
|
|
68
80
|
.createTable('ai_tool_call')
|
|
69
81
|
.ifNotExists()
|
|
70
|
-
.addColumn('id', '
|
|
71
|
-
.addColumn('thread_id', '
|
|
82
|
+
.addColumn('id', 'varchar(36)', (col) => col.primaryKey())
|
|
83
|
+
.addColumn('thread_id', 'varchar(36)', (col) =>
|
|
72
84
|
col.notNull().references('ai_threads.id').onDelete('cascade')
|
|
73
85
|
)
|
|
74
|
-
.addColumn('message_id', '
|
|
86
|
+
.addColumn('message_id', 'varchar(36)', (col) =>
|
|
75
87
|
col.notNull().references('ai_message.id').onDelete('cascade')
|
|
76
88
|
)
|
|
77
|
-
.addColumn('run_id', '
|
|
78
|
-
.addColumn('tool_name', '
|
|
79
|
-
.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())
|
|
80
92
|
.addColumn('result', 'text')
|
|
81
|
-
.addColumn('approval_status', '
|
|
82
|
-
.addColumn('approval_type', '
|
|
83
|
-
.addColumn('agent_run_id', '
|
|
84
|
-
.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)')
|
|
85
97
|
.addColumn('display_args', 'text')
|
|
86
98
|
.addColumn('created_at', 'timestamp', (col) =>
|
|
87
99
|
col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
|
|
88
100
|
)
|
|
89
101
|
.execute()
|
|
90
102
|
|
|
91
|
-
await this.
|
|
92
|
-
.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
+
)
|
|
97
109
|
|
|
98
|
-
await this.
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
+
)
|
|
104
116
|
|
|
105
117
|
await this.db.schema
|
|
106
118
|
.createTable('ai_working_memory')
|
|
107
119
|
.ifNotExists()
|
|
108
|
-
.addColumn('id', '
|
|
109
|
-
.addColumn('scope', '
|
|
120
|
+
.addColumn('id', 'varchar(255)', (col) => col.notNull())
|
|
121
|
+
.addColumn('scope', 'varchar(50)', (col) => col.notNull())
|
|
110
122
|
.addColumn('data', 'text', (col) => col.notNull())
|
|
111
123
|
.addColumn('updated_at', 'timestamp', (col) =>
|
|
112
124
|
col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
|
|
@@ -117,13 +129,15 @@ export class KyselyAIStorageService
|
|
|
117
129
|
await this.db.schema
|
|
118
130
|
.createTable('ai_run')
|
|
119
131
|
.ifNotExists()
|
|
120
|
-
.addColumn('run_id', '
|
|
121
|
-
.addColumn('agent_name', '
|
|
122
|
-
.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) =>
|
|
123
135
|
col.notNull().references('ai_threads.id').onDelete('cascade')
|
|
124
136
|
)
|
|
125
|
-
.addColumn('resource_id', '
|
|
126
|
-
.addColumn('status', '
|
|
137
|
+
.addColumn('resource_id', 'varchar(255)', (col) => col.notNull())
|
|
138
|
+
.addColumn('status', 'varchar(50)', (col) =>
|
|
139
|
+
col.notNull().defaultTo('running')
|
|
140
|
+
)
|
|
127
141
|
.addColumn('suspend_reason', 'text')
|
|
128
142
|
.addColumn('missing_rpcs', 'text')
|
|
129
143
|
.addColumn('usage_input_tokens', 'integer', (col) =>
|
|
@@ -132,7 +146,9 @@ export class KyselyAIStorageService
|
|
|
132
146
|
.addColumn('usage_output_tokens', 'integer', (col) =>
|
|
133
147
|
col.notNull().defaultTo(0)
|
|
134
148
|
)
|
|
135
|
-
.addColumn('usage_model', '
|
|
149
|
+
.addColumn('usage_model', 'varchar(255)', (col) =>
|
|
150
|
+
col.notNull().defaultTo('')
|
|
151
|
+
)
|
|
136
152
|
.addColumn('created_at', 'timestamp', (col) =>
|
|
137
153
|
col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
|
|
138
154
|
)
|
|
@@ -141,12 +157,12 @@ export class KyselyAIStorageService
|
|
|
141
157
|
)
|
|
142
158
|
.execute()
|
|
143
159
|
|
|
144
|
-
await this.
|
|
145
|
-
.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
+
)
|
|
150
166
|
|
|
151
167
|
this.initialized = true
|
|
152
168
|
}
|
|
@@ -290,10 +306,24 @@ export class KyselyAIStorageService
|
|
|
290
306
|
|
|
291
307
|
const messages: AIMessage[] = []
|
|
292
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
|
+
}
|
|
293
323
|
const msg: AIMessage = {
|
|
294
324
|
id: row.id,
|
|
295
325
|
role: row.role as AIMessage['role'],
|
|
296
|
-
content:
|
|
326
|
+
content: parsedContent,
|
|
297
327
|
createdAt: new Date(row.created_at),
|
|
298
328
|
}
|
|
299
329
|
|
|
@@ -342,7 +372,7 @@ export class KyselyAIStorageService
|
|
|
342
372
|
id: msg.id,
|
|
343
373
|
thread_id: threadId,
|
|
344
374
|
role: msg.role,
|
|
345
|
-
content: msg.content
|
|
375
|
+
content: msg.content != null ? JSON.stringify(msg.content) : null,
|
|
346
376
|
created_at: msg.createdAt ?? new Date(),
|
|
347
377
|
}))
|
|
348
378
|
)
|