@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.
Files changed (30) hide show
  1. package/package.descriptor.mjs +136 -0
  2. package/package.json +21 -0
  3. package/src/client/components/AssistantSettingsClientElement.vue +204 -0
  4. package/src/client/components/AssistantSurfaceClientElement.vue +19 -0
  5. package/src/client/composables/useAssistantRuntime.js +759 -0
  6. package/src/client/index.js +4 -0
  7. package/src/client/providers/AssistantClientProvider.js +16 -0
  8. package/src/server/AssistantProvider.js +152 -0
  9. package/src/server/actionIds.js +9 -0
  10. package/src/server/actions.js +151 -0
  11. package/src/server/inputValidators.js +41 -0
  12. package/src/server/registerRoutes.js +450 -0
  13. package/src/server/repositories/assistantConfigRepository.js +148 -0
  14. package/src/server/repositories/conversationsRepository.js +263 -0
  15. package/src/server/repositories/messagesRepository.js +166 -0
  16. package/src/server/services/assistantConfigService.js +132 -0
  17. package/src/server/services/chatService.js +1048 -0
  18. package/src/server/services/transcriptService.js +331 -0
  19. package/src/server/support/assistantServerConfig.js +106 -0
  20. package/src/server/support/createSurfaceAwareToolCatalog.js +64 -0
  21. package/src/shared/assistantRuntimeConfig.js +7 -0
  22. package/src/shared/assistantSurfaces.js +97 -0
  23. package/src/shared/index.js +7 -0
  24. package/templates/migrations/assistant_config_initial.cjs +27 -0
  25. package/templates/migrations/assistant_transcripts_initial.cjs +58 -0
  26. package/test/assistantServerConfig.test.js +72 -0
  27. package/test/assistantSurfaces.test.js +50 -0
  28. package/test/createSurfaceAwareToolCatalog.test.js +77 -0
  29. package/test/lazyAppConfig.test.js +248 -0
  30. 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
+ };