@jskit-ai/assistant 0.1.38 → 0.1.39
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/package.descriptor.mjs +84 -319
- package/package.json +2 -2
- package/src/server/buildTemplateContext.js +39 -14
- package/templates/src/pages/assistant/index.vue +2 -2
- package/templates/src/pages/settings/assistant/index.vue +7 -0
- package/test/buildTemplateContext.test.js +42 -27
- package/test/packageDescriptor.test.js +26 -55
- package/test/templateContracts.test.js +8 -6
- package/templates/migrations/assistant_config_initial.cjs +0 -25
- package/templates/migrations/assistant_transcripts_initial.cjs +0 -56
- package/templates/src/local-package/client/components/AssistantSettingsClientElement.vue +0 -88
- package/templates/src/local-package/client/components/AssistantSurfaceClientElement.vue +0 -10
- package/templates/src/local-package/client/composables/useAssistantRuntime.js +0 -754
- package/templates/src/local-package/client/index.js +0 -4
- package/templates/src/local-package/client/providers/AssistantClientProvider.js +0 -16
- package/templates/src/local-package/package.descriptor.mjs +0 -85
- package/templates/src/local-package/package.json +0 -11
- package/templates/src/local-package/server/AssistantProvider.js +0 -143
- package/templates/src/local-package/server/actionIds.js +0 -9
- package/templates/src/local-package/server/actions.js +0 -183
- package/templates/src/local-package/server/registerRoutes.js +0 -296
- package/templates/src/local-package/server/repositories/assistantConfigRepository.js +0 -141
- package/templates/src/local-package/server/repositories/conversationsRepository.js +0 -240
- package/templates/src/local-package/server/repositories/messagesRepository.js +0 -166
- package/templates/src/local-package/server/services/assistantConfigService.js +0 -90
- package/templates/src/local-package/server/services/chatService.js +0 -995
- package/templates/src/local-package/server/services/transcriptService.js +0 -308
- package/templates/src/local-package/shared/assistantRuntimeConfig.js +0 -13
- package/templates/src/local-package/shared/index.js +0 -1
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
import { withStandardErrorResponses } from "@jskit-ai/http-runtime/shared/validators/errorResponses";
|
|
2
|
-
import { buildWorkspaceInputFromRouteParams } from "@jskit-ai/users-core/server/support/workspaceRouteInput";
|
|
3
|
-
import { workspaceSlugParamsValidator } from "@jskit-ai/users-core/server/validators/routeParamsValidator";
|
|
4
|
-
import {
|
|
5
|
-
assistantConfigResource,
|
|
6
|
-
assistantResource,
|
|
7
|
-
resolveAssistantApiBasePath,
|
|
8
|
-
resolveAssistantSettingsApiPath
|
|
9
|
-
} from "@jskit-ai/assistant-core/shared";
|
|
10
|
-
import {
|
|
11
|
-
endNdjson,
|
|
12
|
-
mapStreamError,
|
|
13
|
-
setNdjsonHeaders,
|
|
14
|
-
writeNdjson
|
|
15
|
-
} from "@jskit-ai/assistant-core/server";
|
|
16
|
-
import { assistantRuntimeConfig } from "../shared/assistantRuntimeConfig.js";
|
|
17
|
-
import { actionIds } from "./actionIds.js";
|
|
18
|
-
|
|
19
|
-
const runtimeVisibility = assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace ? "workspace" : "public";
|
|
20
|
-
const settingsVisibility = assistantRuntimeConfig.settingsSurfaceRequiresWorkspace ? "workspace" : "public";
|
|
21
|
-
const runtimeRouteBase = resolveAssistantApiBasePath({
|
|
22
|
-
requiresWorkspace: assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace
|
|
23
|
-
});
|
|
24
|
-
const settingsRouteBase = resolveAssistantSettingsApiPath({
|
|
25
|
-
requiresWorkspace: assistantRuntimeConfig.settingsSurfaceRequiresWorkspace
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
function buildWorkspaceRouteConfig(requiresWorkspace, baseConfig = {}) {
|
|
29
|
-
if (requiresWorkspace !== true) {
|
|
30
|
-
return baseConfig;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
...baseConfig,
|
|
35
|
-
paramsValidator: workspaceSlugParamsValidator
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function readWorkspaceInput(request, requiresWorkspace) {
|
|
40
|
-
if (requiresWorkspace !== true) {
|
|
41
|
-
return {};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return buildWorkspaceInputFromRouteParams(request?.input?.params);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function registerRoutes(app) {
|
|
48
|
-
if (!app || typeof app.make !== "function") {
|
|
49
|
-
throw new Error("registerRoutes requires application make().");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const router = app.make("jskit.http.router");
|
|
53
|
-
|
|
54
|
-
router.register(
|
|
55
|
-
"GET",
|
|
56
|
-
settingsRouteBase,
|
|
57
|
-
buildWorkspaceRouteConfig(assistantRuntimeConfig.settingsSurfaceRequiresWorkspace, {
|
|
58
|
-
auth: "required",
|
|
59
|
-
surface: assistantRuntimeConfig.settingsSurfaceId,
|
|
60
|
-
visibility: settingsVisibility,
|
|
61
|
-
meta: {
|
|
62
|
-
tags: ["assistant", "settings"],
|
|
63
|
-
summary: "Get assistant settings."
|
|
64
|
-
},
|
|
65
|
-
responseValidators: withStandardErrorResponses({
|
|
66
|
-
200: assistantConfigResource.operations.view.outputValidator
|
|
67
|
-
})
|
|
68
|
-
}),
|
|
69
|
-
async function assistantSettingsReadRoute(request, reply) {
|
|
70
|
-
const response = await request.executeAction({
|
|
71
|
-
actionId: actionIds.settingsRead,
|
|
72
|
-
input: {
|
|
73
|
-
...readWorkspaceInput(request, assistantRuntimeConfig.settingsSurfaceRequiresWorkspace)
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
reply.code(200).send(response);
|
|
78
|
-
}
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
router.register(
|
|
82
|
-
"PATCH",
|
|
83
|
-
settingsRouteBase,
|
|
84
|
-
buildWorkspaceRouteConfig(assistantRuntimeConfig.settingsSurfaceRequiresWorkspace, {
|
|
85
|
-
auth: "required",
|
|
86
|
-
surface: assistantRuntimeConfig.settingsSurfaceId,
|
|
87
|
-
visibility: settingsVisibility,
|
|
88
|
-
meta: {
|
|
89
|
-
tags: ["assistant", "settings"],
|
|
90
|
-
summary: "Update assistant settings."
|
|
91
|
-
},
|
|
92
|
-
bodyValidator: assistantConfigResource.operations.patch.bodyValidator,
|
|
93
|
-
responseValidators: withStandardErrorResponses(
|
|
94
|
-
{
|
|
95
|
-
200: assistantConfigResource.operations.patch.outputValidator
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
includeValidation400: true
|
|
99
|
-
}
|
|
100
|
-
)
|
|
101
|
-
}),
|
|
102
|
-
async function assistantSettingsPatchRoute(request, reply) {
|
|
103
|
-
const response = await request.executeAction({
|
|
104
|
-
actionId: actionIds.settingsUpdate,
|
|
105
|
-
input: {
|
|
106
|
-
...readWorkspaceInput(request, assistantRuntimeConfig.settingsSurfaceRequiresWorkspace),
|
|
107
|
-
patch: request.input.body
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
reply.code(200).send(response);
|
|
112
|
-
}
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
router.register(
|
|
116
|
-
"POST",
|
|
117
|
-
`${runtimeRouteBase}/chat/stream`,
|
|
118
|
-
buildWorkspaceRouteConfig(assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace, {
|
|
119
|
-
auth: "required",
|
|
120
|
-
surface: assistantRuntimeConfig.runtimeSurfaceId,
|
|
121
|
-
visibility: runtimeVisibility,
|
|
122
|
-
meta: {
|
|
123
|
-
tags: ["assistant"],
|
|
124
|
-
summary: "Stream assistant response."
|
|
125
|
-
},
|
|
126
|
-
bodyValidator: assistantResource.operations.chatStream.bodyValidator
|
|
127
|
-
}),
|
|
128
|
-
async function assistantChatStreamRoute(request, reply) {
|
|
129
|
-
const abortController = new AbortController();
|
|
130
|
-
const requestBody = request?.input?.body && typeof request.input.body === "object" ? request.input.body : {};
|
|
131
|
-
const closeListener = () => {
|
|
132
|
-
abortController.abort();
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
let streamStarted = false;
|
|
136
|
-
|
|
137
|
-
function ensureStreamStarted() {
|
|
138
|
-
if (streamStarted) {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
setNdjsonHeaders(reply);
|
|
143
|
-
reply.code(200);
|
|
144
|
-
reply.hijack();
|
|
145
|
-
if (typeof reply.raw.flushHeaders === "function") {
|
|
146
|
-
reply.raw.flushHeaders();
|
|
147
|
-
}
|
|
148
|
-
streamStarted = true;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const streamWriter = Object.freeze({
|
|
152
|
-
sendMeta(payload = {}) {
|
|
153
|
-
ensureStreamStarted();
|
|
154
|
-
writeNdjson(reply, payload);
|
|
155
|
-
},
|
|
156
|
-
sendAssistantDelta(payload = {}) {
|
|
157
|
-
ensureStreamStarted();
|
|
158
|
-
writeNdjson(reply, payload);
|
|
159
|
-
},
|
|
160
|
-
sendAssistantMessage(payload = {}) {
|
|
161
|
-
ensureStreamStarted();
|
|
162
|
-
writeNdjson(reply, payload);
|
|
163
|
-
},
|
|
164
|
-
sendToolCall(payload = {}) {
|
|
165
|
-
ensureStreamStarted();
|
|
166
|
-
writeNdjson(reply, payload);
|
|
167
|
-
},
|
|
168
|
-
sendToolResult(payload = {}) {
|
|
169
|
-
ensureStreamStarted();
|
|
170
|
-
writeNdjson(reply, payload);
|
|
171
|
-
},
|
|
172
|
-
sendError(payload = {}) {
|
|
173
|
-
ensureStreamStarted();
|
|
174
|
-
writeNdjson(reply, payload);
|
|
175
|
-
},
|
|
176
|
-
sendDone(payload = {}) {
|
|
177
|
-
ensureStreamStarted();
|
|
178
|
-
writeNdjson(reply, payload);
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
request.raw.on("close", closeListener);
|
|
184
|
-
|
|
185
|
-
await request.executeAction({
|
|
186
|
-
actionId: actionIds.chatStream,
|
|
187
|
-
input: {
|
|
188
|
-
...readWorkspaceInput(request, assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace),
|
|
189
|
-
messageId: requestBody.messageId,
|
|
190
|
-
conversationId: requestBody.conversationId,
|
|
191
|
-
input: requestBody.input,
|
|
192
|
-
history: requestBody.history,
|
|
193
|
-
clientContext: requestBody.clientContext
|
|
194
|
-
},
|
|
195
|
-
deps: {
|
|
196
|
-
streamWriter,
|
|
197
|
-
abortSignal: abortController.signal
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
if (streamStarted) {
|
|
202
|
-
endNdjson(reply);
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
reply.code(204).send();
|
|
207
|
-
} catch (error) {
|
|
208
|
-
if (!streamStarted) {
|
|
209
|
-
const statusCode = Number(error?.status || error?.statusCode || 500);
|
|
210
|
-
const safeStatusCode = Number.isInteger(statusCode) && statusCode >= 400 && statusCode <= 599 ? statusCode : 500;
|
|
211
|
-
reply.code(safeStatusCode).send({
|
|
212
|
-
error: safeStatusCode >= 500 ? "Internal server error." : String(error?.message || "Request failed.")
|
|
213
|
-
});
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const streamError = mapStreamError(error);
|
|
218
|
-
writeNdjson(reply, {
|
|
219
|
-
type: "error",
|
|
220
|
-
...streamError
|
|
221
|
-
});
|
|
222
|
-
writeNdjson(reply, {
|
|
223
|
-
type: "done",
|
|
224
|
-
status: "failed"
|
|
225
|
-
});
|
|
226
|
-
endNdjson(reply);
|
|
227
|
-
} finally {
|
|
228
|
-
request.raw.off("close", closeListener);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
router.register(
|
|
234
|
-
"GET",
|
|
235
|
-
`${runtimeRouteBase}/conversations`,
|
|
236
|
-
buildWorkspaceRouteConfig(assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace, {
|
|
237
|
-
auth: "required",
|
|
238
|
-
surface: assistantRuntimeConfig.runtimeSurfaceId,
|
|
239
|
-
visibility: runtimeVisibility,
|
|
240
|
-
meta: {
|
|
241
|
-
tags: ["assistant"],
|
|
242
|
-
summary: "List assistant conversations."
|
|
243
|
-
},
|
|
244
|
-
queryValidator: assistantResource.operations.conversationsList.queryValidator,
|
|
245
|
-
responseValidators: withStandardErrorResponses({
|
|
246
|
-
200: assistantResource.operations.conversationsList.outputValidator
|
|
247
|
-
})
|
|
248
|
-
}),
|
|
249
|
-
async function assistantConversationsRoute(request, reply) {
|
|
250
|
-
const response = await request.executeAction({
|
|
251
|
-
actionId: actionIds.conversationsList,
|
|
252
|
-
input: {
|
|
253
|
-
...readWorkspaceInput(request, assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace),
|
|
254
|
-
query: request.input.query
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
reply.code(200).send(response);
|
|
259
|
-
}
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
router.register(
|
|
263
|
-
"GET",
|
|
264
|
-
`${runtimeRouteBase}/conversations/:conversationId/messages`,
|
|
265
|
-
buildWorkspaceRouteConfig(assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace, {
|
|
266
|
-
auth: "required",
|
|
267
|
-
surface: assistantRuntimeConfig.runtimeSurfaceId,
|
|
268
|
-
visibility: runtimeVisibility,
|
|
269
|
-
meta: {
|
|
270
|
-
tags: ["assistant"],
|
|
271
|
-
summary: "List assistant conversation messages."
|
|
272
|
-
},
|
|
273
|
-
paramsValidator: assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace
|
|
274
|
-
? [workspaceSlugParamsValidator, assistantResource.operations.conversationMessagesList.paramsValidator]
|
|
275
|
-
: assistantResource.operations.conversationMessagesList.paramsValidator,
|
|
276
|
-
queryValidator: assistantResource.operations.conversationMessagesList.queryValidator,
|
|
277
|
-
responseValidators: withStandardErrorResponses({
|
|
278
|
-
200: assistantResource.operations.conversationMessagesList.outputValidator
|
|
279
|
-
})
|
|
280
|
-
}),
|
|
281
|
-
async function assistantConversationMessagesRoute(request, reply) {
|
|
282
|
-
const response = await request.executeAction({
|
|
283
|
-
actionId: actionIds.conversationMessagesList,
|
|
284
|
-
input: {
|
|
285
|
-
...readWorkspaceInput(request, assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace),
|
|
286
|
-
conversationId: request.input.params.conversationId,
|
|
287
|
-
query: request.input.query
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
reply.code(200).send(response);
|
|
292
|
-
}
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
export { registerRoutes };
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { parsePositiveInteger } from "@jskit-ai/kernel/server/runtime";
|
|
2
|
-
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
-
import { resolveInsertedId } from "@jskit-ai/assistant-core/server";
|
|
4
|
-
import { assistantRuntimeConfig } from "../../shared/assistantRuntimeConfig.js";
|
|
5
|
-
|
|
6
|
-
function normalizeTargetSurfaceId(value = "") {
|
|
7
|
-
return normalizeText(value).toLowerCase() || assistantRuntimeConfig.runtimeSurfaceId;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function normalizeWorkspaceId(value) {
|
|
11
|
-
return parsePositiveInteger(value) || null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function buildScopeKey(targetSurfaceId, workspaceId = null) {
|
|
15
|
-
const normalizedTargetSurfaceId = normalizeTargetSurfaceId(targetSurfaceId);
|
|
16
|
-
const normalizedWorkspaceId = normalizeWorkspaceId(workspaceId);
|
|
17
|
-
if (normalizedWorkspaceId) {
|
|
18
|
-
return `${normalizedTargetSurfaceId}:workspace:${normalizedWorkspaceId}`;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return `${normalizedTargetSurfaceId}:global`;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function mapConfigRow(row = {}) {
|
|
25
|
-
return {
|
|
26
|
-
targetSurfaceId: normalizeTargetSurfaceId(row.target_surface_id),
|
|
27
|
-
scopeKey: normalizeText(row.scope_key),
|
|
28
|
-
workspaceId: normalizeWorkspaceId(row.workspace_id),
|
|
29
|
-
settings: {
|
|
30
|
-
systemPrompt: String(row.system_prompt || "")
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function createDefaultRecord({ targetSurfaceId = "", workspaceId = null } = {}) {
|
|
36
|
-
const normalizedTargetSurfaceId = normalizeTargetSurfaceId(targetSurfaceId);
|
|
37
|
-
const normalizedWorkspaceId = normalizeWorkspaceId(workspaceId);
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
targetSurfaceId: normalizedTargetSurfaceId,
|
|
41
|
-
scopeKey: buildScopeKey(normalizedTargetSurfaceId, normalizedWorkspaceId),
|
|
42
|
-
workspaceId: normalizedWorkspaceId,
|
|
43
|
-
settings: {
|
|
44
|
-
systemPrompt: ""
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function createRepository(knex) {
|
|
50
|
-
if (!knex || typeof knex !== "function") {
|
|
51
|
-
throw new Error("createAssistantConfigRepository requires knex client.");
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function findByScope({ targetSurfaceId = "", workspaceId = null } = {}, options = {}) {
|
|
55
|
-
const client = options?.trx || knex;
|
|
56
|
-
const defaultRecord = createDefaultRecord({
|
|
57
|
-
targetSurfaceId,
|
|
58
|
-
workspaceId
|
|
59
|
-
});
|
|
60
|
-
const row = await client(assistantRuntimeConfig.configTable)
|
|
61
|
-
.where({
|
|
62
|
-
target_surface_id: defaultRecord.targetSurfaceId,
|
|
63
|
-
scope_key: defaultRecord.scopeKey
|
|
64
|
-
})
|
|
65
|
-
.first();
|
|
66
|
-
|
|
67
|
-
return row ? mapConfigRow(row) : null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function upsertByScope({ targetSurfaceId = "", workspaceId = null, patch = {} } = {}, options = {}) {
|
|
71
|
-
const client = options?.trx || knex;
|
|
72
|
-
const defaultRecord = createDefaultRecord({
|
|
73
|
-
targetSurfaceId,
|
|
74
|
-
workspaceId
|
|
75
|
-
});
|
|
76
|
-
const existing = await findByScope(defaultRecord, {
|
|
77
|
-
trx: client
|
|
78
|
-
});
|
|
79
|
-
const nextSystemPrompt = Object.hasOwn(patch || {}, "systemPrompt")
|
|
80
|
-
? String(patch.systemPrompt || "")
|
|
81
|
-
: String(existing?.settings?.systemPrompt || defaultRecord.settings.systemPrompt);
|
|
82
|
-
const now = new Date();
|
|
83
|
-
|
|
84
|
-
if (existing) {
|
|
85
|
-
await client(assistantRuntimeConfig.configTable)
|
|
86
|
-
.where({
|
|
87
|
-
target_surface_id: defaultRecord.targetSurfaceId,
|
|
88
|
-
scope_key: defaultRecord.scopeKey
|
|
89
|
-
})
|
|
90
|
-
.update({
|
|
91
|
-
workspace_id: defaultRecord.workspaceId,
|
|
92
|
-
system_prompt: nextSystemPrompt,
|
|
93
|
-
updated_at: now
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
...defaultRecord,
|
|
98
|
-
settings: {
|
|
99
|
-
systemPrompt: nextSystemPrompt
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const insertResult = await client(assistantRuntimeConfig.configTable).insert({
|
|
105
|
-
target_surface_id: defaultRecord.targetSurfaceId,
|
|
106
|
-
scope_key: defaultRecord.scopeKey,
|
|
107
|
-
workspace_id: defaultRecord.workspaceId,
|
|
108
|
-
system_prompt: nextSystemPrompt,
|
|
109
|
-
created_at: now,
|
|
110
|
-
updated_at: now
|
|
111
|
-
});
|
|
112
|
-
const insertedId = resolveInsertedId(insertResult);
|
|
113
|
-
if (!insertedId) {
|
|
114
|
-
return {
|
|
115
|
-
...defaultRecord,
|
|
116
|
-
settings: {
|
|
117
|
-
systemPrompt: nextSystemPrompt
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const insertedRow = await client(assistantRuntimeConfig.configTable)
|
|
123
|
-
.where({ id: insertedId })
|
|
124
|
-
.first();
|
|
125
|
-
|
|
126
|
-
return insertedRow ? mapConfigRow(insertedRow) : {
|
|
127
|
-
...defaultRecord,
|
|
128
|
-
settings: {
|
|
129
|
-
systemPrompt: nextSystemPrompt
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return Object.freeze({
|
|
135
|
-
createDefaultRecord,
|
|
136
|
-
findByScope,
|
|
137
|
-
upsertByScope
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export { createRepository };
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import { runInTransaction } from "@jskit-ai/database-runtime/shared/repositoryOptions";
|
|
2
|
-
import { parsePositiveInteger } from "@jskit-ai/kernel/server/runtime";
|
|
3
|
-
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
4
|
-
import {
|
|
5
|
-
parseJsonObject,
|
|
6
|
-
resolveInsertedId,
|
|
7
|
-
stringifyJsonObject,
|
|
8
|
-
toIso
|
|
9
|
-
} from "@jskit-ai/assistant-core/server";
|
|
10
|
-
import { assistantRuntimeConfig } from "../../shared/assistantRuntimeConfig.js";
|
|
11
|
-
|
|
12
|
-
function normalizeWorkspaceId(value) {
|
|
13
|
-
return parsePositiveInteger(value) || null;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function applyWorkspaceScope(query, columnName, workspaceId) {
|
|
17
|
-
const normalizedWorkspaceId = normalizeWorkspaceId(workspaceId);
|
|
18
|
-
if (normalizedWorkspaceId) {
|
|
19
|
-
return query.where(columnName, normalizedWorkspaceId);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return query.whereNull(columnName);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function mapConversationRow(row = {}) {
|
|
26
|
-
return {
|
|
27
|
-
id: Number(row.id),
|
|
28
|
-
workspaceId: normalizeWorkspaceId(row.workspace_id),
|
|
29
|
-
title: String(row.title || "New conversation"),
|
|
30
|
-
createdByUserId: row.created_by_user_id == null ? null : Number(row.created_by_user_id),
|
|
31
|
-
status: String(row.status || "active"),
|
|
32
|
-
provider: String(row.provider || ""),
|
|
33
|
-
model: String(row.model || ""),
|
|
34
|
-
surfaceId: String(row.surface_id || ""),
|
|
35
|
-
startedAt: toIso(row.started_at),
|
|
36
|
-
endedAt: row.ended_at ? toIso(row.ended_at) : null,
|
|
37
|
-
messageCount: Number(row.message_count || 0),
|
|
38
|
-
metadata: parseJsonObject(row.metadata_json),
|
|
39
|
-
createdAt: toIso(row.created_at),
|
|
40
|
-
updatedAt: toIso(row.updated_at)
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function normalizeCursorPagination(pagination = {}, { defaultLimit = 20, maxLimit = 200 } = {}) {
|
|
45
|
-
const cursor = parsePositiveInteger(pagination.cursor) || 0;
|
|
46
|
-
const limit = Math.max(1, Math.min(maxLimit, parsePositiveInteger(pagination.limit) || defaultLimit));
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
cursor,
|
|
50
|
-
limit
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function createConversationBaseQuery(client) {
|
|
55
|
-
return client(`${assistantRuntimeConfig.conversationsTable} as c`).select("c.*");
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function createRepository(knex) {
|
|
59
|
-
if (!knex || typeof knex !== "function") {
|
|
60
|
-
throw new Error("createConversationsRepository requires knex client.");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function findById(conversationId, options = {}) {
|
|
64
|
-
const numericConversationId = parsePositiveInteger(conversationId);
|
|
65
|
-
if (!numericConversationId) {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const client = options?.trx || knex;
|
|
70
|
-
const row = await createConversationBaseQuery(client)
|
|
71
|
-
.where("c.id", numericConversationId)
|
|
72
|
-
.first();
|
|
73
|
-
|
|
74
|
-
return row ? mapConversationRow(row) : null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async function findByIdForActorScope(conversationId, { workspaceId = null, actorUserId = null } = {}, options = {}) {
|
|
78
|
-
const numericConversationId = parsePositiveInteger(conversationId);
|
|
79
|
-
const numericActorUserId = parsePositiveInteger(actorUserId);
|
|
80
|
-
if (!numericConversationId || !numericActorUserId) {
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const client = options?.trx || knex;
|
|
85
|
-
const query = createConversationBaseQuery(client)
|
|
86
|
-
.where("c.id", numericConversationId)
|
|
87
|
-
.where("c.created_by_user_id", numericActorUserId);
|
|
88
|
-
applyWorkspaceScope(query, "c.workspace_id", workspaceId);
|
|
89
|
-
const row = await query.first();
|
|
90
|
-
|
|
91
|
-
return row ? mapConversationRow(row) : null;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async function create(payload = {}, options = {}) {
|
|
95
|
-
const client = options?.trx || knex;
|
|
96
|
-
const now = new Date();
|
|
97
|
-
const insertResult = await client(assistantRuntimeConfig.conversationsTable).insert({
|
|
98
|
-
workspace_id: normalizeWorkspaceId(payload.workspaceId),
|
|
99
|
-
created_by_user_id: parsePositiveInteger(payload.createdByUserId) || null,
|
|
100
|
-
title: normalizeText(payload.title) || "New conversation",
|
|
101
|
-
status: normalizeText(payload.status).toLowerCase() || "active",
|
|
102
|
-
provider: normalizeText(payload.provider),
|
|
103
|
-
model: normalizeText(payload.model),
|
|
104
|
-
surface_id: normalizeText(payload.surfaceId).toLowerCase() || assistantRuntimeConfig.runtimeSurfaceId,
|
|
105
|
-
message_count: parsePositiveInteger(payload.messageCount) || 0,
|
|
106
|
-
metadata_json: stringifyJsonObject(payload.metadata),
|
|
107
|
-
started_at: payload.startedAt ? new Date(payload.startedAt) : now,
|
|
108
|
-
ended_at: payload.endedAt ? new Date(payload.endedAt) : null,
|
|
109
|
-
created_at: now,
|
|
110
|
-
updated_at: now
|
|
111
|
-
});
|
|
112
|
-
const id = resolveInsertedId(insertResult);
|
|
113
|
-
if (!id) {
|
|
114
|
-
throw new Error("conversationsRepository.create could not resolve inserted id.");
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return findById(id, {
|
|
118
|
-
trx: client
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async function updateById(conversationId, patch = {}, options = {}) {
|
|
123
|
-
const numericConversationId = parsePositiveInteger(conversationId);
|
|
124
|
-
if (!numericConversationId) {
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const client = options?.trx || knex;
|
|
129
|
-
const updatePatch = {};
|
|
130
|
-
|
|
131
|
-
if (Object.hasOwn(patch, "title")) {
|
|
132
|
-
updatePatch.title = normalizeText(patch.title) || "New conversation";
|
|
133
|
-
}
|
|
134
|
-
if (Object.hasOwn(patch, "status")) {
|
|
135
|
-
updatePatch.status = normalizeText(patch.status).toLowerCase() || "active";
|
|
136
|
-
}
|
|
137
|
-
if (Object.hasOwn(patch, "provider")) {
|
|
138
|
-
updatePatch.provider = normalizeText(patch.provider);
|
|
139
|
-
}
|
|
140
|
-
if (Object.hasOwn(patch, "model")) {
|
|
141
|
-
updatePatch.model = normalizeText(patch.model);
|
|
142
|
-
}
|
|
143
|
-
if (Object.hasOwn(patch, "surfaceId")) {
|
|
144
|
-
updatePatch.surface_id = normalizeText(patch.surfaceId).toLowerCase() || assistantRuntimeConfig.runtimeSurfaceId;
|
|
145
|
-
}
|
|
146
|
-
if (Object.hasOwn(patch, "messageCount")) {
|
|
147
|
-
updatePatch.message_count = Math.max(0, Number(patch.messageCount || 0));
|
|
148
|
-
}
|
|
149
|
-
if (Object.hasOwn(patch, "endedAt")) {
|
|
150
|
-
updatePatch.ended_at = patch.endedAt ? new Date(patch.endedAt) : null;
|
|
151
|
-
}
|
|
152
|
-
if (Object.hasOwn(patch, "metadata")) {
|
|
153
|
-
updatePatch.metadata_json = stringifyJsonObject(patch.metadata);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (Object.keys(updatePatch).length > 0) {
|
|
157
|
-
updatePatch.updated_at = new Date();
|
|
158
|
-
await client(assistantRuntimeConfig.conversationsTable)
|
|
159
|
-
.where({ id: numericConversationId })
|
|
160
|
-
.update(updatePatch);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return findById(numericConversationId, {
|
|
164
|
-
trx: client
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
async function incrementMessageCount(conversationId, delta = 1, options = {}) {
|
|
169
|
-
const numericConversationId = parsePositiveInteger(conversationId);
|
|
170
|
-
if (!numericConversationId) {
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const client = options?.trx || knex;
|
|
175
|
-
const incrementBy = Number.isInteger(Number(delta)) ? Number(delta) : 1;
|
|
176
|
-
await client(assistantRuntimeConfig.conversationsTable)
|
|
177
|
-
.where({ id: numericConversationId })
|
|
178
|
-
.update({
|
|
179
|
-
message_count: client.raw("GREATEST(0, message_count + ?)", [incrementBy]),
|
|
180
|
-
updated_at: new Date()
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
return findById(numericConversationId, {
|
|
184
|
-
trx: client
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
async function listForActorScope({ workspaceId = null, actorUserId = null, pagination = {}, filters = {} } = {}, options = {}) {
|
|
189
|
-
const numericActorUserId = parsePositiveInteger(actorUserId);
|
|
190
|
-
if (!numericActorUserId) {
|
|
191
|
-
return {
|
|
192
|
-
items: [],
|
|
193
|
-
nextCursor: null
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const client = options?.trx || knex;
|
|
198
|
-
const { cursor, limit } = normalizeCursorPagination(pagination);
|
|
199
|
-
let query = createConversationBaseQuery(client).where("c.created_by_user_id", numericActorUserId);
|
|
200
|
-
query = applyWorkspaceScope(query, "c.workspace_id", workspaceId);
|
|
201
|
-
|
|
202
|
-
const normalizedStatus = normalizeText(filters.status).toLowerCase();
|
|
203
|
-
if (normalizedStatus) {
|
|
204
|
-
query = query.where("c.status", normalizedStatus);
|
|
205
|
-
}
|
|
206
|
-
if (cursor > 0) {
|
|
207
|
-
query = query.where("c.id", "<", cursor);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const rows = await query
|
|
211
|
-
.orderBy("c.id", "desc")
|
|
212
|
-
.limit(limit + 1);
|
|
213
|
-
|
|
214
|
-
const hasMore = rows.length > limit;
|
|
215
|
-
const pageRows = hasMore ? rows.slice(0, limit) : rows;
|
|
216
|
-
const items = pageRows.map(mapConversationRow);
|
|
217
|
-
const nextCursor = hasMore && pageRows.length > 0 ? String(pageRows[pageRows.length - 1].id) : null;
|
|
218
|
-
|
|
219
|
-
return {
|
|
220
|
-
items,
|
|
221
|
-
nextCursor
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
async function transaction(callback) {
|
|
226
|
-
return runInTransaction(knex, callback);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return Object.freeze({
|
|
230
|
-
findById,
|
|
231
|
-
findByIdForActorScope,
|
|
232
|
-
create,
|
|
233
|
-
updateById,
|
|
234
|
-
incrementMessageCount,
|
|
235
|
-
listForActorScope,
|
|
236
|
-
transaction
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export { createRepository };
|