@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,4 @@
|
|
|
1
|
+
export { default as AssistantSurfaceClientElement } from "./components/AssistantSurfaceClientElement.vue";
|
|
2
|
+
export { default as AssistantSettingsClientElement } from "./components/AssistantSettingsClientElement.vue";
|
|
3
|
+
export { AssistantClientProvider } from "./providers/AssistantClientProvider.js";
|
|
4
|
+
export { useAssistantRuntime } from "./composables/useAssistantRuntime.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import AssistantSettingsClientElement from "../components/AssistantSettingsClientElement.vue";
|
|
2
|
+
|
|
3
|
+
class AssistantClientProvider {
|
|
4
|
+
static id = "assistant.web.client";
|
|
5
|
+
static dependsOn = ["users.web.client"];
|
|
6
|
+
|
|
7
|
+
register(app) {
|
|
8
|
+
if (!app || typeof app.singleton !== "function") {
|
|
9
|
+
throw new Error("AssistantClientProvider requires application singleton().");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
app.singleton("assistant.web.settings.element", () => AssistantSettingsClientElement);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { AssistantClientProvider };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
|
|
2
|
+
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
3
|
+
import { normalizeObject, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
4
|
+
import { createAiClient } from "@jskit-ai/assistant-core/server";
|
|
5
|
+
import { assistantActions } from "./actions.js";
|
|
6
|
+
import { registerRoutes } from "./registerRoutes.js";
|
|
7
|
+
import { createRepository as createAssistantConfigRepository } from "./repositories/assistantConfigRepository.js";
|
|
8
|
+
import { createRepository as createConversationsRepository } from "./repositories/conversationsRepository.js";
|
|
9
|
+
import { createRepository as createMessagesRepository } from "./repositories/messagesRepository.js";
|
|
10
|
+
import { createService as createAssistantConfigService } from "./services/assistantConfigService.js";
|
|
11
|
+
import { createChatService } from "./services/chatService.js";
|
|
12
|
+
import { createTranscriptService } from "./services/transcriptService.js";
|
|
13
|
+
import { resolveAssistantAiConfig } from "./support/assistantServerConfig.js";
|
|
14
|
+
import { createSurfaceAwareToolCatalog } from "./support/createSurfaceAwareToolCatalog.js";
|
|
15
|
+
|
|
16
|
+
function resolveGlobalAssistantConfig(scope) {
|
|
17
|
+
const appConfig = resolveAppConfig(scope);
|
|
18
|
+
const env = scope.has("jskit.env") ? normalizeObject(scope.make("jskit.env")) : {};
|
|
19
|
+
|
|
20
|
+
return Object.freeze({
|
|
21
|
+
appConfig,
|
|
22
|
+
env
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function createAssistantAiClientFactory(config = {}) {
|
|
27
|
+
const resolveCurrentAppConfig =
|
|
28
|
+
typeof config.resolveAppConfig === "function"
|
|
29
|
+
? () => normalizeObject(config.resolveAppConfig())
|
|
30
|
+
: () => normalizeObject(config.appConfig);
|
|
31
|
+
const env = normalizeObject(config.env);
|
|
32
|
+
const cache = new Map();
|
|
33
|
+
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
resolveClient(targetSurfaceId = "") {
|
|
36
|
+
const normalizedTargetSurfaceId = normalizeText(targetSurfaceId).toLowerCase();
|
|
37
|
+
if (!normalizedTargetSurfaceId) {
|
|
38
|
+
throw new Error("assistant.ai.client.factory.resolveClient requires targetSurfaceId.");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (cache.has(normalizedTargetSurfaceId)) {
|
|
42
|
+
return cache.get(normalizedTargetSurfaceId);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const assistantAiConfig = resolveAssistantAiConfig(
|
|
46
|
+
{
|
|
47
|
+
appConfig: resolveCurrentAppConfig(),
|
|
48
|
+
env
|
|
49
|
+
},
|
|
50
|
+
normalizedTargetSurfaceId
|
|
51
|
+
);
|
|
52
|
+
const client = createAiClient(assistantAiConfig.ai);
|
|
53
|
+
cache.set(normalizedTargetSurfaceId, client);
|
|
54
|
+
return client;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
class AssistantProvider {
|
|
60
|
+
static id = "assistant.chat.service";
|
|
61
|
+
|
|
62
|
+
static dependsOn = ["runtime.actions", "runtime.database", "auth.policy.fastify", "users.core"];
|
|
63
|
+
|
|
64
|
+
register(app) {
|
|
65
|
+
if (
|
|
66
|
+
!app ||
|
|
67
|
+
typeof app.singleton !== "function" ||
|
|
68
|
+
typeof app.service !== "function" ||
|
|
69
|
+
typeof app.actions !== "function"
|
|
70
|
+
) {
|
|
71
|
+
throw new Error("AssistantProvider requires application singleton()/service()/actions().");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const config = resolveGlobalAssistantConfig(app);
|
|
75
|
+
const resolveCurrentAppConfig = () => resolveAppConfig(app);
|
|
76
|
+
|
|
77
|
+
app.singleton("assistant.config.repository", (scope) => {
|
|
78
|
+
const knex = scope.make("jskit.database.knex");
|
|
79
|
+
return createAssistantConfigRepository(knex);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
app.singleton("assistant.conversation.repository", (scope) => {
|
|
83
|
+
const knex = scope.make("jskit.database.knex");
|
|
84
|
+
return createConversationsRepository(knex);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
app.singleton("assistant.message.repository", (scope) => {
|
|
88
|
+
const knex = scope.make("jskit.database.knex");
|
|
89
|
+
return createMessagesRepository(knex);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
app.singleton("assistant.ai.client.factory", () => {
|
|
93
|
+
return createAssistantAiClientFactory({
|
|
94
|
+
resolveAppConfig: resolveCurrentAppConfig,
|
|
95
|
+
env: config.env
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
app.singleton("assistant.service.tool-catalog", (scope) => {
|
|
100
|
+
return createSurfaceAwareToolCatalog(scope, {
|
|
101
|
+
resolveAppConfig: resolveCurrentAppConfig
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
app.service(
|
|
106
|
+
"assistant.config.service",
|
|
107
|
+
(scope) =>
|
|
108
|
+
createAssistantConfigService({
|
|
109
|
+
assistantConfigRepository: scope.make("assistant.config.repository"),
|
|
110
|
+
consoleService: scope.has("consoleService") ? scope.make("consoleService") : null,
|
|
111
|
+
resolveAppConfig: resolveCurrentAppConfig
|
|
112
|
+
})
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
app.service(
|
|
116
|
+
"assistant.transcript.service",
|
|
117
|
+
(scope) =>
|
|
118
|
+
createTranscriptService({
|
|
119
|
+
conversationsRepository: scope.make("assistant.conversation.repository"),
|
|
120
|
+
messagesRepository: scope.make("assistant.message.repository")
|
|
121
|
+
})
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
app.service(
|
|
125
|
+
"assistant.chat.service",
|
|
126
|
+
(scope) =>
|
|
127
|
+
createChatService({
|
|
128
|
+
aiClientFactory: scope.make("assistant.ai.client.factory"),
|
|
129
|
+
transcriptService: scope.make("assistant.transcript.service"),
|
|
130
|
+
serviceToolCatalog: scope.make("assistant.service.tool-catalog"),
|
|
131
|
+
assistantConfigService: scope.make("assistant.config.service"),
|
|
132
|
+
resolveAppConfig: resolveCurrentAppConfig
|
|
133
|
+
})
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
app.actions(
|
|
137
|
+
withActionDefaults(assistantActions, {
|
|
138
|
+
domain: "assistant",
|
|
139
|
+
dependencies: {
|
|
140
|
+
chatService: "assistant.chat.service",
|
|
141
|
+
assistantConfigService: "assistant.config.service"
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
boot(app) {
|
|
148
|
+
registerRoutes(app);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
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,151 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assistantConfigResource,
|
|
3
|
+
assistantResource
|
|
4
|
+
} from "@jskit-ai/assistant-core/shared";
|
|
5
|
+
import { actionIds } from "./actionIds.js";
|
|
6
|
+
import { assistantTargetSurfaceInputValidator } from "./inputValidators.js";
|
|
7
|
+
|
|
8
|
+
const runtimeQueryInputValidator = [
|
|
9
|
+
assistantTargetSurfaceInputValidator,
|
|
10
|
+
{ query: assistantResource.operations.conversationsList.queryValidator }
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const runtimeMessagesInputValidator = [
|
|
14
|
+
assistantTargetSurfaceInputValidator,
|
|
15
|
+
assistantResource.operations.conversationMessagesList.paramsValidator,
|
|
16
|
+
{
|
|
17
|
+
query: assistantResource.operations.conversationMessagesList.queryValidator
|
|
18
|
+
}
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const runtimeChatInputValidator = [
|
|
22
|
+
assistantTargetSurfaceInputValidator,
|
|
23
|
+
assistantResource.operations.chatStream.bodyValidator
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const settingsReadInputValidator = assistantTargetSurfaceInputValidator;
|
|
27
|
+
|
|
28
|
+
const settingsUpdateInputValidator = [
|
|
29
|
+
assistantTargetSurfaceInputValidator,
|
|
30
|
+
{
|
|
31
|
+
patch: assistantConfigResource.operations.patch.bodyValidator
|
|
32
|
+
}
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const assistantActions = Object.freeze([
|
|
36
|
+
{
|
|
37
|
+
id: actionIds.chatStream,
|
|
38
|
+
version: 1,
|
|
39
|
+
kind: "stream",
|
|
40
|
+
channels: ["api", "internal"],
|
|
41
|
+
surfacesFrom: "enabled",
|
|
42
|
+
permission: {
|
|
43
|
+
require: "authenticated"
|
|
44
|
+
},
|
|
45
|
+
inputValidator: runtimeChatInputValidator,
|
|
46
|
+
idempotency: "optional",
|
|
47
|
+
audit: {
|
|
48
|
+
actionName: actionIds.chatStream
|
|
49
|
+
},
|
|
50
|
+
observability: {},
|
|
51
|
+
async execute(input, context, deps) {
|
|
52
|
+
return deps.chatService.streamChat(input, {
|
|
53
|
+
context,
|
|
54
|
+
streamWriter: deps.streamWriter,
|
|
55
|
+
abortSignal: deps.abortSignal
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: actionIds.conversationsList,
|
|
61
|
+
version: 1,
|
|
62
|
+
kind: "query",
|
|
63
|
+
channels: ["api", "internal"],
|
|
64
|
+
surfacesFrom: "enabled",
|
|
65
|
+
permission: {
|
|
66
|
+
require: "authenticated"
|
|
67
|
+
},
|
|
68
|
+
inputValidator: runtimeQueryInputValidator,
|
|
69
|
+
outputValidator: assistantResource.operations.conversationsList.outputValidator,
|
|
70
|
+
idempotency: "none",
|
|
71
|
+
audit: {
|
|
72
|
+
actionName: actionIds.conversationsList
|
|
73
|
+
},
|
|
74
|
+
observability: {},
|
|
75
|
+
async execute(input, context, deps) {
|
|
76
|
+
return deps.chatService.listConversations(input.query, {
|
|
77
|
+
context,
|
|
78
|
+
input
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: actionIds.conversationMessagesList,
|
|
84
|
+
version: 1,
|
|
85
|
+
kind: "query",
|
|
86
|
+
channels: ["api", "internal"],
|
|
87
|
+
surfacesFrom: "enabled",
|
|
88
|
+
permission: {
|
|
89
|
+
require: "authenticated"
|
|
90
|
+
},
|
|
91
|
+
inputValidator: runtimeMessagesInputValidator,
|
|
92
|
+
outputValidator: assistantResource.operations.conversationMessagesList.outputValidator,
|
|
93
|
+
idempotency: "none",
|
|
94
|
+
audit: {
|
|
95
|
+
actionName: actionIds.conversationMessagesList
|
|
96
|
+
},
|
|
97
|
+
observability: {},
|
|
98
|
+
async execute(input, context, deps) {
|
|
99
|
+
return deps.chatService.getConversationMessages(input.conversationId, input.query, {
|
|
100
|
+
context,
|
|
101
|
+
input
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: actionIds.settingsRead,
|
|
107
|
+
version: 1,
|
|
108
|
+
kind: "query",
|
|
109
|
+
channels: ["api", "automation", "internal"],
|
|
110
|
+
surfacesFrom: "enabled",
|
|
111
|
+
permission: {
|
|
112
|
+
require: "authenticated"
|
|
113
|
+
},
|
|
114
|
+
inputValidator: settingsReadInputValidator,
|
|
115
|
+
outputValidator: assistantConfigResource.operations.view.outputValidator,
|
|
116
|
+
idempotency: "none",
|
|
117
|
+
audit: {
|
|
118
|
+
actionName: actionIds.settingsRead
|
|
119
|
+
},
|
|
120
|
+
observability: {},
|
|
121
|
+
async execute(input, context, deps) {
|
|
122
|
+
return deps.assistantConfigService.getSettings(input, {
|
|
123
|
+
context
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: actionIds.settingsUpdate,
|
|
129
|
+
version: 1,
|
|
130
|
+
kind: "command",
|
|
131
|
+
channels: ["api", "automation", "internal"],
|
|
132
|
+
surfacesFrom: "enabled",
|
|
133
|
+
permission: {
|
|
134
|
+
require: "authenticated"
|
|
135
|
+
},
|
|
136
|
+
inputValidator: settingsUpdateInputValidator,
|
|
137
|
+
outputValidator: assistantConfigResource.operations.patch.outputValidator,
|
|
138
|
+
idempotency: "optional",
|
|
139
|
+
audit: {
|
|
140
|
+
actionName: actionIds.settingsUpdate
|
|
141
|
+
},
|
|
142
|
+
observability: {},
|
|
143
|
+
async execute(input, context, deps) {
|
|
144
|
+
return deps.assistantConfigService.updateSettings(input, input.patch, {
|
|
145
|
+
context
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
export { assistantActions };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import { normalizeSurfaceId } from "@jskit-ai/kernel/shared/surface/registry";
|
|
3
|
+
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
4
|
+
|
|
5
|
+
const assistantSurfaceRouteParamsValidator = Object.freeze({
|
|
6
|
+
schema: Type.Object(
|
|
7
|
+
{
|
|
8
|
+
surfaceId: Type.String({ minLength: 1, maxLength: 64 })
|
|
9
|
+
},
|
|
10
|
+
{ additionalProperties: false }
|
|
11
|
+
),
|
|
12
|
+
normalize(value = {}) {
|
|
13
|
+
return {
|
|
14
|
+
surfaceId: normalizeSurfaceId(value?.surfaceId)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const assistantTargetSurfaceInputValidator = Object.freeze({
|
|
20
|
+
schema: Type.Object(
|
|
21
|
+
{
|
|
22
|
+
targetSurfaceId: Type.String({ minLength: 1, maxLength: 64 }),
|
|
23
|
+
workspaceSlug: Type.Optional(Type.String({ minLength: 1, maxLength: 160 }))
|
|
24
|
+
},
|
|
25
|
+
{ additionalProperties: false }
|
|
26
|
+
),
|
|
27
|
+
normalize(value = {}) {
|
|
28
|
+
const targetSurfaceId = normalizeSurfaceId(value?.targetSurfaceId);
|
|
29
|
+
const workspaceSlug = normalizeText(value?.workspaceSlug).toLowerCase();
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
targetSurfaceId,
|
|
33
|
+
...(workspaceSlug ? { workspaceSlug } : {})
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
assistantSurfaceRouteParamsValidator,
|
|
40
|
+
assistantTargetSurfaceInputValidator
|
|
41
|
+
};
|