@sweetoburrito/backstage-plugin-ai-assistant-backend 0.0.0-snapshot-20251114142343 → 0.0.0-snapshot-20251126133455
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/constants/prompts.cjs.js +15 -7
- package/dist/constants/prompts.cjs.js.map +1 -1
- 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 +25 -8
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/services/chat.cjs.js +31 -12
- 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 +4 -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/router/summary.cjs.js +56 -0
- package/dist/services/router/summary.cjs.js.map +1 -0
- package/dist/services/summarizer.cjs.js +24 -21
- package/dist/services/summarizer.cjs.js.map +1 -1
- 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 +12 -2
- 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 -119
- 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 @@
|
|
|
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,7 +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
|
|
8
|
+
var summary = require('./summary.cjs.js');
|
|
9
|
+
var index = require('./settings/index.cjs.js');
|
|
9
10
|
|
|
10
11
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
11
12
|
|
|
@@ -17,7 +18,8 @@ async function createRouter(options) {
|
|
|
17
18
|
router.use(express__default.default.json());
|
|
18
19
|
router.use("/chat", await chat.createChatRouter(options));
|
|
19
20
|
router.use("/models", await models.createModelRouter(options));
|
|
20
|
-
router.use("/
|
|
21
|
+
router.use("/summary", await summary.createSummaryRouter(options));
|
|
22
|
+
router.use("/settings", await index.createSettingsRouter(options));
|
|
21
23
|
const middleware = rootHttpRouter.MiddlewareFactory.create(options);
|
|
22
24
|
router.use(middleware.error());
|
|
23
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,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var Router = require('express-promise-router');
|
|
4
|
+
var validation = require('./middleware/validation.cjs.js');
|
|
5
|
+
var z = require('zod');
|
|
6
|
+
|
|
7
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
10
|
+
var z__default = /*#__PURE__*/_interopDefaultCompat(z);
|
|
11
|
+
|
|
12
|
+
async function createSummaryRouter(options) {
|
|
13
|
+
const { summarizer } = options;
|
|
14
|
+
const router = Router__default.default();
|
|
15
|
+
const contentSchema = z__default.default.object({
|
|
16
|
+
content: z__default.default.string(),
|
|
17
|
+
length: z__default.default.string().optional()
|
|
18
|
+
});
|
|
19
|
+
router.post(
|
|
20
|
+
"/content",
|
|
21
|
+
validation.validation(contentSchema, "body"),
|
|
22
|
+
async (req, res) => {
|
|
23
|
+
const { content, length } = req.body;
|
|
24
|
+
const summary = await summarizer.summarize({
|
|
25
|
+
content,
|
|
26
|
+
length
|
|
27
|
+
});
|
|
28
|
+
res.json({ summary });
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
const conversationSchema = z__default.default.object({
|
|
32
|
+
messages: z__default.default.array(
|
|
33
|
+
z__default.default.object({
|
|
34
|
+
role: z__default.default.string(),
|
|
35
|
+
content: z__default.default.string()
|
|
36
|
+
})
|
|
37
|
+
),
|
|
38
|
+
length: z__default.default.string().optional()
|
|
39
|
+
});
|
|
40
|
+
router.post(
|
|
41
|
+
"/conversation",
|
|
42
|
+
validation.validation(conversationSchema, "body"),
|
|
43
|
+
async (req, res) => {
|
|
44
|
+
const { messages, length } = req.body;
|
|
45
|
+
const summary = await summarizer.summarizeConversation({
|
|
46
|
+
messages,
|
|
47
|
+
length
|
|
48
|
+
});
|
|
49
|
+
res.json({ summary });
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
return router;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
exports.createSummaryRouter = createSummaryRouter;
|
|
56
|
+
//# sourceMappingURL=summary.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.cjs.js","sources":["../../../src/services/router/summary.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport { SummarizerService } from '../summarizer';\nimport { validation } from './middleware/validation';\nimport z from 'zod';\n\nexport type SummaryRouterOptions = {\n summarizer: SummarizerService;\n};\n\nexport async function createSummaryRouter(\n options: SummaryRouterOptions,\n): Promise<express.Router> {\n const { summarizer } = options;\n\n const router = Router();\n\n const contentSchema = z.object({\n content: z.string(),\n length: z.string().optional(),\n });\n\n router.post(\n '/content',\n validation(contentSchema, 'body'),\n async (req, res) => {\n const { content, length } = req.body;\n\n const summary = await summarizer.summarize({\n content,\n length,\n });\n res.json({ summary });\n },\n );\n\n const conversationSchema = z.object({\n messages: z.array(\n z.object({\n role: z.string(),\n content: z.string(),\n }),\n ),\n length: z.string().optional(),\n });\n\n router.post(\n '/conversation',\n validation(conversationSchema, 'body'),\n async (req, res) => {\n const { messages, length } = req.body;\n const summary = await summarizer.summarizeConversation({\n messages,\n length,\n });\n res.json({ summary });\n },\n );\n\n return router;\n}\n"],"names":["Router","z","validation"],"mappings":";;;;;;;;;;;AAUA,eAAsB,oBACpB,OAAA,EACyB;AACzB,EAAA,MAAM,EAAE,YAAW,GAAI,OAAA;AAEvB,EAAA,MAAM,SAASA,uBAAA,EAAO;AAEtB,EAAA,MAAM,aAAA,GAAgBC,mBAAE,MAAA,CAAO;AAAA,IAC7B,OAAA,EAASA,mBAAE,MAAA,EAAO;AAAA,IAClB,MAAA,EAAQA,kBAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAAS,GAC7B,CAAA;AAED,EAAA,MAAA,CAAO,IAAA;AAAA,IACL,UAAA;AAAA,IACAC,qBAAA,CAAW,eAAe,MAAM,CAAA;AAAA,IAChC,OAAO,KAAK,GAAA,KAAQ;AAClB,MAAA,MAAM,EAAE,OAAA,EAAS,MAAA,EAAO,GAAI,GAAA,CAAI,IAAA;AAEhC,MAAA,MAAM,OAAA,GAAU,MAAM,UAAA,CAAW,SAAA,CAAU;AAAA,QACzC,OAAA;AAAA,QACA;AAAA,OACD,CAAA;AACD,MAAA,GAAA,CAAI,IAAA,CAAK,EAAE,OAAA,EAAS,CAAA;AAAA,IACtB;AAAA,GACF;AAEA,EAAA,MAAM,kBAAA,GAAqBD,mBAAE,MAAA,CAAO;AAAA,IAClC,UAAUA,kBAAA,CAAE,KAAA;AAAA,MACVA,mBAAE,MAAA,CAAO;AAAA,QACP,IAAA,EAAMA,mBAAE,MAAA,EAAO;AAAA,QACf,OAAA,EAASA,mBAAE,MAAA;AAAO,OACnB;AAAA,KACH;AAAA,IACA,MAAA,EAAQA,kBAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAAS,GAC7B,CAAA;AAED,EAAA,MAAA,CAAO,IAAA;AAAA,IACL,eAAA;AAAA,IACAC,qBAAA,CAAW,oBAAoB,MAAM,CAAA;AAAA,IACrC,OAAO,KAAK,GAAA,KAAQ;AAClB,MAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAO,GAAI,GAAA,CAAI,IAAA;AACjC,MAAA,MAAM,OAAA,GAAU,MAAM,UAAA,CAAW,qBAAA,CAAsB;AAAA,QACrD,QAAA;AAAA,QACA;AAAA,OACD,CAAA;AACD,MAAA,GAAA,CAAI,IAAA,CAAK,EAAE,OAAA,EAAS,CAAA;AAAA,IACtB;AAAA,GACF;AAEA,EAAA,OAAO,MAAA;AACT;;;;"}
|
|
@@ -9,34 +9,26 @@ const createSummarizerService = async ({
|
|
|
9
9
|
callback
|
|
10
10
|
}) => {
|
|
11
11
|
const summaryModelId = config.getOptionalString("aiAssistant.conversation.summaryModel") ?? models[0].id;
|
|
12
|
-
const
|
|
12
|
+
const conversationSummaryPrompt = config.getOptionalString("aiAssistant.conversation.summaryPrompt") ?? prompts.DEFAULT_CONVERSATION_SUMMARY_PROMPT;
|
|
13
13
|
const model = models.find((m) => m.id === summaryModelId);
|
|
14
14
|
if (!model) {
|
|
15
15
|
throw new Error(`Summary model with id ${summaryModelId} not found`);
|
|
16
16
|
}
|
|
17
17
|
const llm = model.chatModel;
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{summaryPrompt}
|
|
18
|
+
const summaryPromptTemplate = prompts$1.PromptTemplate.fromTemplate(`
|
|
19
|
+
PURPOSE:
|
|
20
|
+
{summaryPrompt}
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
`),
|
|
25
|
-
prompts$1.HumanMessagePromptTemplate.fromTemplate(`
|
|
26
|
-
Conversation:
|
|
27
|
-
{conversation}
|
|
22
|
+
Summarize the following content in {length}.
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const summarize = async (
|
|
33
|
-
const
|
|
34
|
-
(msg) => msg.role === "ai" || msg.role === "human"
|
|
35
|
-
);
|
|
36
|
-
const prompt = await chatPromptTemplate.formatMessages({
|
|
24
|
+
Content:
|
|
25
|
+
{content}
|
|
26
|
+
`);
|
|
27
|
+
const summarize = async (content, summaryPrompt = prompts.DEFAULT_SUMMARY_PROMPT, length = "as few words as possible") => {
|
|
28
|
+
const prompt = await summaryPromptTemplate.format({
|
|
37
29
|
summaryPrompt,
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
content,
|
|
31
|
+
length
|
|
40
32
|
});
|
|
41
33
|
const { callbacks } = await callback.getChainCallbacks({
|
|
42
34
|
conversationId: "summarizer",
|
|
@@ -55,7 +47,18 @@ const createSummarizerService = async ({
|
|
|
55
47
|
});
|
|
56
48
|
return text.trim();
|
|
57
49
|
};
|
|
58
|
-
|
|
50
|
+
const summarizeConversation = async ({ messages, length = "as few words as possible" }) => {
|
|
51
|
+
const conversationMessages = messages.filter(
|
|
52
|
+
(msg) => msg.role === "ai" || msg.role === "human"
|
|
53
|
+
);
|
|
54
|
+
const conversation = conversationMessages.map((msg) => `${msg.role}: ${msg.content}`).join("\n");
|
|
55
|
+
return summarize({
|
|
56
|
+
content: conversation,
|
|
57
|
+
prompt: conversationSummaryPrompt,
|
|
58
|
+
length
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
return { summarizeConversation, summarize };
|
|
59
62
|
};
|
|
60
63
|
|
|
61
64
|
exports.createSummarizerService = createSummarizerService;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summarizer.cjs.js","sources":["../../src/services/summarizer.ts"],"sourcesContent":["import { RootConfigService } from '@backstage/backend-plugin-api';\nimport { Model } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport {
|
|
1
|
+
{"version":3,"file":"summarizer.cjs.js","sources":["../../src/services/summarizer.ts"],"sourcesContent":["import { RootConfigService } from '@backstage/backend-plugin-api';\nimport { Model } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport {\n DEFAULT_CONVERSATION_SUMMARY_PROMPT,\n DEFAULT_SUMMARY_PROMPT,\n} from '../constants/prompts';\nimport { PromptTemplate } from '@langchain/core/prompts';\nimport { Message } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { CallbackService } from './callbacks';\n\nexport type SummarizerService = {\n summarizeConversation: (options: {\n messages: Message[];\n length?: string;\n }) => Promise<string>;\n\n summarize: (options: {\n content: string;\n prompt?: string;\n length?: string;\n }) => Promise<string>;\n};\n\ntype SummarizerServiceOptions = {\n config: RootConfigService;\n models: Model[];\n callback: CallbackService;\n};\n\nexport const createSummarizerService = async ({\n config,\n models,\n callback,\n}: SummarizerServiceOptions): Promise<SummarizerService> => {\n const summaryModelId =\n config.getOptionalString('aiAssistant.conversation.summaryModel') ??\n models[0].id;\n\n const conversationSummaryPrompt =\n config.getOptionalString('aiAssistant.conversation.summaryPrompt') ??\n DEFAULT_CONVERSATION_SUMMARY_PROMPT;\n\n const model = models.find(m => m.id === summaryModelId);\n\n if (!model) {\n throw new Error(`Summary model with id ${summaryModelId} not found`);\n }\n\n const llm = model.chatModel;\n\n const summaryPromptTemplate = PromptTemplate.fromTemplate(`\n PURPOSE:\n {summaryPrompt}\n\n Summarize the following content in {length}.\n\n Content:\n {content}\n `);\n\n const summarize: SummarizerService['summarize'] = async (\n content,\n summaryPrompt = DEFAULT_SUMMARY_PROMPT,\n length = 'as few words as possible',\n ) => {\n const prompt = await summaryPromptTemplate.format({\n summaryPrompt,\n content,\n length,\n });\n\n const { callbacks } = await callback.getChainCallbacks({\n conversationId: 'summarizer',\n userId: 'system',\n modelId: summaryModelId,\n });\n\n const { metadata } = await callback.getChainMetadata({\n conversationId: 'summarizer',\n userId: 'system',\n modelId: summaryModelId,\n });\n\n const { text } = await llm.invoke(prompt, {\n callbacks,\n runName: 'summarizer',\n metadata,\n });\n\n return text.trim();\n };\n\n const summarizeConversation: SummarizerService['summarizeConversation'] =\n async ({ messages, length = 'as few words as possible' }) => {\n const conversationMessages = messages.filter(\n msg => msg.role === 'ai' || msg.role === 'human',\n );\n\n const conversation = conversationMessages\n .map(msg => `${msg.role}: ${msg.content}`)\n .join('\\n');\n\n return summarize({\n content: conversation,\n prompt: conversationSummaryPrompt,\n length,\n });\n };\n\n return { summarizeConversation, summarize };\n};\n"],"names":["DEFAULT_CONVERSATION_SUMMARY_PROMPT","PromptTemplate","DEFAULT_SUMMARY_PROMPT"],"mappings":";;;;;AA6BO,MAAM,0BAA0B,OAAO;AAAA,EAC5C,MAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA,KAA4D;AAC1D,EAAA,MAAM,iBACJ,MAAA,CAAO,iBAAA,CAAkB,uCAAuC,CAAA,IAChE,MAAA,CAAO,CAAC,CAAA,CAAE,EAAA;AAEZ,EAAA,MAAM,yBAAA,GACJ,MAAA,CAAO,iBAAA,CAAkB,wCAAwC,CAAA,IACjEA,2CAAA;AAEF,EAAA,MAAM,QAAQ,MAAA,CAAO,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,cAAc,CAAA;AAEtD,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,cAAc,CAAA,UAAA,CAAY,CAAA;AAAA,EACrE;AAEA,EAAA,MAAM,MAAM,KAAA,CAAM,SAAA;AAElB,EAAA,MAAM,qBAAA,GAAwBC,yBAAe,YAAA,CAAa;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA,EAAA,CAQzD,CAAA;AAED,EAAA,MAAM,YAA4C,OAChD,OAAA,EACA,aAAA,GAAgBC,8BAAA,EAChB,SAAS,0BAAA,KACN;AACH,IAAA,MAAM,MAAA,GAAS,MAAM,qBAAA,CAAsB,MAAA,CAAO;AAAA,MAChD,aAAA;AAAA,MACA,OAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,MAAM,EAAE,SAAA,EAAU,GAAI,MAAM,SAAS,iBAAA,CAAkB;AAAA,MACrD,cAAA,EAAgB,YAAA;AAAA,MAChB,MAAA,EAAQ,QAAA;AAAA,MACR,OAAA,EAAS;AAAA,KACV,CAAA;AAED,IAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,SAAS,gBAAA,CAAiB;AAAA,MACnD,cAAA,EAAgB,YAAA;AAAA,MAChB,MAAA,EAAQ,QAAA;AAAA,MACR,OAAA,EAAS;AAAA,KACV,CAAA;AAED,IAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,GAAA,CAAI,OAAO,MAAA,EAAQ;AAAA,MACxC,SAAA;AAAA,MACA,OAAA,EAAS,YAAA;AAAA,MACT;AAAA,KACD,CAAA;AAED,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB,CAAA;AAEA,EAAA,MAAM,wBACJ,OAAO,EAAE,QAAA,EAAU,MAAA,GAAS,4BAA2B,KAAM;AAC3D,IAAA,MAAM,uBAAuB,QAAA,CAAS,MAAA;AAAA,MACpC,CAAA,GAAA,KAAO,GAAA,CAAI,IAAA,KAAS,IAAA,IAAQ,IAAI,IAAA,KAAS;AAAA,KAC3C;AAEA,IAAA,MAAM,YAAA,GAAe,oBAAA,CAClB,GAAA,CAAI,CAAA,GAAA,KAAO,CAAA,EAAG,GAAA,CAAI,IAAI,CAAA,EAAA,EAAK,GAAA,CAAI,OAAO,CAAA,CAAE,CAAA,CACxC,KAAK,IAAI,CAAA;AAEZ,IAAA,OAAO,SAAA,CAAU;AAAA,MACf,OAAA,EAAS,YAAA;AAAA,MACT,MAAA,EAAQ,yBAAA;AAAA,MACR;AAAA,KACD,CAAA;AAAA,EACH,CAAA;AAEF,EAAA,OAAO,EAAE,uBAAuB,SAAA,EAAU;AAC5C;;;;"}
|
|
@@ -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({
|
|
@@ -34,9 +35,18 @@ Do NOT use for general knowledge that doesn't require company-specific informati
|
|
|
34
35
|
amount
|
|
35
36
|
);
|
|
36
37
|
if (results.length === 0) {
|
|
37
|
-
return
|
|
38
|
+
return {
|
|
39
|
+
content: "No relevant information found in the knowledge base."
|
|
40
|
+
};
|
|
38
41
|
}
|
|
39
|
-
|
|
42
|
+
const content = results.map((r) => r.content).join("\n---\n");
|
|
43
|
+
const urls = results.map((r) => r.metadata.url);
|
|
44
|
+
return {
|
|
45
|
+
content,
|
|
46
|
+
metadata: {
|
|
47
|
+
urls
|
|
48
|
+
}
|
|
49
|
+
};
|
|
40
50
|
}
|
|
41
51
|
}
|
|
42
52
|
});
|
|
@@ -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
|
+
};
|