@sweetoburrito/backstage-plugin-ai-assistant-backend 0.18.0 → 0.19.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.
@@ -26,6 +26,7 @@ ${formattingPrompt}
26
26
 
27
27
  ${contentPrompt}`;
28
28
  const toolGuideline = config.getOptionalString("aiAssistant.prompt.toolGuideline") || prompts.DEFAULT_TOOL_GUIDELINE;
29
+ const fallbackEnabled = config.getOptionalBoolean("models.fallbackEnabled") ?? true;
29
30
  const systemPromptTemplate = prompts$1.SystemMessagePromptTemplate.fromTemplate(`
30
31
  PURPOSE:
31
32
  {systemPrompt}
@@ -73,10 +74,17 @@ ${contentPrompt}`;
73
74
  conversationId,
74
75
  modelId: resolvedModelId
75
76
  });
77
+ const middleware = [];
78
+ if (fallbackEnabled) {
79
+ const models = model.getAllModels();
80
+ const llms = models.map((m) => m.chatModel);
81
+ middleware.push(langchain.modelFallbackMiddleware(...llms));
82
+ }
76
83
  const agent = langchain.createAgent({
77
84
  model: llm,
78
85
  tools,
79
- systemPrompt: agentPrompt[0].text
86
+ systemPrompt: agentPrompt[0].text,
87
+ middleware
80
88
  }).withConfig({
81
89
  callbacks,
82
90
  metadata,
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../../../src/services/agent/index.ts"],"sourcesContent":["import {\n BackstageCredentials,\n coreServices,\n createServiceFactory,\n createServiceRef,\n RootConfigService,\n ServiceRef,\n} from '@backstage/backend-plugin-api';\nimport { createAgent as createLangchainAgent } from 'langchain';\nimport {\n EnabledTool,\n Message,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { toolFilter } from './helpers/tool-filter';\nimport {\n DEFAULT_FORMATTING_PROMPT,\n DEFAULT_IDENTITY_PROMPT,\n DEFAULT_SYSTEM_PROMPT,\n DEFAULT_TOOL_GUIDELINE,\n} from '../../constants/prompts';\nimport { SystemMessagePromptTemplate } from '@langchain/core/prompts';\n\nimport { CallbackService, callbackServiceRef } from '../callbacks';\nimport { modelServiceRef, ModelService } from '../model';\nimport { toolsServiceRef, ToolsService } from '../tools';\n\nimport { BaseMessage, BaseMessageChunk } from '@langchain/core/messages';\nimport { parseLangchainMessage } from './helpers/message-parser';\nimport { createDeterministicUuid } from './helpers/deterministic-uuid';\n\ntype PromptOptions = {\n credentials: BackstageCredentials;\n metadata: {\n conversationId: string;\n userId: string;\n runName: string;\n runId: string;\n };\n messages: Message[];\n modelId?: string;\n tools?: EnabledTool[];\n systemPrompt?: string;\n context?: string;\n};\n\ntype StreamOptions = PromptOptions & {\n onStreamChunk: (messages: Message[]) => void;\n onStreamEnd?: () => void;\n};\n\nexport type AgentService = {\n prompt: (options: PromptOptions) => Promise<Message[]>;\n stream: (options: StreamOptions) => Promise<void>;\n};\n\ntype AgentServiceOptions = {\n model: ModelService;\n config: RootConfigService;\n tool: ToolsService;\n callback: CallbackService;\n};\n\nconst createAgentService = ({\n model,\n tool,\n config,\n callback,\n}: AgentServiceOptions): AgentService => {\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 defaultBasePrompt = `${identityPrompt}\\n\\n${formattingPrompt}\\n\\n${contentPrompt}`;\n\n const toolGuideline =\n config.getOptionalString('aiAssistant.prompt.toolGuideline') ||\n DEFAULT_TOOL_GUIDELINE;\n\n const systemPromptTemplate = SystemMessagePromptTemplate.fromTemplate(`\n PURPOSE:\n {systemPrompt}\n\n TOOL USAGE GUIDELINES:\n {toolGuideline}\n\n Available tools:\n {toolList}\n\n Context:\n {context}`);\n\n const createAgent = async (\n options: PromptOptions,\n ): Promise<ReturnType<typeof createLangchainAgent>> => {\n const {\n modelId,\n credentials,\n tools: enabledTools,\n systemPrompt = defaultBasePrompt,\n context = 'none',\n metadata: { conversationId, userId, runId, runName },\n } = options;\n\n const { chatModel: llm, id: resolvedModelId } = model.getModel(\n modelId ?? 'default',\n );\n\n const tools = await tool.getPrincipalTools({\n credentials,\n filter: t => {\n return toolFilter(t, enabledTools);\n },\n });\n\n const toolList = tools.map(t => `- ${t.name}: ${t.description}`).join('\\n');\n\n const agentPrompt = await systemPromptTemplate.formatMessages({\n toolGuideline,\n toolList,\n context,\n systemPrompt,\n });\n\n const { callbacks } = await callback.getChainCallbacks({\n userId,\n conversationId,\n modelId: resolvedModelId,\n });\n\n const { metadata } = await callback.getChainMetadata({\n userId,\n conversationId,\n modelId: resolvedModelId,\n });\n\n const agent = createLangchainAgent({\n model: llm,\n tools,\n systemPrompt: agentPrompt[0].text,\n }).withConfig({\n callbacks,\n metadata,\n runId,\n runName,\n });\n\n return agent;\n };\n\n const stream: AgentService['stream'] = async options => {\n const { messages, onStreamChunk, onStreamEnd } = options;\n\n const agent = await createAgent(options);\n\n const promptStream = await agent.stream(\n {\n messages,\n },\n {\n streamMode: ['messages'],\n },\n );\n\n const promptMessages: BaseMessageChunk[] = [];\n\n for await (const [, [chunk]] of promptStream) {\n const messageChunk = chunk as BaseMessageChunk;\n\n messageChunk.id = createDeterministicUuid(messageChunk);\n\n const existingChunksIndex = promptMessages.findIndex(\n m => m.id === messageChunk.id,\n );\n\n if (existingChunksIndex === -1) {\n promptMessages.push(messageChunk);\n } else {\n const existingChunk = promptMessages[existingChunksIndex];\n\n existingChunk.concat(messageChunk);\n\n promptMessages[existingChunksIndex] =\n existingChunk.concat(messageChunk);\n }\n\n const parsedMessages: Message[] = promptMessages.map(m =>\n parseLangchainMessage(m, options.metadata.runId),\n );\n\n onStreamChunk(parsedMessages);\n }\n\n onStreamEnd?.();\n };\n\n const prompt: AgentService['prompt'] = async options => {\n const {\n messages,\n metadata: { runId },\n } = options;\n const agent = await createAgent(options);\n\n const result = await agent.invoke({\n messages,\n });\n\n return (result.messages as BaseMessage[]).map(m =>\n parseLangchainMessage(m, runId),\n );\n };\n\n return { prompt, stream };\n};\n\nexport const agentServiceRef: ServiceRef<AgentService, 'plugin', 'singleton'> =\n createServiceRef<AgentService>({\n id: 'ai-assistant.conversation-service',\n defaultFactory: async service =>\n createServiceFactory({\n service,\n deps: {\n config: coreServices.rootConfig,\n model: modelServiceRef,\n tool: toolsServiceRef,\n callback: callbackServiceRef,\n },\n factory: async options => {\n return createAgentService(options);\n },\n }),\n });\n"],"names":["DEFAULT_IDENTITY_PROMPT","DEFAULT_FORMATTING_PROMPT","DEFAULT_SYSTEM_PROMPT","DEFAULT_TOOL_GUIDELINE","SystemMessagePromptTemplate","toolFilter","createLangchainAgent","createDeterministicUuid","parseLangchainMessage","createServiceRef","createServiceFactory","coreServices","modelServiceRef","toolsServiceRef","callbackServiceRef"],"mappings":";;;;;;;;;;;;;AA8DA,MAAM,qBAAqB,CAAC;AAAA,EAC1B,KAAA;AAAA,EACA,IAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA,KAAyC;AACvC,EAAA,MAAM,cAAA,GACJ,MAAA,CAAO,iBAAA,CAAkB,6BAA6B,CAAA,IACtDA,+BAAA;AAEF,EAAA,MAAM,gBAAA,GACJ,MAAA,CAAO,iBAAA,CAAkB,+BAA+B,CAAA,IACxDC,iCAAA;AAEF,EAAA,MAAM,aAAA,GACJ,MAAA,CAAO,iBAAA,CAAkB,4BAA4B,CAAA,IACrDC,6BAAA;AAEF,EAAA,MAAM,iBAAA,GAAoB,GAAG,cAAc;;AAAA,EAAO,gBAAgB;;AAAA,EAAO,aAAa,CAAA,CAAA;AAEtF,EAAA,MAAM,aAAA,GACJ,MAAA,CAAO,iBAAA,CAAkB,kCAAkC,CAAA,IAC3DC,8BAAA;AAEF,EAAA,MAAM,oBAAA,GAAuBC,sCAA4B,YAAA,CAAa;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA,aAAA,CAW1D,CAAA;AAEZ,EAAA,MAAM,WAAA,GAAc,OAClB,OAAA,KACqD;AACrD,IAAA,MAAM;AAAA,MACJ,OAAA;AAAA,MACA,WAAA;AAAA,MACA,KAAA,EAAO,YAAA;AAAA,MACP,YAAA,GAAe,iBAAA;AAAA,MACf,OAAA,GAAU,MAAA;AAAA,MACV,QAAA,EAAU,EAAE,cAAA,EAAgB,MAAA,EAAQ,OAAO,OAAA;AAAQ,KACrD,GAAI,OAAA;AAEJ,IAAA,MAAM,EAAE,SAAA,EAAW,GAAA,EAAK,EAAA,EAAI,eAAA,KAAoB,KAAA,CAAM,QAAA;AAAA,MACpD,OAAA,IAAW;AAAA,KACb;AAEA,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,iBAAA,CAAkB;AAAA,MACzC,WAAA;AAAA,MACA,QAAQ,CAAA,CAAA,KAAK;AACX,QAAA,OAAOC,qBAAA,CAAW,GAAG,YAAY,CAAA;AAAA,MACnC;AAAA,KACD,CAAA;AAED,IAAA,MAAM,QAAA,GAAW,KAAA,CAAM,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,EAAA,EAAK,CAAA,CAAE,IAAI,CAAA,EAAA,EAAK,CAAA,CAAE,WAAW,CAAA,CAAE,CAAA,CAAE,KAAK,IAAI,CAAA;AAE1E,IAAA,MAAM,WAAA,GAAc,MAAM,oBAAA,CAAqB,cAAA,CAAe;AAAA,MAC5D,aAAA;AAAA,MACA,QAAA;AAAA,MACA,OAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,MAAM,EAAE,SAAA,EAAU,GAAI,MAAM,SAAS,iBAAA,CAAkB;AAAA,MACrD,MAAA;AAAA,MACA,cAAA;AAAA,MACA,OAAA,EAAS;AAAA,KACV,CAAA;AAED,IAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,SAAS,gBAAA,CAAiB;AAAA,MACnD,MAAA;AAAA,MACA,cAAA;AAAA,MACA,OAAA,EAAS;AAAA,KACV,CAAA;AAED,IAAA,MAAM,QAAQC,qBAAA,CAAqB;AAAA,MACjC,KAAA,EAAO,GAAA;AAAA,MACP,KAAA;AAAA,MACA,YAAA,EAAc,WAAA,CAAY,CAAC,CAAA,CAAE;AAAA,KAC9B,EAAE,UAAA,CAAW;AAAA,MACZ,SAAA;AAAA,MACA,QAAA;AAAA,MACA,KAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,MAAA,GAAiC,OAAM,OAAA,KAAW;AACtD,IAAA,MAAM,EAAE,QAAA,EAAU,aAAA,EAAe,WAAA,EAAY,GAAI,OAAA;AAEjD,IAAA,MAAM,KAAA,GAAQ,MAAM,WAAA,CAAY,OAAO,CAAA;AAEvC,IAAA,MAAM,YAAA,GAAe,MAAM,KAAA,CAAM,MAAA;AAAA,MAC/B;AAAA,QACE;AAAA,OACF;AAAA,MACA;AAAA,QACE,UAAA,EAAY,CAAC,UAAU;AAAA;AACzB,KACF;AAEA,IAAA,MAAM,iBAAqC,EAAC;AAE5C,IAAA,WAAA,MAAiB,GAAG,CAAC,KAAK,CAAC,KAAK,YAAA,EAAc;AAC5C,MAAA,MAAM,YAAA,GAAe,KAAA;AAErB,MAAA,YAAA,CAAa,EAAA,GAAKC,0CAAwB,YAAY,CAAA;AAEtD,MAAA,MAAM,sBAAsB,cAAA,CAAe,SAAA;AAAA,QACzC,CAAA,CAAA,KAAK,CAAA,CAAE,EAAA,KAAO,YAAA,CAAa;AAAA,OAC7B;AAEA,MAAA,IAAI,wBAAwB,EAAA,EAAI;AAC9B,QAAA,cAAA,CAAe,KAAK,YAAY,CAAA;AAAA,MAClC,CAAA,MAAO;AACL,QAAA,MAAM,aAAA,GAAgB,eAAe,mBAAmB,CAAA;AAExD,QAAA,aAAA,CAAc,OAAO,YAAY,CAAA;AAEjC,QAAA,cAAA,CAAe,mBAAmB,CAAA,GAChC,aAAA,CAAc,MAAA,CAAO,YAAY,CAAA;AAAA,MACrC;AAEA,MAAA,MAAM,iBAA4B,cAAA,CAAe,GAAA;AAAA,QAAI,CAAA,CAAA,KACnDC,mCAAA,CAAsB,CAAA,EAAG,OAAA,CAAQ,SAAS,KAAK;AAAA,OACjD;AAEA,MAAA,aAAA,CAAc,cAAc,CAAA;AAAA,IAC9B;AAEA,IAAA,WAAA,IAAc;AAAA,EAChB,CAAA;AAEA,EAAA,MAAM,MAAA,GAAiC,OAAM,OAAA,KAAW;AACtD,IAAA,MAAM;AAAA,MACJ,QAAA;AAAA,MACA,QAAA,EAAU,EAAE,KAAA;AAAM,KACpB,GAAI,OAAA;AACJ,IAAA,MAAM,KAAA,GAAQ,MAAM,WAAA,CAAY,OAAO,CAAA;AAEvC,IAAA,MAAM,MAAA,GAAS,MAAM,KAAA,CAAM,MAAA,CAAO;AAAA,MAChC;AAAA,KACD,CAAA;AAED,IAAA,OAAQ,OAAO,QAAA,CAA2B,GAAA;AAAA,MAAI,CAAA,CAAA,KAC5CA,mCAAA,CAAsB,CAAA,EAAG,KAAK;AAAA,KAChC;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,EAAE,QAAQ,MAAA,EAAO;AAC1B,CAAA;AAEO,MAAM,kBACXC,iCAAA,CAA+B;AAAA,EAC7B,EAAA,EAAI,mCAAA;AAAA,EACJ,cAAA,EAAgB,OAAM,OAAA,KACpBC,qCAAA,CAAqB;AAAA,IACnB,OAAA;AAAA,IACA,IAAA,EAAM;AAAA,MACJ,QAAQC,6BAAA,CAAa,UAAA;AAAA,MACrB,KAAA,EAAOC,qBAAA;AAAA,MACP,IAAA,EAAMC,qBAAA;AAAA,MACN,QAAA,EAAUC;AAAA,KACZ;AAAA,IACA,OAAA,EAAS,OAAM,OAAA,KAAW;AACxB,MAAA,OAAO,mBAAmB,OAAO,CAAA;AAAA,IACnC;AAAA,GACD;AACL,CAAC;;;;"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../../../src/services/agent/index.ts"],"sourcesContent":["import {\n BackstageCredentials,\n coreServices,\n createServiceFactory,\n createServiceRef,\n RootConfigService,\n ServiceRef,\n} from '@backstage/backend-plugin-api';\nimport {\n createAgent as createLangchainAgent,\n modelFallbackMiddleware,\n} from 'langchain';\nimport {\n EnabledTool,\n Message,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { toolFilter } from './helpers/tool-filter';\nimport {\n DEFAULT_FORMATTING_PROMPT,\n DEFAULT_IDENTITY_PROMPT,\n DEFAULT_SYSTEM_PROMPT,\n DEFAULT_TOOL_GUIDELINE,\n} from '../../constants/prompts';\nimport { SystemMessagePromptTemplate } from '@langchain/core/prompts';\n\nimport { CallbackService, callbackServiceRef } from '../callbacks';\nimport { modelServiceRef, ModelService } from '../model';\nimport { toolsServiceRef, ToolsService } from '../tools';\n\nimport { BaseMessage, BaseMessageChunk } from '@langchain/core/messages';\nimport { parseLangchainMessage } from './helpers/message-parser';\nimport { createDeterministicUuid } from './helpers/deterministic-uuid';\n\ntype PromptOptions = {\n credentials: BackstageCredentials;\n metadata: {\n conversationId: string;\n userId: string;\n runName: string;\n runId: string;\n };\n messages: Message[];\n modelId?: string;\n tools?: EnabledTool[];\n systemPrompt?: string;\n context?: string;\n};\n\ntype StreamOptions = PromptOptions & {\n onStreamChunk: (messages: Message[]) => void;\n onStreamEnd?: () => void;\n};\n\nexport type AgentService = {\n prompt: (options: PromptOptions) => Promise<Message[]>;\n stream: (options: StreamOptions) => Promise<void>;\n};\n\ntype AgentServiceOptions = {\n model: ModelService;\n config: RootConfigService;\n tool: ToolsService;\n callback: CallbackService;\n};\n\nconst createAgentService = ({\n model,\n tool,\n config,\n callback,\n}: AgentServiceOptions): AgentService => {\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 defaultBasePrompt = `${identityPrompt}\\n\\n${formattingPrompt}\\n\\n${contentPrompt}`;\n\n const toolGuideline =\n config.getOptionalString('aiAssistant.prompt.toolGuideline') ||\n DEFAULT_TOOL_GUIDELINE;\n\n const fallbackEnabled =\n config.getOptionalBoolean('models.fallbackEnabled') ?? true;\n\n const systemPromptTemplate = SystemMessagePromptTemplate.fromTemplate(`\n PURPOSE:\n {systemPrompt}\n\n TOOL USAGE GUIDELINES:\n {toolGuideline}\n\n Available tools:\n {toolList}\n\n Context:\n {context}`);\n\n const createAgent = async (\n options: PromptOptions,\n ): Promise<ReturnType<typeof createLangchainAgent>> => {\n const {\n modelId,\n credentials,\n tools: enabledTools,\n systemPrompt = defaultBasePrompt,\n context = 'none',\n metadata: { conversationId, userId, runId, runName },\n } = options;\n\n const { chatModel: llm, id: resolvedModelId } = model.getModel(\n modelId ?? 'default',\n );\n\n const tools = await tool.getPrincipalTools({\n credentials,\n filter: t => {\n return toolFilter(t, enabledTools);\n },\n });\n\n const toolList = tools.map(t => `- ${t.name}: ${t.description}`).join('\\n');\n\n const agentPrompt = await systemPromptTemplate.formatMessages({\n toolGuideline,\n toolList,\n context,\n systemPrompt,\n });\n\n const { callbacks } = await callback.getChainCallbacks({\n userId,\n conversationId,\n modelId: resolvedModelId,\n });\n\n const { metadata } = await callback.getChainMetadata({\n userId,\n conversationId,\n modelId: resolvedModelId,\n });\n\n const middleware = [];\n\n if (fallbackEnabled) {\n const models = model.getAllModels();\n\n const llms = models.map(m => m.chatModel);\n\n middleware.push(modelFallbackMiddleware(...llms));\n }\n\n const agent = createLangchainAgent({\n model: llm,\n tools,\n systemPrompt: agentPrompt[0].text,\n middleware,\n }).withConfig({\n callbacks,\n metadata,\n runId,\n runName,\n });\n\n return agent;\n };\n\n const stream: AgentService['stream'] = async options => {\n const { messages, onStreamChunk, onStreamEnd } = options;\n\n const agent = await createAgent(options);\n\n const promptStream = await agent.stream(\n {\n messages,\n },\n {\n streamMode: ['messages'],\n },\n );\n\n const promptMessages: BaseMessageChunk[] = [];\n\n for await (const [, [chunk]] of promptStream) {\n const messageChunk = chunk as BaseMessageChunk;\n\n messageChunk.id = createDeterministicUuid(messageChunk);\n\n const existingChunksIndex = promptMessages.findIndex(\n m => m.id === messageChunk.id,\n );\n\n if (existingChunksIndex === -1) {\n promptMessages.push(messageChunk);\n } else {\n const existingChunk = promptMessages[existingChunksIndex];\n\n existingChunk.concat(messageChunk);\n\n promptMessages[existingChunksIndex] =\n existingChunk.concat(messageChunk);\n }\n\n const parsedMessages: Message[] = promptMessages.map(m =>\n parseLangchainMessage(m, options.metadata.runId),\n );\n\n onStreamChunk(parsedMessages);\n }\n\n onStreamEnd?.();\n };\n\n const prompt: AgentService['prompt'] = async options => {\n const {\n messages,\n metadata: { runId },\n } = options;\n const agent = await createAgent(options);\n\n const result = await agent.invoke({\n messages,\n });\n\n return (result.messages as BaseMessage[]).map(m =>\n parseLangchainMessage(m, runId),\n );\n };\n\n return { prompt, stream };\n};\n\nexport const agentServiceRef: ServiceRef<AgentService, 'plugin', 'singleton'> =\n createServiceRef<AgentService>({\n id: 'ai-assistant.conversation-service',\n defaultFactory: async service =>\n createServiceFactory({\n service,\n deps: {\n config: coreServices.rootConfig,\n model: modelServiceRef,\n tool: toolsServiceRef,\n callback: callbackServiceRef,\n },\n factory: async options => {\n return createAgentService(options);\n },\n }),\n });\n"],"names":["DEFAULT_IDENTITY_PROMPT","DEFAULT_FORMATTING_PROMPT","DEFAULT_SYSTEM_PROMPT","DEFAULT_TOOL_GUIDELINE","SystemMessagePromptTemplate","toolFilter","modelFallbackMiddleware","createLangchainAgent","createDeterministicUuid","parseLangchainMessage","createServiceRef","createServiceFactory","coreServices","modelServiceRef","toolsServiceRef","callbackServiceRef"],"mappings":"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"}
@@ -24,7 +24,7 @@ const createCallbackService = async ({}) => {
24
24
  continue;
25
25
  }
26
26
  const callbackData = await chainMetadataCallback(options);
27
- Object.assign(metadata, callbackData.metadata);
27
+ Object.assign(metadata, callbackData);
28
28
  }
29
29
  return { metadata };
30
30
  };
@@ -1 +1 @@
1
- {"version":3,"file":"callbacks.cjs.js","sources":["../../src/services/callbacks.ts"],"sourcesContent":["import {\n CallbackProvider,\n ChainMetadata,\n ChainCallbackOptions,\n ChainCallback,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\n\nimport {\n createServiceFactory,\n createServiceRef,\n ServiceRef,\n} from '@backstage/backend-plugin-api';\n\nexport type CallbackService = {\n getChainCallbacks: (options: ChainCallbackOptions) => Promise<{\n callbacks: ReturnType<ChainCallback>[];\n }>;\n\n getChainMetadata: (\n options: ChainCallbackOptions,\n ) => Promise<{ metadata: ChainMetadata }>;\n\n handleScoreCallbacks: NonNullable<CallbackProvider['scoreCallback']>;\n\n registerCallbacks: (callbacks: CallbackProvider[]) => void;\n};\n\nexport type CreateCallbackServiceOptions = {};\n\nconst createCallbackService =\n async ({}: CreateCallbackServiceOptions): Promise<CallbackService> => {\n const callbacks: CallbackProvider[] = [];\n\n const getChainCallbacks: CallbackService['getChainCallbacks'] =\n async options => {\n const callbackHandlers: ReturnType<ChainCallback>[] = [];\n\n for (const { chainCallback } of callbacks) {\n if (!chainCallback) {\n continue;\n }\n\n const callbackHandler = await chainCallback(options);\n\n callbackHandlers.push(callbackHandler);\n }\n\n return {\n callbacks: callbackHandlers,\n };\n };\n\n const getChainMetadata: CallbackService['getChainMetadata'] =\n async options => {\n const metadata: ChainMetadata = {};\n\n for (const { chainMetadataCallback } of callbacks) {\n if (!chainMetadataCallback) {\n continue;\n }\n\n const callbackData = await chainMetadataCallback(options);\n\n Object.assign(metadata, callbackData.metadata);\n }\n\n return { metadata };\n };\n\n const handleScoreCallbacks: CallbackService['handleScoreCallbacks'] =\n async ({ name, message }) => {\n callbacks.forEach(async ({ scoreCallback }) => {\n if (!scoreCallback) {\n return;\n }\n\n scoreCallback({ name, message });\n });\n };\n\n const registerCallbacks: CallbackService['registerCallbacks'] =\n newCallbacks => {\n newCallbacks.forEach(callback => {\n callbacks.push(callback);\n });\n };\n\n return {\n getChainCallbacks,\n getChainMetadata,\n handleScoreCallbacks,\n registerCallbacks,\n };\n };\n\nexport const callbackServiceRef: ServiceRef<\n CallbackService,\n 'plugin',\n 'singleton'\n> = createServiceRef<CallbackService>({\n id: 'ai-assistant.callback-service',\n defaultFactory: async service =>\n createServiceFactory({\n service,\n deps: {},\n factory: async options => {\n return createCallbackService(options);\n },\n }),\n});\n"],"names":["createServiceRef","createServiceFactory"],"mappings":";;;;AA6BA,MAAM,qBAAA,GACJ,OAAO,EAAC,KAA8D;AACpE,EAAA,MAAM,YAAgC,EAAC;AAEvC,EAAA,MAAM,iBAAA,GACJ,OAAM,OAAA,KAAW;AACf,IAAA,MAAM,mBAAgD,EAAC;AAEvD,IAAA,KAAA,MAAW,EAAE,aAAA,EAAc,IAAK,SAAA,EAAW;AACzC,MAAA,IAAI,CAAC,aAAA,EAAe;AAClB,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,eAAA,GAAkB,MAAM,aAAA,CAAc,OAAO,CAAA;AAEnD,MAAA,gBAAA,CAAiB,KAAK,eAAe,CAAA;AAAA,IACvC;AAEA,IAAA,OAAO;AAAA,MACL,SAAA,EAAW;AAAA,KACb;AAAA,EACF,CAAA;AAEF,EAAA,MAAM,gBAAA,GACJ,OAAM,OAAA,KAAW;AACf,IAAA,MAAM,WAA0B,EAAC;AAEjC,IAAA,KAAA,MAAW,EAAE,qBAAA,EAAsB,IAAK,SAAA,EAAW;AACjD,MAAA,IAAI,CAAC,qBAAA,EAAuB;AAC1B,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,YAAA,GAAe,MAAM,qBAAA,CAAsB,OAAO,CAAA;AAExD,MAAA,MAAA,CAAO,MAAA,CAAO,QAAA,EAAU,YAAA,CAAa,QAAQ,CAAA;AAAA,IAC/C;AAEA,IAAA,OAAO,EAAE,QAAA,EAAS;AAAA,EACpB,CAAA;AAEF,EAAA,MAAM,oBAAA,GACJ,OAAO,EAAE,IAAA,EAAM,SAAQ,KAAM;AAC3B,IAAA,SAAA,CAAU,OAAA,CAAQ,OAAO,EAAE,aAAA,EAAc,KAAM;AAC7C,MAAA,IAAI,CAAC,aAAA,EAAe;AAClB,QAAA;AAAA,MACF;AAEA,MAAA,aAAA,CAAc,EAAE,IAAA,EAAM,OAAA,EAAS,CAAA;AAAA,IACjC,CAAC,CAAA;AAAA,EACH,CAAA;AAEF,EAAA,MAAM,oBACJ,CAAA,YAAA,KAAgB;AACd,IAAA,YAAA,CAAa,QAAQ,CAAA,QAAA,KAAY;AAC/B,MAAA,SAAA,CAAU,KAAK,QAAQ,CAAA;AAAA,IACzB,CAAC,CAAA;AAAA,EACH,CAAA;AAEF,EAAA,OAAO;AAAA,IACL,iBAAA;AAAA,IACA,gBAAA;AAAA,IACA,oBAAA;AAAA,IACA;AAAA,GACF;AACF,CAAA;AAEK,MAAM,qBAITA,iCAAA,CAAkC;AAAA,EACpC,EAAA,EAAI,+BAAA;AAAA,EACJ,cAAA,EAAgB,OAAM,OAAA,KACpBC,qCAAA,CAAqB;AAAA,IACnB,OAAA;AAAA,IACA,MAAM,EAAC;AAAA,IACP,OAAA,EAAS,OAAM,OAAA,KAAW;AACxB,MAAA,OAAO,sBAAsB,OAAO,CAAA;AAAA,IACtC;AAAA,GACD;AACL,CAAC;;;;"}
1
+ {"version":3,"file":"callbacks.cjs.js","sources":["../../src/services/callbacks.ts"],"sourcesContent":["import {\n CallbackProvider,\n ChainMetadata,\n ChainCallbackOptions,\n ChainCallback,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\n\nimport {\n createServiceFactory,\n createServiceRef,\n ServiceRef,\n} from '@backstage/backend-plugin-api';\n\nexport type CallbackService = {\n getChainCallbacks: (options: ChainCallbackOptions) => Promise<{\n callbacks: ReturnType<ChainCallback>[];\n }>;\n\n getChainMetadata: (\n options: ChainCallbackOptions,\n ) => Promise<{ metadata: ChainMetadata }>;\n\n handleScoreCallbacks: NonNullable<CallbackProvider['scoreCallback']>;\n\n registerCallbacks: (callbacks: CallbackProvider[]) => void;\n};\n\nexport type CreateCallbackServiceOptions = {};\n\nconst createCallbackService =\n async ({}: CreateCallbackServiceOptions): Promise<CallbackService> => {\n const callbacks: CallbackProvider[] = [];\n\n const getChainCallbacks: CallbackService['getChainCallbacks'] =\n async options => {\n const callbackHandlers: ReturnType<ChainCallback>[] = [];\n\n for (const { chainCallback } of callbacks) {\n if (!chainCallback) {\n continue;\n }\n\n const callbackHandler = await chainCallback(options);\n\n callbackHandlers.push(callbackHandler);\n }\n\n return {\n callbacks: callbackHandlers,\n };\n };\n\n const getChainMetadata: CallbackService['getChainMetadata'] =\n async options => {\n const metadata: ChainMetadata = {};\n\n for (const { chainMetadataCallback } of callbacks) {\n if (!chainMetadataCallback) {\n continue;\n }\n\n const callbackData = await chainMetadataCallback(options);\n\n Object.assign(metadata, callbackData);\n }\n\n return { metadata };\n };\n\n const handleScoreCallbacks: CallbackService['handleScoreCallbacks'] =\n async ({ name, message }) => {\n callbacks.forEach(async ({ scoreCallback }) => {\n if (!scoreCallback) {\n return;\n }\n\n scoreCallback({ name, message });\n });\n };\n\n const registerCallbacks: CallbackService['registerCallbacks'] =\n newCallbacks => {\n newCallbacks.forEach(callback => {\n callbacks.push(callback);\n });\n };\n\n return {\n getChainCallbacks,\n getChainMetadata,\n handleScoreCallbacks,\n registerCallbacks,\n };\n };\n\nexport const callbackServiceRef: ServiceRef<\n CallbackService,\n 'plugin',\n 'singleton'\n> = createServiceRef<CallbackService>({\n id: 'ai-assistant.callback-service',\n defaultFactory: async service =>\n createServiceFactory({\n service,\n deps: {},\n factory: async options => {\n return createCallbackService(options);\n },\n }),\n});\n"],"names":["createServiceRef","createServiceFactory"],"mappings":";;;;AA6BA,MAAM,qBAAA,GACJ,OAAO,EAAC,KAA8D;AACpE,EAAA,MAAM,YAAgC,EAAC;AAEvC,EAAA,MAAM,iBAAA,GACJ,OAAM,OAAA,KAAW;AACf,IAAA,MAAM,mBAAgD,EAAC;AAEvD,IAAA,KAAA,MAAW,EAAE,aAAA,EAAc,IAAK,SAAA,EAAW;AACzC,MAAA,IAAI,CAAC,aAAA,EAAe;AAClB,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,eAAA,GAAkB,MAAM,aAAA,CAAc,OAAO,CAAA;AAEnD,MAAA,gBAAA,CAAiB,KAAK,eAAe,CAAA;AAAA,IACvC;AAEA,IAAA,OAAO;AAAA,MACL,SAAA,EAAW;AAAA,KACb;AAAA,EACF,CAAA;AAEF,EAAA,MAAM,gBAAA,GACJ,OAAM,OAAA,KAAW;AACf,IAAA,MAAM,WAA0B,EAAC;AAEjC,IAAA,KAAA,MAAW,EAAE,qBAAA,EAAsB,IAAK,SAAA,EAAW;AACjD,MAAA,IAAI,CAAC,qBAAA,EAAuB;AAC1B,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,YAAA,GAAe,MAAM,qBAAA,CAAsB,OAAO,CAAA;AAExD,MAAA,MAAA,CAAO,MAAA,CAAO,UAAU,YAAY,CAAA;AAAA,IACtC;AAEA,IAAA,OAAO,EAAE,QAAA,EAAS;AAAA,EACpB,CAAA;AAEF,EAAA,MAAM,oBAAA,GACJ,OAAO,EAAE,IAAA,EAAM,SAAQ,KAAM;AAC3B,IAAA,SAAA,CAAU,OAAA,CAAQ,OAAO,EAAE,aAAA,EAAc,KAAM;AAC7C,MAAA,IAAI,CAAC,aAAA,EAAe;AAClB,QAAA;AAAA,MACF;AAEA,MAAA,aAAA,CAAc,EAAE,IAAA,EAAM,OAAA,EAAS,CAAA;AAAA,IACjC,CAAC,CAAA;AAAA,EACH,CAAA;AAEF,EAAA,MAAM,oBACJ,CAAA,YAAA,KAAgB;AACd,IAAA,YAAA,CAAa,QAAQ,CAAA,QAAA,KAAY;AAC/B,MAAA,SAAA,CAAU,KAAK,QAAQ,CAAA;AAAA,IACzB,CAAC,CAAA;AAAA,EACH,CAAA;AAEF,EAAA,OAAO;AAAA,IACL,iBAAA;AAAA,IACA,gBAAA;AAAA,IACA,oBAAA;AAAA,IACA;AAAA,GACF;AACF,CAAA;AAEK,MAAM,qBAITA,iCAAA,CAAkC;AAAA,EACpC,EAAA,EAAI,+BAAA;AAAA,EACJ,cAAA,EAAgB,OAAM,OAAA,KACpBC,qCAAA,CAAqB;AAAA,IACnB,OAAA;AAAA,IACA,MAAM,EAAC;AAAA,IACP,OAAA,EAAS,OAAM,OAAA,KAAW;AACxB,MAAA,OAAO,sBAAsB,OAAO,CAAA;AAAA,IACtC;AAAA,GACD;AACL,CAAC;;;;"}
@@ -7,6 +7,20 @@ var helpers = require('./helpers.cjs.js');
7
7
  var userSettings = require('../user-settings.cjs.js');
8
8
 
9
9
  const MCP_SETTINGS_TYPE = "mcp_server_config";
10
+ const normalizeMcpServerName = (name) => {
11
+ return name.trim().replace(/[\s-]/g, "_");
12
+ };
13
+ const normalizeMcpServerConfigs = (configs) => {
14
+ const normalizedByName = configs.reduce((acc, server) => {
15
+ const normalizedName = normalizeMcpServerName(server.name);
16
+ acc[normalizedName] = {
17
+ ...server,
18
+ name: normalizedName
19
+ };
20
+ return acc;
21
+ }, {});
22
+ return Object.values(normalizedByName);
23
+ };
10
24
  const createMcpService = async ({
11
25
  config,
12
26
  userSettings
@@ -93,15 +107,21 @@ const createMcpService = async ({
93
107
  }
94
108
  };
95
109
  const setUserMcpServerConfig = async (credentials, mcpConfig) => {
96
- const { name } = mcpConfig;
97
- const existingConfig = await getUserMcpServerConfig(credentials);
110
+ const normalizedName = normalizeMcpServerName(mcpConfig.name);
111
+ const normalizedConfig = {
112
+ ...mcpConfig,
113
+ name: normalizedName
114
+ };
115
+ const existingConfig = normalizeMcpServerConfigs(
116
+ await getUserMcpServerConfig(credentials)
117
+ );
98
118
  const existingServerIndex = existingConfig.findIndex(
99
- (server) => server.name === name
119
+ (server) => server.name === normalizedName
100
120
  );
101
121
  if (existingServerIndex === -1) {
102
- existingConfig.push(mcpConfig);
122
+ existingConfig.push(normalizedConfig);
103
123
  } else {
104
- existingConfig[existingServerIndex] = mcpConfig;
124
+ existingConfig[existingServerIndex] = normalizedConfig;
105
125
  }
106
126
  await validateMcpServerConfig(existingConfig);
107
127
  const updatedConfig = existingConfig.reduce(
@@ -121,8 +141,11 @@ const createMcpService = async ({
121
141
  );
122
142
  };
123
143
  const deleteUserMcpServerConfig = async (credentials, name) => {
124
- const existingConfig = await getUserMcpServerConfig(credentials);
125
- const updatedConfig = existingConfig.filter((server) => server.name !== name).reduce((acc, server) => {
144
+ const normalizedName = normalizeMcpServerName(name);
145
+ const existingConfig = normalizeMcpServerConfigs(
146
+ await getUserMcpServerConfig(credentials)
147
+ );
148
+ const updatedConfig = existingConfig.filter((server) => server.name !== normalizedName).reduce((acc, server) => {
126
149
  acc[server.name] = backstagePluginAiAssistantNode.encrypt(
127
150
  JSON.stringify(server.options),
128
151
  encryptionKey
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../../../src/services/mcp/index.ts"],"sourcesContent":["import {\n BackstageCredentials,\n DatabaseService,\n RootConfigService,\n createServiceFactory,\n createServiceRef,\n ServiceRef,\n coreServices,\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 { UserSettingsService, userSettingsServiceRef } from '../user-settings';\n\ntype CreateMcpServiceOptions = {\n config: RootConfigService;\n database: DatabaseService;\n userSettings: UserSettingsService;\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 userSettings,\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 getUserMcpServerConfigNames: McpService['getUserMcpServerConfigNames'] =\n async credentials => {\n const mcpConfig = await userSettings.getSettingsForType(\n credentials,\n MCP_SETTINGS_TYPE,\n );\n\n const names = Object.keys(mcpConfig);\n\n if (names.length === 0) {\n return [];\n }\n\n return names;\n };\n\n const getUserMcpServerConfig = async (\n credentials: BackstageCredentials,\n ): Promise<McpServerConfig[]> => {\n const mcpConfigEncrypted = await userSettings.getSettingsForType(\n credentials,\n MCP_SETTINGS_TYPE,\n );\n\n const entries = Object.entries(mcpConfigEncrypted) as [string, string][];\n\n if (entries.length === 0) {\n return [];\n }\n\n const mcpConfig: McpServerConfig[] = entries.map(([name, data]) => ({\n name,\n options: JSON.parse(decrypt(data, 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: 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 { 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 userSettings.setSettingsForType(\n credentials,\n MCP_SETTINGS_TYPE,\n updatedConfig,\n );\n };\n\n const deleteUserMcpServerConfig: McpService['deleteUserMcpServerConfig'] =\n async (credentials, name) => {\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 userSettings.setSettingsForType(\n credentials,\n MCP_SETTINGS_TYPE,\n updatedConfig,\n );\n };\n\n return {\n getTools,\n getUserMcpServerConfigNames,\n deleteUserMcpServerConfig,\n setUserMcpServerConfig,\n };\n};\n\nexport const mcpServiceRef: ServiceRef<McpService, 'plugin', 'singleton'> =\n createServiceRef<McpService>({\n id: 'ai-assistant.mcp-service',\n defaultFactory: async service =>\n createServiceFactory({\n service,\n deps: {\n config: coreServices.rootConfig,\n userInfo: coreServices.userInfo,\n database: coreServices.database,\n userSettings: userSettingsServiceRef,\n },\n factory: async options => {\n return createMcpService(options);\n },\n }),\n });\n"],"names":["decrypt","MultiServerMCPClient","getToolsForServer","encrypt","createServiceRef","createServiceFactory","coreServices","userSettingsServiceRef"],"mappings":";;;;;;;;AA4BA,MAAM,iBAAA,GAAoB,mBAAA;AAiBnB,MAAM,mBAAmB,OAAO;AAAA,EACrC,MAAA;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,0BACJ,aAAA,GACI,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,EAAC;AAEP,EAAA,MAAM,2BAAA,GACJ,OAAM,WAAA,KAAe;AACnB,IAAA,MAAM,SAAA,GAAY,MAAM,YAAA,CAAa,kBAAA;AAAA,MACnC,WAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA;AAEnC,IAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,MAAA,OAAO,EAAC;AAAA,IACV;AAEA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAEF,EAAA,MAAM,sBAAA,GAAyB,OAC7B,WAAA,KAC+B;AAC/B,IAAA,MAAM,kBAAA,GAAqB,MAAM,YAAA,CAAa,kBAAA;AAAA,MAC5C,WAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,kBAAkB,CAAA;AAEjD,IAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,MAAA,OAAO,EAAC;AAAA,IACV;AAEA,IAAA,MAAM,YAA+B,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAC,IAAA,EAAM,IAAI,CAAA,MAAO;AAAA,MAClE,IAAA;AAAA,MACA,SAAS,IAAA,CAAK,KAAA,CAAMA,sCAAA,CAAQ,IAAA,EAAM,aAAa,CAAC;AAAA,KAClD,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,iBACJ,aAAA,CAAc,MAAA,GACV,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,EAAC;AAEP,IAAA,MAAM,UAAA,GAAqD;AAAA,MACzD,GAAG,uBAAA;AAAA,MACH,GAAG;AAAA,KACL;AAEA,IAAA,MAAM,WAAA,GAAc,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA;AAE1C,IAAA,IAAI,WAAA,CAAY,WAAW,CAAA,EAAG;AAC5B,MAAA,OAAO,EAAC;AAAA,IACV;AAEA,IAAA,MAAM,SAAA,GAAY,IAAIC,gCAAA,CAAqB;AAAA,MACzC,4BAAA,EAA8B,IAAA;AAAA,MAC9B,wBAAA,EAA0B,IAAA;AAAA,MAC1B;AAAA,KACD,CAAA;AAED,IAAA,MAAM,qBAAqB,WAAA,CAAY,GAAA;AAAA,MAAI,CAAA,UAAA,KACzCC,yBAAA,CAAkB,SAAA,EAAW,UAAU;AAAA,KACzC;AACA,IAAA,MAAM,aAAA,GAAgB,MAAM,OAAA,CAAQ,GAAA,CAAI,kBAAkB,CAAA;AAE1D,IAAA,OAAO,cAAc,IAAA,EAAK;AAAA,EAC5B,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,IAAID,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,sBAAA,GAA+D,OACnE,WAAA,EACA,SAAA,KACG;AACH,IAAA,MAAM,EAAE,MAAK,GAAI,SAAA;AAEjB,IAAA,MAAM,cAAA,GAAiB,MAAM,sBAAA,CAAuB,WAAW,CAAA;AAE/D,IAAA,MAAM,sBAAsB,cAAA,CAAe,SAAA;AAAA,MACzC,CAAA,MAAA,KAAU,OAAO,IAAA,KAAS;AAAA,KAC5B;AAEA,IAAA,IAAI,wBAAwB,EAAA,EAAI;AAC9B,MAAA,cAAA,CAAe,KAAK,SAAS,CAAA;AAAA,IAC/B,CAAA,MAAO;AACL,MAAA,cAAA,CAAe,mBAAmB,CAAA,GAAI,SAAA;AAAA,IACxC;AAEA,IAAA,MAAM,wBAAwB,cAAc,CAAA;AAE5C,IAAA,MAAM,gBAAwC,cAAA,CAAe,MAAA;AAAA,MAC3D,CAAC,KAAK,MAAA,KAAW;AACf,QAAA,GAAA,CAAI,MAAA,CAAO,IAAI,CAAA,GAAIE,sCAAA;AAAA,UACjB,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,OAAO,CAAA;AAAA,UAC7B;AAAA,SACF;AACA,QAAA,OAAO,GAAA;AAAA,MACT,CAAA;AAAA,MACA;AAAC,KACH;AAEA,IAAA,MAAM,YAAA,CAAa,kBAAA;AAAA,MACjB,WAAA;AAAA,MACA,iBAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,yBAAA,GACJ,OAAO,WAAA,EAAa,IAAA,KAAS;AAC3B,IAAA,MAAM,cAAA,GAAiB,MAAM,sBAAA,CAAuB,WAAW,CAAA;AAE/D,IAAA,MAAM,aAAA,GAAwC,cAAA,CAC3C,MAAA,CAAO,CAAA,MAAA,KAAU,MAAA,CAAO,IAAA,KAAS,IAAI,CAAA,CACrC,MAAA,CAAO,CAAC,GAAA,EAAK,MAAA,KAAW;AACvB,MAAA,GAAA,CAAI,MAAA,CAAO,IAAI,CAAA,GAAIA,sCAAA;AAAA,QACjB,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,OAAO,CAAA;AAAA,QAC7B;AAAA,OACF;AACA,MAAA,OAAO,GAAA;AAAA,IACT,CAAA,EAAG,EAA4B,CAAA;AAEjC,IAAA,MAAM,YAAA,CAAa,kBAAA;AAAA,MACjB,WAAA;AAAA,MACA,iBAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF,CAAA;AAEF,EAAA,OAAO;AAAA,IACL,QAAA;AAAA,IACA,2BAAA;AAAA,IACA,yBAAA;AAAA,IACA;AAAA,GACF;AACF;AAEO,MAAM,gBACXC,iCAAA,CAA6B;AAAA,EAC3B,EAAA,EAAI,0BAAA;AAAA,EACJ,cAAA,EAAgB,OAAM,OAAA,KACpBC,qCAAA,CAAqB;AAAA,IACnB,OAAA;AAAA,IACA,IAAA,EAAM;AAAA,MACJ,QAAQC,6BAAA,CAAa,UAAA;AAAA,MACrB,UAAUA,6BAAA,CAAa,QAAA;AAAA,MACvB,UAAUA,6BAAA,CAAa,QAAA;AAAA,MACvB,YAAA,EAAcC;AAAA,KAChB;AAAA,IACA,OAAA,EAAS,OAAM,OAAA,KAAW;AACxB,MAAA,OAAO,iBAAiB,OAAO,CAAA;AAAA,IACjC;AAAA,GACD;AACL,CAAC;;;;;"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../../../src/services/mcp/index.ts"],"sourcesContent":["import {\n BackstageCredentials,\n DatabaseService,\n RootConfigService,\n createServiceFactory,\n createServiceRef,\n ServiceRef,\n coreServices,\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 { UserSettingsService, userSettingsServiceRef } from '../user-settings';\n\ntype CreateMcpServiceOptions = {\n config: RootConfigService;\n database: DatabaseService;\n userSettings: UserSettingsService;\n};\n\nconst MCP_SETTINGS_TYPE = 'mcp_server_config';\n\nconst normalizeMcpServerName = (name: string): string => {\n return name.trim().replace(/[\\s-]/g, '_');\n};\n\nconst normalizeMcpServerConfigs = (\n configs: McpServerConfig[],\n): McpServerConfig[] => {\n const normalizedByName = configs.reduce((acc, server) => {\n const normalizedName = normalizeMcpServerName(server.name);\n\n acc[normalizedName] = {\n ...server,\n name: normalizedName,\n };\n\n return acc;\n }, {} as Record<string, McpServerConfig>);\n\n return Object.values(normalizedByName);\n};\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 userSettings,\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 getUserMcpServerConfigNames: McpService['getUserMcpServerConfigNames'] =\n async credentials => {\n const mcpConfig = await userSettings.getSettingsForType(\n credentials,\n MCP_SETTINGS_TYPE,\n );\n\n const names = Object.keys(mcpConfig);\n\n if (names.length === 0) {\n return [];\n }\n\n return names;\n };\n\n const getUserMcpServerConfig = async (\n credentials: BackstageCredentials,\n ): Promise<McpServerConfig[]> => {\n const mcpConfigEncrypted = await userSettings.getSettingsForType(\n credentials,\n MCP_SETTINGS_TYPE,\n );\n\n const entries = Object.entries(mcpConfigEncrypted) as [string, string][];\n\n if (entries.length === 0) {\n return [];\n }\n\n const mcpConfig: McpServerConfig[] = entries.map(([name, data]) => ({\n name,\n options: JSON.parse(decrypt(data, 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: 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 normalizedName = normalizeMcpServerName(mcpConfig.name);\n const normalizedConfig: McpServerConfig = {\n ...mcpConfig,\n name: normalizedName,\n };\n\n const existingConfig = normalizeMcpServerConfigs(\n await getUserMcpServerConfig(credentials),\n );\n\n const existingServerIndex = existingConfig.findIndex(\n server => server.name === normalizedName,\n );\n\n if (existingServerIndex === -1) {\n existingConfig.push(normalizedConfig);\n } else {\n existingConfig[existingServerIndex] = normalizedConfig;\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 userSettings.setSettingsForType(\n credentials,\n MCP_SETTINGS_TYPE,\n updatedConfig,\n );\n };\n\n const deleteUserMcpServerConfig: McpService['deleteUserMcpServerConfig'] =\n async (credentials, name) => {\n const normalizedName = normalizeMcpServerName(name);\n const existingConfig = normalizeMcpServerConfigs(\n await getUserMcpServerConfig(credentials),\n );\n\n const updatedConfig: Record<string, string> = existingConfig\n .filter(server => server.name !== normalizedName)\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 userSettings.setSettingsForType(\n credentials,\n MCP_SETTINGS_TYPE,\n updatedConfig,\n );\n };\n\n return {\n getTools,\n getUserMcpServerConfigNames,\n deleteUserMcpServerConfig,\n setUserMcpServerConfig,\n };\n};\n\nexport const mcpServiceRef: ServiceRef<McpService, 'plugin', 'singleton'> =\n createServiceRef<McpService>({\n id: 'ai-assistant.mcp-service',\n defaultFactory: async service =>\n createServiceFactory({\n service,\n deps: {\n config: coreServices.rootConfig,\n userInfo: coreServices.userInfo,\n database: coreServices.database,\n userSettings: userSettingsServiceRef,\n },\n factory: async options => {\n return createMcpService(options);\n },\n }),\n });\n"],"names":["decrypt","MultiServerMCPClient","getToolsForServer","encrypt","createServiceRef","createServiceFactory","coreServices","userSettingsServiceRef"],"mappings":"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"}
@@ -26,7 +26,14 @@ const createModelService = async ({
26
26
  const getAvailableModels = () => {
27
27
  return models.map((x) => x.id);
28
28
  };
29
- return { registerModels, getModel, getAvailableModels };
29
+ const getAllModels = () => {
30
+ if (models.length === 0) {
31
+ logger.error("No models have been registered.");
32
+ throw new Error("No models have been registered.");
33
+ }
34
+ return models;
35
+ };
36
+ return { registerModels, getModel, getAvailableModels, getAllModels };
30
37
  };
31
38
  const modelServiceRef = backendPluginApi.createServiceRef({
32
39
  id: "ai-assistant.model-service",
@@ -1 +1 @@
1
- {"version":3,"file":"model.cjs.js","sources":["../../src/services/model.ts"],"sourcesContent":["import {\n coreServices,\n createServiceFactory,\n createServiceRef,\n LoggerService,\n ServiceRef,\n} from '@backstage/backend-plugin-api';\nimport { Model } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\n\nexport type ModelService = {\n registerModels: (modelProviders: Model[]) => void;\n getModel: (id: string) => Model;\n getAvailableModels: () => string[];\n};\n\nexport type CreateModelServiceOptions = {\n logger: LoggerService;\n};\n\nconst createModelService = async ({\n logger,\n}: CreateModelServiceOptions): Promise<ModelService> => {\n const models: Model[] = [];\n\n const registerModels: ModelService['registerModels'] = async providers => {\n models.push(...providers);\n };\n\n const getModel: ModelService['getModel'] = id => {\n if (models.length === 0) {\n logger.error('No models have been registered.');\n throw new Error('No models have been registered.');\n }\n\n const provider = models.find(m => m.id === id);\n\n if (!provider) {\n logger.warn(\n `Model with id ${id} not found, using default ${models[0].id}.`,\n );\n return models[0];\n }\n\n return provider;\n };\n\n const getAvailableModels: ModelService['getAvailableModels'] = () => {\n return models.map(x => x.id);\n };\n\n return { registerModels, getModel, getAvailableModels };\n};\n\nexport const modelServiceRef: ServiceRef<ModelService, 'plugin', 'singleton'> =\n createServiceRef<ModelService>({\n id: 'ai-assistant.model-service',\n defaultFactory: async service =>\n createServiceFactory({\n service,\n deps: {\n logger: coreServices.logger,\n },\n factory: async options => {\n return createModelService(options);\n },\n }),\n });\n"],"names":["createServiceRef","createServiceFactory","coreServices"],"mappings":";;;;AAmBA,MAAM,qBAAqB,OAAO;AAAA,EAChC;AACF,CAAA,KAAwD;AACtD,EAAA,MAAM,SAAkB,EAAC;AAEzB,EAAA,MAAM,cAAA,GAAiD,OAAM,SAAA,KAAa;AACxE,IAAA,MAAA,CAAO,IAAA,CAAK,GAAG,SAAS,CAAA;AAAA,EAC1B,CAAA;AAEA,EAAA,MAAM,WAAqC,CAAA,EAAA,KAAM;AAC/C,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,MAAA,MAAA,CAAO,MAAM,iCAAiC,CAAA;AAC9C,MAAA,MAAM,IAAI,MAAM,iCAAiC,CAAA;AAAA,IACnD;AAEA,IAAA,MAAM,WAAW,MAAA,CAAO,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,EAAE,CAAA;AAE7C,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,iBAAiB,EAAE,CAAA,0BAAA,EAA6B,MAAA,CAAO,CAAC,EAAE,EAAE,CAAA,CAAA;AAAA,OAC9D;AACA,MAAA,OAAO,OAAO,CAAC,CAAA;AAAA,IACjB;AAEA,IAAA,OAAO,QAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,qBAAyD,MAAM;AACnE,IAAA,OAAO,MAAA,CAAO,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,EAAE,CAAA;AAAA,EAC7B,CAAA;AAEA,EAAA,OAAO,EAAE,cAAA,EAAgB,QAAA,EAAU,kBAAA,EAAmB;AACxD,CAAA;AAEO,MAAM,kBACXA,iCAAA,CAA+B;AAAA,EAC7B,EAAA,EAAI,4BAAA;AAAA,EACJ,cAAA,EAAgB,OAAM,OAAA,KACpBC,qCAAA,CAAqB;AAAA,IACnB,OAAA;AAAA,IACA,IAAA,EAAM;AAAA,MACJ,QAAQC,6BAAA,CAAa;AAAA,KACvB;AAAA,IACA,OAAA,EAAS,OAAM,OAAA,KAAW;AACxB,MAAA,OAAO,mBAAmB,OAAO,CAAA;AAAA,IACnC;AAAA,GACD;AACL,CAAC;;;;"}
1
+ {"version":3,"file":"model.cjs.js","sources":["../../src/services/model.ts"],"sourcesContent":["import {\n coreServices,\n createServiceFactory,\n createServiceRef,\n LoggerService,\n ServiceRef,\n} from '@backstage/backend-plugin-api';\nimport { Model } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\n\nexport type ModelService = {\n registerModels: (modelProviders: Model[]) => void;\n getModel: (id: string) => Model;\n getAllModels: () => Model[];\n getAvailableModels: () => string[];\n};\n\nexport type CreateModelServiceOptions = {\n logger: LoggerService;\n};\n\nconst createModelService = async ({\n logger,\n}: CreateModelServiceOptions): Promise<ModelService> => {\n const models: Model[] = [];\n\n const registerModels: ModelService['registerModels'] = async providers => {\n models.push(...providers);\n };\n\n const getModel: ModelService['getModel'] = id => {\n if (models.length === 0) {\n logger.error('No models have been registered.');\n throw new Error('No models have been registered.');\n }\n\n const provider = models.find(m => m.id === id);\n\n if (!provider) {\n logger.warn(\n `Model with id ${id} not found, using default ${models[0].id}.`,\n );\n return models[0];\n }\n\n return provider;\n };\n\n const getAvailableModels: ModelService['getAvailableModels'] = () => {\n return models.map(x => x.id);\n };\n\n const getAllModels = () => {\n if (models.length === 0) {\n logger.error('No models have been registered.');\n throw new Error('No models have been registered.');\n }\n return models;\n };\n\n return { registerModels, getModel, getAvailableModels, getAllModels };\n};\n\nexport const modelServiceRef: ServiceRef<ModelService, 'plugin', 'singleton'> =\n createServiceRef<ModelService>({\n id: 'ai-assistant.model-service',\n defaultFactory: async service =>\n createServiceFactory({\n service,\n deps: {\n logger: coreServices.logger,\n },\n factory: async options => {\n return createModelService(options);\n },\n }),\n });\n"],"names":["createServiceRef","createServiceFactory","coreServices"],"mappings":";;;;AAoBA,MAAM,qBAAqB,OAAO;AAAA,EAChC;AACF,CAAA,KAAwD;AACtD,EAAA,MAAM,SAAkB,EAAC;AAEzB,EAAA,MAAM,cAAA,GAAiD,OAAM,SAAA,KAAa;AACxE,IAAA,MAAA,CAAO,IAAA,CAAK,GAAG,SAAS,CAAA;AAAA,EAC1B,CAAA;AAEA,EAAA,MAAM,WAAqC,CAAA,EAAA,KAAM;AAC/C,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,MAAA,MAAA,CAAO,MAAM,iCAAiC,CAAA;AAC9C,MAAA,MAAM,IAAI,MAAM,iCAAiC,CAAA;AAAA,IACnD;AAEA,IAAA,MAAM,WAAW,MAAA,CAAO,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,EAAE,CAAA;AAE7C,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,iBAAiB,EAAE,CAAA,0BAAA,EAA6B,MAAA,CAAO,CAAC,EAAE,EAAE,CAAA,CAAA;AAAA,OAC9D;AACA,MAAA,OAAO,OAAO,CAAC,CAAA;AAAA,IACjB;AAEA,IAAA,OAAO,QAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,qBAAyD,MAAM;AACnE,IAAA,OAAO,MAAA,CAAO,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,EAAE,CAAA;AAAA,EAC7B,CAAA;AAEA,EAAA,MAAM,eAAe,MAAM;AACzB,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,MAAA,MAAA,CAAO,MAAM,iCAAiC,CAAA;AAC9C,MAAA,MAAM,IAAI,MAAM,iCAAiC,CAAA;AAAA,IACnD;AACA,IAAA,OAAO,MAAA;AAAA,EACT,CAAA;AAEA,EAAA,OAAO,EAAE,cAAA,EAAgB,QAAA,EAAU,kBAAA,EAAoB,YAAA,EAAa;AACtE,CAAA;AAEO,MAAM,kBACXA,iCAAA,CAA+B;AAAA,EAC7B,EAAA,EAAI,4BAAA;AAAA,EACJ,cAAA,EAAgB,OAAM,OAAA,KACpBC,qCAAA,CAAqB;AAAA,IACnB,OAAA;AAAA,IACA,IAAA,EAAM;AAAA,MACJ,QAAQC,6BAAA,CAAa;AAAA,KACvB;AAAA,IACA,OAAA,EAAS,OAAM,OAAA,KAAW;AACxB,MAAA,OAAO,mBAAmB,OAAO,CAAA;AAAA,IACnC;AAAA,GACD;AACL,CAAC;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sweetoburrito/backstage-plugin-ai-assistant-backend",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",