@sweetoburrito/backstage-plugin-ai-assistant-backend 0.15.4 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/services/agent/helpers/deterministic-uuid.cjs.js +17 -0
- package/dist/services/agent/helpers/deterministic-uuid.cjs.js.map +1 -0
- package/dist/services/agent/helpers/message-parser.cjs.js +5 -5
- package/dist/services/agent/helpers/message-parser.cjs.js.map +1 -1
- package/dist/services/agent/index.cjs.js +25 -13
- package/dist/services/agent/index.cjs.js.map +1 -1
- package/dist/services/chat.cjs.js +30 -29
- package/dist/services/chat.cjs.js.map +1 -1
- package/package.json +8 -8
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var uuid = require('uuid');
|
|
4
|
+
|
|
5
|
+
const MESSAGE_ID_NAMESPACE = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
|
|
6
|
+
const createDeterministicUuid = (message) => {
|
|
7
|
+
if (message.id && uuid.validate(message.id)) {
|
|
8
|
+
return message.id;
|
|
9
|
+
}
|
|
10
|
+
if (message.id) {
|
|
11
|
+
return uuid.v5(message.id, MESSAGE_ID_NAMESPACE);
|
|
12
|
+
}
|
|
13
|
+
return uuid.v4();
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
exports.createDeterministicUuid = createDeterministicUuid;
|
|
17
|
+
//# sourceMappingURL=deterministic-uuid.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deterministic-uuid.cjs.js","sources":["../../../../src/services/agent/helpers/deterministic-uuid.ts"],"sourcesContent":["import { v5 as uuidv5, v4 as uuidv4, validate } from 'uuid';\n\nconst MESSAGE_ID_NAMESPACE = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';\n\nexport const createDeterministicUuid = (message: { id?: string }): string => {\n if (message.id && validate(message.id)) {\n return message.id;\n }\n if (message.id) {\n return uuidv5(message.id, MESSAGE_ID_NAMESPACE);\n }\n\n return uuidv4();\n};\n"],"names":["validate","uuidv5","uuidv4"],"mappings":";;;;AAEA,MAAM,oBAAA,GAAuB,sCAAA;AAEtB,MAAM,uBAAA,GAA0B,CAAC,OAAA,KAAqC;AAC3E,EAAA,IAAI,OAAA,CAAQ,EAAA,IAAMA,aAAA,CAAS,OAAA,CAAQ,EAAE,CAAA,EAAG;AACtC,IAAA,OAAO,OAAA,CAAQ,EAAA;AAAA,EACjB;AACA,EAAA,IAAI,QAAQ,EAAA,EAAI;AACd,IAAA,OAAOC,OAAA,CAAO,OAAA,CAAQ,EAAA,EAAI,oBAAoB,CAAA;AAAA,EAChD;AAEA,EAAA,OAAOC,OAAA,EAAO;AAChB;;;;"}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var deterministicUuid = require('./deterministic-uuid.cjs.js');
|
|
4
4
|
|
|
5
5
|
const parseLangchainMessage = (message, traceId) => {
|
|
6
|
-
const id =
|
|
7
|
-
const role = message.
|
|
6
|
+
const id = deterministicUuid.createDeterministicUuid(message);
|
|
7
|
+
const role = message.type;
|
|
8
8
|
const content = typeof message.content === "string" ? message.content : JSON.stringify(message.content);
|
|
9
9
|
const metadata = {};
|
|
10
10
|
if (role === "ai") {
|
|
11
11
|
const aiMessage = message;
|
|
12
12
|
metadata.toolCalls = aiMessage.tool_calls || [];
|
|
13
|
-
metadata.finishReason = aiMessage.response_metadata
|
|
14
|
-
metadata.modelName = aiMessage.response_metadata
|
|
13
|
+
metadata.finishReason = aiMessage.response_metadata?.finish_reason || void 0;
|
|
14
|
+
metadata.modelName = aiMessage.response_metadata?.model_name || void 0;
|
|
15
15
|
}
|
|
16
16
|
if (role === "tool") {
|
|
17
17
|
const toolMessage = message;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-parser.cjs.js","sources":["../../../../src/services/agent/helpers/message-parser.ts"],"sourcesContent":["import { AIMessage, ToolMessage, BaseMessage } from '@langchain/core/messages';\n\nimport {\n JsonObject,\n Message,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport {
|
|
1
|
+
{"version":3,"file":"message-parser.cjs.js","sources":["../../../../src/services/agent/helpers/message-parser.ts"],"sourcesContent":["import { AIMessage, ToolMessage, BaseMessage } from '@langchain/core/messages';\n\nimport {\n JsonObject,\n Message,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { createDeterministicUuid } from './deterministic-uuid';\n\nexport const parseLangchainMessage = (\n message: BaseMessage,\n traceId: string,\n): Message => {\n const id = createDeterministicUuid(message);\n const role = message.type as Message['role'];\n const content =\n typeof message.content === 'string'\n ? message.content\n : JSON.stringify(message.content);\n\n const metadata: JsonObject = {};\n\n if (role === 'ai') {\n const aiMessage = message as AIMessage;\n\n metadata.toolCalls = aiMessage.tool_calls || [];\n\n metadata.finishReason =\n (aiMessage.response_metadata?.finish_reason as string) || undefined;\n metadata.modelName = aiMessage.response_metadata?.model_name || undefined;\n }\n\n if (role === 'tool') {\n const toolMessage = message as ToolMessage;\n metadata.name = toolMessage.name || '';\n }\n\n return {\n id,\n role,\n content,\n metadata,\n score: 0,\n traceId,\n };\n};\n"],"names":["createDeterministicUuid"],"mappings":";;;;AAQO,MAAM,qBAAA,GAAwB,CACnC,OAAA,EACA,OAAA,KACY;AACZ,EAAA,MAAM,EAAA,GAAKA,0CAAwB,OAAO,CAAA;AAC1C,EAAA,MAAM,OAAO,OAAA,CAAQ,IAAA;AACrB,EAAA,MAAM,OAAA,GACJ,OAAO,OAAA,CAAQ,OAAA,KAAY,QAAA,GACvB,QAAQ,OAAA,GACR,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,OAAO,CAAA;AAEpC,EAAA,MAAM,WAAuB,EAAC;AAE9B,EAAA,IAAI,SAAS,IAAA,EAAM;AACjB,IAAA,MAAM,SAAA,GAAY,OAAA;AAElB,IAAA,QAAA,CAAS,SAAA,GAAY,SAAA,CAAU,UAAA,IAAc,EAAC;AAE9C,IAAA,QAAA,CAAS,YAAA,GACN,SAAA,CAAU,iBAAA,EAAmB,aAAA,IAA4B,MAAA;AAC5D,IAAA,QAAA,CAAS,SAAA,GAAY,SAAA,CAAU,iBAAA,EAAmB,UAAA,IAAc,MAAA;AAAA,EAClE;AAEA,EAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,IAAA,MAAM,WAAA,GAAc,OAAA;AACpB,IAAA,QAAA,CAAS,IAAA,GAAO,YAAY,IAAA,IAAQ,EAAA;AAAA,EACtC;AAEA,EAAA,OAAO;AAAA,IACL,EAAA;AAAA,IACA,IAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA,KAAA,EAAO,CAAA;AAAA,IACP;AAAA,GACF;AACF;;;;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
|
-
var
|
|
4
|
+
var langchain = require('langchain');
|
|
5
5
|
var toolFilter = require('./helpers/tool-filter.cjs.js');
|
|
6
6
|
var prompts = require('../../constants/prompts.cjs.js');
|
|
7
7
|
var prompts$1 = require('@langchain/core/prompts');
|
|
@@ -9,6 +9,7 @@ var callbacks = require('../callbacks.cjs.js');
|
|
|
9
9
|
var model = require('../model.cjs.js');
|
|
10
10
|
var tools = require('../tools.cjs.js');
|
|
11
11
|
var messageParser = require('./helpers/message-parser.cjs.js');
|
|
12
|
+
var deterministicUuid = require('./helpers/deterministic-uuid.cjs.js');
|
|
12
13
|
|
|
13
14
|
const createAgentService = ({
|
|
14
15
|
model,
|
|
@@ -62,11 +63,6 @@ ${contentPrompt}`;
|
|
|
62
63
|
context,
|
|
63
64
|
systemPrompt
|
|
64
65
|
});
|
|
65
|
-
const agent = prebuilt.createReactAgent({
|
|
66
|
-
llm,
|
|
67
|
-
tools,
|
|
68
|
-
prompt: agentPrompt[0].text
|
|
69
|
-
});
|
|
70
66
|
const { callbacks } = await callback.getChainCallbacks({
|
|
71
67
|
userId,
|
|
72
68
|
conversationId,
|
|
@@ -77,33 +73,49 @@ ${contentPrompt}`;
|
|
|
77
73
|
conversationId,
|
|
78
74
|
modelId: resolvedModelId
|
|
79
75
|
});
|
|
80
|
-
agent
|
|
81
|
-
|
|
76
|
+
const agent = langchain.createAgent({
|
|
77
|
+
model: llm,
|
|
78
|
+
tools,
|
|
79
|
+
systemPrompt: agentPrompt[0].text
|
|
80
|
+
}).withConfig({
|
|
82
81
|
callbacks,
|
|
83
82
|
metadata,
|
|
84
83
|
runId,
|
|
85
84
|
runName
|
|
86
|
-
};
|
|
85
|
+
});
|
|
87
86
|
return agent;
|
|
88
87
|
};
|
|
89
88
|
const stream = async (options) => {
|
|
90
|
-
const { messages, onStreamChunk } = options;
|
|
89
|
+
const { messages, onStreamChunk, onStreamEnd } = options;
|
|
91
90
|
const agent = await createAgent(options);
|
|
92
91
|
const promptStream = await agent.stream(
|
|
93
92
|
{
|
|
94
93
|
messages
|
|
95
94
|
},
|
|
96
95
|
{
|
|
97
|
-
streamMode: ["
|
|
96
|
+
streamMode: ["messages"]
|
|
98
97
|
}
|
|
99
98
|
);
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
const promptMessages = [];
|
|
100
|
+
for await (const [, [chunk]] of promptStream) {
|
|
101
|
+
const messageChunk = chunk;
|
|
102
|
+
messageChunk.id = deterministicUuid.createDeterministicUuid(messageChunk);
|
|
103
|
+
const existingChunksIndex = promptMessages.findIndex(
|
|
104
|
+
(m) => m.id === messageChunk.id
|
|
105
|
+
);
|
|
106
|
+
if (existingChunksIndex === -1) {
|
|
107
|
+
promptMessages.push(messageChunk);
|
|
108
|
+
} else {
|
|
109
|
+
const existingChunk = promptMessages[existingChunksIndex];
|
|
110
|
+
existingChunk.concat(messageChunk);
|
|
111
|
+
promptMessages[existingChunksIndex] = existingChunk.concat(messageChunk);
|
|
112
|
+
}
|
|
102
113
|
const parsedMessages = promptMessages.map(
|
|
103
114
|
(m) => messageParser.parseLangchainMessage(m, options.metadata.runId)
|
|
104
115
|
);
|
|
105
116
|
onStreamChunk(parsedMessages);
|
|
106
117
|
}
|
|
118
|
+
onStreamEnd?.();
|
|
107
119
|
};
|
|
108
120
|
const prompt = async (options) => {
|
|
109
121
|
const {
|
|
@@ -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 { createReactAgent } from '@langchain/langgraph/prebuilt';\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 } from '@langchain/core/messages';\nimport { parseLangchainMessage } from './helpers/message-parser';\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};\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 createReactAgent>> => {\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 agent = createReactAgent({\n llm,\n tools,\n prompt: agentPrompt[0].text,\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 agent.config = {\n ...agent.config,\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 } = options;\n\n const agent = await createAgent(options);\n\n const promptStream = await agent.stream(\n {\n messages,\n },\n {\n streamMode: ['values'],\n },\n );\n\n for await (const [, chunk] of promptStream) {\n const { messages: promptMessages } = chunk;\n\n const parsedMessages: Message[] = (promptMessages as BaseMessage[]).map(\n m => parseLangchainMessage(m, options.metadata.runId),\n );\n\n onStreamChunk(parsedMessages);\n }\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","createReactAgent","parseLangchainMessage","createServiceRef","createServiceFactory","coreServices","modelServiceRef","toolsServiceRef","callbackServiceRef"],"mappings":";;;;;;;;;;;;AA4DA,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,KACiD;AACjD,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,QAAQC,yBAAA,CAAiB;AAAA,MAC7B,GAAA;AAAA,MACA,KAAA;AAAA,MACA,MAAA,EAAQ,WAAA,CAAY,CAAC,CAAA,CAAE;AAAA,KACxB,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,KAAA,CAAM,MAAA,GAAS;AAAA,MACb,GAAG,KAAA,CAAM,MAAA;AAAA,MACT,SAAA;AAAA,MACA,QAAA;AAAA,MACA,KAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,MAAA,GAAiC,OAAM,OAAA,KAAW;AACtD,IAAA,MAAM,EAAE,QAAA,EAAU,aAAA,EAAc,GAAI,OAAA;AAEpC,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,QAAQ;AAAA;AACvB,KACF;AAEA,IAAA,WAAA,MAAiB,GAAG,KAAK,CAAA,IAAK,YAAA,EAAc;AAC1C,MAAA,MAAM,EAAE,QAAA,EAAU,cAAA,EAAe,GAAI,KAAA;AAErC,MAAA,MAAM,iBAA6B,cAAA,CAAiC,GAAA;AAAA,QAClE,CAAA,CAAA,KAAKC,mCAAA,CAAsB,CAAA,EAAG,OAAA,CAAQ,SAAS,KAAK;AAAA,OACtD;AAEA,MAAA,aAAA,CAAc,cAAc,CAAA;AAAA,IAC9B;AAAA,EACF,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 { 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":"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"}
|
|
@@ -51,6 +51,7 @@ const createChatService = async ({
|
|
|
51
51
|
});
|
|
52
52
|
const responseMessages = [];
|
|
53
53
|
const conversationMessages = [...recentConversationMessages, ...messages];
|
|
54
|
+
const newMessages = [];
|
|
54
55
|
agent.stream({
|
|
55
56
|
credentials,
|
|
56
57
|
messages: conversationMessages,
|
|
@@ -64,38 +65,38 @@ const createChatService = async ({
|
|
|
64
65
|
},
|
|
65
66
|
context: context[0].text,
|
|
66
67
|
onStreamChunk: async (chunkMessages) => {
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
if (chunkMessages.length === 0) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const existingNewMessageIndex = newMessages.findIndex(
|
|
72
|
+
(cm) => cm.id === chunkMessages[0].id
|
|
69
73
|
);
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
conversationMessages
|
|
74
|
+
if (existingNewMessageIndex !== -1) {
|
|
75
|
+
newMessages.splice(
|
|
76
|
+
existingNewMessageIndex,
|
|
77
|
+
chunkMessages.length,
|
|
78
|
+
...chunkMessages
|
|
76
79
|
);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
for await (const m of newMessages) {
|
|
80
|
-
const words = m.content.split(" ");
|
|
81
|
-
const chunkSize = 5;
|
|
82
|
-
let messageBuilder = "";
|
|
83
|
-
for (let i = 0; i < words.length; i += chunkSize) {
|
|
84
|
-
const wordChunk = words.slice(i, i + chunkSize).join(" ");
|
|
85
|
-
messageBuilder = messageBuilder.concat(wordChunk).concat(" ");
|
|
86
|
-
m.content = messageBuilder;
|
|
87
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
88
|
-
signals.publish({
|
|
89
|
-
channel: `ai-assistant.chat.conversation-stream:${conversationId}`,
|
|
90
|
-
message: { messages: [m] },
|
|
91
|
-
recipients: {
|
|
92
|
-
type: "user",
|
|
93
|
-
entityRef: userEntityRef
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
}
|
|
80
|
+
} else {
|
|
81
|
+
newMessages.push(...chunkMessages);
|
|
98
82
|
}
|
|
83
|
+
responseMessages.push(...chunkMessages);
|
|
84
|
+
signals.publish({
|
|
85
|
+
channel: `ai-assistant.chat.conversation-stream:${conversationId}`,
|
|
86
|
+
message: { messages: chunkMessages },
|
|
87
|
+
recipients: {
|
|
88
|
+
type: "user",
|
|
89
|
+
entityRef: userEntityRef
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
onStreamEnd: async () => {
|
|
94
|
+
conversation.addMessages(
|
|
95
|
+
newMessages,
|
|
96
|
+
userEntityRef,
|
|
97
|
+
conversationId,
|
|
98
|
+
conversationMessages
|
|
99
|
+
);
|
|
99
100
|
}
|
|
100
101
|
});
|
|
101
102
|
return responseMessages;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.cjs.js","sources":["../../src/services/chat.ts"],"sourcesContent":["import {\n CatalogService,\n catalogServiceRef,\n} from '@backstage/plugin-catalog-node';\nimport {\n SignalsService,\n signalsServiceRef,\n} from '@backstage/plugin-signals-node';\n\nimport {\n Message,\n EnabledTool,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\n\nimport { getUser } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { v4 as uuid } from 'uuid';\nimport type {\n BackstageCredentials,\n CacheService,\n UserInfoService,\n AuthService,\n ServiceRef,\n} from '@backstage/backend-plugin-api';\nimport {\n coreServices,\n createServiceFactory,\n createServiceRef,\n} from '@backstage/backend-plugin-api';\nimport { ConversationService, conversationServiceRef } from './conversation';\nimport { agentServiceRef, AgentService } from './agent';\nimport { SystemMessagePromptTemplate } from '@langchain/core/prompts';\n\nexport type ChatServiceOptions = {\n signals: SignalsService;\n catalog: CatalogService;\n cache: CacheService;\n auth: AuthService;\n userInfo: UserInfoService;\n conversation: ConversationService;\n agent: AgentService;\n};\n\ntype PromptOptions = {\n credentials: BackstageCredentials;\n messages: Message[];\n conversationId: string;\n stream?: boolean;\n tools?: EnabledTool[];\n modelId?: string;\n};\n\nexport type ChatService = {\n prompt: (options: PromptOptions) => Promise<Message[]>;\n};\n\nexport const createChatService = async ({\n signals,\n catalog,\n cache,\n auth,\n userInfo,\n conversation,\n agent,\n}: ChatServiceOptions): Promise<ChatService> => {\n const contextPromptTemplate = SystemMessagePromptTemplate.fromTemplate(`\n Calling User:\n {user}`);\n\n const prompt: ChatService['prompt'] = async ({\n conversationId,\n messages,\n stream = true,\n credentials,\n tools: enabledTools,\n modelId,\n }: PromptOptions) => {\n const streamFn = async () => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n const recentConversationMessages =\n await conversation.getRecentConversationMessages({\n conversationId,\n userEntityRef,\n limit: 10,\n excludeRoles: ['tool'],\n });\n\n const user = await getUser(cache, userEntityRef, catalog, auth);\n\n const messagesWithoutSystem = messages.filter(m => m.role !== 'system');\n\n conversation.addMessages(\n messagesWithoutSystem,\n userEntityRef,\n conversationId,\n recentConversationMessages,\n );\n\n const traceId = uuid();\n\n const context = await contextPromptTemplate.formatMessages({\n user,\n });\n\n const responseMessages: Message[] = [];\n\n const conversationMessages = [...recentConversationMessages, ...messages];\n\n agent.stream({\n credentials,\n messages: conversationMessages,\n tools: enabledTools,\n modelId,\n metadata: {\n conversationId,\n userId: userEntityRef,\n runName: 'ai-assistant-chat',\n runId: traceId,\n },\n context: context[0].text,\n onStreamChunk: async chunkMessages => {\n
|
|
1
|
+
{"version":3,"file":"chat.cjs.js","sources":["../../src/services/chat.ts"],"sourcesContent":["import {\n CatalogService,\n catalogServiceRef,\n} from '@backstage/plugin-catalog-node';\nimport {\n SignalsService,\n signalsServiceRef,\n} from '@backstage/plugin-signals-node';\n\nimport {\n Message,\n EnabledTool,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\n\nimport { getUser } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { v4 as uuid } from 'uuid';\nimport type {\n BackstageCredentials,\n CacheService,\n UserInfoService,\n AuthService,\n ServiceRef,\n} from '@backstage/backend-plugin-api';\nimport {\n coreServices,\n createServiceFactory,\n createServiceRef,\n} from '@backstage/backend-plugin-api';\nimport { ConversationService, conversationServiceRef } from './conversation';\nimport { agentServiceRef, AgentService } from './agent';\nimport { SystemMessagePromptTemplate } from '@langchain/core/prompts';\n\nexport type ChatServiceOptions = {\n signals: SignalsService;\n catalog: CatalogService;\n cache: CacheService;\n auth: AuthService;\n userInfo: UserInfoService;\n conversation: ConversationService;\n agent: AgentService;\n};\n\ntype PromptOptions = {\n credentials: BackstageCredentials;\n messages: Message[];\n conversationId: string;\n stream?: boolean;\n tools?: EnabledTool[];\n modelId?: string;\n};\n\nexport type ChatService = {\n prompt: (options: PromptOptions) => Promise<Message[]>;\n};\n\nexport const createChatService = async ({\n signals,\n catalog,\n cache,\n auth,\n userInfo,\n conversation,\n agent,\n}: ChatServiceOptions): Promise<ChatService> => {\n const contextPromptTemplate = SystemMessagePromptTemplate.fromTemplate(`\n Calling User:\n {user}`);\n\n const prompt: ChatService['prompt'] = async ({\n conversationId,\n messages,\n stream = true,\n credentials,\n tools: enabledTools,\n modelId,\n }: PromptOptions) => {\n const streamFn = async () => {\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n const recentConversationMessages =\n await conversation.getRecentConversationMessages({\n conversationId,\n userEntityRef,\n limit: 10,\n excludeRoles: ['tool'],\n });\n\n const user = await getUser(cache, userEntityRef, catalog, auth);\n\n const messagesWithoutSystem = messages.filter(m => m.role !== 'system');\n\n conversation.addMessages(\n messagesWithoutSystem,\n userEntityRef,\n conversationId,\n recentConversationMessages,\n );\n\n const traceId = uuid();\n\n const context = await contextPromptTemplate.formatMessages({\n user,\n });\n\n const responseMessages: Message[] = [];\n\n const conversationMessages = [...recentConversationMessages, ...messages];\n\n const newMessages: Message[] = [];\n\n agent.stream({\n credentials,\n messages: conversationMessages,\n tools: enabledTools,\n modelId,\n metadata: {\n conversationId,\n userId: userEntityRef,\n runName: 'ai-assistant-chat',\n runId: traceId,\n },\n context: context[0].text,\n onStreamChunk: async chunkMessages => {\n if (chunkMessages.length === 0) {\n return;\n }\n\n const existingNewMessageIndex = newMessages.findIndex(\n cm => cm.id === chunkMessages[0].id,\n );\n\n if (existingNewMessageIndex !== -1) {\n newMessages.splice(\n existingNewMessageIndex,\n chunkMessages.length,\n ...chunkMessages,\n );\n } else {\n newMessages.push(...chunkMessages);\n }\n\n responseMessages.push(...chunkMessages);\n\n signals.publish({\n channel: `ai-assistant.chat.conversation-stream:${conversationId}`,\n message: { messages: chunkMessages },\n recipients: {\n type: 'user',\n entityRef: userEntityRef,\n },\n });\n },\n onStreamEnd: async () => {\n conversation.addMessages(\n newMessages,\n userEntityRef,\n conversationId,\n conversationMessages,\n );\n },\n });\n\n return responseMessages;\n };\n\n return stream ? await streamFn() : [];\n };\n\n return {\n prompt,\n };\n};\n\nexport const chatServiceRef: ServiceRef<ChatService, 'plugin', 'singleton'> =\n createServiceRef<ChatService>({\n id: 'ai-assistant.chat-service',\n defaultFactory: async service =>\n createServiceFactory({\n service,\n deps: {\n cache: coreServices.cache,\n auth: coreServices.auth,\n userInfo: coreServices.userInfo,\n signals: signalsServiceRef,\n catalog: catalogServiceRef,\n conversation: conversationServiceRef,\n agent: agentServiceRef,\n },\n factory: async options => {\n return createChatService(options);\n },\n }),\n });\n"],"names":["SystemMessagePromptTemplate","getUser","uuid","createServiceRef","createServiceFactory","coreServices","signalsServiceRef","catalogServiceRef","conversationServiceRef","agentServiceRef"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sweetoburrito/backstage-plugin-ai-assistant-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
"@backstage/errors": "backstage:^",
|
|
40
40
|
"@backstage/plugin-catalog-node": "backstage:^",
|
|
41
41
|
"@backstage/plugin-signals-node": "backstage:^",
|
|
42
|
-
"@langchain/core": "^
|
|
43
|
-
"@langchain/
|
|
44
|
-
"@langchain/
|
|
45
|
-
"@
|
|
46
|
-
"@sweetoburrito/backstage-plugin-ai-assistant-
|
|
47
|
-
"@sweetoburrito/backstage-plugin-ai-assistant-node": "^0.10.0",
|
|
42
|
+
"@langchain/core": "^1.1.19",
|
|
43
|
+
"@langchain/mcp-adapters": "^1.1.2",
|
|
44
|
+
"@langchain/textsplitters": "^1.0.1",
|
|
45
|
+
"@sweetoburrito/backstage-plugin-ai-assistant-common": "^0.9.0",
|
|
46
|
+
"@sweetoburrito/backstage-plugin-ai-assistant-node": "^0.11.0",
|
|
48
47
|
"express": "^4.17.1",
|
|
49
48
|
"express-promise-router": "^4.1.0",
|
|
50
49
|
"knex": "^3.1.0",
|
|
50
|
+
"langchain": "^1.2.17",
|
|
51
51
|
"uuid": "^11.1.0",
|
|
52
52
|
"zod": "^4.1.11"
|
|
53
53
|
},
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"@backstage/plugin-events-backend": "backstage:^",
|
|
61
61
|
"@backstage/plugin-signals-backend": "backstage:^",
|
|
62
62
|
"@backstage/types": "backstage:^",
|
|
63
|
-
"@drodil/backstage-plugin-qeta-backend": "^3.
|
|
63
|
+
"@drodil/backstage-plugin-qeta-backend": "^3.58.0",
|
|
64
64
|
"@sweetoburrito/backstage-plugin-ai-assistant-backend-module-callback-provider-langfuse": "workspace:^",
|
|
65
65
|
"@sweetoburrito/backstage-plugin-ai-assistant-backend-module-embeddings-provider-azure-open-ai": "workspace:^",
|
|
66
66
|
"@sweetoburrito/backstage-plugin-ai-assistant-backend-module-embeddings-provider-ollama": "workspace:^",
|