@jskit-ai/assistant 0.1.32 → 0.1.35
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
|
@@ -1,33 +1,21 @@
|
|
|
1
1
|
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
2
2
|
import { normalizeObject, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
-
import { createRepository as createConversationsRepository } from "./repositories/conversationsRepository.js";
|
|
4
|
-
import { createRepository as createMessagesRepository } from "./repositories/messagesRepository.js";
|
|
5
|
-
import { createRepository as createAssistantSettingsRepository } from "./repositories/assistantSettingsRepository.js";
|
|
6
|
-
import { createChatService } from "./services/chatService.js";
|
|
7
|
-
import {
|
|
8
|
-
createService as createAssistantSettingsService,
|
|
9
|
-
serviceEvents as assistantSettingsServiceEvents
|
|
10
|
-
} from "./services/assistantSettingsService.js";
|
|
11
3
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
4
|
+
DEFAULT_AI_TIMEOUT_MS,
|
|
5
|
+
createAiClient,
|
|
6
|
+
createServiceToolCatalog,
|
|
7
|
+
normalizeOptionalHttpUrl,
|
|
8
|
+
normalizeTimeoutMs
|
|
9
|
+
} from "@jskit-ai/assistant-core/server";
|
|
10
|
+
import { assistantRuntimeConfig } from "../shared/assistantRuntimeConfig.js";
|
|
18
11
|
import { assistantActions } from "./actions.js";
|
|
19
12
|
import { registerRoutes } from "./registerRoutes.js";
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return fallback;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return parsed;
|
|
30
|
-
}
|
|
13
|
+
import { createRepository as createAssistantConfigRepository } from "./repositories/assistantConfigRepository.js";
|
|
14
|
+
import { createRepository as createConversationsRepository } from "./repositories/conversationsRepository.js";
|
|
15
|
+
import { createRepository as createMessagesRepository } from "./repositories/messagesRepository.js";
|
|
16
|
+
import { createService as createAssistantConfigService } from "./services/assistantConfigService.js";
|
|
17
|
+
import { createChatService } from "./services/chatService.js";
|
|
18
|
+
import { createTranscriptService } from "./services/transcriptService.js";
|
|
31
19
|
|
|
32
20
|
function normalizeStringArray(value) {
|
|
33
21
|
const source = Array.isArray(value) ? value : [value];
|
|
@@ -45,7 +33,7 @@ function resolveAssistantConfig(scope) {
|
|
|
45
33
|
context: "assistant AI_BASE_URL"
|
|
46
34
|
});
|
|
47
35
|
const model = normalizeText(assistantConfig.model);
|
|
48
|
-
const timeoutMs =
|
|
36
|
+
const timeoutMs = normalizeTimeoutMs(
|
|
49
37
|
env.AI_TIMEOUT_MS || assistantConfig.timeoutMs,
|
|
50
38
|
DEFAULT_AI_TIMEOUT_MS
|
|
51
39
|
);
|
|
@@ -59,16 +47,15 @@ function resolveAssistantConfig(scope) {
|
|
|
59
47
|
model,
|
|
60
48
|
timeoutMs
|
|
61
49
|
}),
|
|
62
|
-
timeoutMs,
|
|
63
50
|
barredActionIds: normalizeStringArray(assistantConfig.barredActionIds),
|
|
64
51
|
toolSkipActionPrefixes: normalizeStringArray(assistantConfig.toolSkipActionPrefixes)
|
|
65
52
|
});
|
|
66
53
|
}
|
|
67
54
|
|
|
68
|
-
class
|
|
55
|
+
class AssistantProvider {
|
|
69
56
|
static id = "assistant.chat.service";
|
|
70
57
|
|
|
71
|
-
static dependsOn = ["runtime.actions", "runtime.database", "auth.policy.fastify", "users.core"
|
|
58
|
+
static dependsOn = ["runtime.actions", "runtime.database", "auth.policy.fastify", "users.core"];
|
|
72
59
|
|
|
73
60
|
register(app) {
|
|
74
61
|
if (
|
|
@@ -77,11 +64,16 @@ class AssistantServiceProvider {
|
|
|
77
64
|
typeof app.service !== "function" ||
|
|
78
65
|
typeof app.actions !== "function"
|
|
79
66
|
) {
|
|
80
|
-
throw new Error("
|
|
67
|
+
throw new Error("AssistantProvider requires application singleton()/service()/actions().");
|
|
81
68
|
}
|
|
82
69
|
|
|
83
70
|
const config = resolveAssistantConfig(app);
|
|
84
71
|
|
|
72
|
+
app.singleton("assistant.config.repository", (scope) => {
|
|
73
|
+
const knex = scope.make("jskit.database.knex");
|
|
74
|
+
return createAssistantConfigRepository(knex);
|
|
75
|
+
});
|
|
76
|
+
|
|
85
77
|
app.singleton("assistant.conversation.repository", (scope) => {
|
|
86
78
|
const knex = scope.make("jskit.database.knex");
|
|
87
79
|
return createConversationsRepository(knex);
|
|
@@ -92,46 +84,33 @@ class AssistantServiceProvider {
|
|
|
92
84
|
return createMessagesRepository(knex);
|
|
93
85
|
});
|
|
94
86
|
|
|
95
|
-
app.singleton("assistant.settings.repository", (scope) => {
|
|
96
|
-
const knex = scope.make("jskit.database.knex");
|
|
97
|
-
return createAssistantSettingsRepository(knex);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
87
|
app.singleton("assistant.ai.client", () => {
|
|
101
88
|
return createAiClient(config.ai);
|
|
102
89
|
});
|
|
103
90
|
|
|
104
91
|
app.singleton("assistant.service.tool-catalog", (scope) => {
|
|
105
|
-
const skipPrefixes = ["assistant.", ...config.toolSkipActionPrefixes];
|
|
106
|
-
|
|
107
92
|
return createServiceToolCatalog(scope, {
|
|
108
93
|
barredActionIds: config.barredActionIds,
|
|
109
|
-
skipActionPrefixes:
|
|
94
|
+
skipActionPrefixes: ["assistant.", ...config.toolSkipActionPrefixes]
|
|
110
95
|
});
|
|
111
96
|
});
|
|
112
97
|
|
|
113
98
|
app.service(
|
|
114
|
-
"assistant.
|
|
99
|
+
"assistant.config.service",
|
|
115
100
|
(scope) =>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
})
|
|
120
|
-
{
|
|
121
|
-
events: transcriptServiceEvents
|
|
122
|
-
}
|
|
101
|
+
createAssistantConfigService({
|
|
102
|
+
assistantConfigRepository: scope.make("assistant.config.repository"),
|
|
103
|
+
consoleService: scope.has("consoleService") ? scope.make("consoleService") : null
|
|
104
|
+
})
|
|
123
105
|
);
|
|
124
106
|
|
|
125
107
|
app.service(
|
|
126
|
-
"assistant.
|
|
108
|
+
"assistant.transcript.service",
|
|
127
109
|
(scope) =>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
})
|
|
132
|
-
{
|
|
133
|
-
events: assistantSettingsServiceEvents
|
|
134
|
-
}
|
|
110
|
+
createTranscriptService({
|
|
111
|
+
conversationsRepository: scope.make("assistant.conversation.repository"),
|
|
112
|
+
messagesRepository: scope.make("assistant.message.repository")
|
|
113
|
+
})
|
|
135
114
|
);
|
|
136
115
|
|
|
137
116
|
app.service(
|
|
@@ -141,11 +120,8 @@ class AssistantServiceProvider {
|
|
|
141
120
|
aiClient: scope.make("assistant.ai.client"),
|
|
142
121
|
transcriptService: scope.make("assistant.transcript.service"),
|
|
143
122
|
serviceToolCatalog: scope.make("assistant.service.tool-catalog"),
|
|
144
|
-
|
|
145
|
-
})
|
|
146
|
-
{
|
|
147
|
-
events: {}
|
|
148
|
-
}
|
|
123
|
+
assistantConfigService: scope.make("assistant.config.service")
|
|
124
|
+
})
|
|
149
125
|
);
|
|
150
126
|
|
|
151
127
|
app.actions(
|
|
@@ -153,7 +129,7 @@ class AssistantServiceProvider {
|
|
|
153
129
|
domain: "assistant",
|
|
154
130
|
dependencies: {
|
|
155
131
|
chatService: "assistant.chat.service",
|
|
156
|
-
|
|
132
|
+
assistantConfigService: "assistant.config.service"
|
|
157
133
|
}
|
|
158
134
|
})
|
|
159
135
|
);
|
|
@@ -164,4 +140,4 @@ class AssistantServiceProvider {
|
|
|
164
140
|
}
|
|
165
141
|
}
|
|
166
142
|
|
|
167
|
-
export {
|
|
143
|
+
export { AssistantProvider };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const actionIds = Object.freeze({
|
|
2
|
+
chatStream: "assistant.chat.stream",
|
|
3
|
+
conversationsList: "assistant.conversations.list",
|
|
4
|
+
conversationMessagesList: "assistant.conversation.messages.list",
|
|
5
|
+
settingsRead: "assistant.settings.read",
|
|
6
|
+
settingsUpdate: "assistant.settings.update"
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export { actionIds };
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EMPTY_INPUT_VALIDATOR
|
|
3
|
+
} from "@jskit-ai/kernel/shared/actions/actionContributorHelpers";
|
|
4
|
+
import { workspaceSlugParamsValidator } from "@jskit-ai/users-core/server/validators/routeParamsValidator";
|
|
5
|
+
import {
|
|
6
|
+
assistantConfigResource,
|
|
7
|
+
assistantResource
|
|
8
|
+
} from "@jskit-ai/assistant-core/shared";
|
|
9
|
+
import { assistantRuntimeConfig } from "../shared/assistantRuntimeConfig.js";
|
|
10
|
+
import { actionIds } from "./actionIds.js";
|
|
11
|
+
|
|
12
|
+
const runtimeSurfaces = Object.freeze([assistantRuntimeConfig.runtimeSurfaceId]);
|
|
13
|
+
const settingsSurfaces = Object.freeze([assistantRuntimeConfig.settingsSurfaceId]);
|
|
14
|
+
const runtimeVisibility = assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace ? "workspace" : "public";
|
|
15
|
+
const settingsVisibility = assistantRuntimeConfig.settingsSurfaceRequiresWorkspace ? "workspace" : "public";
|
|
16
|
+
|
|
17
|
+
const runtimeQueryInputValidator = assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace
|
|
18
|
+
? [workspaceSlugParamsValidator, { query: assistantResource.operations.conversationsList.queryValidator }]
|
|
19
|
+
: { query: assistantResource.operations.conversationsList.queryValidator };
|
|
20
|
+
|
|
21
|
+
const runtimeMessagesInputValidator = assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace
|
|
22
|
+
? [
|
|
23
|
+
workspaceSlugParamsValidator,
|
|
24
|
+
assistantResource.operations.conversationMessagesList.paramsValidator,
|
|
25
|
+
{
|
|
26
|
+
query: assistantResource.operations.conversationMessagesList.queryValidator
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
: [
|
|
30
|
+
assistantResource.operations.conversationMessagesList.paramsValidator,
|
|
31
|
+
{
|
|
32
|
+
query: assistantResource.operations.conversationMessagesList.queryValidator
|
|
33
|
+
}
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const runtimeChatInputValidator = assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace
|
|
37
|
+
? [workspaceSlugParamsValidator, assistantResource.operations.chatStream.bodyValidator]
|
|
38
|
+
: assistantResource.operations.chatStream.bodyValidator;
|
|
39
|
+
|
|
40
|
+
const settingsReadInputValidator = assistantRuntimeConfig.settingsSurfaceRequiresWorkspace
|
|
41
|
+
? workspaceSlugParamsValidator
|
|
42
|
+
: EMPTY_INPUT_VALIDATOR;
|
|
43
|
+
|
|
44
|
+
const settingsUpdateInputValidator = assistantRuntimeConfig.settingsSurfaceRequiresWorkspace
|
|
45
|
+
? [
|
|
46
|
+
workspaceSlugParamsValidator,
|
|
47
|
+
{
|
|
48
|
+
patch: assistantConfigResource.operations.patch.bodyValidator
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
: {
|
|
52
|
+
patch: assistantConfigResource.operations.patch.bodyValidator
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const settingsReadPermission = assistantRuntimeConfig.settingsSurfaceRequiresWorkspace
|
|
56
|
+
? {
|
|
57
|
+
require: "any",
|
|
58
|
+
permissions: ["workspace.settings.view", "workspace.settings.update"]
|
|
59
|
+
}
|
|
60
|
+
: {
|
|
61
|
+
require: "authenticated"
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const settingsUpdatePermission = assistantRuntimeConfig.settingsSurfaceRequiresWorkspace
|
|
65
|
+
? {
|
|
66
|
+
require: "all",
|
|
67
|
+
permissions: ["workspace.settings.update"]
|
|
68
|
+
}
|
|
69
|
+
: {
|
|
70
|
+
require: "authenticated"
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const assistantActions = Object.freeze([
|
|
74
|
+
{
|
|
75
|
+
id: actionIds.chatStream,
|
|
76
|
+
version: 1,
|
|
77
|
+
kind: "stream",
|
|
78
|
+
channels: ["api", "internal"],
|
|
79
|
+
surfaces: runtimeSurfaces,
|
|
80
|
+
permission: {
|
|
81
|
+
require: "authenticated"
|
|
82
|
+
},
|
|
83
|
+
inputValidator: runtimeChatInputValidator,
|
|
84
|
+
visibility: runtimeVisibility,
|
|
85
|
+
idempotency: "optional",
|
|
86
|
+
audit: {
|
|
87
|
+
actionName: actionIds.chatStream
|
|
88
|
+
},
|
|
89
|
+
observability: {},
|
|
90
|
+
async execute(input, context, deps) {
|
|
91
|
+
return deps.chatService.streamChat(input, {
|
|
92
|
+
context,
|
|
93
|
+
streamWriter: deps.streamWriter,
|
|
94
|
+
abortSignal: deps.abortSignal
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: actionIds.conversationsList,
|
|
100
|
+
version: 1,
|
|
101
|
+
kind: "query",
|
|
102
|
+
channels: ["api", "internal"],
|
|
103
|
+
surfaces: runtimeSurfaces,
|
|
104
|
+
permission: {
|
|
105
|
+
require: "authenticated"
|
|
106
|
+
},
|
|
107
|
+
inputValidator: runtimeQueryInputValidator,
|
|
108
|
+
outputValidator: assistantResource.operations.conversationsList.outputValidator,
|
|
109
|
+
visibility: runtimeVisibility,
|
|
110
|
+
idempotency: "none",
|
|
111
|
+
audit: {
|
|
112
|
+
actionName: actionIds.conversationsList
|
|
113
|
+
},
|
|
114
|
+
observability: {},
|
|
115
|
+
async execute(input, context, deps) {
|
|
116
|
+
return deps.chatService.listConversations(input.query, {
|
|
117
|
+
context,
|
|
118
|
+
input
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: actionIds.conversationMessagesList,
|
|
124
|
+
version: 1,
|
|
125
|
+
kind: "query",
|
|
126
|
+
channels: ["api", "internal"],
|
|
127
|
+
surfaces: runtimeSurfaces,
|
|
128
|
+
permission: {
|
|
129
|
+
require: "authenticated"
|
|
130
|
+
},
|
|
131
|
+
inputValidator: runtimeMessagesInputValidator,
|
|
132
|
+
outputValidator: assistantResource.operations.conversationMessagesList.outputValidator,
|
|
133
|
+
visibility: runtimeVisibility,
|
|
134
|
+
idempotency: "none",
|
|
135
|
+
audit: {
|
|
136
|
+
actionName: actionIds.conversationMessagesList
|
|
137
|
+
},
|
|
138
|
+
observability: {},
|
|
139
|
+
async execute(input, context, deps) {
|
|
140
|
+
return deps.chatService.getConversationMessages(input.conversationId, input.query, {
|
|
141
|
+
context,
|
|
142
|
+
input
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: actionIds.settingsRead,
|
|
148
|
+
version: 1,
|
|
149
|
+
kind: "query",
|
|
150
|
+
channels: ["api", "automation", "internal"],
|
|
151
|
+
surfaces: settingsSurfaces,
|
|
152
|
+
permission: settingsReadPermission,
|
|
153
|
+
inputValidator: settingsReadInputValidator,
|
|
154
|
+
outputValidator: assistantConfigResource.operations.view.outputValidator,
|
|
155
|
+
visibility: settingsVisibility,
|
|
156
|
+
idempotency: "none",
|
|
157
|
+
audit: {
|
|
158
|
+
actionName: actionIds.settingsRead
|
|
159
|
+
},
|
|
160
|
+
observability: {},
|
|
161
|
+
async execute(input, context, deps) {
|
|
162
|
+
return deps.assistantConfigService.getSettings(input, {
|
|
163
|
+
context
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: actionIds.settingsUpdate,
|
|
169
|
+
version: 1,
|
|
170
|
+
kind: "command",
|
|
171
|
+
channels: ["api", "automation", "internal"],
|
|
172
|
+
surfaces: settingsSurfaces,
|
|
173
|
+
permission: settingsUpdatePermission,
|
|
174
|
+
inputValidator: settingsUpdateInputValidator,
|
|
175
|
+
outputValidator: assistantConfigResource.operations.patch.outputValidator,
|
|
176
|
+
visibility: settingsVisibility,
|
|
177
|
+
idempotency: "optional",
|
|
178
|
+
audit: {
|
|
179
|
+
actionName: actionIds.settingsUpdate
|
|
180
|
+
},
|
|
181
|
+
observability: {},
|
|
182
|
+
async execute(input, context, deps) {
|
|
183
|
+
return deps.assistantConfigService.updateSettings(input, input.patch, {
|
|
184
|
+
context
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
export { assistantActions };
|
|
@@ -0,0 +1,296 @@
|
|
|
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 };
|