@jskit-ai/assistant 0.1.32 → 0.1.34
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 +346 -142
- package/package.json +3 -19
- package/src/server/buildTemplateContext.js +107 -0
- package/templates/migrations/assistant_config_initial.cjs +25 -0
- package/templates/migrations/assistant_transcripts_initial.cjs +21 -14
- package/templates/src/local-package/client/components/AssistantSettingsClientElement.vue +88 -0
- package/templates/src/local-package/client/components/AssistantSurfaceClientElement.vue +10 -0
- package/{src/client/composables/useAssistantWorkspaceRuntime.js → templates/src/local-package/client/composables/useAssistantRuntime.js} +91 -114
- package/templates/src/local-package/client/index.js +3 -0
- package/templates/src/local-package/client/providers/AssistantClientProvider.js +16 -0
- package/templates/src/local-package/package.descriptor.mjs +85 -0
- package/templates/src/local-package/package.json +11 -0
- package/{src/server/AssistantServiceProvider.js → templates/src/local-package/server/AssistantProvider.js} +37 -61
- package/templates/src/local-package/server/actionIds.js +9 -0
- package/templates/src/local-package/server/actions.js +190 -0
- package/templates/src/local-package/server/registerRoutes.js +296 -0
- package/templates/src/local-package/server/repositories/assistantConfigRepository.js +141 -0
- package/{src → templates/src/local-package}/server/repositories/conversationsRepository.js +44 -45
- package/{src → templates/src/local-package}/server/repositories/messagesRepository.js +49 -34
- package/templates/src/local-package/server/services/assistantConfigService.js +90 -0
- package/{src → templates/src/local-package}/server/services/chatService.js +45 -37
- package/{src → templates/src/local-package}/server/services/transcriptService.js +61 -82
- package/templates/src/local-package/shared/assistantRuntimeConfig.js +13 -0
- package/templates/src/local-package/shared/index.js +1 -0
- package/templates/src/pages/assistant/index.vue +7 -0
- package/test/buildTemplateContext.test.js +112 -0
- package/test/packageDescriptor.test.js +69 -0
- package/src/client/components/AssistantClientElement.vue +0 -1316
- package/src/client/components/AssistantConsoleSettingsClientElement.vue +0 -70
- package/src/client/components/AssistantSettingsFormCard.vue +0 -76
- package/src/client/components/AssistantWorkspaceClientElement.vue +0 -15
- package/src/client/components/AssistantWorkspaceSettingsClientElement.vue +0 -72
- package/src/client/index.js +0 -10
- package/src/client/lib/assistantApi.js +0 -137
- package/src/client/lib/assistantHttpClient.js +0 -10
- package/src/client/lib/markdownRenderer.js +0 -31
- package/src/client/providers/AssistantWebClientProvider.js +0 -20
- package/src/server/actionIds.js +0 -11
- package/src/server/actions.js +0 -191
- package/src/server/lib/aiClient.js +0 -43
- package/src/server/lib/ndjson.js +0 -47
- package/src/server/lib/providers/anthropicClient.js +0 -375
- package/src/server/lib/providers/common.js +0 -150
- package/src/server/lib/providers/deepSeekClient.js +0 -22
- package/src/server/lib/providers/openAiClient.js +0 -13
- package/src/server/lib/providers/openAiCompatibleClient.js +0 -69
- package/src/server/lib/resolveWorkspaceSlug.js +0 -24
- package/src/server/lib/serviceToolCatalog.js +0 -459
- package/src/server/registerRoutes.js +0 -383
- package/src/server/repositories/assistantSettingsRepository.js +0 -100
- package/src/server/repositories/repositoryPersistenceUtils.js +0 -48
- package/src/server/services/assistantSettingsService.js +0 -149
- package/src/shared/assistantPaths.js +0 -50
- package/src/shared/assistantResource.js +0 -317
- package/src/shared/assistantSettingsResource.js +0 -197
- package/src/shared/index.js +0 -43
- package/src/shared/queryKeys.js +0 -69
- package/src/shared/settingsEvents.js +0 -6
- package/src/shared/streamEvents.js +0 -29
- package/src/shared/support/conversationStatus.js +0 -18
- package/src/shared/support/jsonObject.js +0 -18
- package/src/shared/support/positiveInteger.js +0 -9
- package/templates/migrations/assistant_settings_initial.cjs +0 -37
- package/templates/src/pages/admin/workspace/assistant/index.vue +0 -7
- package/test/aiConfigValidation.test.js +0 -15
- package/test/assistantApiSurfaceHeader.test.js +0 -64
- package/test/assistantResource.test.js +0 -53
- package/test/assistantSettingsResource.test.js +0 -48
- package/test/assistantSettingsService.test.js +0 -133
- package/test/chatService.test.js +0 -841
- package/test/descriptorSurfaceOption.test.js +0 -35
- package/test/queryKeys.test.js +0 -41
- package/test/resolveWorkspaceSlug.test.js +0 -83
- package/test/routeInputContracts.test.js +0 -286
- package/test/serviceToolCatalog.test.js +0 -1235
- package/test/transcriptService.test.js +0 -175
|
@@ -0,0 +1,141 @@
|
|
|
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,27 +1,37 @@
|
|
|
1
|
+
import { runInTransaction } from "@jskit-ai/database-runtime/shared/repositoryOptions";
|
|
1
2
|
import { parsePositiveInteger } from "@jskit-ai/kernel/server/runtime";
|
|
2
3
|
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
-
import { runInTransaction } from "@jskit-ai/database-runtime/shared/repositoryOptions";
|
|
4
4
|
import {
|
|
5
5
|
parseJsonObject,
|
|
6
|
+
resolveInsertedId,
|
|
6
7
|
stringifyJsonObject,
|
|
7
|
-
toIso
|
|
8
|
-
|
|
9
|
-
} from "
|
|
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
|
+
}
|
|
10
24
|
|
|
11
25
|
function mapConversationRow(row = {}) {
|
|
12
26
|
return {
|
|
13
27
|
id: Number(row.id),
|
|
14
|
-
workspaceId:
|
|
15
|
-
workspaceSlug: String(row.workspace_slug || ""),
|
|
16
|
-
workspaceName: String(row.workspace_name || ""),
|
|
28
|
+
workspaceId: normalizeWorkspaceId(row.workspace_id),
|
|
17
29
|
title: String(row.title || "New conversation"),
|
|
18
30
|
createdByUserId: row.created_by_user_id == null ? null : Number(row.created_by_user_id),
|
|
19
|
-
createdByUserDisplayName: String(row.created_by_user_display_name || ""),
|
|
20
|
-
createdByUserEmail: String(row.created_by_user_email || ""),
|
|
21
31
|
status: String(row.status || "active"),
|
|
22
32
|
provider: String(row.provider || ""),
|
|
23
33
|
model: String(row.model || ""),
|
|
24
|
-
|
|
34
|
+
surfaceId: String(row.surface_id || ""),
|
|
25
35
|
startedAt: toIso(row.started_at),
|
|
26
36
|
endedAt: row.ended_at ? toIso(row.ended_at) : null,
|
|
27
37
|
messageCount: Number(row.message_count || 0),
|
|
@@ -42,19 +52,10 @@ function normalizeCursorPagination(pagination = {}, { defaultLimit = 20, maxLimi
|
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
function createConversationBaseQuery(client) {
|
|
45
|
-
return client(
|
|
46
|
-
.leftJoin("workspaces as w", "w.id", "c.workspace_id")
|
|
47
|
-
.leftJoin("users as u", "u.id", "c.created_by_user_id")
|
|
48
|
-
.select(
|
|
49
|
-
"c.*",
|
|
50
|
-
client.raw("COALESCE(w.slug, '') AS workspace_slug"),
|
|
51
|
-
client.raw("COALESCE(w.name, '') AS workspace_name"),
|
|
52
|
-
client.raw("COALESCE(u.display_name, '') AS created_by_user_display_name"),
|
|
53
|
-
client.raw("COALESCE(u.email, '') AS created_by_user_email")
|
|
54
|
-
);
|
|
55
|
+
return client(`${assistantRuntimeConfig.conversationsTable} as c`).select("c.*");
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
function
|
|
58
|
+
function createRepository(knex) {
|
|
58
59
|
if (!knex || typeof knex !== "function") {
|
|
59
60
|
throw new Error("createConversationsRepository requires knex client.");
|
|
60
61
|
}
|
|
@@ -73,20 +74,19 @@ function createConversationsRepository(knex) {
|
|
|
73
74
|
return row ? mapConversationRow(row) : null;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
async function
|
|
77
|
+
async function findByIdForActorScope(conversationId, { workspaceId = null, actorUserId = null } = {}, options = {}) {
|
|
77
78
|
const numericConversationId = parsePositiveInteger(conversationId);
|
|
78
|
-
const numericWorkspaceId = parsePositiveInteger(workspaceId);
|
|
79
79
|
const numericActorUserId = parsePositiveInteger(actorUserId);
|
|
80
|
-
if (!numericConversationId || !
|
|
80
|
+
if (!numericConversationId || !numericActorUserId) {
|
|
81
81
|
return null;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
const client = options?.trx || knex;
|
|
85
|
-
const
|
|
85
|
+
const query = createConversationBaseQuery(client)
|
|
86
86
|
.where("c.id", numericConversationId)
|
|
87
|
-
.where("c.
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
.where("c.created_by_user_id", numericActorUserId);
|
|
88
|
+
applyWorkspaceScope(query, "c.workspace_id", workspaceId);
|
|
89
|
+
const row = await query.first();
|
|
90
90
|
|
|
91
91
|
return row ? mapConversationRow(row) : null;
|
|
92
92
|
}
|
|
@@ -94,14 +94,14 @@ function createConversationsRepository(knex) {
|
|
|
94
94
|
async function create(payload = {}, options = {}) {
|
|
95
95
|
const client = options?.trx || knex;
|
|
96
96
|
const now = new Date();
|
|
97
|
-
const insertResult = await client(
|
|
98
|
-
workspace_id:
|
|
97
|
+
const insertResult = await client(assistantRuntimeConfig.conversationsTable).insert({
|
|
98
|
+
workspace_id: normalizeWorkspaceId(payload.workspaceId),
|
|
99
99
|
created_by_user_id: parsePositiveInteger(payload.createdByUserId) || null,
|
|
100
100
|
title: normalizeText(payload.title) || "New conversation",
|
|
101
101
|
status: normalizeText(payload.status).toLowerCase() || "active",
|
|
102
102
|
provider: normalizeText(payload.provider),
|
|
103
103
|
model: normalizeText(payload.model),
|
|
104
|
-
|
|
104
|
+
surface_id: normalizeText(payload.surfaceId).toLowerCase() || assistantRuntimeConfig.runtimeSurfaceId,
|
|
105
105
|
message_count: parsePositiveInteger(payload.messageCount) || 0,
|
|
106
106
|
metadata_json: stringifyJsonObject(payload.metadata),
|
|
107
107
|
started_at: payload.startedAt ? new Date(payload.startedAt) : now,
|
|
@@ -140,8 +140,8 @@ function createConversationsRepository(knex) {
|
|
|
140
140
|
if (Object.hasOwn(patch, "model")) {
|
|
141
141
|
updatePatch.model = normalizeText(patch.model);
|
|
142
142
|
}
|
|
143
|
-
if (Object.hasOwn(patch, "
|
|
144
|
-
updatePatch.
|
|
143
|
+
if (Object.hasOwn(patch, "surfaceId")) {
|
|
144
|
+
updatePatch.surface_id = normalizeText(patch.surfaceId).toLowerCase() || assistantRuntimeConfig.runtimeSurfaceId;
|
|
145
145
|
}
|
|
146
146
|
if (Object.hasOwn(patch, "messageCount")) {
|
|
147
147
|
updatePatch.message_count = Math.max(0, Number(patch.messageCount || 0));
|
|
@@ -155,7 +155,9 @@ function createConversationsRepository(knex) {
|
|
|
155
155
|
|
|
156
156
|
if (Object.keys(updatePatch).length > 0) {
|
|
157
157
|
updatePatch.updated_at = new Date();
|
|
158
|
-
await client(
|
|
158
|
+
await client(assistantRuntimeConfig.conversationsTable)
|
|
159
|
+
.where({ id: numericConversationId })
|
|
160
|
+
.update(updatePatch);
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
return findById(numericConversationId, {
|
|
@@ -171,7 +173,7 @@ function createConversationsRepository(knex) {
|
|
|
171
173
|
|
|
172
174
|
const client = options?.trx || knex;
|
|
173
175
|
const incrementBy = Number.isInteger(Number(delta)) ? Number(delta) : 1;
|
|
174
|
-
await client(
|
|
176
|
+
await client(assistantRuntimeConfig.conversationsTable)
|
|
175
177
|
.where({ id: numericConversationId })
|
|
176
178
|
.update({
|
|
177
179
|
message_count: client.raw("GREATEST(0, message_count + ?)", [incrementBy]),
|
|
@@ -183,10 +185,9 @@ function createConversationsRepository(knex) {
|
|
|
183
185
|
});
|
|
184
186
|
}
|
|
185
187
|
|
|
186
|
-
async function
|
|
187
|
-
const numericWorkspaceId = parsePositiveInteger(workspaceId);
|
|
188
|
+
async function listForActorScope({ workspaceId = null, actorUserId = null, pagination = {}, filters = {} } = {}, options = {}) {
|
|
188
189
|
const numericActorUserId = parsePositiveInteger(actorUserId);
|
|
189
|
-
if (!
|
|
190
|
+
if (!numericActorUserId) {
|
|
190
191
|
return {
|
|
191
192
|
items: [],
|
|
192
193
|
nextCursor: null
|
|
@@ -195,10 +196,8 @@ function createConversationsRepository(knex) {
|
|
|
195
196
|
|
|
196
197
|
const client = options?.trx || knex;
|
|
197
198
|
const { cursor, limit } = normalizeCursorPagination(pagination);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
.where("c.workspace_id", numericWorkspaceId)
|
|
201
|
-
.where("c.created_by_user_id", numericActorUserId);
|
|
199
|
+
let query = createConversationBaseQuery(client).where("c.created_by_user_id", numericActorUserId);
|
|
200
|
+
query = applyWorkspaceScope(query, "c.workspace_id", workspaceId);
|
|
202
201
|
|
|
203
202
|
const normalizedStatus = normalizeText(filters.status).toLowerCase();
|
|
204
203
|
if (normalizedStatus) {
|
|
@@ -229,13 +228,13 @@ function createConversationsRepository(knex) {
|
|
|
229
228
|
|
|
230
229
|
return Object.freeze({
|
|
231
230
|
findById,
|
|
232
|
-
|
|
231
|
+
findByIdForActorScope,
|
|
233
232
|
create,
|
|
234
233
|
updateById,
|
|
235
234
|
incrementMessageCount,
|
|
236
|
-
|
|
235
|
+
listForActorScope,
|
|
237
236
|
transaction
|
|
238
237
|
});
|
|
239
238
|
}
|
|
240
239
|
|
|
241
|
-
export {
|
|
240
|
+
export { createRepository };
|
|
@@ -1,18 +1,32 @@
|
|
|
1
|
+
import { runInTransaction } from "@jskit-ai/database-runtime/shared/repositoryOptions";
|
|
1
2
|
import { parsePositiveInteger } from "@jskit-ai/kernel/server/runtime";
|
|
2
3
|
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
-
import { runInTransaction } from "@jskit-ai/database-runtime/shared/repositoryOptions";
|
|
4
4
|
import {
|
|
5
5
|
parseJsonObject,
|
|
6
|
+
resolveInsertedId,
|
|
6
7
|
stringifyJsonObject,
|
|
7
|
-
toIso
|
|
8
|
-
|
|
9
|
-
} from "
|
|
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
|
+
}
|
|
10
24
|
|
|
11
25
|
function mapMessageRow(row = {}) {
|
|
12
26
|
return {
|
|
13
27
|
id: Number(row.id),
|
|
14
28
|
conversationId: Number(row.conversation_id),
|
|
15
|
-
workspaceId:
|
|
29
|
+
workspaceId: normalizeWorkspaceId(row.workspace_id),
|
|
16
30
|
seq: Number(row.seq),
|
|
17
31
|
role: String(row.role || ""),
|
|
18
32
|
kind: String(row.kind || "chat"),
|
|
@@ -35,7 +49,10 @@ function normalizePagination(pagination = {}, { defaultPage = 1, defaultPageSize
|
|
|
35
49
|
}
|
|
36
50
|
|
|
37
51
|
async function resolveNextSequence(client, conversationId) {
|
|
38
|
-
const row = await client(
|
|
52
|
+
const row = await client(assistantRuntimeConfig.messagesTable)
|
|
53
|
+
.where({ conversation_id: conversationId })
|
|
54
|
+
.max({ maxSeq: "seq" })
|
|
55
|
+
.first();
|
|
39
56
|
const maxSeq = Number(row?.maxSeq || 0);
|
|
40
57
|
if (!Number.isInteger(maxSeq) || maxSeq < 0) {
|
|
41
58
|
return 1;
|
|
@@ -44,7 +61,7 @@ async function resolveNextSequence(client, conversationId) {
|
|
|
44
61
|
return maxSeq + 1;
|
|
45
62
|
}
|
|
46
63
|
|
|
47
|
-
function
|
|
64
|
+
function createRepository(knex) {
|
|
48
65
|
if (!knex || typeof knex !== "function") {
|
|
49
66
|
throw new Error("createMessagesRepository requires knex client.");
|
|
50
67
|
}
|
|
@@ -56,22 +73,24 @@ function createMessagesRepository(knex) {
|
|
|
56
73
|
}
|
|
57
74
|
|
|
58
75
|
const client = options?.trx || knex;
|
|
59
|
-
const row = await client(
|
|
76
|
+
const row = await client(assistantRuntimeConfig.messagesTable)
|
|
77
|
+
.where({ id: numericMessageId })
|
|
78
|
+
.first();
|
|
79
|
+
|
|
60
80
|
return row ? mapMessageRow(row) : null;
|
|
61
81
|
}
|
|
62
82
|
|
|
63
83
|
async function create(payload = {}, options = {}) {
|
|
64
84
|
const client = options?.trx || knex;
|
|
65
85
|
const conversationId = parsePositiveInteger(payload.conversationId);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
throw new TypeError("messagesRepository.create requires conversationId and workspaceId.");
|
|
86
|
+
if (!conversationId) {
|
|
87
|
+
throw new TypeError("messagesRepository.create requires conversationId.");
|
|
69
88
|
}
|
|
70
89
|
|
|
71
90
|
const seq = parsePositiveInteger(payload.seq) || (await resolveNextSequence(client, conversationId));
|
|
72
|
-
const insertResult = await client(
|
|
91
|
+
const insertResult = await client(assistantRuntimeConfig.messagesTable).insert({
|
|
73
92
|
conversation_id: conversationId,
|
|
74
|
-
workspace_id: workspaceId,
|
|
93
|
+
workspace_id: normalizeWorkspaceId(payload.workspaceId),
|
|
75
94
|
seq,
|
|
76
95
|
role: normalizeText(payload.role).toLowerCase(),
|
|
77
96
|
kind: normalizeText(payload.kind).toLowerCase() || "chat",
|
|
@@ -91,30 +110,26 @@ function createMessagesRepository(knex) {
|
|
|
91
110
|
});
|
|
92
111
|
}
|
|
93
112
|
|
|
94
|
-
async function
|
|
113
|
+
async function countByConversationScope(conversationId, { workspaceId = null } = {}, options = {}) {
|
|
95
114
|
const numericConversationId = parsePositiveInteger(conversationId);
|
|
96
|
-
|
|
97
|
-
if (!numericConversationId || !numericWorkspaceId) {
|
|
115
|
+
if (!numericConversationId) {
|
|
98
116
|
return 0;
|
|
99
117
|
}
|
|
100
118
|
|
|
101
119
|
const client = options?.trx || knex;
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
.count({ total: "*" })
|
|
108
|
-
.first();
|
|
120
|
+
const query = client(assistantRuntimeConfig.messagesTable).where({
|
|
121
|
+
conversation_id: numericConversationId
|
|
122
|
+
});
|
|
123
|
+
applyWorkspaceScope(query, "workspace_id", workspaceId);
|
|
124
|
+
const row = await query.count({ total: "*" }).first();
|
|
109
125
|
|
|
110
126
|
const total = Number(row?.total || 0);
|
|
111
127
|
return Number.isFinite(total) && total > 0 ? total : 0;
|
|
112
128
|
}
|
|
113
129
|
|
|
114
|
-
async function
|
|
130
|
+
async function listByConversationScope(conversationId, { workspaceId = null } = {}, pagination = {}, options = {}) {
|
|
115
131
|
const numericConversationId = parsePositiveInteger(conversationId);
|
|
116
|
-
|
|
117
|
-
if (!numericConversationId || !numericWorkspaceId) {
|
|
132
|
+
if (!numericConversationId) {
|
|
118
133
|
return [];
|
|
119
134
|
}
|
|
120
135
|
|
|
@@ -122,11 +137,11 @@ function createMessagesRepository(knex) {
|
|
|
122
137
|
const { page, pageSize } = normalizePagination(pagination);
|
|
123
138
|
const offset = (page - 1) * pageSize;
|
|
124
139
|
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
140
|
+
const query = client(assistantRuntimeConfig.messagesTable).where({
|
|
141
|
+
conversation_id: numericConversationId
|
|
142
|
+
});
|
|
143
|
+
applyWorkspaceScope(query, "workspace_id", workspaceId);
|
|
144
|
+
const rows = await query
|
|
130
145
|
.orderBy("seq", "asc")
|
|
131
146
|
.orderBy("id", "asc")
|
|
132
147
|
.limit(pageSize)
|
|
@@ -142,10 +157,10 @@ function createMessagesRepository(knex) {
|
|
|
142
157
|
return Object.freeze({
|
|
143
158
|
findById,
|
|
144
159
|
create,
|
|
145
|
-
|
|
146
|
-
|
|
160
|
+
countByConversationScope,
|
|
161
|
+
listByConversationScope,
|
|
147
162
|
transaction
|
|
148
163
|
});
|
|
149
164
|
}
|
|
150
165
|
|
|
151
|
-
export {
|
|
166
|
+
export { createRepository };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { AppError, parsePositiveInteger } from "@jskit-ai/kernel/server/runtime";
|
|
2
|
+
import { normalizeObject } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
+
import { resolveWorkspace } from "@jskit-ai/users-core/server/support/resolveWorkspace";
|
|
4
|
+
import { assistantRuntimeConfig } from "../../shared/assistantRuntimeConfig.js";
|
|
5
|
+
|
|
6
|
+
function createService({ assistantConfigRepository, consoleService = null } = {}) {
|
|
7
|
+
if (!assistantConfigRepository || typeof assistantConfigRepository.findByScope !== "function") {
|
|
8
|
+
throw new Error("assistantConfigService requires assistantConfigRepository.findByScope().");
|
|
9
|
+
}
|
|
10
|
+
if (typeof assistantConfigRepository.upsertByScope !== "function") {
|
|
11
|
+
throw new Error("assistantConfigService requires assistantConfigRepository.upsertByScope().");
|
|
12
|
+
}
|
|
13
|
+
if (typeof assistantConfigRepository.createDefaultRecord !== "function") {
|
|
14
|
+
throw new Error("assistantConfigService requires assistantConfigRepository.createDefaultRecord().");
|
|
15
|
+
}
|
|
16
|
+
if (
|
|
17
|
+
assistantRuntimeConfig.settingsSurfaceRequiresConsoleOwner &&
|
|
18
|
+
(!consoleService || typeof consoleService.requireConsoleOwner !== "function")
|
|
19
|
+
) {
|
|
20
|
+
throw new Error("assistantConfigService requires consoleService.requireConsoleOwner() for console-owner settings surfaces.");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function requireSettingsAccess(context = {}, options = {}) {
|
|
24
|
+
if (assistantRuntimeConfig.settingsSurfaceRequiresConsoleOwner !== true) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
await consoleService.requireConsoleOwner(context, options);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function resolveConfigWorkspaceId(workspace = null, input = {}, context = {}) {
|
|
32
|
+
if (assistantRuntimeConfig.configScope !== "workspace") {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const resolvedWorkspace = workspace || resolveWorkspace(context, input);
|
|
37
|
+
const workspaceId = parsePositiveInteger(resolvedWorkspace?.id);
|
|
38
|
+
if (!workspaceId) {
|
|
39
|
+
throw new AppError(409, "Workspace selection required.");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return workspaceId;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function getSettings(input = {}, options = {}) {
|
|
46
|
+
await requireSettingsAccess(options?.context, options);
|
|
47
|
+
|
|
48
|
+
const workspaceId = resolveConfigWorkspaceId(null, input, options?.context);
|
|
49
|
+
const existing = await assistantConfigRepository.findByScope({
|
|
50
|
+
targetSurfaceId: assistantRuntimeConfig.runtimeSurfaceId,
|
|
51
|
+
workspaceId
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return existing || assistantConfigRepository.createDefaultRecord({
|
|
55
|
+
targetSurfaceId: assistantRuntimeConfig.runtimeSurfaceId,
|
|
56
|
+
workspaceId
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function updateSettings(input = {}, patch = {}, options = {}) {
|
|
61
|
+
await requireSettingsAccess(options?.context, options);
|
|
62
|
+
|
|
63
|
+
const workspaceId = resolveConfigWorkspaceId(null, input, options?.context);
|
|
64
|
+
const normalizedPatch = normalizeObject(patch);
|
|
65
|
+
|
|
66
|
+
return assistantConfigRepository.upsertByScope({
|
|
67
|
+
targetSurfaceId: assistantRuntimeConfig.runtimeSurfaceId,
|
|
68
|
+
workspaceId,
|
|
69
|
+
patch: normalizedPatch
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function resolveSystemPrompt(workspace = null, _options = {}, serviceOptions = {}) {
|
|
74
|
+
const workspaceId = resolveConfigWorkspaceId(workspace, serviceOptions?.input || {}, serviceOptions?.context);
|
|
75
|
+
const existing = await assistantConfigRepository.findByScope({
|
|
76
|
+
targetSurfaceId: assistantRuntimeConfig.runtimeSurfaceId,
|
|
77
|
+
workspaceId
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return String(existing?.settings?.systemPrompt || "");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return Object.freeze({
|
|
84
|
+
getSettings,
|
|
85
|
+
updateSettings,
|
|
86
|
+
resolveSystemPrompt
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export { createService };
|