@sweetoburrito/backstage-plugin-ai-assistant-backend 0.11.0 → 0.12.0
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/dist/database/user-settings-store.cjs.js +42 -0
- package/dist/database/user-settings-store.cjs.js.map +1 -0
- package/dist/plugin.cjs.js +14 -6
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/services/chat.cjs.js +23 -3
- package/dist/services/chat.cjs.js.map +1 -1
- package/dist/services/mcp/helpers.cjs.js +31 -0
- package/dist/services/mcp/helpers.cjs.js.map +1 -0
- package/dist/services/mcp/index.cjs.js +150 -0
- package/dist/services/mcp/index.cjs.js.map +1 -0
- package/dist/services/router/chat.cjs.js +17 -5
- package/dist/services/router/chat.cjs.js.map +1 -1
- package/dist/services/router/index.cjs.js +2 -2
- package/dist/services/router/index.cjs.js.map +1 -1
- package/dist/services/router/settings/index.cjs.js +51 -0
- package/dist/services/router/settings/index.cjs.js.map +1 -0
- package/dist/services/router/{mcp.cjs.js → settings/mcp.cjs.js} +3 -3
- package/dist/services/router/settings/mcp.cjs.js.map +1 -0
- package/dist/services/user-settings.cjs.js +34 -0
- package/dist/services/user-settings.cjs.js.map +1 -0
- package/dist/{services/tools → tools}/searchKnowledge.cjs.js +1 -1
- package/dist/tools/searchKnowledge.cjs.js.map +1 -0
- package/migrations/20251124_user_settings.js +131 -0
- package/package.json +3 -3
- package/dist/database/mcp-store.cjs.js +0 -46
- package/dist/database/mcp-store.cjs.js.map +0 -1
- package/dist/services/mcp.cjs.js +0 -121
- package/dist/services/mcp.cjs.js.map +0 -1
- package/dist/services/router/mcp.cjs.js.map +0 -1
- package/dist/services/tools/searchKnowledge.cjs.js.map +0 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const USER_SETTINGS_TABLE = "user_ai_assistant_settings";
|
|
4
|
+
class UserSettingsStore {
|
|
5
|
+
/**
|
|
6
|
+
* Creates an instance of UserSettingsStore.
|
|
7
|
+
* @param client - The Knex client to interact with the PostgreSQL database.
|
|
8
|
+
*/
|
|
9
|
+
constructor(client) {
|
|
10
|
+
this.client = client;
|
|
11
|
+
}
|
|
12
|
+
static async fromConfig({ database }) {
|
|
13
|
+
const client = await database.getClient();
|
|
14
|
+
return new UserSettingsStore(client);
|
|
15
|
+
}
|
|
16
|
+
userSettingsTable() {
|
|
17
|
+
return this.client(USER_SETTINGS_TABLE);
|
|
18
|
+
}
|
|
19
|
+
async getUserSettings(userRef) {
|
|
20
|
+
const rows = await this.userSettingsTable().where({ userRef }).select();
|
|
21
|
+
return rows.map((row) => {
|
|
22
|
+
const data = JSON.parse(row.data);
|
|
23
|
+
return { type: row.type, data };
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
async getUserSettingsByType(userRef, type) {
|
|
27
|
+
const row = await this.userSettingsTable().where({ userRef, type }).first();
|
|
28
|
+
if (!row) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return row.data;
|
|
32
|
+
}
|
|
33
|
+
async setUserSettings(userRef, type, data) {
|
|
34
|
+
await this.userSettingsTable().insert({ userRef, type, data }).onConflict(["userRef", "type"]).merge({ data, updated_at: this.client.fn.now() });
|
|
35
|
+
}
|
|
36
|
+
async deleteUserSettings(userRef, type) {
|
|
37
|
+
await this.userSettingsTable().where({ userRef, type }).delete();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
exports.UserSettingsStore = UserSettingsStore;
|
|
42
|
+
//# sourceMappingURL=user-settings-store.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-settings-store.cjs.js","sources":["../../src/database/user-settings-store.ts"],"sourcesContent":["import { DatabaseService } from '@backstage/backend-plugin-api';\n\nimport { Knex } from 'knex';\n\nconst USER_SETTINGS_TABLE = 'user_ai_assistant_settings';\n\nexport type UserSettingsStoreOptions = {\n database: DatabaseService;\n};\n\nexport class UserSettingsStore {\n /**\n * Creates an instance of UserSettingsStore.\n * @param client - The Knex client to interact with the PostgreSQL database.\n */\n constructor(private readonly client: Knex) {}\n\n static async fromConfig({ database }: UserSettingsStoreOptions) {\n const client = await database.getClient();\n return new UserSettingsStore(client);\n }\n\n userSettingsTable() {\n return this.client(USER_SETTINGS_TABLE);\n }\n\n async getUserSettings<T extends Record<string, unknown>>(\n userRef: string,\n ): Promise<Array<{ type: string; data: Partial<T> }>> {\n const rows = await this.userSettingsTable().where({ userRef }).select();\n return rows.map(row => {\n const data: T = JSON.parse(row.data);\n\n return { type: row.type, data };\n });\n }\n\n async getUserSettingsByType<T extends Record<string, unknown>>(\n userRef: string,\n type: string,\n ): Promise<T | null> {\n const row = await this.userSettingsTable().where({ userRef, type }).first();\n if (!row) {\n return null;\n }\n\n return row.data;\n }\n\n async setUserSettings<T extends Record<string, unknown>>(\n userRef: string,\n type: string,\n data: Partial<T>,\n ): Promise<void> {\n await this.userSettingsTable()\n .insert({ userRef, type, data })\n .onConflict(['userRef', 'type'])\n .merge({ data, updated_at: this.client.fn.now() });\n }\n\n async deleteUserSettings(userRef: string, type: string): Promise<void> {\n await this.userSettingsTable().where({ userRef, type }).delete();\n }\n}\n"],"names":[],"mappings":";;AAIA,MAAM,mBAAA,GAAsB,4BAAA;AAMrB,MAAM,iBAAA,CAAkB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK7B,YAA6B,MAAA,EAAc;AAAd,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAe;AAAA,EAE5C,aAAa,UAAA,CAAW,EAAE,QAAA,EAAS,EAA6B;AAC9D,IAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,SAAA,EAAU;AACxC,IAAA,OAAO,IAAI,kBAAkB,MAAM,CAAA;AAAA,EACrC;AAAA,EAEA,iBAAA,GAAoB;AAClB,IAAA,OAAO,IAAA,CAAK,OAAO,mBAAmB,CAAA;AAAA,EACxC;AAAA,EAEA,MAAM,gBACJ,OAAA,EACoD;AACpD,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,iBAAA,EAAkB,CAAE,MAAM,EAAE,OAAA,EAAS,CAAA,CAAE,MAAA,EAAO;AACtE,IAAA,OAAO,IAAA,CAAK,IAAI,CAAA,GAAA,KAAO;AACrB,MAAA,MAAM,IAAA,GAAU,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,IAAI,CAAA;AAEnC,MAAA,OAAO,EAAE,IAAA,EAAM,GAAA,CAAI,IAAA,EAAM,IAAA,EAAK;AAAA,IAChC,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,qBAAA,CACJ,OAAA,EACA,IAAA,EACmB;AACnB,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,iBAAA,EAAkB,CAAE,KAAA,CAAM,EAAE,OAAA,EAAS,IAAA,EAAM,CAAA,CAAE,KAAA,EAAM;AAC1E,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAO,GAAA,CAAI,IAAA;AAAA,EACb;AAAA,EAEA,MAAM,eAAA,CACJ,OAAA,EACA,IAAA,EACA,IAAA,EACe;AACf,IAAA,MAAM,IAAA,CAAK,iBAAA,EAAkB,CAC1B,MAAA,CAAO,EAAE,SAAS,IAAA,EAAM,IAAA,EAAM,CAAA,CAC9B,UAAA,CAAW,CAAC,WAAW,MAAM,CAAC,CAAA,CAC9B,KAAA,CAAM,EAAE,IAAA,EAAM,UAAA,EAAY,IAAA,CAAK,MAAA,CAAO,EAAA,CAAG,GAAA,EAAI,EAAG,CAAA;AAAA,EACrD;AAAA,EAEA,MAAM,kBAAA,CAAmB,OAAA,EAAiB,IAAA,EAA6B;AACrE,IAAA,MAAM,IAAA,CAAK,mBAAkB,CAAE,KAAA,CAAM,EAAE,OAAA,EAAS,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO;AAAA,EACjE;AACF;;;;"}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
|
-
var index = require('./services/router/index.cjs.js');
|
|
4
|
+
var index$1 = require('./services/router/index.cjs.js');
|
|
5
5
|
var backstagePluginAiAssistantNode = require('@sweetoburrito/backstage-plugin-ai-assistant-node');
|
|
6
6
|
var ingestor = require('./services/ingestor.cjs.js');
|
|
7
7
|
var chat = require('./services/chat.cjs.js');
|
|
8
8
|
var migrations = require('./database/migrations.cjs.js');
|
|
9
9
|
var pgVectorStore = require('./database/pg-vector-store.cjs.js');
|
|
10
10
|
var pluginSignalsNode = require('@backstage/plugin-signals-node');
|
|
11
|
-
var searchKnowledge = require('./
|
|
11
|
+
var searchKnowledge = require('./tools/searchKnowledge.cjs.js');
|
|
12
12
|
var pluginCatalogNode = require('@backstage/plugin-catalog-node');
|
|
13
|
-
var
|
|
13
|
+
var index = require('./services/mcp/index.cjs.js');
|
|
14
14
|
var callbacks = require('./services/callbacks.cjs.js');
|
|
15
15
|
var summarizer = require('./services/summarizer.cjs.js');
|
|
16
|
+
var userSettings = require('./services/user-settings.cjs.js');
|
|
16
17
|
|
|
17
18
|
const aiAssistantPlugin = backendPluginApi.createBackendPlugin({
|
|
18
19
|
pluginId: "ai-assistant",
|
|
@@ -89,7 +90,7 @@ const aiAssistantPlugin = backendPluginApi.createBackendPlugin({
|
|
|
89
90
|
vectorStore,
|
|
90
91
|
ingestors
|
|
91
92
|
});
|
|
92
|
-
const mcp
|
|
93
|
+
const mcp = await index.createMcpService(options);
|
|
93
94
|
const searchKnowledgeTool = searchKnowledge.createSearchKnowledgeTool({ vectorStore });
|
|
94
95
|
tools.push(searchKnowledgeTool);
|
|
95
96
|
const callback = await callbacks.createCallbackService({
|
|
@@ -104,12 +105,19 @@ const aiAssistantPlugin = backendPluginApi.createBackendPlugin({
|
|
|
104
105
|
...options,
|
|
105
106
|
models,
|
|
106
107
|
tools,
|
|
107
|
-
mcp
|
|
108
|
+
mcp,
|
|
108
109
|
callback,
|
|
109
110
|
summarizer: summarizer$1
|
|
110
111
|
});
|
|
112
|
+
const userSettings$1 = await userSettings.createUserSettingsService(options);
|
|
111
113
|
httpRouter.use(
|
|
112
|
-
await index.createRouter({
|
|
114
|
+
await index$1.createRouter({
|
|
115
|
+
...options,
|
|
116
|
+
chat: chat$1,
|
|
117
|
+
mcp,
|
|
118
|
+
summarizer: summarizer$1,
|
|
119
|
+
userSettings: userSettings$1
|
|
120
|
+
})
|
|
113
121
|
);
|
|
114
122
|
dataIngestionPipeline.start();
|
|
115
123
|
}
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["import {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './services/router';\nimport {\n dataIngestorExtensionPoint,\n EmbeddingsProvider,\n embeddingsProviderExtensionPoint,\n Ingestor,\n Model,\n modelProviderExtensionPoint,\n
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["import {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './services/router';\nimport {\n dataIngestorExtensionPoint,\n EmbeddingsProvider,\n embeddingsProviderExtensionPoint,\n Ingestor,\n Model,\n modelProviderExtensionPoint,\n toolExtensionPoint,\n callbackProviderExtensionPoint,\n CallbackProvider,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { Tool } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { createDataIngestionPipeline } from './services/ingestor';\nimport { createChatService } from './services/chat';\nimport { applyDatabaseMigrations } from './database/migrations';\nimport { PgVectorStore } from './database';\nimport { signalsServiceRef } from '@backstage/plugin-signals-node';\nimport { createSearchKnowledgeTool } from './tools/searchKnowledge';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node';\nimport { createMcpService } from './services/mcp';\nimport { createCallbackService } from './services/callbacks';\nimport { createSummarizerService } from './services/summarizer';\nimport { createUserSettingsService } from './services/user-settings';\n/**\n * aiAssistantPlugin backend plugin\n *\n * @public\n */\n\nexport const aiAssistantPlugin = createBackendPlugin({\n pluginId: 'ai-assistant',\n register(env) {\n const ingestors: Ingestor[] = [];\n const models: Model[] = [];\n const tools: Tool[] = [];\n const callbacks: CallbackProvider[] = [];\n\n let embeddingsProvider: EmbeddingsProvider;\n\n env.registerExtensionPoint(dataIngestorExtensionPoint, {\n registerIngestor: ingestor => {\n const existingIngestor = ingestors.find(i => i.id === ingestor.id);\n if (existingIngestor) {\n throw new Error(\n `Ingestor with id ${ingestor.id} is already registered.`,\n );\n }\n ingestors.push(ingestor);\n },\n });\n\n env.registerExtensionPoint(embeddingsProviderExtensionPoint, {\n register: provider => {\n embeddingsProvider = provider;\n },\n });\n\n env.registerExtensionPoint(modelProviderExtensionPoint, {\n register: model => {\n const existingModel = models.find(m => m.id === model.id);\n if (existingModel) {\n throw new Error(`Model with id ${model.id} is already registered.`);\n }\n models.push(model);\n },\n });\n\n env.registerExtensionPoint(toolExtensionPoint, {\n register: tool => {\n const existingTool = tools.find(t => t.name === tool.name);\n if (existingTool) {\n throw new Error(`Tool with name ${tool.name} is already registered.`);\n }\n tools.push(tool);\n },\n });\n\n env.registerExtensionPoint(callbackProviderExtensionPoint, {\n register: callbackProvider => {\n callbacks.push(callbackProvider);\n },\n });\n\n env.registerInit({\n deps: {\n httpRouter: coreServices.httpRouter,\n database: coreServices.database,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n scheduler: coreServices.scheduler,\n httpAuth: coreServices.httpAuth,\n userInfo: coreServices.userInfo,\n signals: signalsServiceRef,\n catalog: catalogServiceRef,\n cache: coreServices.cache,\n auth: coreServices.auth,\n },\n\n async init(options) {\n const { httpRouter, database, config } = options;\n\n const client = await database.getClient();\n\n await applyDatabaseMigrations(client);\n\n const vectorStore = await PgVectorStore.fromConfig(options);\n\n if (!embeddingsProvider) {\n throw new Error('No Embeddings Provider was registered.');\n }\n\n vectorStore.connectEmbeddings(await embeddingsProvider.getEmbeddings());\n\n const dataIngestionPipeline = createDataIngestionPipeline({\n ...options,\n vectorStore,\n ingestors,\n });\n\n const mcp = await createMcpService(options);\n\n const searchKnowledgeTool = createSearchKnowledgeTool({ vectorStore });\n tools.push(searchKnowledgeTool);\n\n const callback = await createCallbackService({\n callbacks,\n });\n\n const summarizer = await createSummarizerService({\n config,\n models,\n callback,\n });\n\n const chat = await createChatService({\n ...options,\n models,\n tools,\n mcp,\n callback,\n summarizer,\n });\n\n const userSettings = await createUserSettingsService(options);\n\n httpRouter.use(\n await createRouter({\n ...options,\n chat,\n mcp,\n summarizer,\n userSettings,\n }),\n );\n dataIngestionPipeline.start();\n },\n });\n },\n});\n"],"names":["createBackendPlugin","callbacks","dataIngestorExtensionPoint","embeddingsProviderExtensionPoint","modelProviderExtensionPoint","toolExtensionPoint","callbackProviderExtensionPoint","coreServices","signalsServiceRef","catalogServiceRef","applyDatabaseMigrations","PgVectorStore","createDataIngestionPipeline","createMcpService","createSearchKnowledgeTool","createCallbackService","summarizer","createSummarizerService","chat","createChatService","userSettings","createUserSettingsService","createRouter"],"mappings":"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"}
|
|
@@ -104,7 +104,8 @@ ${contentPrompt}`;
|
|
|
104
104
|
messages,
|
|
105
105
|
modelId,
|
|
106
106
|
stream = true,
|
|
107
|
-
userCredentials
|
|
107
|
+
userCredentials,
|
|
108
|
+
tools: enabledTools
|
|
108
109
|
}) => {
|
|
109
110
|
const model = models.find((m) => m.id === modelId)?.chatModel;
|
|
110
111
|
if (!model) {
|
|
@@ -121,7 +122,14 @@ ${contentPrompt}`;
|
|
|
121
122
|
const credentials = await auth.getOwnServiceCredentials();
|
|
122
123
|
const user = await backstagePluginAiAssistantNode.getUser(cache, userEntityRef, credentials, catalog);
|
|
123
124
|
const mcpTools = await mcp.getTools(userCredentials);
|
|
124
|
-
const agentTools = tools$1
|
|
125
|
+
const agentTools = [...tools$1, ...mcpTools].filter((tool) => {
|
|
126
|
+
if (enabledTools === void 0) return true;
|
|
127
|
+
if (enabledTools.length === 0) return false;
|
|
128
|
+
const enabled = enabledTools.find(
|
|
129
|
+
(enabledTool) => enabledTool.name === tool.name && enabledTool.provider === tool.provider
|
|
130
|
+
);
|
|
131
|
+
return !!enabled;
|
|
132
|
+
}).map((tool) => new tools.DynamicStructuredTool(tool));
|
|
125
133
|
const messagesWithoutSystem = messages.filter((m) => m.role !== "system");
|
|
126
134
|
addMessages(
|
|
127
135
|
messagesWithoutSystem,
|
|
@@ -261,13 +269,25 @@ ${contentPrompt}`;
|
|
|
261
269
|
message: updatedMessage
|
|
262
270
|
});
|
|
263
271
|
};
|
|
272
|
+
const getAvailableTools = async ({
|
|
273
|
+
credentials
|
|
274
|
+
}) => {
|
|
275
|
+
const mcpTools = await mcp.getTools(credentials);
|
|
276
|
+
const availableTools = tools$1.concat(mcpTools).map((tool) => ({
|
|
277
|
+
name: tool.name,
|
|
278
|
+
provider: tool.provider,
|
|
279
|
+
description: tool.description
|
|
280
|
+
}));
|
|
281
|
+
return availableTools;
|
|
282
|
+
};
|
|
264
283
|
return {
|
|
265
284
|
prompt,
|
|
266
285
|
getAvailableModels,
|
|
267
286
|
getConversation,
|
|
268
287
|
getConversations,
|
|
269
288
|
addMessages,
|
|
270
|
-
scoreMessage
|
|
289
|
+
scoreMessage,
|
|
290
|
+
getAvailableTools
|
|
271
291
|
};
|
|
272
292
|
};
|
|
273
293
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.cjs.js","sources":["../../src/services/chat.ts"],"sourcesContent":["import { CatalogService } from '@backstage/plugin-catalog-node';\nimport {\n LoggerService,\n RootConfigService,\n DatabaseService,\n AuthService,\n} from '@backstage/backend-plugin-api';\nimport { ChatStore } from '../database/chat-store';\nimport {\n Conversation,\n Message,\n JsonObject,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { SignalsService } from '@backstage/plugin-signals-node';\nimport {\n DEFAULT_FORMATTING_PROMPT,\n DEFAULT_IDENTITY_PROMPT,\n DEFAULT_SYSTEM_PROMPT,\n DEFAULT_TOOL_GUIDELINE,\n} from '../constants/prompts';\nimport {\n Tool,\n Model,\n getUser,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { DynamicStructuredTool } from '@langchain/core/tools';\nimport { createReactAgent } from '@langchain/langgraph/prebuilt';\nimport { SystemMessagePromptTemplate } from '@langchain/core/prompts';\nimport { SummarizerService } from './summarizer';\nimport { v4 as uuid } from 'uuid';\nimport type {\n BackstageCredentials,\n CacheService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { AIMessage, ToolMessage } from '@langchain/core/messages';\nimport { McpService } from './mcp';\nimport { CallbackService } from './callbacks';\n\nexport type ChatServiceOptions = {\n models: Model[];\n tools: Tool[];\n logger: LoggerService;\n config: RootConfigService;\n database: DatabaseService;\n signals: SignalsService;\n catalog: CatalogService;\n cache: CacheService;\n auth: AuthService;\n mcp: McpService;\n userInfo: UserInfoService;\n callback: CallbackService;\n summarizer: SummarizerService;\n};\n\ntype PromptOptions = {\n modelId: string;\n messages: Message[];\n conversationId: string;\n stream?: boolean;\n userCredentials: BackstageCredentials;\n};\n\ntype GetConversationOptions = {\n conversationId: string;\n userEntityRef: string;\n};\n\ntype GetConversationsOptions = {\n userEntityRef: string;\n};\n\n// Helper type for messages with required fields except traceId which remains optional\ntype MessageWithRequiredFields = Required<Omit<Message, 'traceId'>> &\n Pick<Message, 'traceId'>;\n\nexport type ChatService = {\n prompt: (options: PromptOptions) => Promise<MessageWithRequiredFields[]>;\n getAvailableModels: () => Promise<string[]>;\n getConversation: (\n options: GetConversationOptions,\n ) => Promise<MessageWithRequiredFields[]>;\n getConversations: (\n options: GetConversationsOptions,\n ) => Promise<Conversation[]>;\n addMessages: (\n messages: Message[],\n userRef: string,\n conversationId: string,\n recentConversationMessages?: Message[],\n ) => Promise<void>;\n scoreMessage: (messageId: string, score: number) => Promise<void>;\n};\n\nexport const createChatService = async ({\n models,\n tools,\n logger,\n database,\n signals,\n config,\n catalog,\n cache,\n auth,\n mcp,\n userInfo,\n callback,\n summarizer,\n}: ChatServiceOptions): Promise<ChatService> => {\n logger.info(`Available models: ${models.map(m => m.id).join(', ')}`);\n logger.info(`Available tools: ${tools.map(t => t.name).join(', ')}`);\n\n const identityPrompt =\n config.getOptionalString('aiAssistant.prompt.identity') ||\n DEFAULT_IDENTITY_PROMPT;\n\n const formattingPrompt =\n config.getOptionalString('aiAssistant.prompt.formatting') ||\n DEFAULT_FORMATTING_PROMPT;\n\n const contentPrompt =\n config.getOptionalString('aiAssistant.prompt.content') ||\n DEFAULT_SYSTEM_PROMPT;\n\n const combinedBasePrompt = `${identityPrompt}\\n\\n${formattingPrompt}\\n\\n${contentPrompt}`;\n\n const toolGuideline =\n config.getOptionalString('aiAssistant.prompt.toolGuideline') ||\n DEFAULT_TOOL_GUIDELINE;\n\n const chatStore = await ChatStore.fromConfig({ database });\n\n const systemPromptTemplate = SystemMessagePromptTemplate.fromTemplate(`\n PURPOSE:\n {basePrompt}\n\n TOOL USAGE GUIDELINES:\n {toolGuideline}\n\n Available tools:\n {toolList}\n\n Calling User:\n {user}\n\n Context:\n {context}`);\n\n const addMessages: ChatService['addMessages'] = async (\n messages,\n userRef,\n conversationId,\n recentConversationMessages,\n ) => {\n // If we have recentConversationMessages, use them; otherwise, fetch the last 5 messages\n const recentMessages =\n recentConversationMessages ||\n (await chatStore.getChatMessages(conversationId, userRef, 5, ['tool']));\n\n const conversationSize = (recentMessages?.length ?? 0) + messages.length;\n\n if (recentMessages.length === 0) {\n const conversation: Conversation = {\n id: conversationId,\n title: 'New Conversation',\n userRef,\n };\n chatStore.createConversation(conversation);\n chatStore.addChatMessage(messages, userRef, conversationId);\n\n signals.publish({\n channel: `ai-assistant.chat.conversation-details-update`,\n message: { conversation },\n recipients: {\n type: 'user',\n entityRef: userRef,\n },\n });\n return;\n }\n\n if (conversationSize < 5) {\n chatStore.addChatMessage(messages, userRef, conversationId);\n return;\n }\n\n const conversation = await chatStore.getConversation(\n conversationId,\n userRef,\n );\n\n if (conversation.title !== 'New Conversation') {\n chatStore.addChatMessage(messages, userRef, conversationId);\n return;\n }\n\n const summary = await summarizer.summarizeConversation({\n messages: recentMessages,\n length: '25 characters',\n });\n\n conversation.title = summary;\n\n chatStore.updateConversation(conversation);\n chatStore.addChatMessage(messages, userRef, conversationId);\n\n signals.publish({\n channel: `ai-assistant.chat.conversation-details-update`,\n message: { conversation },\n recipients: {\n type: 'user',\n entityRef: userRef,\n },\n });\n };\n\n const prompt: ChatService['prompt'] = async ({\n conversationId,\n messages,\n modelId,\n stream = true,\n userCredentials,\n }: PromptOptions) => {\n const model = models.find(m => m.id === modelId)?.chatModel;\n\n if (!model) {\n throw new Error(`Model with id ${modelId} not found`);\n }\n\n const { userEntityRef } = await userInfo.getUserInfo(userCredentials);\n\n const streamFn = async () => {\n const recentConversationMessages = await chatStore.getChatMessages(\n conversationId,\n userEntityRef,\n 10,\n ['tool'],\n );\n\n const credentials = await auth.getOwnServiceCredentials();\n const user = await getUser(cache, userEntityRef, credentials, catalog);\n\n const mcpTools = await mcp.getTools(userCredentials);\n\n const agentTools = tools\n .map(tool => new DynamicStructuredTool(tool))\n .concat(mcpTools.map(tool => new DynamicStructuredTool(tool)));\n\n const messagesWithoutSystem = messages.filter(m => m.role !== 'system');\n\n addMessages(\n messagesWithoutSystem,\n userEntityRef,\n conversationId,\n recentConversationMessages,\n );\n\n const systemPrompt = await systemPromptTemplate.formatMessages({\n basePrompt: combinedBasePrompt,\n toolGuideline,\n toolList: agentTools\n .map(tool => `- ${tool.name}: ${tool.description}`)\n .join('\\n'),\n context: `none`,\n user,\n });\n\n const agent = createReactAgent({\n llm: model,\n tools: agentTools,\n prompt: systemPrompt[0].text,\n });\n\n const { callbacks } = await callback.getChainCallbacks({\n conversationId,\n userId: userEntityRef,\n modelId,\n });\n\n const { metadata: promptMetadata } = await callback.getChainMetadata({\n conversationId,\n userId: userEntityRef,\n modelId,\n });\n\n const traceId = uuid();\n\n const promptStream = await agent.stream(\n {\n messages: [...recentConversationMessages, ...messages],\n },\n {\n streamMode: ['values'],\n runName: 'ai-assistant-chat',\n runId: traceId,\n metadata: promptMetadata,\n callbacks,\n },\n );\n\n const responseMessages: MessageWithRequiredFields[] = [];\n\n for await (const [, chunk] of promptStream) {\n const { messages: promptMessages } = chunk;\n\n const newMessages: MessageWithRequiredFields[] = promptMessages\n .filter(\n m =>\n responseMessages.findIndex(\n rm => m.id === rm.metadata.langGraphId,\n ) === -1,\n )\n .filter(\n m =>\n recentConversationMessages.findIndex(rm => rm.id === m.id) === -1,\n )\n .filter(m => m.getType() !== 'human')\n .map(m => {\n const id = uuid();\n const role = m.getType();\n const content =\n typeof m.content === 'string'\n ? m.content\n : JSON.stringify(m.content);\n\n const metadata: JsonObject = {\n langGraphId: m.id ?? '',\n };\n\n if (role === 'ai') {\n const aiMessage = m as AIMessage;\n metadata.toolCalls = aiMessage.tool_calls || [];\n metadata.finishReason =\n aiMessage.response_metadata.finish_reason || undefined;\n metadata.modelName =\n aiMessage.response_metadata.model_name || undefined;\n }\n\n if (role === 'tool') {\n const toolMessage = m as ToolMessage;\n metadata.name = toolMessage.name || '';\n }\n\n return {\n id,\n role,\n content,\n metadata,\n score: 0,\n traceId: traceId,\n };\n });\n\n // Simulate streaming until langchain messages error is better understood\n for await (const m of newMessages) {\n const words = m.content.split(' ');\n const chunkSize = 5; // Send 5 words at a time\n let messageBuilder = '';\n\n for (let i = 0; i < words.length; i += chunkSize) {\n const wordChunk = words.slice(i, i + chunkSize).join(' ');\n messageBuilder = messageBuilder.concat(wordChunk).concat(' ');\n m.content = messageBuilder;\n\n await new Promise(resolve => setTimeout(resolve, 50));\n\n signals.publish({\n channel: `ai-assistant.chat.conversation-stream:${conversationId}`,\n message: { messages: [m] },\n recipients: {\n type: 'user',\n entityRef: userEntityRef,\n },\n });\n }\n }\n\n responseMessages.push(...newMessages);\n }\n\n addMessages(responseMessages, userEntityRef, conversationId, [\n ...recentConversationMessages,\n ...messages,\n ]);\n\n return responseMessages;\n };\n\n const result = streamFn();\n\n return stream ? [] : result;\n };\n\n const getAvailableModels: ChatService['getAvailableModels'] = async () => {\n return models.map(x => x.id);\n };\n\n const getConversation: ChatService['getConversation'] = async (\n options: GetConversationOptions,\n ) => {\n const { conversationId, userEntityRef } = options;\n\n const conversation = await chatStore.getChatMessages(\n conversationId,\n userEntityRef,\n );\n\n return conversation;\n };\n\n const getConversations: ChatService['getConversations'] = async ({\n userEntityRef,\n }: GetConversationsOptions) => {\n const conversations = await chatStore.getConversations(userEntityRef);\n\n return conversations;\n };\n\n const scoreMessage: ChatService['scoreMessage'] = async (\n messageId: string,\n score: number,\n ) => {\n const message = await chatStore.getMessageById(messageId);\n\n if (!message) {\n throw new Error(`Message with id ${messageId} not found`);\n }\n\n const updatedMessage: Required<Message> = {\n ...message,\n score,\n };\n\n chatStore.updateMessage(updatedMessage);\n\n callback.handleScoreCallbacks({\n name: 'helpfulness',\n message: updatedMessage,\n });\n };\n\n return {\n prompt,\n getAvailableModels,\n getConversation,\n getConversations,\n addMessages,\n scoreMessage,\n };\n};\n"],"names":["tools","DEFAULT_IDENTITY_PROMPT","DEFAULT_FORMATTING_PROMPT","DEFAULT_SYSTEM_PROMPT","DEFAULT_TOOL_GUIDELINE","chatStore","ChatStore","SystemMessagePromptTemplate","conversation","getUser","DynamicStructuredTool","createReactAgent","uuid"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chat.cjs.js","sources":["../../src/services/chat.ts"],"sourcesContent":["import { CatalogService } from '@backstage/plugin-catalog-node';\nimport {\n LoggerService,\n RootConfigService,\n DatabaseService,\n AuthService,\n} from '@backstage/backend-plugin-api';\nimport { ChatStore } from '../database/chat-store';\nimport {\n Conversation,\n Message,\n JsonObject,\n Tool,\n UserTool,\n EnabledTool,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { SignalsService } from '@backstage/plugin-signals-node';\nimport {\n DEFAULT_FORMATTING_PROMPT,\n DEFAULT_IDENTITY_PROMPT,\n DEFAULT_SYSTEM_PROMPT,\n DEFAULT_TOOL_GUIDELINE,\n} from '../constants/prompts';\nimport {\n Model,\n getUser,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { DynamicStructuredTool } from '@langchain/core/tools';\nimport { createReactAgent } from '@langchain/langgraph/prebuilt';\nimport { SystemMessagePromptTemplate } from '@langchain/core/prompts';\nimport { SummarizerService } from './summarizer';\nimport { v4 as uuid } from 'uuid';\nimport type {\n BackstageCredentials,\n CacheService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { AIMessage, ToolMessage } from '@langchain/core/messages';\nimport { McpService } from './mcp';\nimport { CallbackService } from './callbacks';\n\nexport type ChatServiceOptions = {\n models: Model[];\n tools: Tool[];\n logger: LoggerService;\n config: RootConfigService;\n database: DatabaseService;\n signals: SignalsService;\n catalog: CatalogService;\n cache: CacheService;\n auth: AuthService;\n mcp: McpService;\n userInfo: UserInfoService;\n callback: CallbackService;\n summarizer: SummarizerService;\n};\n\ntype PromptOptions = {\n modelId: string;\n messages: Message[];\n conversationId: string;\n stream?: boolean;\n userCredentials: BackstageCredentials;\n tools?: EnabledTool[];\n};\n\ntype GetConversationOptions = {\n conversationId: string;\n userEntityRef: string;\n};\n\ntype GetConversationsOptions = {\n userEntityRef: string;\n};\n\n// Helper type for messages with required fields except traceId which remains optional\ntype MessageWithRequiredFields = Required<Omit<Message, 'traceId'>> &\n Pick<Message, 'traceId'>;\n\nexport type ChatService = {\n prompt: (options: PromptOptions) => Promise<MessageWithRequiredFields[]>;\n getAvailableModels: () => Promise<string[]>;\n getConversation: (\n options: GetConversationOptions,\n ) => Promise<MessageWithRequiredFields[]>;\n getConversations: (\n options: GetConversationsOptions,\n ) => Promise<Conversation[]>;\n addMessages: (\n messages: Message[],\n userRef: string,\n conversationId: string,\n recentConversationMessages?: Message[],\n ) => Promise<void>;\n scoreMessage: (messageId: string, score: number) => Promise<void>;\n getAvailableTools: (options: {\n credentials: BackstageCredentials;\n }) => Promise<UserTool[]>;\n};\n\nexport const createChatService = async ({\n models,\n tools,\n logger,\n database,\n signals,\n config,\n catalog,\n cache,\n auth,\n mcp,\n userInfo,\n callback,\n summarizer,\n}: ChatServiceOptions): Promise<ChatService> => {\n logger.info(`Available models: ${models.map(m => m.id).join(', ')}`);\n logger.info(`Available tools: ${tools.map(t => t.name).join(', ')}`);\n\n const identityPrompt =\n config.getOptionalString('aiAssistant.prompt.identity') ||\n DEFAULT_IDENTITY_PROMPT;\n\n const formattingPrompt =\n config.getOptionalString('aiAssistant.prompt.formatting') ||\n DEFAULT_FORMATTING_PROMPT;\n\n const contentPrompt =\n config.getOptionalString('aiAssistant.prompt.content') ||\n DEFAULT_SYSTEM_PROMPT;\n\n const combinedBasePrompt = `${identityPrompt}\\n\\n${formattingPrompt}\\n\\n${contentPrompt}`;\n\n const toolGuideline =\n config.getOptionalString('aiAssistant.prompt.toolGuideline') ||\n DEFAULT_TOOL_GUIDELINE;\n\n const chatStore = await ChatStore.fromConfig({ database });\n\n const systemPromptTemplate = SystemMessagePromptTemplate.fromTemplate(`\n PURPOSE:\n {basePrompt}\n\n TOOL USAGE GUIDELINES:\n {toolGuideline}\n\n Available tools:\n {toolList}\n\n Calling User:\n {user}\n\n Context:\n {context}`);\n\n const addMessages: ChatService['addMessages'] = async (\n messages,\n userRef,\n conversationId,\n recentConversationMessages,\n ) => {\n // If we have recentConversationMessages, use them; otherwise, fetch the last 5 messages\n const recentMessages =\n recentConversationMessages ||\n (await chatStore.getChatMessages(conversationId, userRef, 5, ['tool']));\n\n const conversationSize = (recentMessages?.length ?? 0) + messages.length;\n\n if (recentMessages.length === 0) {\n const conversation: Conversation = {\n id: conversationId,\n title: 'New Conversation',\n userRef,\n };\n chatStore.createConversation(conversation);\n chatStore.addChatMessage(messages, userRef, conversationId);\n\n signals.publish({\n channel: `ai-assistant.chat.conversation-details-update`,\n message: { conversation },\n recipients: {\n type: 'user',\n entityRef: userRef,\n },\n });\n return;\n }\n\n if (conversationSize < 5) {\n chatStore.addChatMessage(messages, userRef, conversationId);\n return;\n }\n\n const conversation = await chatStore.getConversation(\n conversationId,\n userRef,\n );\n\n if (conversation.title !== 'New Conversation') {\n chatStore.addChatMessage(messages, userRef, conversationId);\n return;\n }\n\n const summary = await summarizer.summarizeConversation({\n messages: recentMessages,\n length: '25 characters',\n });\n\n conversation.title = summary;\n\n chatStore.updateConversation(conversation);\n chatStore.addChatMessage(messages, userRef, conversationId);\n\n signals.publish({\n channel: `ai-assistant.chat.conversation-details-update`,\n message: { conversation },\n recipients: {\n type: 'user',\n entityRef: userRef,\n },\n });\n };\n\n const prompt: ChatService['prompt'] = async ({\n conversationId,\n messages,\n modelId,\n stream = true,\n userCredentials,\n tools: enabledTools,\n }: PromptOptions) => {\n const model = models.find(m => m.id === modelId)?.chatModel;\n\n if (!model) {\n throw new Error(`Model with id ${modelId} not found`);\n }\n\n const { userEntityRef } = await userInfo.getUserInfo(userCredentials);\n\n const streamFn = async () => {\n const recentConversationMessages = await chatStore.getChatMessages(\n conversationId,\n userEntityRef,\n 10,\n ['tool'],\n );\n\n const credentials = await auth.getOwnServiceCredentials();\n const user = await getUser(cache, userEntityRef, credentials, catalog);\n\n const mcpTools = await mcp.getTools(userCredentials);\n\n const agentTools = [...tools, ...mcpTools]\n .filter(tool => {\n // If tools parameter is undefined, allow all tools\n if (enabledTools === undefined) return true;\n\n // If empty array, no tools should be enabled\n if (enabledTools.length === 0) return false;\n // Otherwise, only allow tools that are in the enabled list\n const enabled = enabledTools.find(\n enabledTool =>\n enabledTool.name === tool.name &&\n enabledTool.provider === tool.provider,\n );\n return !!enabled;\n })\n .map(tool => new DynamicStructuredTool(tool));\n\n const messagesWithoutSystem = messages.filter(m => m.role !== 'system');\n\n addMessages(\n messagesWithoutSystem,\n userEntityRef,\n conversationId,\n recentConversationMessages,\n );\n\n const systemPrompt = await systemPromptTemplate.formatMessages({\n basePrompt: combinedBasePrompt,\n toolGuideline,\n toolList: agentTools\n .map(tool => `- ${tool.name}: ${tool.description}`)\n .join('\\n'),\n context: `none`,\n user,\n });\n\n const agent = createReactAgent({\n llm: model,\n tools: agentTools,\n prompt: systemPrompt[0].text,\n });\n\n const { callbacks } = await callback.getChainCallbacks({\n conversationId,\n userId: userEntityRef,\n modelId,\n });\n\n const { metadata: promptMetadata } = await callback.getChainMetadata({\n conversationId,\n userId: userEntityRef,\n modelId,\n });\n\n const traceId = uuid();\n\n const promptStream = await agent.stream(\n {\n messages: [...recentConversationMessages, ...messages],\n },\n {\n streamMode: ['values'],\n runName: 'ai-assistant-chat',\n runId: traceId,\n metadata: promptMetadata,\n callbacks,\n },\n );\n\n const responseMessages: MessageWithRequiredFields[] = [];\n\n for await (const [, chunk] of promptStream) {\n const { messages: promptMessages } = chunk;\n\n const newMessages: MessageWithRequiredFields[] = promptMessages\n .filter(\n m =>\n responseMessages.findIndex(\n rm => m.id === rm.metadata.langGraphId,\n ) === -1,\n )\n .filter(\n m =>\n recentConversationMessages.findIndex(rm => rm.id === m.id) === -1,\n )\n .filter(m => m.getType() !== 'human')\n .map(m => {\n const id = uuid();\n const role = m.getType();\n const content =\n typeof m.content === 'string'\n ? m.content\n : JSON.stringify(m.content);\n\n const metadata: JsonObject = {\n langGraphId: m.id ?? '',\n };\n\n if (role === 'ai') {\n const aiMessage = m as AIMessage;\n metadata.toolCalls = aiMessage.tool_calls || [];\n metadata.finishReason =\n aiMessage.response_metadata.finish_reason || undefined;\n metadata.modelName =\n aiMessage.response_metadata.model_name || undefined;\n }\n\n if (role === 'tool') {\n const toolMessage = m as ToolMessage;\n metadata.name = toolMessage.name || '';\n }\n\n return {\n id,\n role,\n content,\n metadata,\n score: 0,\n traceId: traceId,\n };\n });\n\n // Simulate streaming until langchain messages error is better understood\n for await (const m of newMessages) {\n const words = m.content.split(' ');\n const chunkSize = 5; // Send 5 words at a time\n let messageBuilder = '';\n\n for (let i = 0; i < words.length; i += chunkSize) {\n const wordChunk = words.slice(i, i + chunkSize).join(' ');\n messageBuilder = messageBuilder.concat(wordChunk).concat(' ');\n m.content = messageBuilder;\n\n await new Promise(resolve => setTimeout(resolve, 50));\n\n signals.publish({\n channel: `ai-assistant.chat.conversation-stream:${conversationId}`,\n message: { messages: [m] },\n recipients: {\n type: 'user',\n entityRef: userEntityRef,\n },\n });\n }\n }\n\n responseMessages.push(...newMessages);\n }\n\n addMessages(responseMessages, userEntityRef, conversationId, [\n ...recentConversationMessages,\n ...messages,\n ]);\n\n return responseMessages;\n };\n\n const result = streamFn();\n\n return stream ? [] : result;\n };\n\n const getAvailableModels: ChatService['getAvailableModels'] = async () => {\n return models.map(x => x.id);\n };\n\n const getConversation: ChatService['getConversation'] = async (\n options: GetConversationOptions,\n ) => {\n const { conversationId, userEntityRef } = options;\n\n const conversation = await chatStore.getChatMessages(\n conversationId,\n userEntityRef,\n );\n\n return conversation;\n };\n\n const getConversations: ChatService['getConversations'] = async ({\n userEntityRef,\n }: GetConversationsOptions) => {\n const conversations = await chatStore.getConversations(userEntityRef);\n\n return conversations;\n };\n\n const scoreMessage: ChatService['scoreMessage'] = async (\n messageId: string,\n score: number,\n ) => {\n const message = await chatStore.getMessageById(messageId);\n\n if (!message) {\n throw new Error(`Message with id ${messageId} not found`);\n }\n\n const updatedMessage: Required<Message> = {\n ...message,\n score,\n };\n\n chatStore.updateMessage(updatedMessage);\n\n callback.handleScoreCallbacks({\n name: 'helpfulness',\n message: updatedMessage,\n });\n };\n\n const getAvailableTools: ChatService['getAvailableTools'] = async ({\n credentials,\n }) => {\n const mcpTools = await mcp.getTools(credentials);\n\n const availableTools: UserTool[] = tools.concat(mcpTools).map(tool => ({\n name: tool.name,\n provider: tool.provider,\n description: tool.description,\n }));\n\n return availableTools;\n };\n\n return {\n prompt,\n getAvailableModels,\n getConversation,\n getConversations,\n addMessages,\n scoreMessage,\n getAvailableTools,\n };\n};\n"],"names":["tools","DEFAULT_IDENTITY_PROMPT","DEFAULT_FORMATTING_PROMPT","DEFAULT_SYSTEM_PROMPT","DEFAULT_TOOL_GUIDELINE","chatStore","ChatStore","SystemMessagePromptTemplate","conversation","getUser","DynamicStructuredTool","createReactAgent","uuid"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var backstagePluginAiAssistantNode = require('@sweetoburrito/backstage-plugin-ai-assistant-node');
|
|
4
|
+
|
|
5
|
+
const convertToAssistantTool = (mcpTool, serverName) => {
|
|
6
|
+
const { name, description, schema } = mcpTool;
|
|
7
|
+
const provider = `mcp server:${serverName}`;
|
|
8
|
+
const tool = {
|
|
9
|
+
name,
|
|
10
|
+
provider,
|
|
11
|
+
description,
|
|
12
|
+
schema,
|
|
13
|
+
func: async (params) => {
|
|
14
|
+
const result = await mcpTool.invoke(params);
|
|
15
|
+
return {
|
|
16
|
+
content: JSON.stringify(result)
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
return backstagePluginAiAssistantNode.createAssistantTool({ tool });
|
|
21
|
+
};
|
|
22
|
+
const getToolsForServer = async (mcpClient, serverName) => {
|
|
23
|
+
const mcpTools = await mcpClient.getTools(serverName);
|
|
24
|
+
const assistantTools = mcpTools.map(
|
|
25
|
+
(mcpTool) => convertToAssistantTool(mcpTool, serverName)
|
|
26
|
+
);
|
|
27
|
+
return assistantTools;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
exports.getToolsForServer = getToolsForServer;
|
|
31
|
+
//# sourceMappingURL=helpers.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.cjs.js","sources":["../../../src/services/mcp/helpers.ts"],"sourcesContent":["import { DynamicStructuredTool } from '@langchain/core/tools';\nimport { MultiServerMCPClient } from '@langchain/mcp-adapters';\nimport { Tool } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { createAssistantTool } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\n\nconst convertToAssistantTool = (\n mcpTool: DynamicStructuredTool,\n serverName: string,\n): Tool => {\n const { name, description, schema } = mcpTool;\n const provider = `mcp server:${serverName}`;\n\n const tool = {\n name,\n provider,\n description,\n schema: schema as Tool['schema'],\n func: async (params: any) => {\n const result = await mcpTool.invoke(params);\n return {\n content: JSON.stringify(result),\n };\n },\n };\n\n return createAssistantTool({ tool });\n};\n\nexport const getToolsForServer = async (\n mcpClient: MultiServerMCPClient,\n serverName: string,\n): Promise<Tool[]> => {\n const mcpTools = await mcpClient.getTools(serverName);\n\n const assistantTools = mcpTools.map(mcpTool =>\n convertToAssistantTool(mcpTool, serverName),\n );\n return assistantTools;\n};\n"],"names":["createAssistantTool"],"mappings":";;;;AAKA,MAAM,sBAAA,GAAyB,CAC7B,OAAA,EACA,UAAA,KACS;AACT,EAAA,MAAM,EAAE,IAAA,EAAM,WAAA,EAAa,MAAA,EAAO,GAAI,OAAA;AACtC,EAAA,MAAM,QAAA,GAAW,cAAc,UAAU,CAAA,CAAA;AAEzC,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,IAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA,MAAA;AAAA,IACA,IAAA,EAAM,OAAO,MAAA,KAAgB;AAC3B,MAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,MAAA,CAAO,MAAM,CAAA;AAC1C,MAAA,OAAO;AAAA,QACL,OAAA,EAAS,IAAA,CAAK,SAAA,CAAU,MAAM;AAAA,OAChC;AAAA,IACF;AAAA,GACF;AAEA,EAAA,OAAOA,kDAAA,CAAoB,EAAE,IAAA,EAAM,CAAA;AACrC,CAAA;AAEO,MAAM,iBAAA,GAAoB,OAC/B,SAAA,EACA,UAAA,KACoB;AACpB,EAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,QAAA,CAAS,UAAU,CAAA;AAEpD,EAAA,MAAM,iBAAiB,QAAA,CAAS,GAAA;AAAA,IAAI,CAAA,OAAA,KAClC,sBAAA,CAAuB,OAAA,EAAS,UAAU;AAAA,GAC5C;AACA,EAAA,OAAO,cAAA;AACT;;;;"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var mcpAdapters = require('@langchain/mcp-adapters');
|
|
4
|
+
var backstagePluginAiAssistantNode = require('@sweetoburrito/backstage-plugin-ai-assistant-node');
|
|
5
|
+
var helpers = require('./helpers.cjs.js');
|
|
6
|
+
var userSettingsStore = require('../../database/user-settings-store.cjs.js');
|
|
7
|
+
|
|
8
|
+
const MCP_SETTINGS_TYPE = "mcp_server_config";
|
|
9
|
+
const createMcpService = async ({
|
|
10
|
+
config,
|
|
11
|
+
userInfo,
|
|
12
|
+
database
|
|
13
|
+
}) => {
|
|
14
|
+
const serversConfig = config.getOptionalConfigArray(
|
|
15
|
+
"aiAssistant.mcp.servers"
|
|
16
|
+
);
|
|
17
|
+
const encryptionKey = config.getString("aiAssistant.mcp.encryptionKey");
|
|
18
|
+
const preConfiguredMcpServers = serversConfig ? serversConfig.reduce((acc, server) => {
|
|
19
|
+
const serverName = server.getString("name");
|
|
20
|
+
const options = server.get("options");
|
|
21
|
+
acc[serverName] = options;
|
|
22
|
+
return acc;
|
|
23
|
+
}, {}) : {};
|
|
24
|
+
const userSettingsStore$1 = await userSettingsStore.UserSettingsStore.fromConfig({ database });
|
|
25
|
+
const getUserMcpServerConfigNames = async (credentials) => {
|
|
26
|
+
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
27
|
+
const mcpConfig = await userSettingsStore$1.getUserSettingsByType(
|
|
28
|
+
userEntityRef,
|
|
29
|
+
MCP_SETTINGS_TYPE
|
|
30
|
+
);
|
|
31
|
+
if (!mcpConfig) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const names = Object.keys(mcpConfig);
|
|
35
|
+
return names;
|
|
36
|
+
};
|
|
37
|
+
const getUserMcpServerConfig = async (credentials) => {
|
|
38
|
+
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
39
|
+
const mcpConfigEncrypted = await userSettingsStore$1.getUserSettingsByType(userEntityRef, MCP_SETTINGS_TYPE);
|
|
40
|
+
if (!mcpConfigEncrypted) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
const mcpConfig = Object.entries(mcpConfigEncrypted).map(
|
|
44
|
+
([name, data]) => ({
|
|
45
|
+
name,
|
|
46
|
+
options: JSON.parse(backstagePluginAiAssistantNode.decrypt(data, encryptionKey))
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
return mcpConfig;
|
|
50
|
+
};
|
|
51
|
+
const getTools = async (credentials) => {
|
|
52
|
+
const userMcpConfig = await getUserMcpServerConfig(credentials);
|
|
53
|
+
const userMcpServers = userMcpConfig.length ? userMcpConfig.reduce((acc, server) => {
|
|
54
|
+
const { name, options } = server;
|
|
55
|
+
acc[name] = options;
|
|
56
|
+
return acc;
|
|
57
|
+
}, {}) : {};
|
|
58
|
+
const mcpServers = {
|
|
59
|
+
...preConfiguredMcpServers,
|
|
60
|
+
...userMcpServers
|
|
61
|
+
};
|
|
62
|
+
const serverNames = Object.keys(mcpServers);
|
|
63
|
+
if (serverNames.length === 0) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
const mcpClient = new mcpAdapters.MultiServerMCPClient({
|
|
67
|
+
prefixToolNameWithServerName: true,
|
|
68
|
+
useStandardContentBlocks: true,
|
|
69
|
+
mcpServers
|
|
70
|
+
});
|
|
71
|
+
const serverToolPromises = serverNames.map(
|
|
72
|
+
(serverName) => helpers.getToolsForServer(mcpClient, serverName)
|
|
73
|
+
);
|
|
74
|
+
const toolsByServer = await Promise.all(serverToolPromises);
|
|
75
|
+
return toolsByServer.flat();
|
|
76
|
+
};
|
|
77
|
+
const validateMcpServerConfig = async (mcpConfig) => {
|
|
78
|
+
try {
|
|
79
|
+
const userMcpServers = mcpConfig.reduce((acc, server) => {
|
|
80
|
+
const { name, options } = server;
|
|
81
|
+
acc[name] = options;
|
|
82
|
+
return acc;
|
|
83
|
+
}, {});
|
|
84
|
+
const userConfigClient = new mcpAdapters.MultiServerMCPClient({
|
|
85
|
+
prefixToolNameWithServerName: true,
|
|
86
|
+
useStandardContentBlocks: true,
|
|
87
|
+
mcpServers: userMcpServers
|
|
88
|
+
});
|
|
89
|
+
await userConfigClient.getTools();
|
|
90
|
+
} catch (e) {
|
|
91
|
+
const error = new Error("Invalid MCP server configuration");
|
|
92
|
+
error.name = "McpConfigurationError";
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const setUserMcpServerConfig = async (credentials, mcpConfig) => {
|
|
97
|
+
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
98
|
+
const { name } = mcpConfig;
|
|
99
|
+
const existingConfig = await getUserMcpServerConfig(credentials);
|
|
100
|
+
const existingServerIndex = existingConfig.findIndex(
|
|
101
|
+
(server) => server.name === name
|
|
102
|
+
);
|
|
103
|
+
if (existingServerIndex === -1) {
|
|
104
|
+
existingConfig.push(mcpConfig);
|
|
105
|
+
} else {
|
|
106
|
+
existingConfig[existingServerIndex] = mcpConfig;
|
|
107
|
+
}
|
|
108
|
+
await validateMcpServerConfig(existingConfig);
|
|
109
|
+
const updatedConfig = existingConfig.reduce(
|
|
110
|
+
(acc, server) => {
|
|
111
|
+
acc[server.name] = backstagePluginAiAssistantNode.encrypt(
|
|
112
|
+
JSON.stringify(server.options),
|
|
113
|
+
encryptionKey
|
|
114
|
+
);
|
|
115
|
+
return acc;
|
|
116
|
+
},
|
|
117
|
+
{}
|
|
118
|
+
);
|
|
119
|
+
await userSettingsStore$1.setUserSettings(
|
|
120
|
+
userEntityRef,
|
|
121
|
+
MCP_SETTINGS_TYPE,
|
|
122
|
+
updatedConfig
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
const deleteUserMcpServerConfig = async (credentials, name) => {
|
|
126
|
+
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
127
|
+
const existingConfig = await getUserMcpServerConfig(credentials);
|
|
128
|
+
const updatedConfig = existingConfig.filter((server) => server.name !== name).reduce((acc, server) => {
|
|
129
|
+
acc[server.name] = backstagePluginAiAssistantNode.encrypt(
|
|
130
|
+
JSON.stringify(server.options),
|
|
131
|
+
encryptionKey
|
|
132
|
+
);
|
|
133
|
+
return acc;
|
|
134
|
+
}, {});
|
|
135
|
+
await userSettingsStore$1.setUserSettings(
|
|
136
|
+
userEntityRef,
|
|
137
|
+
MCP_SETTINGS_TYPE,
|
|
138
|
+
updatedConfig
|
|
139
|
+
);
|
|
140
|
+
};
|
|
141
|
+
return {
|
|
142
|
+
getTools,
|
|
143
|
+
getUserMcpServerConfigNames,
|
|
144
|
+
deleteUserMcpServerConfig,
|
|
145
|
+
setUserMcpServerConfig
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
exports.createMcpService = createMcpService;
|
|
150
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../../src/services/mcp/index.ts"],"sourcesContent":["import {\n BackstageCredentials,\n DatabaseService,\n RootConfigService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { MultiServerMCPClient } from '@langchain/mcp-adapters';\nimport {\n McpServerConfig,\n McpServerConfigOptions,\n Tool,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport {\n encrypt,\n decrypt,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { getToolsForServer } from './helpers';\nimport { UserSettingsStore } from '../../database/user-settings-store';\n\ntype CreateMcpServiceOptions = {\n config: RootConfigService;\n userInfo: UserInfoService;\n database: DatabaseService;\n};\n\nconst MCP_SETTINGS_TYPE = 'mcp_server_config';\n\nexport type McpService = {\n getTools: (credentials: BackstageCredentials) => Promise<Tool[]>;\n getUserMcpServerConfigNames: (\n credentials: BackstageCredentials,\n ) => Promise<string[]>;\n deleteUserMcpServerConfig: (\n credentials: BackstageCredentials,\n name: string,\n ) => Promise<void>;\n setUserMcpServerConfig: (\n credentials: BackstageCredentials,\n config: McpServerConfig,\n ) => Promise<void>;\n};\n\nexport const createMcpService = async ({\n config,\n userInfo,\n database,\n}: CreateMcpServiceOptions): Promise<McpService> => {\n const serversConfig = config.getOptionalConfigArray(\n 'aiAssistant.mcp.servers',\n );\n const encryptionKey = config.getString('aiAssistant.mcp.encryptionKey');\n\n const preConfiguredMcpServers: Record<string, McpServerConfigOptions> =\n serversConfig\n ? serversConfig.reduce((acc, server) => {\n const serverName = server.getString('name');\n const options = server.get<McpServerConfigOptions>('options');\n\n acc[serverName] = options;\n\n return acc;\n }, {} as Record<string, McpServerConfigOptions>)\n : {};\n\n const userSettingsStore = await UserSettingsStore.fromConfig({ database });\n\n const getUserMcpServerConfigNames: McpService['getUserMcpServerConfigNames'] =\n async credentials => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n\n const mcpConfig = await userSettingsStore.getUserSettingsByType(\n userEntityRef,\n MCP_SETTINGS_TYPE,\n );\n if (!mcpConfig) {\n return [];\n }\n\n const names = Object.keys(mcpConfig);\n\n return names;\n };\n\n const getUserMcpServerConfig = async (\n credentials: BackstageCredentials,\n ): Promise<McpServerConfig[]> => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n\n const mcpConfigEncrypted = await userSettingsStore.getUserSettingsByType<\n Record<string, string>\n >(userEntityRef, MCP_SETTINGS_TYPE);\n\n if (!mcpConfigEncrypted) {\n return [];\n }\n\n const mcpConfig: McpServerConfig[] = Object.entries(mcpConfigEncrypted).map(\n ([name, data]) => ({\n name,\n options: JSON.parse(decrypt(data, encryptionKey)),\n }),\n );\n\n return mcpConfig;\n };\n\n const getTools: McpService['getTools'] = async credentials => {\n const userMcpConfig = await getUserMcpServerConfig(credentials);\n\n const userMcpServers: Record<string, McpServerConfigOptions> =\n userMcpConfig.length\n ? userMcpConfig.reduce((acc, server) => {\n const { name, options } = server;\n\n acc[name] = options;\n\n return acc;\n }, {} as Record<string, McpServerConfigOptions>)\n : {};\n\n const mcpServers: Record<string, McpServerConfigOptions> = {\n ...preConfiguredMcpServers,\n ...userMcpServers,\n };\n\n const serverNames = Object.keys(mcpServers);\n\n if (serverNames.length === 0) {\n return [];\n }\n\n const mcpClient = new MultiServerMCPClient({\n prefixToolNameWithServerName: true,\n useStandardContentBlocks: true,\n mcpServers,\n });\n\n const serverToolPromises = serverNames.map(serverName =>\n getToolsForServer(mcpClient, serverName),\n );\n const toolsByServer = await Promise.all(serverToolPromises);\n\n return toolsByServer.flat();\n };\n\n const validateMcpServerConfig = async (\n mcpConfig: McpServerConfig[],\n ): Promise<void> => {\n try {\n const userMcpServers = mcpConfig.reduce((acc, server) => {\n const { name, options } = server;\n\n acc[name] = options;\n\n return acc;\n }, {} as Record<string, McpServerConfigOptions>);\n\n const userConfigClient = new MultiServerMCPClient({\n prefixToolNameWithServerName: true,\n useStandardContentBlocks: true,\n mcpServers: userMcpServers,\n });\n\n await userConfigClient.getTools();\n } catch (e) {\n const error = new Error('Invalid MCP server configuration');\n error.name = 'McpConfigurationError';\n throw error;\n }\n };\n\n const setUserMcpServerConfig: McpService['setUserMcpServerConfig'] = async (\n credentials,\n mcpConfig,\n ) => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n const { name } = mcpConfig;\n\n const existingConfig = await getUserMcpServerConfig(credentials);\n\n const existingServerIndex = existingConfig.findIndex(\n server => server.name === name,\n );\n\n if (existingServerIndex === -1) {\n existingConfig.push(mcpConfig);\n } else {\n existingConfig[existingServerIndex] = mcpConfig;\n }\n\n await validateMcpServerConfig(existingConfig);\n\n const updatedConfig: Record<string, string> = existingConfig.reduce(\n (acc, server) => {\n acc[server.name] = encrypt(\n JSON.stringify(server.options),\n encryptionKey,\n );\n return acc;\n },\n {} as Record<string, string>,\n );\n\n await userSettingsStore.setUserSettings(\n userEntityRef,\n MCP_SETTINGS_TYPE,\n updatedConfig,\n );\n };\n\n const deleteUserMcpServerConfig: McpService['deleteUserMcpServerConfig'] =\n async (credentials, name) => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n\n const existingConfig = await getUserMcpServerConfig(credentials);\n\n const updatedConfig: Record<string, string> = existingConfig\n .filter(server => server.name !== name)\n .reduce((acc, server) => {\n acc[server.name] = encrypt(\n JSON.stringify(server.options),\n encryptionKey,\n );\n return acc;\n }, {} as Record<string, string>);\n\n await userSettingsStore.setUserSettings(\n userEntityRef,\n MCP_SETTINGS_TYPE,\n updatedConfig,\n );\n };\n\n return {\n getTools,\n getUserMcpServerConfigNames,\n deleteUserMcpServerConfig,\n setUserMcpServerConfig,\n };\n};\n"],"names":["userSettingsStore","UserSettingsStore","decrypt","MultiServerMCPClient","getToolsForServer","encrypt"],"mappings":"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"}
|
|
@@ -22,21 +22,28 @@ async function createChatRouter(options) {
|
|
|
22
22
|
})
|
|
23
23
|
),
|
|
24
24
|
modelId: z__default.default.string(),
|
|
25
|
-
conversationId: z__default.default.
|
|
26
|
-
stream: z__default.default.boolean().optional()
|
|
25
|
+
conversationId: z__default.default.uuid().optional().default(uuid.v4),
|
|
26
|
+
stream: z__default.default.boolean().optional(),
|
|
27
|
+
tools: z__default.default.array(
|
|
28
|
+
z__default.default.object({
|
|
29
|
+
name: z__default.default.string(),
|
|
30
|
+
provider: z__default.default.string()
|
|
31
|
+
})
|
|
32
|
+
).optional()
|
|
27
33
|
});
|
|
28
34
|
router.post(
|
|
29
35
|
"/message",
|
|
30
36
|
validation.validation(messageSchema, "body"),
|
|
31
37
|
async (req, res) => {
|
|
32
|
-
const { messages, conversationId, modelId, stream } = req.body;
|
|
38
|
+
const { messages, conversationId, modelId, stream, tools } = req.body;
|
|
33
39
|
const userCredentials = await httpAuth.credentials(req);
|
|
34
40
|
const responseMessages = await chat.prompt({
|
|
35
41
|
modelId,
|
|
36
42
|
messages,
|
|
37
43
|
conversationId,
|
|
38
44
|
stream,
|
|
39
|
-
userCredentials
|
|
45
|
+
userCredentials,
|
|
46
|
+
tools
|
|
40
47
|
});
|
|
41
48
|
res.json({
|
|
42
49
|
messages: responseMessages,
|
|
@@ -45,7 +52,7 @@ async function createChatRouter(options) {
|
|
|
45
52
|
}
|
|
46
53
|
);
|
|
47
54
|
const chatSchema = z__default.default.object({
|
|
48
|
-
id: z__default.default.
|
|
55
|
+
id: z__default.default.uuid()
|
|
49
56
|
});
|
|
50
57
|
router.get("/conversations", async (req, res) => {
|
|
51
58
|
const credentials = await httpAuth.credentials(req);
|
|
@@ -55,6 +62,11 @@ async function createChatRouter(options) {
|
|
|
55
62
|
});
|
|
56
63
|
res.json({ conversations });
|
|
57
64
|
});
|
|
65
|
+
router.get("/tools", async (req, res) => {
|
|
66
|
+
const credentials = await httpAuth.credentials(req);
|
|
67
|
+
const tools = await chat.getAvailableTools({ credentials });
|
|
68
|
+
res.json({ tools });
|
|
69
|
+
});
|
|
58
70
|
router.get("/:id", validation.validation(chatSchema, "params"), async (req, res) => {
|
|
59
71
|
const { id } = req.params;
|
|
60
72
|
const credentials = await httpAuth.credentials(req);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.cjs.js","sources":["../../../src/services/router/chat.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport { ChatService } from '../chat';\nimport z from 'zod';\nimport { validation } from './middleware/validation';\nimport { v4 as uuid } from 'uuid';\nimport {\n DatabaseService,\n HttpAuthService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\n\nexport type ChatRouterOptions = {\n chat: ChatService;\n database: DatabaseService;\n httpAuth: HttpAuthService;\n userInfo: UserInfoService;\n};\n\nexport async function createChatRouter(\n options: ChatRouterOptions,\n): Promise<express.Router> {\n const { chat, httpAuth, userInfo } = options;\n\n const router = Router();\n\n const messageSchema = z.object({\n messages: z.array(\n z.object({\n id: z.string().uuid().optional().default(uuid),\n role: z.string(),\n content: z.string(),\n }),\n ),\n modelId: z.string(),\n conversationId: z.
|
|
1
|
+
{"version":3,"file":"chat.cjs.js","sources":["../../../src/services/router/chat.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport { ChatService } from '../chat';\nimport z from 'zod';\nimport { validation } from './middleware/validation';\nimport { v4 as uuid } from 'uuid';\nimport {\n DatabaseService,\n HttpAuthService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\n\nexport type ChatRouterOptions = {\n chat: ChatService;\n database: DatabaseService;\n httpAuth: HttpAuthService;\n userInfo: UserInfoService;\n};\n\nexport async function createChatRouter(\n options: ChatRouterOptions,\n): Promise<express.Router> {\n const { chat, httpAuth, userInfo } = options;\n\n const router = Router();\n\n const messageSchema = z.object({\n messages: z.array(\n z.object({\n id: z.string().uuid().optional().default(uuid),\n role: z.string(),\n content: z.string(),\n }),\n ),\n modelId: z.string(),\n conversationId: z.uuid().optional().default(uuid),\n stream: z.boolean().optional(),\n tools: z\n .array(\n z.object({\n name: z.string(),\n provider: z.string(),\n }),\n )\n .optional(),\n });\n\n router.post(\n '/message',\n validation(messageSchema, 'body'),\n async (req, res) => {\n const { messages, conversationId, modelId, stream, tools } = req.body;\n\n const userCredentials = await httpAuth.credentials(req);\n\n const responseMessages = await chat.prompt({\n modelId,\n messages,\n conversationId,\n stream,\n userCredentials,\n tools,\n });\n\n res.json({\n messages: responseMessages,\n conversationId,\n });\n },\n );\n\n const chatSchema = z.object({\n id: z.uuid(),\n });\n\n router.get('/conversations', async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n\n const conversations = await chat.getConversations({\n userEntityRef,\n });\n\n res.json({ conversations });\n });\n\n router.get('/tools', async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n\n const tools = await chat.getAvailableTools({ credentials });\n\n res.json({ tools });\n });\n\n router.get('/:id', validation(chatSchema, 'params'), async (req, res) => {\n const { id } = req.params;\n\n const credentials = await httpAuth.credentials(req);\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n\n const conversation = await chat.getConversation({\n conversationId: id,\n userEntityRef,\n });\n\n res.json({ conversation });\n });\n\n router.post(\n '/message/:messageId/score',\n validation(\n z.object({\n messageId: z.string().uuid(),\n }),\n 'params',\n ),\n validation(\n z.object({\n score: z.number(),\n }),\n 'body',\n ),\n async (req, res) => {\n const { messageId } = req.params;\n const { score } = req.body;\n\n await chat.scoreMessage(messageId, score);\n\n res.status(204).end();\n },\n );\n\n return router;\n}\n"],"names":["Router","z","uuid","validation"],"mappings":"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"}
|
|
@@ -5,8 +5,8 @@ var Router = require('express-promise-router');
|
|
|
5
5
|
var chat = require('./chat.cjs.js');
|
|
6
6
|
var models = require('./models.cjs.js');
|
|
7
7
|
var rootHttpRouter = require('@backstage/backend-defaults/rootHttpRouter');
|
|
8
|
-
var mcp = require('./mcp.cjs.js');
|
|
9
8
|
var summary = require('./summary.cjs.js');
|
|
9
|
+
var index = require('./settings/index.cjs.js');
|
|
10
10
|
|
|
11
11
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
12
12
|
|
|
@@ -18,8 +18,8 @@ async function createRouter(options) {
|
|
|
18
18
|
router.use(express__default.default.json());
|
|
19
19
|
router.use("/chat", await chat.createChatRouter(options));
|
|
20
20
|
router.use("/models", await models.createModelRouter(options));
|
|
21
|
-
router.use("/mcp", await mcp.createMcpRouter(options));
|
|
22
21
|
router.use("/summary", await summary.createSummaryRouter(options));
|
|
22
|
+
router.use("/settings", await index.createSettingsRouter(options));
|
|
23
23
|
const middleware = rootHttpRouter.MiddlewareFactory.create(options);
|
|
24
24
|
router.use(middleware.error());
|
|
25
25
|
return router;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../../../src/services/router/index.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport { createChatRouter, ChatRouterOptions } from './chat';\nimport { createModelRouter } from './models';\nimport {\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport {
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../../src/services/router/index.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport { createChatRouter, ChatRouterOptions } from './chat';\nimport { createModelRouter, ModelRouterOptions } from './models';\nimport {\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport { SummaryRouterOptions, createSummaryRouter } from './summary';\nimport { createSettingsRouter, SettingsRouterOptions } from './settings';\n\nexport type RouterOptions = ChatRouterOptions &\n SummaryRouterOptions &\n ModelRouterOptions &\n SettingsRouterOptions & {\n config: RootConfigService;\n logger: LoggerService;\n };\n\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const router = Router();\n router.use(express.json());\n\n router.use('/chat', await createChatRouter(options));\n router.use('/models', await createModelRouter(options));\n router.use('/summary', await createSummaryRouter(options));\n router.use('/settings', await createSettingsRouter(options));\n\n const middleware = MiddlewareFactory.create(options);\n\n router.use(middleware.error());\n\n return router;\n}\n"],"names":["Router","express","createChatRouter","createModelRouter","createSummaryRouter","createSettingsRouter","MiddlewareFactory"],"mappings":";;;;;;;;;;;;;;;AAoBA,eAAsB,aACpB,OAAA,EACyB;AACzB,EAAA,MAAM,SAASA,uBAAA,EAAO;AACtB,EAAA,MAAA,CAAO,GAAA,CAAIC,wBAAA,CAAQ,IAAA,EAAM,CAAA;AAEzB,EAAA,MAAA,CAAO,GAAA,CAAI,OAAA,EAAS,MAAMC,qBAAA,CAAiB,OAAO,CAAC,CAAA;AACnD,EAAA,MAAA,CAAO,GAAA,CAAI,SAAA,EAAW,MAAMC,wBAAA,CAAkB,OAAO,CAAC,CAAA;AACtD,EAAA,MAAA,CAAO,GAAA,CAAI,UAAA,EAAY,MAAMC,2BAAA,CAAoB,OAAO,CAAC,CAAA;AACzD,EAAA,MAAA,CAAO,GAAA,CAAI,WAAA,EAAa,MAAMC,0BAAA,CAAqB,OAAO,CAAC,CAAA;AAE3D,EAAA,MAAM,UAAA,GAAaC,gCAAA,CAAkB,MAAA,CAAO,OAAO,CAAA;AAEnD,EAAA,MAAA,CAAO,GAAA,CAAI,UAAA,CAAW,KAAA,EAAO,CAAA;AAE7B,EAAA,OAAO,MAAA;AACT;;;;"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var express = require('express');
|
|
4
|
+
var Router = require('express-promise-router');
|
|
5
|
+
var rootHttpRouter = require('@backstage/backend-defaults/rootHttpRouter');
|
|
6
|
+
var mcp = require('./mcp.cjs.js');
|
|
7
|
+
var z = require('zod');
|
|
8
|
+
var validation = require('../middleware/validation.cjs.js');
|
|
9
|
+
|
|
10
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
11
|
+
|
|
12
|
+
var express__default = /*#__PURE__*/_interopDefaultCompat(express);
|
|
13
|
+
var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
14
|
+
var z__default = /*#__PURE__*/_interopDefaultCompat(z);
|
|
15
|
+
|
|
16
|
+
async function createSettingsRouter(options) {
|
|
17
|
+
const { userSettings, httpAuth } = options;
|
|
18
|
+
const router = Router__default.default();
|
|
19
|
+
router.use(express__default.default.json());
|
|
20
|
+
router.use("/mcp", await mcp.createMcpRouter(options));
|
|
21
|
+
const settingsSchema = z__default.default.object({
|
|
22
|
+
type: z__default.default.string()
|
|
23
|
+
});
|
|
24
|
+
router.get("/", validation.validation(settingsSchema, "query"), async (req, res) => {
|
|
25
|
+
const credentials = await httpAuth.credentials(req);
|
|
26
|
+
const { type } = req.query;
|
|
27
|
+
const settings = await userSettings.getSettingsForType(
|
|
28
|
+
credentials,
|
|
29
|
+
type
|
|
30
|
+
);
|
|
31
|
+
res.json({
|
|
32
|
+
settings
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
const setSettingsSchema = z__default.default.object({
|
|
36
|
+
type: z__default.default.string(),
|
|
37
|
+
settings: z__default.default.any()
|
|
38
|
+
});
|
|
39
|
+
router.patch("/", validation.validation(setSettingsSchema, "body"), async (req, res) => {
|
|
40
|
+
const credentials = await httpAuth.credentials(req);
|
|
41
|
+
const { type, settings } = req.body;
|
|
42
|
+
await userSettings.setSettingsForType(credentials, type, settings);
|
|
43
|
+
res.status(204).send();
|
|
44
|
+
});
|
|
45
|
+
const middleware = rootHttpRouter.MiddlewareFactory.create(options);
|
|
46
|
+
router.use(middleware.error());
|
|
47
|
+
return router;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
exports.createSettingsRouter = createSettingsRouter;
|
|
51
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../../../src/services/router/settings/index.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport {\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport { createMcpRouter, McpRouterOptions } from './mcp';\nimport { UserSettingsService } from '../../user-settings';\nimport z from 'zod';\nimport { validation } from '../middleware/validation';\n\nexport type SettingsRouterOptions = McpRouterOptions & {\n config: RootConfigService;\n logger: LoggerService;\n userSettings: UserSettingsService;\n};\n\nexport async function createSettingsRouter(\n options: SettingsRouterOptions,\n): Promise<express.Router> {\n const { userSettings, httpAuth } = options;\n const router = Router();\n router.use(express.json());\n\n router.use('/mcp', await createMcpRouter(options));\n\n const settingsSchema = z.object({\n type: z.string(),\n });\n\n router.get('/', validation(settingsSchema, 'query'), async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n\n const { type } = req.query;\n\n const settings = await userSettings.getSettingsForType(\n credentials,\n type as string,\n );\n\n res.json({\n settings,\n });\n });\n\n const setSettingsSchema = z.object({\n type: z.string(),\n settings: z.any(),\n });\n\n router.patch('/', validation(setSettingsSchema, 'body'), async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n const { type, settings } = req.body;\n\n await userSettings.setSettingsForType(credentials, type, settings);\n res.status(204).send();\n });\n\n const middleware = MiddlewareFactory.create(options);\n\n router.use(middleware.error());\n\n return router;\n}\n"],"names":["Router","express","createMcpRouter","z","validation","MiddlewareFactory"],"mappings":";;;;;;;;;;;;;;;AAkBA,eAAsB,qBACpB,OAAA,EACyB;AACzB,EAAA,MAAM,EAAE,YAAA,EAAc,QAAA,EAAS,GAAI,OAAA;AACnC,EAAA,MAAM,SAASA,uBAAA,EAAO;AACtB,EAAA,MAAA,CAAO,GAAA,CAAIC,wBAAA,CAAQ,IAAA,EAAM,CAAA;AAEzB,EAAA,MAAA,CAAO,GAAA,CAAI,MAAA,EAAQ,MAAMC,mBAAA,CAAgB,OAAO,CAAC,CAAA;AAEjD,EAAA,MAAM,cAAA,GAAiBC,mBAAE,MAAA,CAAO;AAAA,IAC9B,IAAA,EAAMA,mBAAE,MAAA;AAAO,GAChB,CAAA;AAED,EAAA,MAAA,CAAO,GAAA,CAAI,KAAKC,qBAAA,CAAW,cAAA,EAAgB,OAAO,CAAA,EAAG,OAAO,KAAK,GAAA,KAAQ;AACvE,IAAA,MAAM,WAAA,GAAc,MAAM,QAAA,CAAS,WAAA,CAAY,GAAG,CAAA;AAElD,IAAA,MAAM,EAAE,IAAA,EAAK,GAAI,GAAA,CAAI,KAAA;AAErB,IAAA,MAAM,QAAA,GAAW,MAAM,YAAA,CAAa,kBAAA;AAAA,MAClC,WAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,GAAA,CAAI,IAAA,CAAK;AAAA,MACP;AAAA,KACD,CAAA;AAAA,EACH,CAAC,CAAA;AAED,EAAA,MAAM,iBAAA,GAAoBD,mBAAE,MAAA,CAAO;AAAA,IACjC,IAAA,EAAMA,mBAAE,MAAA,EAAO;AAAA,IACf,QAAA,EAAUA,mBAAE,GAAA;AAAI,GACjB,CAAA;AAED,EAAA,MAAA,CAAO,KAAA,CAAM,KAAKC,qBAAA,CAAW,iBAAA,EAAmB,MAAM,CAAA,EAAG,OAAO,KAAK,GAAA,KAAQ;AAC3E,IAAA,MAAM,WAAA,GAAc,MAAM,QAAA,CAAS,WAAA,CAAY,GAAG,CAAA;AAClD,IAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,GAAA,CAAI,IAAA;AAE/B,IAAA,MAAM,YAAA,CAAa,kBAAA,CAAmB,WAAA,EAAa,IAAA,EAAM,QAAQ,CAAA;AACjE,IAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,EAAK;AAAA,EACvB,CAAC,CAAA;AAED,EAAA,MAAM,UAAA,GAAaC,gCAAA,CAAkB,MAAA,CAAO,OAAO,CAAA;AAEnD,EAAA,MAAA,CAAO,GAAA,CAAI,UAAA,CAAW,KAAA,EAAO,CAAA;AAE7B,EAAA,OAAO,MAAA;AACT;;;;"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var Router = require('express-promise-router');
|
|
4
4
|
var z = require('zod');
|
|
5
|
-
var validation = require('
|
|
5
|
+
var validation = require('../middleware/validation.cjs.js');
|
|
6
6
|
|
|
7
7
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
8
8
|
|
|
@@ -27,7 +27,7 @@ async function createMcpRouter(options) {
|
|
|
27
27
|
const credentials = await httpAuth.credentials(req);
|
|
28
28
|
const { name, options: mcpOptions } = req.body;
|
|
29
29
|
try {
|
|
30
|
-
await mcp.
|
|
30
|
+
await mcp.setUserMcpServerConfig(credentials, {
|
|
31
31
|
name,
|
|
32
32
|
options: mcpOptions
|
|
33
33
|
});
|
|
@@ -47,7 +47,7 @@ async function createMcpRouter(options) {
|
|
|
47
47
|
const credentials = await httpAuth.credentials(req);
|
|
48
48
|
const { name, options: mcpOptions } = req.body;
|
|
49
49
|
try {
|
|
50
|
-
await mcp.
|
|
50
|
+
await mcp.setUserMcpServerConfig(credentials, {
|
|
51
51
|
name,
|
|
52
52
|
options: mcpOptions
|
|
53
53
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.cjs.js","sources":["../../../../src/services/router/settings/mcp.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport { McpService } from '../../mcp';\nimport { HttpAuthService } from '@backstage/backend-plugin-api';\nimport z from 'zod';\nimport { validation } from '../middleware/validation';\n\nexport type McpRouterOptions = {\n mcp: McpService;\n httpAuth: HttpAuthService;\n};\n\nexport async function createMcpRouter(\n options: McpRouterOptions,\n): Promise<express.Router> {\n const { mcp, httpAuth } = options;\n const router = Router();\n\n router.get('/config', async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n\n const names = await mcp.getUserMcpServerConfigNames(credentials);\n\n res.json({\n names,\n });\n });\n\n const configSchema = z.object({\n name: z.string(),\n options: z.record(z.string(), z.any()),\n });\n\n router.post('/config', validation(configSchema, 'body'), async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n const { name, options: mcpOptions } = req.body;\n try {\n await mcp.setUserMcpServerConfig(credentials, {\n name,\n options: mcpOptions,\n });\n\n res.status(201).send();\n } catch (error) {\n if (error instanceof Error && error.name === 'McpConfigurationError') {\n res.status(400).json({ error: error.message });\n return;\n }\n throw error;\n }\n });\n\n router.patch(\n '/config',\n validation(configSchema, 'body'),\n async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n const { name, options: mcpOptions } = req.body;\n\n try {\n await mcp.setUserMcpServerConfig(credentials, {\n name,\n options: mcpOptions,\n });\n res.status(204).send();\n } catch (error) {\n if (error instanceof Error && error.name === 'McpConfigurationError') {\n res.status(400).json({ error: error.message });\n return;\n }\n throw error;\n }\n },\n );\n\n const deleteConfigSchema = z.object({\n name: z.string(),\n });\n\n router.delete(\n '/config',\n validation(deleteConfigSchema, 'body'),\n async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n const { name } = req.body;\n\n await mcp.deleteUserMcpServerConfig(credentials, name);\n\n res.status(204).send();\n },\n );\n\n return router;\n}\n"],"names":["Router","z","validation"],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var userSettingsStore = require('../database/user-settings-store.cjs.js');
|
|
4
|
+
|
|
5
|
+
const createUserSettingsService = async ({
|
|
6
|
+
userInfo,
|
|
7
|
+
database
|
|
8
|
+
}) => {
|
|
9
|
+
const userSettingsStore$1 = await userSettingsStore.UserSettingsStore.fromConfig({ database });
|
|
10
|
+
const getSettingsForType = async (credentials, type) => {
|
|
11
|
+
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
12
|
+
const settings = await userSettingsStore$1.getUserSettingsByType(
|
|
13
|
+
userEntityRef,
|
|
14
|
+
type
|
|
15
|
+
);
|
|
16
|
+
return settings ?? {};
|
|
17
|
+
};
|
|
18
|
+
const setSettingsForType = async (credentials, type, settings) => {
|
|
19
|
+
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
20
|
+
await userSettingsStore$1.setUserSettings(userEntityRef, type, settings);
|
|
21
|
+
};
|
|
22
|
+
const deleteSettingsForType = async (credentials, type) => {
|
|
23
|
+
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
24
|
+
await userSettingsStore$1.deleteUserSettings(userEntityRef, type);
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
getSettingsForType,
|
|
28
|
+
setSettingsForType,
|
|
29
|
+
deleteSettingsForType
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
exports.createUserSettingsService = createUserSettingsService;
|
|
34
|
+
//# sourceMappingURL=user-settings.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-settings.cjs.js","sources":["../../src/services/user-settings.ts"],"sourcesContent":["import {\n BackstageCredentials,\n DatabaseService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { UserSettingsStore } from '../database/user-settings-store';\n\ntype CreateUserSettingsServiceOptions = {\n userInfo: UserInfoService;\n database: DatabaseService;\n};\n\nexport type UserSettingsService = {\n getSettingsForType: (\n credentials: BackstageCredentials,\n type: string,\n ) => Promise<Record<string, unknown>>;\n setSettingsForType: (\n credentials: BackstageCredentials,\n type: string,\n settings: Record<string, unknown>,\n ) => Promise<void>;\n deleteSettingsForType: (\n credentials: BackstageCredentials,\n type: string,\n ) => Promise<void>;\n};\n\nexport const createUserSettingsService = async ({\n userInfo,\n database,\n}: CreateUserSettingsServiceOptions): Promise<UserSettingsService> => {\n const userSettingsStore = await UserSettingsStore.fromConfig({ database });\n\n const getSettingsForType: UserSettingsService['getSettingsForType'] = async (\n credentials,\n type,\n ) => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n const settings = await userSettingsStore.getUserSettingsByType(\n userEntityRef,\n type,\n );\n\n return settings ?? {};\n };\n\n const setSettingsForType: UserSettingsService['setSettingsForType'] = async (\n credentials,\n type,\n settings,\n ) => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n await userSettingsStore.setUserSettings(userEntityRef, type, settings);\n };\n\n const deleteSettingsForType: UserSettingsService['deleteSettingsForType'] =\n async (credentials, type) => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n await userSettingsStore.deleteUserSettings(userEntityRef, type);\n };\n\n return {\n getSettingsForType,\n setSettingsForType,\n deleteSettingsForType,\n };\n};\n"],"names":["userSettingsStore","UserSettingsStore"],"mappings":";;;;AA4BO,MAAM,4BAA4B,OAAO;AAAA,EAC9C,QAAA;AAAA,EACA;AACF,CAAA,KAAsE;AACpE,EAAA,MAAMA,sBAAoB,MAAMC,mCAAA,CAAkB,UAAA,CAAW,EAAE,UAAU,CAAA;AAEzE,EAAA,MAAM,kBAAA,GAAgE,OACpE,WAAA,EACA,IAAA,KACG;AACH,IAAA,MAAM,EAAE,aAAA,EAAc,GAAI,MAAM,QAAA,CAAS,YAAY,WAAW,CAAA;AAChE,IAAA,MAAM,QAAA,GAAW,MAAMD,mBAAA,CAAkB,qBAAA;AAAA,MACvC,aAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,OAAO,YAAY,EAAC;AAAA,EACtB,CAAA;AAEA,EAAA,MAAM,kBAAA,GAAgE,OACpE,WAAA,EACA,IAAA,EACA,QAAA,KACG;AACH,IAAA,MAAM,EAAE,aAAA,EAAc,GAAI,MAAM,QAAA,CAAS,YAAY,WAAW,CAAA;AAChE,IAAA,MAAMA,mBAAA,CAAkB,eAAA,CAAgB,aAAA,EAAe,IAAA,EAAM,QAAQ,CAAA;AAAA,EACvE,CAAA;AAEA,EAAA,MAAM,qBAAA,GACJ,OAAO,WAAA,EAAa,IAAA,KAAS;AAC3B,IAAA,MAAM,EAAE,aAAA,EAAc,GAAI,MAAM,QAAA,CAAS,YAAY,WAAW,CAAA;AAChE,IAAA,MAAMA,mBAAA,CAAkB,kBAAA,CAAmB,aAAA,EAAe,IAAI,CAAA;AAAA,EAChE,CAAA;AAEF,EAAA,OAAO;AAAA,IACL,kBAAA;AAAA,IACA,kBAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
|
|
@@ -19,6 +19,7 @@ Use this tool when users ask about:
|
|
|
19
19
|
- General questions about the company or internal information
|
|
20
20
|
|
|
21
21
|
Do NOT use for general knowledge that doesn't require company-specific information.`,
|
|
22
|
+
provider: "core",
|
|
22
23
|
schema: z__default.default.object({
|
|
23
24
|
query: z__default.default.string().describe("The query to search for."),
|
|
24
25
|
filter: z__default.default.object({
|
|
@@ -33,7 +34,6 @@ Do NOT use for general knowledge that doesn't require company-specific informati
|
|
|
33
34
|
filter,
|
|
34
35
|
amount
|
|
35
36
|
);
|
|
36
|
-
console.log(results);
|
|
37
37
|
if (results.length === 0) {
|
|
38
38
|
return {
|
|
39
39
|
content: "No relevant information found in the knowledge base."
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchKnowledge.cjs.js","sources":["../../src/tools/searchKnowledge.ts"],"sourcesContent":["import {\n createAssistantTool,\n VectorStore,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { Tool } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport z from 'zod';\n\ntype CreateSearchKnowledgeToolOptions = {\n vectorStore: VectorStore;\n};\n\nexport const createSearchKnowledgeTool = ({\n vectorStore,\n}: CreateSearchKnowledgeToolOptions): Tool => {\n const knowledgeTool = createAssistantTool({\n tool: {\n name: 'search-knowledge-base',\n description: `Search the internal knowledge base containing company specific information.\n\nUse this tool when users ask about:\n- General questions about the company or internal information\n\nDo NOT use for general knowledge that doesn't require company-specific information.`,\n provider: 'core',\n schema: z.object({\n query: z.string().describe('The query to search for.'),\n filter: z\n .object({\n source: z.string().optional().describe('Source to filter by.'),\n id: z.string().optional().describe('ID to filter by.'),\n })\n .optional()\n .describe('Filters to apply to the search.'),\n amount: z\n .number()\n .min(1)\n .optional()\n .describe('The number of results to return.'),\n }),\n func: async ({ query, filter, amount }) => {\n const results = await vectorStore.similaritySearch(\n query,\n filter,\n amount,\n );\n\n if (results.length === 0) {\n return {\n content: 'No relevant information found in the knowledge base.',\n };\n }\n\n const content = results.map(r => r.content).join('\\n---\\n');\n\n const urls = results.map(r => r.metadata.url);\n\n return {\n content,\n metadata: {\n urls,\n },\n };\n },\n },\n });\n\n return knowledgeTool;\n};\n"],"names":["createAssistantTool","z"],"mappings":";;;;;;;;;AAWO,MAAM,4BAA4B,CAAC;AAAA,EACxC;AACF,CAAA,KAA8C;AAC5C,EAAA,MAAM,gBAAgBA,kDAAA,CAAoB;AAAA,IACxC,IAAA,EAAM;AAAA,MACJ,IAAA,EAAM,uBAAA;AAAA,MACN,WAAA,EAAa,CAAA;;AAAA;AAAA;;AAAA,mFAAA,CAAA;AAAA,MAMb,QAAA,EAAU,MAAA;AAAA,MACV,MAAA,EAAQC,mBAAE,MAAA,CAAO;AAAA,QACf,KAAA,EAAOA,kBAAA,CAAE,MAAA,EAAO,CAAE,SAAS,0BAA0B,CAAA;AAAA,QACrD,MAAA,EAAQA,mBACL,MAAA,CAAO;AAAA,UACN,QAAQA,kBAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,sBAAsB,CAAA;AAAA,UAC7D,IAAIA,kBAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,kBAAkB;AAAA,SACtD,CAAA,CACA,QAAA,EAAS,CACT,SAAS,iCAAiC,CAAA;AAAA,QAC7C,MAAA,EAAQA,kBAAA,CACL,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA,EAAS,CACT,QAAA,CAAS,kCAAkC;AAAA,OAC/C,CAAA;AAAA,MACD,MAAM,OAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,QAAO,KAAM;AACzC,QAAA,MAAM,OAAA,GAAU,MAAM,WAAA,CAAY,gBAAA;AAAA,UAChC,KAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACF;AAEA,QAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,UAAA,OAAO;AAAA,YACL,OAAA,EAAS;AAAA,WACX;AAAA,QACF;AAEA,QAAA,MAAM,OAAA,GAAU,QAAQ,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,OAAO,CAAA,CAAE,KAAK,SAAS,CAAA;AAE1D,QAAA,MAAM,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,SAAS,GAAG,CAAA;AAE5C,QAAA,OAAO;AAAA,UACL,OAAA;AAAA,UACA,QAAA,EAAU;AAAA,YACR;AAAA;AACF,SACF;AAAA,MACF;AAAA;AACF,GACD,CAAA;AAED,EAAA,OAAO,aAAA;AACT;;;;"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
const mcpConfigTable = 'user_mcp_config';
|
|
2
|
+
const newConfigTable = 'user_ai_assistant_settings';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {import('knex').knex} knex
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
exports.down = async knex => {
|
|
10
|
+
// Recreate old table
|
|
11
|
+
await knex.schema.createTable(mcpConfigTable, table => {
|
|
12
|
+
table
|
|
13
|
+
.uuid('id')
|
|
14
|
+
.primary()
|
|
15
|
+
.notNullable()
|
|
16
|
+
.defaultTo(knex.raw('gen_random_uuid()'));
|
|
17
|
+
table
|
|
18
|
+
.text('userRef')
|
|
19
|
+
.notNullable()
|
|
20
|
+
.comment('Reference to the user who sent the message');
|
|
21
|
+
table
|
|
22
|
+
.text('name')
|
|
23
|
+
.notNullable()
|
|
24
|
+
.comment('Name of the MCP server configuration');
|
|
25
|
+
table
|
|
26
|
+
.text('encryptedOptions')
|
|
27
|
+
.notNullable()
|
|
28
|
+
.comment('Encrypted MCP server configuration');
|
|
29
|
+
table.timestamps(true, true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const mcpSettings = await knex(newConfigTable)
|
|
33
|
+
.where('type', 'mcp_server_config')
|
|
34
|
+
.select('*');
|
|
35
|
+
|
|
36
|
+
for (const setting of mcpSettings) {
|
|
37
|
+
const servers = setting.data;
|
|
38
|
+
const insertRecords = Object.entries(servers).map(
|
|
39
|
+
([name, encryptedOptions]) => ({
|
|
40
|
+
userRef: setting.userRef,
|
|
41
|
+
name,
|
|
42
|
+
encryptedOptions,
|
|
43
|
+
created_at: setting.created_at,
|
|
44
|
+
updated_at: setting.updated_at,
|
|
45
|
+
}),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (insertRecords.length > 0) {
|
|
49
|
+
await knex.batchInsert(mcpConfigTable, insertRecords);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await knex.schema.dropTable(newConfigTable);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
*
|
|
58
|
+
* @param {import('knex').knex} knex
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
exports.up = async knex => {
|
|
62
|
+
await knex.schema.createTable(newConfigTable, table => {
|
|
63
|
+
table
|
|
64
|
+
.uuid('id')
|
|
65
|
+
.primary()
|
|
66
|
+
.notNullable()
|
|
67
|
+
.defaultTo(knex.raw('gen_random_uuid()'));
|
|
68
|
+
|
|
69
|
+
table.text('userRef').notNullable().comment('Reference to the user');
|
|
70
|
+
|
|
71
|
+
table
|
|
72
|
+
.text('type')
|
|
73
|
+
.notNullable()
|
|
74
|
+
.comment('AI Assistant setting type. i.e "MCP, tools etc"');
|
|
75
|
+
|
|
76
|
+
table
|
|
77
|
+
.jsonb('data')
|
|
78
|
+
.notNullable()
|
|
79
|
+
.comment('AI assistant settings for the user');
|
|
80
|
+
|
|
81
|
+
table.unique(['userRef', 'type']);
|
|
82
|
+
table.timestamps(true, true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Check if old table exists
|
|
86
|
+
const hasMcpConfigTable = await knex.schema.hasTable(mcpConfigTable);
|
|
87
|
+
|
|
88
|
+
if (!hasMcpConfigTable) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const mcpConfigs = await knex(mcpConfigTable).select('*');
|
|
93
|
+
|
|
94
|
+
if (mcpConfigs.length <= 0) {
|
|
95
|
+
await knex.schema.dropTable(mcpConfigTable);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const configsByUser = mcpConfigs.reduce((acc, config) => {
|
|
100
|
+
if (!acc[config.userRef]) {
|
|
101
|
+
acc[config.userRef] = {
|
|
102
|
+
userRef: config.userRef,
|
|
103
|
+
servers: {},
|
|
104
|
+
created_at: config.created_at,
|
|
105
|
+
updated_at: config.updated_at,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
// Use name as key, encryptedOptions as value
|
|
109
|
+
acc[config.userRef].servers[config.name] = config.encryptedOptions;
|
|
110
|
+
|
|
111
|
+
// Keep the most recent timestamps
|
|
112
|
+
if (config.updated_at > acc[config.userRef].updated_at) {
|
|
113
|
+
acc[config.userRef].updated_at = config.updated_at;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return acc;
|
|
117
|
+
}, {});
|
|
118
|
+
|
|
119
|
+
await knex.batchInsert(
|
|
120
|
+
newConfigTable,
|
|
121
|
+
Object.values(configsByUser).map(userConfig => ({
|
|
122
|
+
userRef: userConfig.userRef,
|
|
123
|
+
type: 'mcp_server_config',
|
|
124
|
+
data: userConfig.servers,
|
|
125
|
+
created_at: userConfig.created_at,
|
|
126
|
+
updated_at: userConfig.updated_at,
|
|
127
|
+
})),
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
await knex.schema.dropTable(mcpConfigTable);
|
|
131
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sweetoburrito/backstage-plugin-ai-assistant-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"@langchain/langgraph": "^0.4.9",
|
|
44
44
|
"@langchain/mcp-adapters": "^1.0.0",
|
|
45
45
|
"@langchain/textsplitters": "^0.1.0",
|
|
46
|
-
"@sweetoburrito/backstage-plugin-ai-assistant-common": "^0.
|
|
47
|
-
"@sweetoburrito/backstage-plugin-ai-assistant-node": "^0.
|
|
46
|
+
"@sweetoburrito/backstage-plugin-ai-assistant-common": "^0.7.0",
|
|
47
|
+
"@sweetoburrito/backstage-plugin-ai-assistant-node": "^0.8.0",
|
|
48
48
|
"express": "^4.17.1",
|
|
49
49
|
"express-promise-router": "^4.1.0",
|
|
50
50
|
"knex": "^3.1.0",
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const MCP_TABLE_NAME = "user_mcp_config";
|
|
4
|
-
class McpStore {
|
|
5
|
-
/**
|
|
6
|
-
* Creates an instance of ChatStore.
|
|
7
|
-
* @param client - The Knex client to interact with the PostgreSQL database.
|
|
8
|
-
*/
|
|
9
|
-
constructor(client) {
|
|
10
|
-
this.client = client;
|
|
11
|
-
}
|
|
12
|
-
static async fromConfig({ database }) {
|
|
13
|
-
const client = await database.getClient();
|
|
14
|
-
return new McpStore(client);
|
|
15
|
-
}
|
|
16
|
-
mcpTable() {
|
|
17
|
-
return this.client(MCP_TABLE_NAME);
|
|
18
|
-
}
|
|
19
|
-
async getUserUserMcpConfigNames(userRef) {
|
|
20
|
-
const rows = await this.mcpTable().where({ userRef }).select("name");
|
|
21
|
-
return rows.map((row) => row.name);
|
|
22
|
-
}
|
|
23
|
-
async getUserMcpConfigs(userRef) {
|
|
24
|
-
const rows = await this.mcpTable().where({ userRef }).select();
|
|
25
|
-
return rows.map((row) => ({
|
|
26
|
-
name: row.name,
|
|
27
|
-
encryptedOptions: row.encryptedOptions
|
|
28
|
-
}));
|
|
29
|
-
}
|
|
30
|
-
async createUserMcpConfig(userRef, name, encryptedOptions) {
|
|
31
|
-
await this.mcpTable().insert({
|
|
32
|
-
userRef,
|
|
33
|
-
name,
|
|
34
|
-
encryptedOptions
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
async updateUserMcpConfig(userRef, name, encryptedOptions) {
|
|
38
|
-
await this.mcpTable().where({ userRef, name }).update({ encryptedOptions });
|
|
39
|
-
}
|
|
40
|
-
async deleteUserMcpConfig(userRef, name) {
|
|
41
|
-
await this.mcpTable().where({ userRef, name }).delete();
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
exports.McpStore = McpStore;
|
|
46
|
-
//# sourceMappingURL=mcp-store.cjs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-store.cjs.js","sources":["../../src/database/mcp-store.ts"],"sourcesContent":["import { DatabaseService } from '@backstage/backend-plugin-api';\n\nimport { Knex } from 'knex';\n\nconst MCP_TABLE_NAME = 'user_mcp_config';\n\nexport type McpStoreOptions = {\n database: DatabaseService;\n};\n\nexport class McpStore {\n /**\n * Creates an instance of ChatStore.\n * @param client - The Knex client to interact with the PostgreSQL database.\n */\n constructor(private readonly client: Knex) {}\n\n static async fromConfig({ database }: McpStoreOptions) {\n const client = await database.getClient();\n return new McpStore(client);\n }\n\n mcpTable() {\n return this.client(MCP_TABLE_NAME);\n }\n\n async getUserUserMcpConfigNames(userRef: string): Promise<string[]> {\n const rows = await this.mcpTable().where({ userRef }).select('name');\n return rows.map(row => row.name);\n }\n\n async getUserMcpConfigs(\n userRef: string,\n ): Promise<Array<{ name: string; encryptedOptions: string }>> {\n const rows = await this.mcpTable().where({ userRef }).select();\n return rows.map(row => ({\n name: row.name,\n encryptedOptions: row.encryptedOptions,\n }));\n }\n\n async createUserMcpConfig(\n userRef: string,\n name: string,\n encryptedOptions: string,\n ): Promise<void> {\n await this.mcpTable().insert({\n userRef,\n name,\n encryptedOptions,\n });\n }\n\n async updateUserMcpConfig(\n userRef: string,\n name: string,\n encryptedOptions: string,\n ): Promise<void> {\n await this.mcpTable().where({ userRef, name }).update({ encryptedOptions });\n }\n\n async deleteUserMcpConfig(userRef: string, name: string): Promise<void> {\n await this.mcpTable().where({ userRef, name }).delete();\n }\n}\n"],"names":[],"mappings":";;AAIA,MAAM,cAAA,GAAiB,iBAAA;AAMhB,MAAM,QAAA,CAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpB,YAA6B,MAAA,EAAc;AAAd,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAe;AAAA,EAE5C,aAAa,UAAA,CAAW,EAAE,QAAA,EAAS,EAAoB;AACrD,IAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,SAAA,EAAU;AACxC,IAAA,OAAO,IAAI,SAAS,MAAM,CAAA;AAAA,EAC5B;AAAA,EAEA,QAAA,GAAW;AACT,IAAA,OAAO,IAAA,CAAK,OAAO,cAAc,CAAA;AAAA,EACnC;AAAA,EAEA,MAAM,0BAA0B,OAAA,EAAoC;AAClE,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,QAAA,EAAS,CAAE,KAAA,CAAM,EAAE,OAAA,EAAS,CAAA,CAAE,MAAA,CAAO,MAAM,CAAA;AACnE,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAA,GAAA,KAAO,GAAA,CAAI,IAAI,CAAA;AAAA,EACjC;AAAA,EAEA,MAAM,kBACJ,OAAA,EAC4D;AAC5D,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,QAAA,EAAS,CAAE,MAAM,EAAE,OAAA,EAAS,CAAA,CAAE,MAAA,EAAO;AAC7D,IAAA,OAAO,IAAA,CAAK,IAAI,CAAA,GAAA,MAAQ;AAAA,MACtB,MAAM,GAAA,CAAI,IAAA;AAAA,MACV,kBAAkB,GAAA,CAAI;AAAA,KACxB,CAAE,CAAA;AAAA,EACJ;AAAA,EAEA,MAAM,mBAAA,CACJ,OAAA,EACA,IAAA,EACA,gBAAA,EACe;AACf,IAAA,MAAM,IAAA,CAAK,QAAA,EAAS,CAAE,MAAA,CAAO;AAAA,MAC3B,OAAA;AAAA,MACA,IAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,mBAAA,CACJ,OAAA,EACA,IAAA,EACA,gBAAA,EACe;AACf,IAAA,MAAM,IAAA,CAAK,QAAA,EAAS,CAAE,KAAA,CAAM,EAAE,OAAA,EAAS,IAAA,EAAM,CAAA,CAAE,MAAA,CAAO,EAAE,gBAAA,EAAkB,CAAA;AAAA,EAC5E;AAAA,EAEA,MAAM,mBAAA,CAAoB,OAAA,EAAiB,IAAA,EAA6B;AACtE,IAAA,MAAM,IAAA,CAAK,UAAS,CAAE,KAAA,CAAM,EAAE,OAAA,EAAS,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO;AAAA,EACxD;AACF;;;;"}
|
package/dist/services/mcp.cjs.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var mcpAdapters = require('@langchain/mcp-adapters');
|
|
4
|
-
var backstagePluginAiAssistantNode = require('@sweetoburrito/backstage-plugin-ai-assistant-node');
|
|
5
|
-
var mcpStore = require('../database/mcp-store.cjs.js');
|
|
6
|
-
|
|
7
|
-
const createMcpService = async ({
|
|
8
|
-
config,
|
|
9
|
-
userInfo,
|
|
10
|
-
database
|
|
11
|
-
}) => {
|
|
12
|
-
const serversConfig = config.getOptionalConfigArray(
|
|
13
|
-
"aiAssistant.mcp.servers"
|
|
14
|
-
);
|
|
15
|
-
const encryptionKey = config.getString("aiAssistant.mcp.encryptionKey");
|
|
16
|
-
const preConfiguredMcpServers = serversConfig ? serversConfig.reduce((acc, server) => {
|
|
17
|
-
const serverName = server.getString("name");
|
|
18
|
-
const options = server.get("options");
|
|
19
|
-
acc[serverName] = options;
|
|
20
|
-
return acc;
|
|
21
|
-
}, {}) : null;
|
|
22
|
-
const preConfiguredMcpClient = preConfiguredMcpServers ? new mcpAdapters.MultiServerMCPClient({
|
|
23
|
-
prefixToolNameWithServerName: true,
|
|
24
|
-
useStandardContentBlocks: true,
|
|
25
|
-
mcpServers: preConfiguredMcpServers
|
|
26
|
-
}) : null;
|
|
27
|
-
const mcpStore$1 = await mcpStore.McpStore.fromConfig({ database });
|
|
28
|
-
const getUserMcpServerConfigNames = async (credentials) => {
|
|
29
|
-
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
30
|
-
return mcpStore$1.getUserUserMcpConfigNames(userEntityRef);
|
|
31
|
-
};
|
|
32
|
-
const getUserMcpServerConfig = async (credentials) => {
|
|
33
|
-
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
34
|
-
const encryptedMcpConfig = await mcpStore$1.getUserMcpConfigs(userEntityRef);
|
|
35
|
-
const mcpConfig = encryptedMcpConfig.map((c) => ({
|
|
36
|
-
name: c.name,
|
|
37
|
-
options: JSON.parse(backstagePluginAiAssistantNode.decrypt(c.encryptedOptions, encryptionKey))
|
|
38
|
-
}));
|
|
39
|
-
return mcpConfig;
|
|
40
|
-
};
|
|
41
|
-
const getTools = async (credentials) => {
|
|
42
|
-
const userMcpConfig = await getUserMcpServerConfig(credentials);
|
|
43
|
-
const userMcpServers = userMcpConfig.length ? userMcpConfig.reduce((acc, server) => {
|
|
44
|
-
const { name, options } = server;
|
|
45
|
-
acc[name] = options;
|
|
46
|
-
return acc;
|
|
47
|
-
}, {}) : null;
|
|
48
|
-
const userConfigClient = userMcpServers ? new mcpAdapters.MultiServerMCPClient({
|
|
49
|
-
prefixToolNameWithServerName: true,
|
|
50
|
-
useStandardContentBlocks: true,
|
|
51
|
-
mcpServers: userMcpServers
|
|
52
|
-
}) : null;
|
|
53
|
-
const userMcpTools = userConfigClient ? await userConfigClient.getTools() : [];
|
|
54
|
-
const preConfiguredMcpTools = preConfiguredMcpClient ? await preConfiguredMcpClient.getTools() : [];
|
|
55
|
-
const mcpTools = [...userMcpTools, ...preConfiguredMcpTools];
|
|
56
|
-
const tools = mcpTools.map((mcpTool) => {
|
|
57
|
-
const { name, description, schema } = mcpTool;
|
|
58
|
-
return backstagePluginAiAssistantNode.createAssistantTool({
|
|
59
|
-
tool: {
|
|
60
|
-
name,
|
|
61
|
-
description,
|
|
62
|
-
schema,
|
|
63
|
-
func: async (params) => {
|
|
64
|
-
const result = await mcpTool.invoke(params);
|
|
65
|
-
return {
|
|
66
|
-
content: JSON.stringify(result)
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
return tools;
|
|
73
|
-
};
|
|
74
|
-
const validateMcpServerConfig = async (mcpConfig) => {
|
|
75
|
-
try {
|
|
76
|
-
const userMcpServers = mcpConfig.reduce((acc, server) => {
|
|
77
|
-
const { name, options } = server;
|
|
78
|
-
acc[name] = options;
|
|
79
|
-
return acc;
|
|
80
|
-
}, {});
|
|
81
|
-
const userConfigClient = new mcpAdapters.MultiServerMCPClient({
|
|
82
|
-
prefixToolNameWithServerName: true,
|
|
83
|
-
useStandardContentBlocks: true,
|
|
84
|
-
mcpServers: userMcpServers
|
|
85
|
-
});
|
|
86
|
-
await userConfigClient.getTools();
|
|
87
|
-
} catch (e) {
|
|
88
|
-
const error = new Error("Invalid MCP server configuration");
|
|
89
|
-
error.name = "McpConfigurationError";
|
|
90
|
-
throw error;
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
const createUserMcpServerConfig = async (credentials, mcpConfig) => {
|
|
94
|
-
await validateMcpServerConfig([mcpConfig]);
|
|
95
|
-
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
96
|
-
const { name, options } = mcpConfig;
|
|
97
|
-
const encryptedOptions = backstagePluginAiAssistantNode.encrypt(JSON.stringify(options), encryptionKey);
|
|
98
|
-
await mcpStore$1.createUserMcpConfig(userEntityRef, name, encryptedOptions);
|
|
99
|
-
};
|
|
100
|
-
const updateUserMcpServerConfig = async (credentials, mcpConfig) => {
|
|
101
|
-
await validateMcpServerConfig([mcpConfig]);
|
|
102
|
-
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
103
|
-
const { name, options } = mcpConfig;
|
|
104
|
-
const encryptedOptions = backstagePluginAiAssistantNode.encrypt(JSON.stringify(options), encryptionKey);
|
|
105
|
-
await mcpStore$1.updateUserMcpConfig(userEntityRef, name, encryptedOptions);
|
|
106
|
-
};
|
|
107
|
-
const deleteUserMcpServerConfig = async (credentials, name) => {
|
|
108
|
-
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
109
|
-
await mcpStore$1.deleteUserMcpConfig(userEntityRef, name);
|
|
110
|
-
};
|
|
111
|
-
return {
|
|
112
|
-
getTools,
|
|
113
|
-
createUserMcpServerConfig,
|
|
114
|
-
updateUserMcpServerConfig,
|
|
115
|
-
getUserMcpServerConfigNames,
|
|
116
|
-
deleteUserMcpServerConfig
|
|
117
|
-
};
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
exports.createMcpService = createMcpService;
|
|
121
|
-
//# sourceMappingURL=mcp.cjs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.cjs.js","sources":["../../src/services/mcp.ts"],"sourcesContent":["import {\n BackstageCredentials,\n DatabaseService,\n RootConfigService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { MultiServerMCPClient } from '@langchain/mcp-adapters';\nimport {\n McpServerConfig,\n McpServerConfigOptions,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport {\n createAssistantTool,\n Tool,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { McpStore } from '../database/mcp-store';\nimport {\n encrypt,\n decrypt,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\n\ntype CreateMcpServiceOptions = {\n config: RootConfigService;\n userInfo: UserInfoService;\n database: DatabaseService;\n};\n\nexport type McpService = {\n getTools: (credentials: BackstageCredentials) => Promise<Tool[]>;\n createUserMcpServerConfig: (\n credentials: BackstageCredentials,\n config: McpServerConfig,\n ) => Promise<void>;\n updateUserMcpServerConfig: (\n credentials: BackstageCredentials,\n config: McpServerConfig,\n ) => Promise<void>;\n getUserMcpServerConfigNames: (\n credentials: BackstageCredentials,\n ) => Promise<string[]>;\n deleteUserMcpServerConfig: (\n credentials: BackstageCredentials,\n name: string,\n ) => Promise<void>;\n};\n\nexport const createMcpService = async ({\n config,\n userInfo,\n database,\n}: CreateMcpServiceOptions): Promise<McpService> => {\n const serversConfig = config.getOptionalConfigArray(\n 'aiAssistant.mcp.servers',\n );\n const encryptionKey = config.getString('aiAssistant.mcp.encryptionKey');\n\n const preConfiguredMcpServers = serversConfig\n ? serversConfig.reduce((acc, server) => {\n const serverName = server.getString('name');\n const options = server.get<McpServerConfigOptions>('options');\n\n acc[serverName] = options;\n\n return acc;\n }, {} as Record<string, McpServerConfigOptions>)\n : null;\n\n const preConfiguredMcpClient = preConfiguredMcpServers\n ? new MultiServerMCPClient({\n prefixToolNameWithServerName: true,\n useStandardContentBlocks: true,\n mcpServers: preConfiguredMcpServers,\n })\n : null;\n\n const mcpStore = await McpStore.fromConfig({ database });\n\n const getUserMcpServerConfigNames: McpService['getUserMcpServerConfigNames'] =\n async credentials => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n return mcpStore.getUserUserMcpConfigNames(userEntityRef);\n };\n\n const getUserMcpServerConfig = async (\n credentials: BackstageCredentials,\n ): Promise<McpServerConfig[]> => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n\n const encryptedMcpConfig = await mcpStore.getUserMcpConfigs(userEntityRef);\n\n const mcpConfig: McpServerConfig[] = encryptedMcpConfig.map(c => ({\n name: c.name,\n options: JSON.parse(decrypt(c.encryptedOptions, encryptionKey)),\n }));\n\n return mcpConfig;\n };\n\n const getTools: McpService['getTools'] = async credentials => {\n const userMcpConfig = await getUserMcpServerConfig(credentials);\n\n const userMcpServers = userMcpConfig.length\n ? userMcpConfig.reduce((acc, server) => {\n const { name, options } = server;\n\n acc[name] = options;\n\n return acc;\n }, {} as Record<string, McpServerConfigOptions>)\n : null;\n\n const userConfigClient = userMcpServers\n ? new MultiServerMCPClient({\n prefixToolNameWithServerName: true,\n useStandardContentBlocks: true,\n mcpServers: userMcpServers,\n })\n : null;\n\n const userMcpTools = userConfigClient\n ? await userConfigClient.getTools()\n : [];\n const preConfiguredMcpTools = preConfiguredMcpClient\n ? await preConfiguredMcpClient.getTools()\n : [];\n\n const mcpTools = [...userMcpTools, ...preConfiguredMcpTools];\n\n const tools = mcpTools.map(mcpTool => {\n const { name, description, schema } = mcpTool;\n return createAssistantTool({\n tool: {\n name,\n description,\n schema: schema as Tool['schema'],\n func: async (params: any) => {\n const result = await mcpTool.invoke(params);\n return {\n content: JSON.stringify(result),\n };\n },\n },\n });\n });\n\n return tools;\n };\n\n const validateMcpServerConfig = async (\n mcpConfig: McpServerConfig[],\n ): Promise<void> => {\n try {\n const userMcpServers = mcpConfig.reduce((acc, server) => {\n const { name, options } = server;\n\n acc[name] = options;\n\n return acc;\n }, {} as Record<string, McpServerConfigOptions>);\n\n const userConfigClient = new MultiServerMCPClient({\n prefixToolNameWithServerName: true,\n useStandardContentBlocks: true,\n mcpServers: userMcpServers,\n });\n\n await userConfigClient.getTools();\n } catch (e) {\n const error = new Error('Invalid MCP server configuration');\n error.name = 'McpConfigurationError';\n throw error;\n }\n };\n\n const createUserMcpServerConfig: McpService['createUserMcpServerConfig'] =\n async (credentials, mcpConfig) => {\n await validateMcpServerConfig([mcpConfig]);\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n const { name, options } = mcpConfig;\n\n const encryptedOptions = encrypt(JSON.stringify(options), encryptionKey);\n\n await mcpStore.createUserMcpConfig(userEntityRef, name, encryptedOptions);\n };\n\n const updateUserMcpServerConfig: McpService['updateUserMcpServerConfig'] =\n async (credentials, mcpConfig) => {\n await validateMcpServerConfig([mcpConfig]);\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n const { name, options } = mcpConfig;\n\n const encryptedOptions = encrypt(JSON.stringify(options), encryptionKey);\n\n await mcpStore.updateUserMcpConfig(userEntityRef, name, encryptedOptions);\n };\n\n const deleteUserMcpServerConfig: McpService['deleteUserMcpServerConfig'] =\n async (credentials, name) => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n await mcpStore.deleteUserMcpConfig(userEntityRef, name);\n };\n\n return {\n getTools,\n createUserMcpServerConfig,\n updateUserMcpServerConfig,\n getUserMcpServerConfigNames,\n deleteUserMcpServerConfig,\n };\n};\n"],"names":["MultiServerMCPClient","mcpStore","McpStore","decrypt","createAssistantTool","encrypt"],"mappings":";;;;;;AA8CO,MAAM,mBAAmB,OAAO;AAAA,EACrC,MAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,KAAoD;AAClD,EAAA,MAAM,gBAAgB,MAAA,CAAO,sBAAA;AAAA,IAC3B;AAAA,GACF;AACA,EAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,SAAA,CAAU,+BAA+B,CAAA;AAEtE,EAAA,MAAM,0BAA0B,aAAA,GAC5B,aAAA,CAAc,MAAA,CAAO,CAAC,KAAK,MAAA,KAAW;AACpC,IAAA,MAAM,UAAA,GAAa,MAAA,CAAO,SAAA,CAAU,MAAM,CAAA;AAC1C,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,GAAA,CAA4B,SAAS,CAAA;AAE5D,IAAA,GAAA,CAAI,UAAU,CAAA,GAAI,OAAA;AAElB,IAAA,OAAO,GAAA;AAAA,EACT,CAAA,EAAG,EAA4C,CAAA,GAC/C,IAAA;AAEJ,EAAA,MAAM,sBAAA,GAAyB,uBAAA,GAC3B,IAAIA,gCAAA,CAAqB;AAAA,IACvB,4BAAA,EAA8B,IAAA;AAAA,IAC9B,wBAAA,EAA0B,IAAA;AAAA,IAC1B,UAAA,EAAY;AAAA,GACb,CAAA,GACD,IAAA;AAEJ,EAAA,MAAMC,aAAW,MAAMC,iBAAA,CAAS,UAAA,CAAW,EAAE,UAAU,CAAA;AAEvD,EAAA,MAAM,2BAAA,GACJ,OAAM,WAAA,KAAe;AACnB,IAAA,MAAM,EAAE,aAAA,EAAc,GAAI,MAAM,QAAA,CAAS,YAAY,WAAW,CAAA;AAChE,IAAA,OAAOD,UAAA,CAAS,0BAA0B,aAAa,CAAA;AAAA,EACzD,CAAA;AAEF,EAAA,MAAM,sBAAA,GAAyB,OAC7B,WAAA,KAC+B;AAC/B,IAAA,MAAM,EAAE,aAAA,EAAc,GAAI,MAAM,QAAA,CAAS,YAAY,WAAW,CAAA;AAEhE,IAAA,MAAM,kBAAA,GAAqB,MAAMA,UAAA,CAAS,iBAAA,CAAkB,aAAa,CAAA;AAEzE,IAAA,MAAM,SAAA,GAA+B,kBAAA,CAAmB,GAAA,CAAI,CAAA,CAAA,MAAM;AAAA,MAChE,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,SAAS,IAAA,CAAK,KAAA,CAAME,uCAAQ,CAAA,CAAE,gBAAA,EAAkB,aAAa,CAAC;AAAA,KAChE,CAAE,CAAA;AAEF,IAAA,OAAO,SAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,QAAA,GAAmC,OAAM,WAAA,KAAe;AAC5D,IAAA,MAAM,aAAA,GAAgB,MAAM,sBAAA,CAAuB,WAAW,CAAA;AAE9D,IAAA,MAAM,iBAAiB,aAAA,CAAc,MAAA,GACjC,cAAc,MAAA,CAAO,CAAC,KAAK,MAAA,KAAW;AACpC,MAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAQ,GAAI,MAAA;AAE1B,MAAA,GAAA,CAAI,IAAI,CAAA,GAAI,OAAA;AAEZ,MAAA,OAAO,GAAA;AAAA,IACT,CAAA,EAAG,EAA4C,CAAA,GAC/C,IAAA;AAEJ,IAAA,MAAM,gBAAA,GAAmB,cAAA,GACrB,IAAIH,gCAAA,CAAqB;AAAA,MACvB,4BAAA,EAA8B,IAAA;AAAA,MAC9B,wBAAA,EAA0B,IAAA;AAAA,MAC1B,UAAA,EAAY;AAAA,KACb,CAAA,GACD,IAAA;AAEJ,IAAA,MAAM,eAAe,gBAAA,GACjB,MAAM,gBAAA,CAAiB,QAAA,KACvB,EAAC;AACL,IAAA,MAAM,wBAAwB,sBAAA,GAC1B,MAAM,sBAAA,CAAuB,QAAA,KAC7B,EAAC;AAEL,IAAA,MAAM,QAAA,GAAW,CAAC,GAAG,YAAA,EAAc,GAAG,qBAAqB,CAAA;AAE3D,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,GAAA,CAAI,CAAA,OAAA,KAAW;AACpC,MAAA,MAAM,EAAE,IAAA,EAAM,WAAA,EAAa,MAAA,EAAO,GAAI,OAAA;AACtC,MAAA,OAAOI,kDAAA,CAAoB;AAAA,QACzB,IAAA,EAAM;AAAA,UACJ,IAAA;AAAA,UACA,WAAA;AAAA,UACA,MAAA;AAAA,UACA,IAAA,EAAM,OAAO,MAAA,KAAgB;AAC3B,YAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,MAAA,CAAO,MAAM,CAAA;AAC1C,YAAA,OAAO;AAAA,cACL,OAAA,EAAS,IAAA,CAAK,SAAA,CAAU,MAAM;AAAA,aAChC;AAAA,UACF;AAAA;AACF,OACD,CAAA;AAAA,IACH,CAAC,CAAA;AAED,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,uBAAA,GAA0B,OAC9B,SAAA,KACkB;AAClB,IAAA,IAAI;AACF,MAAA,MAAM,cAAA,GAAiB,SAAA,CAAU,MAAA,CAAO,CAAC,KAAK,MAAA,KAAW;AACvD,QAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAQ,GAAI,MAAA;AAE1B,QAAA,GAAA,CAAI,IAAI,CAAA,GAAI,OAAA;AAEZ,QAAA,OAAO,GAAA;AAAA,MACT,CAAA,EAAG,EAA4C,CAAA;AAE/C,MAAA,MAAM,gBAAA,GAAmB,IAAIJ,gCAAA,CAAqB;AAAA,QAChD,4BAAA,EAA8B,IAAA;AAAA,QAC9B,wBAAA,EAA0B,IAAA;AAAA,QAC1B,UAAA,EAAY;AAAA,OACb,CAAA;AAED,MAAA,MAAM,iBAAiB,QAAA,EAAS;AAAA,IAClC,SAAS,CAAA,EAAG;AACV,MAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,CAAM,kCAAkC,CAAA;AAC1D,MAAA,KAAA,CAAM,IAAA,GAAO,uBAAA;AACb,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,yBAAA,GACJ,OAAO,WAAA,EAAa,SAAA,KAAc;AAChC,IAAA,MAAM,uBAAA,CAAwB,CAAC,SAAS,CAAC,CAAA;AACzC,IAAA,MAAM,EAAE,aAAA,EAAc,GAAI,MAAM,QAAA,CAAS,YAAY,WAAW,CAAA;AAChE,IAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAQ,GAAI,SAAA;AAE1B,IAAA,MAAM,mBAAmBK,sCAAA,CAAQ,IAAA,CAAK,SAAA,CAAU,OAAO,GAAG,aAAa,CAAA;AAEvE,IAAA,MAAMJ,UAAA,CAAS,mBAAA,CAAoB,aAAA,EAAe,IAAA,EAAM,gBAAgB,CAAA;AAAA,EAC1E,CAAA;AAEF,EAAA,MAAM,yBAAA,GACJ,OAAO,WAAA,EAAa,SAAA,KAAc;AAChC,IAAA,MAAM,uBAAA,CAAwB,CAAC,SAAS,CAAC,CAAA;AACzC,IAAA,MAAM,EAAE,aAAA,EAAc,GAAI,MAAM,QAAA,CAAS,YAAY,WAAW,CAAA;AAChE,IAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAQ,GAAI,SAAA;AAE1B,IAAA,MAAM,mBAAmBI,sCAAA,CAAQ,IAAA,CAAK,SAAA,CAAU,OAAO,GAAG,aAAa,CAAA;AAEvE,IAAA,MAAMJ,UAAA,CAAS,mBAAA,CAAoB,aAAA,EAAe,IAAA,EAAM,gBAAgB,CAAA;AAAA,EAC1E,CAAA;AAEF,EAAA,MAAM,yBAAA,GACJ,OAAO,WAAA,EAAa,IAAA,KAAS;AAC3B,IAAA,MAAM,EAAE,aAAA,EAAc,GAAI,MAAM,QAAA,CAAS,YAAY,WAAW,CAAA;AAChE,IAAA,MAAMA,UAAA,CAAS,mBAAA,CAAoB,aAAA,EAAe,IAAI,CAAA;AAAA,EACxD,CAAA;AAEF,EAAA,OAAO;AAAA,IACL,QAAA;AAAA,IACA,yBAAA;AAAA,IACA,yBAAA;AAAA,IACA,2BAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.cjs.js","sources":["../../../src/services/router/mcp.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport { McpService } from '../mcp';\nimport { HttpAuthService } from '@backstage/backend-plugin-api';\nimport z from 'zod';\nimport { validation } from './middleware/validation';\n\nexport type McpRouterOptions = {\n mcp: McpService;\n httpAuth: HttpAuthService;\n};\n\nexport async function createMcpRouter(\n options: McpRouterOptions,\n): Promise<express.Router> {\n const { mcp, httpAuth } = options;\n const router = Router();\n\n router.get('/config', async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n\n const names = await mcp.getUserMcpServerConfigNames(credentials);\n\n res.json({\n names,\n });\n });\n\n const configSchema = z.object({\n name: z.string(),\n options: z.record(z.string(), z.any()),\n });\n\n router.post('/config', validation(configSchema, 'body'), async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n const { name, options: mcpOptions } = req.body;\n try {\n await mcp.createUserMcpServerConfig(credentials, {\n name,\n options: mcpOptions,\n });\n\n res.status(201).send();\n } catch (error) {\n if (error instanceof Error && error.name === 'McpConfigurationError') {\n res.status(400).json({ error: error.message });\n return;\n }\n throw error;\n }\n });\n\n router.patch(\n '/config',\n validation(configSchema, 'body'),\n async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n const { name, options: mcpOptions } = req.body;\n\n try {\n await mcp.updateUserMcpServerConfig(credentials, {\n name,\n options: mcpOptions,\n });\n res.status(204).send();\n } catch (error) {\n if (error instanceof Error && error.name === 'McpConfigurationError') {\n res.status(400).json({ error: error.message });\n return;\n }\n throw error;\n }\n },\n );\n\n const deleteConfigSchema = z.object({\n name: z.string(),\n });\n\n router.delete(\n '/config',\n validation(deleteConfigSchema, 'body'),\n async (req, res) => {\n const credentials = await httpAuth.credentials(req);\n const { name } = req.body;\n\n await mcp.deleteUserMcpServerConfig(credentials, name);\n\n res.status(204).send();\n },\n );\n\n return router;\n}\n"],"names":["Router","z","validation"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"searchKnowledge.cjs.js","sources":["../../../src/services/tools/searchKnowledge.ts"],"sourcesContent":["import {\n createAssistantTool,\n Tool,\n VectorStore,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport z from 'zod';\n\ntype CreateSearchKnowledgeToolOptions = {\n vectorStore: VectorStore;\n};\n\nexport const createSearchKnowledgeTool = ({\n vectorStore,\n}: CreateSearchKnowledgeToolOptions): Tool => {\n const knowledgeTool = createAssistantTool({\n tool: {\n name: 'search-knowledge-base',\n description: `Search the internal knowledge base containing company specific information.\n\nUse this tool when users ask about:\n- General questions about the company or internal information\n\nDo NOT use for general knowledge that doesn't require company-specific information.`,\n schema: z.object({\n query: z.string().describe('The query to search for.'),\n filter: z\n .object({\n source: z.string().optional().describe('Source to filter by.'),\n id: z.string().optional().describe('ID to filter by.'),\n })\n .optional()\n .describe('Filters to apply to the search.'),\n amount: z\n .number()\n .min(1)\n .optional()\n .describe('The number of results to return.'),\n }),\n func: async ({ query, filter, amount }) => {\n const results = await vectorStore.similaritySearch(\n query,\n filter,\n amount,\n );\n\n console.log(results);\n\n if (results.length === 0) {\n return {\n content: 'No relevant information found in the knowledge base.',\n };\n }\n\n const content = results.map(r => r.content).join('\\n---\\n');\n\n const urls = results.map(r => r.metadata.url);\n\n return {\n content,\n metadata: {\n urls,\n },\n };\n },\n },\n });\n\n return knowledgeTool;\n};\n"],"names":["createAssistantTool","z"],"mappings":";;;;;;;;;AAWO,MAAM,4BAA4B,CAAC;AAAA,EACxC;AACF,CAAA,KAA8C;AAC5C,EAAA,MAAM,gBAAgBA,kDAAA,CAAoB;AAAA,IACxC,IAAA,EAAM;AAAA,MACJ,IAAA,EAAM,uBAAA;AAAA,MACN,WAAA,EAAa,CAAA;;AAAA;AAAA;;AAAA,mFAAA,CAAA;AAAA,MAMb,MAAA,EAAQC,mBAAE,MAAA,CAAO;AAAA,QACf,KAAA,EAAOA,kBAAA,CAAE,MAAA,EAAO,CAAE,SAAS,0BAA0B,CAAA;AAAA,QACrD,MAAA,EAAQA,mBACL,MAAA,CAAO;AAAA,UACN,QAAQA,kBAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,sBAAsB,CAAA;AAAA,UAC7D,IAAIA,kBAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,kBAAkB;AAAA,SACtD,CAAA,CACA,QAAA,EAAS,CACT,SAAS,iCAAiC,CAAA;AAAA,QAC7C,MAAA,EAAQA,kBAAA,CACL,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA,EAAS,CACT,QAAA,CAAS,kCAAkC;AAAA,OAC/C,CAAA;AAAA,MACD,MAAM,OAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,QAAO,KAAM;AACzC,QAAA,MAAM,OAAA,GAAU,MAAM,WAAA,CAAY,gBAAA;AAAA,UAChC,KAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACF;AAEA,QAAA,OAAA,CAAQ,IAAI,OAAO,CAAA;AAEnB,QAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,UAAA,OAAO;AAAA,YACL,OAAA,EAAS;AAAA,WACX;AAAA,QACF;AAEA,QAAA,MAAM,OAAA,GAAU,QAAQ,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,OAAO,CAAA,CAAE,KAAK,SAAS,CAAA;AAE1D,QAAA,MAAM,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,SAAS,GAAG,CAAA;AAE5C,QAAA,OAAO;AAAA,UACL,OAAA;AAAA,UACA,QAAA,EAAU;AAAA,YACR;AAAA;AACF,SACF;AAAA,MACF;AAAA;AACF,GACD,CAAA;AAED,EAAA,OAAO,aAAA;AACT;;;;"}
|