@jskit-ai/assistant-runtime 0.1.1
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 +136 -0
- package/package.json +21 -0
- package/src/client/components/AssistantSettingsClientElement.vue +204 -0
- package/src/client/components/AssistantSurfaceClientElement.vue +19 -0
- package/src/client/composables/useAssistantRuntime.js +759 -0
- package/src/client/index.js +4 -0
- package/src/client/providers/AssistantClientProvider.js +16 -0
- package/src/server/AssistantProvider.js +152 -0
- package/src/server/actionIds.js +9 -0
- package/src/server/actions.js +151 -0
- package/src/server/inputValidators.js +41 -0
- package/src/server/registerRoutes.js +450 -0
- package/src/server/repositories/assistantConfigRepository.js +148 -0
- package/src/server/repositories/conversationsRepository.js +263 -0
- package/src/server/repositories/messagesRepository.js +166 -0
- package/src/server/services/assistantConfigService.js +132 -0
- package/src/server/services/chatService.js +1048 -0
- package/src/server/services/transcriptService.js +331 -0
- package/src/server/support/assistantServerConfig.js +106 -0
- package/src/server/support/createSurfaceAwareToolCatalog.js +64 -0
- package/src/shared/assistantRuntimeConfig.js +7 -0
- package/src/shared/assistantSurfaces.js +97 -0
- package/src/shared/index.js +7 -0
- package/templates/migrations/assistant_config_initial.cjs +27 -0
- package/templates/migrations/assistant_transcripts_initial.cjs +58 -0
- package/test/assistantServerConfig.test.js +72 -0
- package/test/assistantSurfaces.test.js +50 -0
- package/test/createSurfaceAwareToolCatalog.test.js +77 -0
- package/test/lazyAppConfig.test.js +248 -0
- package/test/packageDescriptor.test.js +34 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
export default Object.freeze({
|
|
2
|
+
packageVersion: 1,
|
|
3
|
+
packageId: "@jskit-ai/assistant-runtime",
|
|
4
|
+
version: "0.1.1",
|
|
5
|
+
kind: "runtime",
|
|
6
|
+
description: "Shared assistant runtime with per-surface assistant registration.",
|
|
7
|
+
dependsOn: [
|
|
8
|
+
"@jskit-ai/assistant-core",
|
|
9
|
+
"@jskit-ai/database-runtime",
|
|
10
|
+
"@jskit-ai/http-runtime",
|
|
11
|
+
"@jskit-ai/shell-web",
|
|
12
|
+
"@jskit-ai/users-core",
|
|
13
|
+
"@jskit-ai/users-web"
|
|
14
|
+
],
|
|
15
|
+
capabilities: {
|
|
16
|
+
provides: ["assistant.runtime"],
|
|
17
|
+
requires: [
|
|
18
|
+
"runtime.actions",
|
|
19
|
+
"runtime.database",
|
|
20
|
+
"auth.policy",
|
|
21
|
+
"runtime.http-client",
|
|
22
|
+
"users.core",
|
|
23
|
+
"users.web"
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
runtime: {
|
|
27
|
+
server: {
|
|
28
|
+
providers: [
|
|
29
|
+
{
|
|
30
|
+
entrypoint: "src/server/AssistantProvider.js",
|
|
31
|
+
export: "AssistantProvider"
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
client: {
|
|
36
|
+
providers: [
|
|
37
|
+
{
|
|
38
|
+
entrypoint: "src/client/providers/AssistantClientProvider.js",
|
|
39
|
+
export: "AssistantClientProvider"
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
metadata: {
|
|
45
|
+
apiSummary: {
|
|
46
|
+
surfaces: [
|
|
47
|
+
{
|
|
48
|
+
subpath: "./client",
|
|
49
|
+
summary: "Exports assistant runtime client elements and composables."
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
subpath: "./shared",
|
|
53
|
+
summary: "Exports assistant runtime shared config helpers."
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
subpath: "./server/actionIds",
|
|
57
|
+
summary: "Exports assistant runtime action identifiers."
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
containerTokens: {
|
|
61
|
+
server: [
|
|
62
|
+
"assistant.config.repository",
|
|
63
|
+
"assistant.conversation.repository",
|
|
64
|
+
"assistant.message.repository",
|
|
65
|
+
"assistant.ai.client.factory",
|
|
66
|
+
"assistant.service.tool-catalog"
|
|
67
|
+
],
|
|
68
|
+
client: [
|
|
69
|
+
"assistant.web.settings.element"
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
mutations: {
|
|
75
|
+
dependencies: {
|
|
76
|
+
runtime: {
|
|
77
|
+
"@jskit-ai/assistant-core": "0.1.6",
|
|
78
|
+
"@jskit-ai/database-runtime": "0.1.30",
|
|
79
|
+
"@jskit-ai/http-runtime": "0.1.29",
|
|
80
|
+
"@jskit-ai/kernel": "0.1.30",
|
|
81
|
+
"@jskit-ai/shell-web": "0.1.29",
|
|
82
|
+
"@jskit-ai/users-core": "0.1.40",
|
|
83
|
+
"@jskit-ai/users-web": "0.1.45",
|
|
84
|
+
"@tanstack/vue-query": "^5.90.5",
|
|
85
|
+
"vuetify": "^4.0.0"
|
|
86
|
+
},
|
|
87
|
+
dev: {}
|
|
88
|
+
},
|
|
89
|
+
packageJson: {
|
|
90
|
+
scripts: {}
|
|
91
|
+
},
|
|
92
|
+
procfile: {},
|
|
93
|
+
files: [
|
|
94
|
+
{
|
|
95
|
+
op: "install-migration",
|
|
96
|
+
from: "templates/migrations/assistant_config_initial.cjs",
|
|
97
|
+
toDir: "migrations",
|
|
98
|
+
extension: ".cjs",
|
|
99
|
+
reason: "Install assistant configuration schema migration.",
|
|
100
|
+
category: "assistant-runtime",
|
|
101
|
+
id: "assistant-runtime-config-initial-schema"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
op: "install-migration",
|
|
105
|
+
from: "templates/migrations/assistant_transcripts_initial.cjs",
|
|
106
|
+
toDir: "migrations",
|
|
107
|
+
extension: ".cjs",
|
|
108
|
+
reason: "Install assistant transcript schema migration.",
|
|
109
|
+
category: "assistant-runtime",
|
|
110
|
+
id: "assistant-runtime-transcripts-initial-schema"
|
|
111
|
+
}
|
|
112
|
+
],
|
|
113
|
+
text: [
|
|
114
|
+
{
|
|
115
|
+
op: "append-text",
|
|
116
|
+
file: "config/public.js",
|
|
117
|
+
position: "bottom",
|
|
118
|
+
skipIfContains: "config.assistantSurfaces ||= {};",
|
|
119
|
+
value: "\nconfig.assistantSurfaces ||= {};\n",
|
|
120
|
+
reason: "Initialize the shared assistant surface registry in public app config.",
|
|
121
|
+
category: "assistant-runtime",
|
|
122
|
+
id: "assistant-runtime-public-surface-registry-init"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
op: "append-text",
|
|
126
|
+
file: "config/server.js",
|
|
127
|
+
position: "bottom",
|
|
128
|
+
skipIfContains: "config.assistantServer ||= {};",
|
|
129
|
+
value: "\nconfig.assistantServer ||= {};\n",
|
|
130
|
+
reason: "Initialize the shared assistant server config registry.",
|
|
131
|
+
category: "assistant-runtime",
|
|
132
|
+
id: "assistant-runtime-server-surface-registry-init"
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jskit-ai/assistant-runtime",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
"./client": "./src/client/index.js",
|
|
7
|
+
"./shared": "./src/shared/index.js",
|
|
8
|
+
"./server/actionIds": "./src/server/actionIds.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@jskit-ai/assistant-core": "0.1.6",
|
|
12
|
+
"@jskit-ai/database-runtime": "0.1.30",
|
|
13
|
+
"@jskit-ai/http-runtime": "0.1.29",
|
|
14
|
+
"@jskit-ai/kernel": "0.1.30",
|
|
15
|
+
"@jskit-ai/shell-web": "0.1.29",
|
|
16
|
+
"@jskit-ai/users-core": "0.1.40",
|
|
17
|
+
"@jskit-ai/users-web": "0.1.45",
|
|
18
|
+
"@tanstack/vue-query": "^5.90.5",
|
|
19
|
+
"vuetify": "^4.0.0"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<AssistantSettingsFormCard
|
|
3
|
+
root-class="assistant-settings-client-element"
|
|
4
|
+
title="Assistant settings"
|
|
5
|
+
:subtitle="subtitle"
|
|
6
|
+
no-permission-message="You do not have permission to view assistant settings."
|
|
7
|
+
save-label="Save assistant settings"
|
|
8
|
+
:add-edit="addEdit"
|
|
9
|
+
:show-form-skeleton="showFormSkeleton"
|
|
10
|
+
>
|
|
11
|
+
<v-textarea
|
|
12
|
+
v-model="form.systemPrompt"
|
|
13
|
+
label="System prompt"
|
|
14
|
+
variant="outlined"
|
|
15
|
+
density="comfortable"
|
|
16
|
+
rows="8"
|
|
17
|
+
auto-grow
|
|
18
|
+
:readonly="!addEdit.canSave || addEdit.isSaving || addEdit.isRefetching"
|
|
19
|
+
:error-messages="fieldErrors.systemPrompt ? [fieldErrors.systemPrompt] : []"
|
|
20
|
+
/>
|
|
21
|
+
</AssistantSettingsFormCard>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup>
|
|
25
|
+
import { computed, reactive, watch } from "vue";
|
|
26
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
|
|
27
|
+
import { getClientAppConfig } from "@jskit-ai/kernel/client";
|
|
28
|
+
import { normalizeObject, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
29
|
+
import { validateOperationSection } from "@jskit-ai/http-runtime/shared/validators/operationValidation";
|
|
30
|
+
import { assistantHttpClient, createAssistantApi, AssistantSettingsFormCard } from "@jskit-ai/assistant-core/client";
|
|
31
|
+
import { assistantConfigResource, assistantSettingsQueryKey, buildAssistantApiPath } from "@jskit-ai/assistant-core/shared";
|
|
32
|
+
import { useShellWebErrorRuntime } from "@jskit-ai/shell-web/client/error";
|
|
33
|
+
import { useWorkspaceRouteContext } from "@jskit-ai/users-web/client/composables/useWorkspaceRouteContext";
|
|
34
|
+
import { resolveAssistantSurfaceConfig } from "../../shared/assistantSurfaces.js";
|
|
35
|
+
|
|
36
|
+
const props = defineProps({
|
|
37
|
+
targetSurfaceId: {
|
|
38
|
+
type: String,
|
|
39
|
+
required: true
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const form = reactive({
|
|
44
|
+
systemPrompt: ""
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const fieldErrors = reactive({
|
|
48
|
+
systemPrompt: ""
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const errorRuntime = useShellWebErrorRuntime();
|
|
52
|
+
const queryClient = useQueryClient();
|
|
53
|
+
const appConfig = getClientAppConfig();
|
|
54
|
+
const { placementContext, currentSurfaceId, workspaceSlugFromRoute } = useWorkspaceRouteContext();
|
|
55
|
+
|
|
56
|
+
const assistantSurface = computed(() => resolveAssistantSurfaceConfig(appConfig, props.targetSurfaceId));
|
|
57
|
+
const placementSnapshot = computed(() => normalizeObject(placementContext.value));
|
|
58
|
+
const scope = computed(() => {
|
|
59
|
+
const settingsRequiresWorkspace = assistantSurface.value?.settingsSurfaceRequiresWorkspace === true;
|
|
60
|
+
const workspaceSlug = settingsRequiresWorkspace
|
|
61
|
+
? normalizeText(workspaceSlugFromRoute.value).toLowerCase()
|
|
62
|
+
: "";
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
targetSurfaceId: normalizeText(assistantSurface.value?.targetSurfaceId).toLowerCase(),
|
|
66
|
+
workspaceSlug,
|
|
67
|
+
workspaceId: settingsRequiresWorkspace
|
|
68
|
+
? Number(placementSnapshot.value?.workspace?.id || 0) || 0
|
|
69
|
+
: 0
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
const hasScope = computed(() =>
|
|
73
|
+
Boolean(assistantSurface.value) &&
|
|
74
|
+
(assistantSurface.value?.settingsSurfaceRequiresWorkspace ? Boolean(scope.value.workspaceSlug) : true)
|
|
75
|
+
);
|
|
76
|
+
const queryKey = computed(() => assistantSettingsQueryKey(scope.value));
|
|
77
|
+
|
|
78
|
+
const settingsApi = createAssistantApi({
|
|
79
|
+
request: assistantHttpClient.request,
|
|
80
|
+
requestStream: assistantHttpClient.requestStream,
|
|
81
|
+
resolveBasePath: () =>
|
|
82
|
+
buildAssistantApiPath({
|
|
83
|
+
requiresWorkspace: assistantSurface.value?.settingsSurfaceRequiresWorkspace === true,
|
|
84
|
+
workspaceSlug: scope.value.workspaceSlug,
|
|
85
|
+
suffix: `/${scope.value.targetSurfaceId}`
|
|
86
|
+
}),
|
|
87
|
+
resolveSurfaceId: () => normalizeText(currentSurfaceId.value).toLowerCase()
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const subtitle = computed(() => {
|
|
91
|
+
if (!assistantSurface.value) {
|
|
92
|
+
return "Configure assistant settings.";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (assistantSurface.value.configScope === "workspace") {
|
|
96
|
+
return `Configure the prompt used on the ${assistantSurface.value.targetSurfaceId} surface for this workspace.`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return `Configure the prompt used on the ${assistantSurface.value.targetSurfaceId} surface.`;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
function clearFieldErrors() {
|
|
103
|
+
fieldErrors.systemPrompt = "";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function reportAssistantSettingsError(message, dedupeKey) {
|
|
107
|
+
const normalizedMessage = normalizeText(message);
|
|
108
|
+
if (!normalizedMessage) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
errorRuntime.report({
|
|
113
|
+
source: "assistant.settings",
|
|
114
|
+
message: normalizedMessage,
|
|
115
|
+
severity: "error",
|
|
116
|
+
channel: "banner",
|
|
117
|
+
dedupeKey,
|
|
118
|
+
dedupeWindowMs: 3000
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const settingsQuery = useQuery({
|
|
123
|
+
queryKey,
|
|
124
|
+
enabled: hasScope,
|
|
125
|
+
retry: false,
|
|
126
|
+
queryFn: () => settingsApi.getSettings()
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
watch(
|
|
130
|
+
() => settingsQuery.data.value,
|
|
131
|
+
(payload) => {
|
|
132
|
+
const settings = payload?.settings && typeof payload.settings === "object" ? payload.settings : {};
|
|
133
|
+
form.systemPrompt = String(settings.systemPrompt || "");
|
|
134
|
+
clearFieldErrors();
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
immediate: true
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const settingsMutation = useMutation({
|
|
142
|
+
mutationFn: (patch) => settingsApi.updateSettings(patch),
|
|
143
|
+
onSuccess(payload) {
|
|
144
|
+
queryClient.setQueryData(queryKey.value, payload);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const loadError = computed(() => {
|
|
149
|
+
if (!assistantSurface.value) {
|
|
150
|
+
return "Assistant settings are not configured for this surface.";
|
|
151
|
+
}
|
|
152
|
+
if (hasScope.value) {
|
|
153
|
+
return normalizeText(settingsQuery.error.value?.message);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (assistantSurface.value?.settingsSurfaceRequiresWorkspace) {
|
|
157
|
+
return "Select a workspace to configure assistant settings.";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return "";
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
async function submit() {
|
|
164
|
+
clearFieldErrors();
|
|
165
|
+
|
|
166
|
+
const validation = validateOperationSection({
|
|
167
|
+
operation: assistantConfigResource.operations.patch,
|
|
168
|
+
section: "bodyValidator",
|
|
169
|
+
value: {
|
|
170
|
+
systemPrompt: form.systemPrompt
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
if (!validation.ok) {
|
|
174
|
+
fieldErrors.systemPrompt = String(validation.fieldErrors.systemPrompt || "");
|
|
175
|
+
if (validation.globalErrors.length > 0) {
|
|
176
|
+
reportAssistantSettingsError(validation.globalErrors[0], "assistant.settings:validation");
|
|
177
|
+
}
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
await settingsMutation.mutateAsync(validation.value);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
const nextFieldErrors = normalizeObject(error?.details?.fieldErrors);
|
|
185
|
+
fieldErrors.systemPrompt = String(nextFieldErrors.systemPrompt || "");
|
|
186
|
+
reportAssistantSettingsError(
|
|
187
|
+
normalizeText(error?.message) || "Unable to update assistant settings.",
|
|
188
|
+
"assistant.settings:save"
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const addEdit = computed(() => ({
|
|
194
|
+
canView: Boolean(assistantSurface.value) && hasScope.value,
|
|
195
|
+
canSave: Boolean(assistantSurface.value) && hasScope.value,
|
|
196
|
+
loadError: loadError.value,
|
|
197
|
+
isInitialLoading: Boolean(settingsQuery.isLoading.value),
|
|
198
|
+
isRefetching: Boolean(settingsQuery.isFetching.value && !settingsQuery.isLoading.value),
|
|
199
|
+
isSaving: Boolean(settingsMutation.isPending.value),
|
|
200
|
+
submit
|
|
201
|
+
}));
|
|
202
|
+
|
|
203
|
+
const showFormSkeleton = computed(() => Boolean(settingsQuery.isLoading.value));
|
|
204
|
+
</script>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<AssistantClientElement v-bind="runtime" />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup>
|
|
6
|
+
import { AssistantClientElement } from "@jskit-ai/assistant-core/client";
|
|
7
|
+
import { useAssistantRuntime } from "../composables/useAssistantRuntime.js";
|
|
8
|
+
|
|
9
|
+
const props = defineProps({
|
|
10
|
+
surfaceId: {
|
|
11
|
+
type: String,
|
|
12
|
+
required: true
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const runtime = useAssistantRuntime({
|
|
17
|
+
surfaceId: props.surfaceId
|
|
18
|
+
});
|
|
19
|
+
</script>
|