@sweetoburrito/backstage-plugin-ai-assistant-backend 0.0.0-snapshot-20251030154701 → 0.0.0-snapshot-20251113134620
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 +0 -2
- package/dist/constants/prompts.cjs.js.map +1 -1
- package/dist/plugin.cjs.js +11 -24
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/services/callbacks.cjs.js +27 -0
- package/dist/services/callbacks.cjs.js.map +1 -0
- package/dist/services/chat.cjs.js +9 -31
- package/dist/services/chat.cjs.js.map +1 -1
- package/dist/services/router/index.cjs.js +0 -2
- package/dist/services/router/index.cjs.js.map +1 -1
- package/dist/services/summarizer.cjs.js +14 -79
- package/dist/services/summarizer.cjs.js.map +1 -1
- package/dist/services/tools/searchKnowledge.cjs.js +2 -1
- package/dist/services/tools/searchKnowledge.cjs.js.map +1 -1
- package/package.json +4 -10
- package/dist/services/langfuse.cjs.js +0 -48
- package/dist/services/langfuse.cjs.js.map +0 -1
- package/dist/services/router/page-summary.cjs.js +0 -128
- package/dist/services/router/page-summary.cjs.js.map +0 -1
- package/dist/utils/content-cleaner.cjs.js +0 -80
- package/dist/utils/content-cleaner.cjs.js.map +0 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const DEFAULT_SUMMARY_PROMPT = "Summarize this conversation in a concise manner. The summary should capture the main points. Return the summary only, without any additional text. Do not include any introductions or other part of the conversation that doesn't contribute to the summary or form part of the overall conversation as part of the summary.";
|
|
4
|
-
const DEFAULT_PAGE_SUMMARY_PROMPT = "Create a concise summary of the provided page content. Focus on the main topics, key information, and essential details. The summary should be informative and help users understand what the page contains without needing to read the full content. Return only the summary without any additional text or introductions.";
|
|
5
4
|
const DEFAULT_IDENTITY_PROMPT = `
|
|
6
5
|
You are a helpful assistant that answers questions based on provided context from various documents. The context may come from sources such as internal wikis, code repositories, technical documentation, or other structured or unstructured data.
|
|
7
6
|
`;
|
|
@@ -38,7 +37,6 @@ TOOL USAGE GUIDELINES:
|
|
|
38
37
|
|
|
39
38
|
exports.DEFAULT_FORMATTING_PROMPT = DEFAULT_FORMATTING_PROMPT;
|
|
40
39
|
exports.DEFAULT_IDENTITY_PROMPT = DEFAULT_IDENTITY_PROMPT;
|
|
41
|
-
exports.DEFAULT_PAGE_SUMMARY_PROMPT = DEFAULT_PAGE_SUMMARY_PROMPT;
|
|
42
40
|
exports.DEFAULT_SUMMARY_PROMPT = DEFAULT_SUMMARY_PROMPT;
|
|
43
41
|
exports.DEFAULT_SYSTEM_PROMPT = DEFAULT_SYSTEM_PROMPT;
|
|
44
42
|
exports.DEFAULT_TOOL_GUIDELINE = DEFAULT_TOOL_GUIDELINE;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.cjs.js","sources":["../../src/constants/prompts.ts"],"sourcesContent":["export const DEFAULT_SUMMARY_PROMPT =\n \"Summarize this conversation in a concise manner. The summary should capture the main points. Return the summary only, without any additional text. Do not include any introductions or other part of the conversation that doesn't contribute to the summary or form part of the overall conversation as part of the summary.\";\n\nexport const
|
|
1
|
+
{"version":3,"file":"prompts.cjs.js","sources":["../../src/constants/prompts.ts"],"sourcesContent":["export const DEFAULT_SUMMARY_PROMPT =\n \"Summarize this conversation in a concise manner. The summary should capture the main points. Return the summary only, without any additional text. Do not include any introductions or other part of the conversation that doesn't contribute to the summary or form part of the overall conversation as part of the summary.\";\n\nexport const DEFAULT_IDENTITY_PROMPT = `\nYou are a helpful assistant that answers questions based on provided context from various documents. The context may come from sources such as internal wikis, code repositories, technical documentation, or other structured or unstructured data.\n`;\n\nexport const DEFAULT_FORMATTING_PROMPT = `\nCRITICAL FORMATTING RULES - MUST ALWAYS FOLLOW:\n1. **ALWAYS use proper markdown formatting in ALL responses**\n2. **NEVER output plain URLs** - ALWAYS convert them to clickable markdown links using [description](url) syntax\n3. **For images, ALWAYS use markdown image syntax**: \n4. **For all URLs, ALWAYS format as**: [descriptive text](url) - never just paste the raw URL\n5. Use headings (##, ###), bullet points, numbered lists, and **bold**/*italic* text appropriately\n6. Format code with backticks: \\`inline code\\` or \\`\\`\\`language for code blocks\n7. Structure responses clearly with proper spacing and organization\n`;\n\nexport const DEFAULT_SYSTEM_PROMPT = `\nContent Rules:\n1. Always base your answers on the provided context. Do not make up information.\n2. When relevant, cite or reference the source information provided in the context.\n3. Maintain a professional, friendly, and helpful tone.\n4. Return only the relevant information without any filler or unnecessary details.\n5. If you don't know the answer, admit it and suggest ways to find the information.\n6. **Actively use available tools** to enhance your responses\n7. Adapt your approach based on the specific tools and capabilities available in the current session\n8. When you do not have the information needed to answer, use the tools provided to gather more context before responding.\n`;\n\nexport const DEFAULT_TOOL_GUIDELINE = `\nTOOL USAGE GUIDELINES:\n- Only use tools when explicitly needed to answer the user's question\n- Read tool descriptions carefully before using them\n- If you can answer without tools, do so\n- IMPORTANT: When using tools, always explain why you're using each tool\n- Use tools in logical sequence, not randomly\n- If a tool fails, try an alternative approach before using another tool\n`;\n"],"names":[],"mappings":";;AAAO,MAAM,sBAAA,GACX;AAEK,MAAM,uBAAA,GAA0B;AAAA;AAAA;AAIhC,MAAM,yBAAA,GAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWlC,MAAM,qBAAA,GAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAY9B,MAAM,sBAAA,GAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;;;"}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -11,7 +11,7 @@ var pluginSignalsNode = require('@backstage/plugin-signals-node');
|
|
|
11
11
|
var searchKnowledge = require('./services/tools/searchKnowledge.cjs.js');
|
|
12
12
|
var pluginCatalogNode = require('@backstage/plugin-catalog-node');
|
|
13
13
|
var mcp = require('./services/mcp.cjs.js');
|
|
14
|
-
var
|
|
14
|
+
var callbacks = require('./services/callbacks.cjs.js');
|
|
15
15
|
|
|
16
16
|
const aiAssistantPlugin = backendPluginApi.createBackendPlugin({
|
|
17
17
|
pluginId: "ai-assistant",
|
|
@@ -19,7 +19,7 @@ const aiAssistantPlugin = backendPluginApi.createBackendPlugin({
|
|
|
19
19
|
const ingestors = [];
|
|
20
20
|
const models = [];
|
|
21
21
|
const tools = [];
|
|
22
|
-
const
|
|
22
|
+
const callbacks$1 = [];
|
|
23
23
|
let embeddingsProvider;
|
|
24
24
|
env.registerExtensionPoint(backstagePluginAiAssistantNode.dataIngestorExtensionPoint, {
|
|
25
25
|
registerIngestor: (ingestor) => {
|
|
@@ -55,9 +55,9 @@ const aiAssistantPlugin = backendPluginApi.createBackendPlugin({
|
|
|
55
55
|
tools.push(tool);
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
|
-
env.registerExtensionPoint(backstagePluginAiAssistantNode.
|
|
59
|
-
register: (
|
|
60
|
-
|
|
58
|
+
env.registerExtensionPoint(backstagePluginAiAssistantNode.callbackFactoryExtensionPoint, {
|
|
59
|
+
register: (callbackFactory) => {
|
|
60
|
+
callbacks$1.push(callbackFactory);
|
|
61
61
|
}
|
|
62
62
|
});
|
|
63
63
|
env.registerInit({
|
|
@@ -75,11 +75,7 @@ const aiAssistantPlugin = backendPluginApi.createBackendPlugin({
|
|
|
75
75
|
auth: backendPluginApi.coreServices.auth
|
|
76
76
|
},
|
|
77
77
|
async init(options) {
|
|
78
|
-
const { httpRouter, database
|
|
79
|
-
const { langfuseEnabled, langfuseClient } = langfuse.initLangfuse(
|
|
80
|
-
config,
|
|
81
|
-
logger
|
|
82
|
-
);
|
|
78
|
+
const { httpRouter, database } = options;
|
|
83
79
|
const client = await database.getClient();
|
|
84
80
|
await migrations.applyDatabaseMigrations(client);
|
|
85
81
|
const vectorStore = await pgVectorStore.PgVectorStore.fromConfig(options);
|
|
@@ -95,26 +91,17 @@ const aiAssistantPlugin = backendPluginApi.createBackendPlugin({
|
|
|
95
91
|
const mcp$1 = await mcp.createMcpService(options);
|
|
96
92
|
const searchKnowledgeTool = searchKnowledge.createSearchKnowledgeTool({ vectorStore });
|
|
97
93
|
tools.push(searchKnowledgeTool);
|
|
94
|
+
const callback = await callbacks.createCallbackService({
|
|
95
|
+
callbacks: callbacks$1
|
|
96
|
+
});
|
|
98
97
|
const chat$1 = await chat.createChatService({
|
|
99
98
|
...options,
|
|
100
99
|
models,
|
|
101
100
|
tools,
|
|
102
101
|
mcp: mcp$1,
|
|
103
|
-
|
|
104
|
-
langfuseClient
|
|
102
|
+
callback
|
|
105
103
|
});
|
|
106
|
-
|
|
107
|
-
realtimeVoiceService.initialize({ tools });
|
|
108
|
-
}
|
|
109
|
-
httpRouter.use(
|
|
110
|
-
await index.createRouter({
|
|
111
|
-
...options,
|
|
112
|
-
chat: chat$1,
|
|
113
|
-
models,
|
|
114
|
-
langfuseEnabled,
|
|
115
|
-
mcp: mcp$1
|
|
116
|
-
})
|
|
117
|
-
);
|
|
104
|
+
httpRouter.use(await index.createRouter({ ...options, chat: chat$1, mcp: mcp$1 }));
|
|
118
105
|
dataIngestionPipeline.start();
|
|
119
106
|
}
|
|
120
107
|
});
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["import {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './services/router';\nimport {\n dataIngestorExtensionPoint,\n EmbeddingsProvider,\n embeddingsProviderExtensionPoint,\n Ingestor,\n Model,\n modelProviderExtensionPoint,\n Tool,\n toolExtensionPoint,\n
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["import {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './services/router';\nimport {\n dataIngestorExtensionPoint,\n EmbeddingsProvider,\n embeddingsProviderExtensionPoint,\n Ingestor,\n Model,\n modelProviderExtensionPoint,\n Tool,\n toolExtensionPoint,\n callbackFactoryExtensionPoint,\n CallbackFactory,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { createDataIngestionPipeline } from './services/ingestor';\nimport { createChatService } from './services/chat';\nimport { applyDatabaseMigrations } from './database/migrations';\nimport { PgVectorStore } from './database';\nimport { signalsServiceRef } from '@backstage/plugin-signals-node';\nimport { createSearchKnowledgeTool } from './services/tools/searchKnowledge';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node';\nimport { createMcpService } from './services/mcp';\n\nimport { createCallbackService } from './services/callbacks';\n/**\n * aiAssistantPlugin backend plugin\n *\n * @public\n */\n\nexport const aiAssistantPlugin = createBackendPlugin({\n pluginId: 'ai-assistant',\n register(env) {\n const ingestors: Ingestor[] = [];\n const models: Model[] = [];\n const tools: Tool[] = [];\n const callbacks: CallbackFactory[] = [];\n\n let embeddingsProvider: EmbeddingsProvider;\n\n env.registerExtensionPoint(dataIngestorExtensionPoint, {\n registerIngestor: ingestor => {\n const existingIngestor = ingestors.find(i => i.id === ingestor.id);\n if (existingIngestor) {\n throw new Error(\n `Ingestor with id ${ingestor.id} is already registered.`,\n );\n }\n ingestors.push(ingestor);\n },\n });\n\n env.registerExtensionPoint(embeddingsProviderExtensionPoint, {\n register: provider => {\n embeddingsProvider = provider;\n },\n });\n\n env.registerExtensionPoint(modelProviderExtensionPoint, {\n register: model => {\n const existingModel = models.find(m => m.id === model.id);\n if (existingModel) {\n throw new Error(`Model with id ${model.id} is already registered.`);\n }\n models.push(model);\n },\n });\n\n env.registerExtensionPoint(toolExtensionPoint, {\n register: tool => {\n const existingTool = tools.find(t => t.name === tool.name);\n if (existingTool) {\n throw new Error(`Tool with name ${tool.name} is already registered.`);\n }\n tools.push(tool);\n },\n });\n\n env.registerExtensionPoint(callbackFactoryExtensionPoint, {\n register: callbackFactory => {\n callbacks.push(callbackFactory);\n },\n });\n\n env.registerInit({\n deps: {\n httpRouter: coreServices.httpRouter,\n database: coreServices.database,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n scheduler: coreServices.scheduler,\n httpAuth: coreServices.httpAuth,\n userInfo: coreServices.userInfo,\n signals: signalsServiceRef,\n catalog: catalogServiceRef,\n cache: coreServices.cache,\n auth: coreServices.auth,\n },\n\n async init(options) {\n const { httpRouter, database } = options;\n\n const client = await database.getClient();\n\n await applyDatabaseMigrations(client);\n\n const vectorStore = await PgVectorStore.fromConfig(options);\n\n if (!embeddingsProvider) {\n throw new Error('No Embeddings Provider was registered.');\n }\n\n vectorStore.connectEmbeddings(await embeddingsProvider.getEmbeddings());\n\n const dataIngestionPipeline = createDataIngestionPipeline({\n ...options,\n vectorStore,\n ingestors,\n });\n\n const mcp = await createMcpService(options);\n\n const searchKnowledgeTool = createSearchKnowledgeTool({ vectorStore });\n tools.push(searchKnowledgeTool);\n\n const callback = await createCallbackService({\n callbacks,\n });\n\n const chat = await createChatService({\n ...options,\n models,\n tools,\n mcp,\n callback,\n });\n\n httpRouter.use(await createRouter({ ...options, chat, mcp }));\n dataIngestionPipeline.start();\n },\n });\n },\n});\n"],"names":["createBackendPlugin","callbacks","dataIngestorExtensionPoint","embeddingsProviderExtensionPoint","modelProviderExtensionPoint","toolExtensionPoint","callbackFactoryExtensionPoint","coreServices","signalsServiceRef","catalogServiceRef","applyDatabaseMigrations","PgVectorStore","createDataIngestionPipeline","mcp","createMcpService","createSearchKnowledgeTool","createCallbackService","chat","createChatService","createRouter"],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const createCallbackService = async ({
|
|
4
|
+
callbacks
|
|
5
|
+
}) => {
|
|
6
|
+
const getAgentCallbackData = async (options) => {
|
|
7
|
+
const metadata = {};
|
|
8
|
+
const callbackHandlers = [];
|
|
9
|
+
for (const createCallback of callbacks) {
|
|
10
|
+
const { metadata: cbMetadata, callback } = await createCallback(
|
|
11
|
+
options
|
|
12
|
+
);
|
|
13
|
+
Object.assign(metadata, cbMetadata);
|
|
14
|
+
callbackHandlers.push(callback);
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
metadata,
|
|
18
|
+
callbacks: callbackHandlers
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
getAgentCallbackData
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
exports.createCallbackService = createCallbackService;
|
|
27
|
+
//# sourceMappingURL=callbacks.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callbacks.cjs.js","sources":["../../src/services/callbacks.ts"],"sourcesContent":["import {\n Callback,\n CallbackFactory,\n CallbackOptions,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\n\nexport type CallbackService = {\n getAgentCallbackData: (options: CallbackOptions) => Promise<{\n metadata: Callback['metadata'];\n callbacks: Callback['callback'][];\n }>;\n};\n\nexport type CreateCallbackServiceOptions = {\n callbacks: CallbackFactory[];\n};\n\nexport const createCallbackService = async ({\n callbacks,\n}: CreateCallbackServiceOptions): Promise<CallbackService> => {\n const getAgentCallbackData: CallbackService['getAgentCallbackData'] =\n async options => {\n const metadata = {};\n const callbackHandlers: Callback['callback'][] = [];\n\n for (const createCallback of callbacks) {\n const { metadata: cbMetadata, callback } = await createCallback(\n options,\n );\n Object.assign(metadata, cbMetadata);\n callbackHandlers.push(callback);\n }\n\n return {\n metadata,\n callbacks: callbackHandlers,\n };\n };\n\n return {\n getAgentCallbackData,\n };\n};\n"],"names":[],"mappings":";;AAiBO,MAAM,wBAAwB,OAAO;AAAA,EAC1C;AACF,CAAA,KAA8D;AAC5D,EAAA,MAAM,oBAAA,GACJ,OAAM,OAAA,KAAW;AACf,IAAA,MAAM,WAAW,EAAC;AAClB,IAAA,MAAM,mBAA2C,EAAC;AAElD,IAAA,KAAA,MAAW,kBAAkB,SAAA,EAAW;AACtC,MAAA,MAAM,EAAE,QAAA,EAAU,UAAA,EAAY,QAAA,KAAa,MAAM,cAAA;AAAA,QAC/C;AAAA,OACF;AACA,MAAA,MAAA,CAAO,MAAA,CAAO,UAAU,UAAU,CAAA;AAClC,MAAA,gBAAA,CAAiB,KAAK,QAAQ,CAAA;AAAA,IAChC;AAEA,IAAA,OAAO;AAAA,MACL,QAAA;AAAA,MACA,SAAA,EAAW;AAAA,KACb;AAAA,EACF,CAAA;AAEF,EAAA,OAAO;AAAA,IACL;AAAA,GACF;AACF;;;;"}
|
|
@@ -6,7 +6,6 @@ var tools = require('@langchain/core/tools');
|
|
|
6
6
|
var prebuilt = require('@langchain/langgraph/prebuilt');
|
|
7
7
|
var prompts$1 = require('@langchain/core/prompts');
|
|
8
8
|
var summarizer = require('./summarizer.cjs.js');
|
|
9
|
-
var langchain = require('@langfuse/langchain');
|
|
10
9
|
var uuid = require('uuid');
|
|
11
10
|
|
|
12
11
|
const createChatService = async ({
|
|
@@ -21,8 +20,7 @@ const createChatService = async ({
|
|
|
21
20
|
auth,
|
|
22
21
|
mcp,
|
|
23
22
|
userInfo,
|
|
24
|
-
|
|
25
|
-
langfuseClient
|
|
23
|
+
callback
|
|
26
24
|
}) => {
|
|
27
25
|
logger.info(`Available models: ${models.map((m) => m.id).join(", ")}`);
|
|
28
26
|
logger.info(`Available tools: ${tools$1.map((t) => t.name).join(", ")}`);
|
|
@@ -39,8 +37,7 @@ ${contentPrompt}`;
|
|
|
39
37
|
const summarizer$1 = await summarizer.createSummarizerService({
|
|
40
38
|
config,
|
|
41
39
|
models,
|
|
42
|
-
|
|
43
|
-
logger
|
|
40
|
+
callback
|
|
44
41
|
});
|
|
45
42
|
const systemPromptTemplate = prompts$1.SystemMessagePromptTemplate.fromTemplate(`
|
|
46
43
|
PURPOSE:
|
|
@@ -144,11 +141,11 @@ ${contentPrompt}`;
|
|
|
144
141
|
tools: agentTools,
|
|
145
142
|
prompt: systemPrompt[0].text
|
|
146
143
|
});
|
|
147
|
-
const
|
|
148
|
-
|
|
144
|
+
const { metadata: promptMetadata, callbacks } = await callback.getAgentCallbackData({
|
|
145
|
+
conversationId,
|
|
149
146
|
userId: userEntityRef,
|
|
150
|
-
|
|
151
|
-
})
|
|
147
|
+
modelId
|
|
148
|
+
});
|
|
152
149
|
const promptStream = await agent.stream(
|
|
153
150
|
{
|
|
154
151
|
messages: [...recentConversationMessages, ...messages]
|
|
@@ -156,12 +153,8 @@ ${contentPrompt}`;
|
|
|
156
153
|
{
|
|
157
154
|
streamMode: ["values"],
|
|
158
155
|
runName: "ai-assistant-chat",
|
|
159
|
-
metadata:
|
|
160
|
-
|
|
161
|
-
langfuseSessionId: conversationId,
|
|
162
|
-
langfuseTags: ["ai-assistant", "chat", modelId]
|
|
163
|
-
},
|
|
164
|
-
callbacks: langfuseHandler ? [langfuseHandler] : []
|
|
156
|
+
metadata: promptMetadata,
|
|
157
|
+
callbacks
|
|
165
158
|
}
|
|
166
159
|
);
|
|
167
160
|
const responseMessages = [];
|
|
@@ -214,9 +207,8 @@ ${contentPrompt}`;
|
|
|
214
207
|
}
|
|
215
208
|
responseMessages.push(...newMessages);
|
|
216
209
|
}
|
|
217
|
-
const traceId = langfuseHandler?.last_trace_id ?? void 0;
|
|
218
210
|
addMessages(
|
|
219
|
-
responseMessages.map((m) => ({ ...m, id: uuid.v4()
|
|
211
|
+
responseMessages.map((m) => ({ ...m, id: uuid.v4() })),
|
|
220
212
|
userEntityRef,
|
|
221
213
|
conversationId,
|
|
222
214
|
[...recentConversationMessages, ...messages]
|
|
@@ -248,20 +240,6 @@ ${contentPrompt}`;
|
|
|
248
240
|
if (!message) {
|
|
249
241
|
throw new Error(`Message with id ${messageId} not found`);
|
|
250
242
|
}
|
|
251
|
-
if (langfuseEnabled && message.traceId) {
|
|
252
|
-
langfuseClient.score.create({
|
|
253
|
-
traceId: message.traceId,
|
|
254
|
-
name: "helpfulness",
|
|
255
|
-
value: score
|
|
256
|
-
});
|
|
257
|
-
logger.info(
|
|
258
|
-
`Scored message ${messageId} on Langfuse with trace ID ${message.traceId} - ${score} for helpfulness`
|
|
259
|
-
);
|
|
260
|
-
} else if (langfuseEnabled && !message.traceId) {
|
|
261
|
-
logger.warn(
|
|
262
|
-
`Message ${messageId} does not have a traceId, cannot score on Langfuse`
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
243
|
const updatedMessage = {
|
|
266
244
|
...message,
|
|
267
245
|
score
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.cjs.js","sources":["../../src/services/chat.ts"],"sourcesContent":["import { Model } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { UserEntity } from '@backstage/catalog-model';\nimport {\n LoggerService,\n RootConfigService,\n DatabaseService,\n AuthService,\n} from '@backstage/backend-plugin-api';\nimport { ChatStore } from '../database/chat-store';\nimport {\n Conversation,\n Message,\n JsonObject,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { SignalsService } from '@backstage/plugin-signals-node';\nimport {\n DEFAULT_FORMATTING_PROMPT,\n DEFAULT_IDENTITY_PROMPT,\n DEFAULT_SYSTEM_PROMPT,\n DEFAULT_TOOL_GUIDELINE,\n} from '../constants/prompts';\nimport { Tool } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { DynamicStructuredTool } from '@langchain/core/tools';\nimport { createReactAgent } from '@langchain/langgraph/prebuilt';\nimport { SystemMessagePromptTemplate } from '@langchain/core/prompts';\nimport { createSummarizerService } from './summarizer';\nimport { CallbackHandler } from '@langfuse/langchain';\nimport { LangfuseClient } from '@langfuse/client';\nimport { v4 as uuid } from 'uuid';\nimport type {\n BackstageCredentials,\n CacheService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { AIMessage, ToolMessage } from '@langchain/core/messages';\nimport { McpService } from './mcp';\n\nexport type ChatServiceOptions = {\n models: Model[];\n tools: Tool[];\n logger: LoggerService;\n config: RootConfigService;\n database: DatabaseService;\n signals: SignalsService;\n catalog: CatalogService;\n cache: CacheService;\n auth: AuthService;\n mcp: McpService;\n userInfo: UserInfoService;\n langfuseEnabled: boolean;\n langfuseClient?: LangfuseClient;\n};\n\ntype PromptOptions = {\n modelId: string;\n messages: Message[];\n conversationId: string;\n stream?: boolean;\n userCredentials: BackstageCredentials;\n};\n\ntype GetConversationOptions = {\n conversationId: string;\n userEntityRef: string;\n};\n\ntype GetConversationsOptions = {\n userEntityRef: string;\n};\n\n// Helper type for messages with required fields except traceId which remains optional\ntype MessageWithRequiredFields = Required<Omit<Message, 'traceId'>> &\n Pick<Message, 'traceId'>;\n\nexport type ChatService = {\n prompt: (options: PromptOptions) => Promise<MessageWithRequiredFields[]>;\n getAvailableModels: () => Promise<string[]>;\n getConversation: (\n options: GetConversationOptions,\n ) => Promise<MessageWithRequiredFields[]>;\n getConversations: (\n options: GetConversationsOptions,\n ) => Promise<Conversation[]>;\n addMessages: (\n messages: Message[],\n userRef: string,\n conversationId: string,\n recentConversationMessages?: Message[],\n ) => Promise<void>;\n scoreMessage: (messageId: string, score: number) => Promise<void>;\n};\n\nexport const createChatService = async ({\n models,\n tools,\n logger,\n database,\n signals,\n config,\n catalog,\n cache,\n auth,\n mcp,\n userInfo,\n langfuseEnabled,\n langfuseClient,\n}: ChatServiceOptions): Promise<ChatService> => {\n logger.info(`Available models: ${models.map(m => m.id).join(', ')}`);\n logger.info(`Available tools: ${tools.map(t => t.name).join(', ')}`);\n\n const identityPrompt =\n config.getOptionalString('aiAssistant.prompt.identity') ||\n DEFAULT_IDENTITY_PROMPT;\n\n const formattingPrompt =\n config.getOptionalString('aiAssistant.prompt.formatting') ||\n DEFAULT_FORMATTING_PROMPT;\n\n const contentPrompt =\n config.getOptionalString('aiAssistant.prompt.content') ||\n DEFAULT_SYSTEM_PROMPT;\n\n const combinedBasePrompt = `${identityPrompt}\\n\\n${formattingPrompt}\\n\\n${contentPrompt}`;\n\n const toolGuideline =\n config.getOptionalString('aiAssistant.prompt.toolGuideline') ||\n DEFAULT_TOOL_GUIDELINE;\n\n const chatStore = await ChatStore.fromConfig({ database });\n const summarizer = await createSummarizerService({\n config,\n models,\n langfuseEnabled,\n logger,\n });\n\n const systemPromptTemplate = SystemMessagePromptTemplate.fromTemplate(`\n PURPOSE:\n {basePrompt}\n\n TOOL USAGE GUIDELINES:\n {toolGuideline}\n\n Available tools:\n {toolList}\n\n Calling User:\n {user}\n\n Context:\n {context}`);\n\n const addMessages: ChatService['addMessages'] = async (\n messages,\n userRef,\n conversationId,\n recentConversationMessages,\n ) => {\n // If we have recentConversationMessages, use them; otherwise, fetch the last 5 messages\n const recentMessages =\n recentConversationMessages ||\n (await chatStore.getChatMessages(conversationId, userRef, 5, ['tool']));\n\n const conversationSize = (recentMessages?.length ?? 0) + messages.length;\n\n if (recentMessages.length === 0) {\n const conversation: Conversation = {\n id: conversationId,\n title: 'New Conversation',\n userRef,\n };\n chatStore.createConversation(conversation);\n chatStore.addChatMessage(messages, userRef, conversationId);\n\n signals.publish({\n channel: `ai-assistant.chat.conversation-details-update`,\n message: { conversation },\n recipients: {\n type: 'user',\n entityRef: userRef,\n },\n });\n return;\n }\n\n if (conversationSize < 5) {\n chatStore.addChatMessage(messages, userRef, conversationId);\n return;\n }\n\n const conversation = await chatStore.getConversation(\n conversationId,\n userRef,\n );\n\n if (conversation.title !== 'New Conversation') {\n chatStore.addChatMessage(messages, userRef, conversationId);\n return;\n }\n\n const summary = await summarizer.summarize(recentMessages, '25 characters');\n\n conversation.title = summary;\n\n chatStore.updateConversation(conversation);\n chatStore.addChatMessage(messages, userRef, conversationId);\n\n signals.publish({\n channel: `ai-assistant.chat.conversation-details-update`,\n message: { conversation },\n recipients: {\n type: 'user',\n entityRef: userRef,\n },\n });\n };\n\n const prompt: ChatService['prompt'] = async ({\n conversationId,\n messages,\n modelId,\n stream = true,\n userCredentials,\n }: PromptOptions) => {\n const model = models.find(m => m.id === modelId)?.chatModel;\n\n if (!model) {\n throw new Error(`Model with id ${modelId} not found`);\n }\n\n const { userEntityRef } = await userInfo.getUserInfo(userCredentials);\n\n const streamFn = async () => {\n const recentConversationMessages = await chatStore.getChatMessages(\n conversationId,\n userEntityRef,\n 10,\n ['tool'],\n );\n\n const credentials = await auth.getOwnServiceCredentials();\n const user = await getUser(cache, userEntityRef, credentials, catalog);\n\n const mcpTools = await mcp.getTools(userCredentials);\n\n const agentTools = tools\n .map(tool => new DynamicStructuredTool(tool))\n .concat(mcpTools.map(tool => new DynamicStructuredTool(tool)));\n\n addMessages(\n messages,\n userEntityRef,\n conversationId,\n recentConversationMessages,\n );\n\n const systemPrompt = await systemPromptTemplate.formatMessages({\n basePrompt: combinedBasePrompt,\n toolGuideline,\n toolList: agentTools\n .map(tool => `- ${tool.name}: ${tool.description}`)\n .join('\\n'),\n context: `none`,\n user,\n });\n\n const agent = createReactAgent({\n llm: model,\n tools: agentTools,\n prompt: systemPrompt[0].text,\n });\n\n // Initialize Langfuse CallbackHandler for tracing if credentials are available\n const langfuseHandler = langfuseEnabled\n ? new CallbackHandler({\n sessionId: conversationId,\n userId: userEntityRef,\n tags: ['backstage-ai-assistant', 'chat'],\n })\n : undefined;\n\n const promptStream = await agent.stream(\n {\n messages: [...recentConversationMessages, ...messages],\n },\n {\n streamMode: ['values'],\n runName: 'ai-assistant-chat',\n metadata: {\n langfuseUserId: userEntityRef,\n langfuseSessionId: conversationId,\n langfuseTags: ['ai-assistant', 'chat', modelId],\n },\n callbacks: langfuseHandler ? [langfuseHandler] : [],\n },\n );\n\n const responseMessages: MessageWithRequiredFields[] = [];\n\n for await (const [, chunk] of promptStream) {\n const { messages: promptMessages } = chunk;\n\n const newMessages: MessageWithRequiredFields[] = promptMessages\n .filter(m => responseMessages.findIndex(rm => rm.id === m.id) === -1)\n .filter(\n m =>\n recentConversationMessages.findIndex(rm => rm.id === m.id) === -1,\n )\n .filter(m => m.getType() !== 'human')\n .map(m => {\n const id = m.id ?? '';\n const role = m.getType();\n const content =\n typeof m.content === 'string'\n ? m.content\n : JSON.stringify(m.content);\n\n const metadata: JsonObject = {};\n\n if (role === 'ai') {\n const aiMessage = m as AIMessage;\n metadata.toolCalls = aiMessage.tool_calls || [];\n metadata.finishReason =\n aiMessage.response_metadata.finish_reason || undefined;\n metadata.modelName =\n aiMessage.response_metadata.model_name || undefined;\n }\n\n if (role === 'tool') {\n const toolMessage = m as ToolMessage;\n metadata.name = toolMessage.name || '';\n }\n\n return {\n id,\n role,\n content,\n metadata,\n score: 0,\n traceId: undefined,\n };\n });\n\n // Simulate streaming until langchain messages error is better understood\n for await (const m of newMessages) {\n const words = m.content.split(' ');\n const chunkSize = 5; // Send 5 words at a time\n let messageBuilder = '';\n\n for (let i = 0; i < words.length; i += chunkSize) {\n const wordChunk = words.slice(i, i + chunkSize).join(' ');\n messageBuilder = messageBuilder.concat(wordChunk).concat(' ');\n m.content = messageBuilder;\n\n await new Promise(resolve => setTimeout(resolve, 50));\n\n signals.publish({\n channel: `ai-assistant.chat.conversation-stream:${conversationId}`,\n message: { messages: [m] },\n recipients: {\n type: 'user',\n entityRef: userEntityRef,\n },\n });\n }\n }\n\n responseMessages.push(...newMessages);\n }\n\n // Get the traceId from Langfuse if enabled\n const traceId = langfuseHandler?.last_trace_id ?? undefined;\n\n addMessages(\n responseMessages.map(m => ({ ...m, id: uuid(), traceId })),\n userEntityRef,\n conversationId,\n [...recentConversationMessages, ...messages],\n );\n\n return responseMessages;\n };\n\n const result = streamFn();\n\n return stream ? [] : result;\n };\n\n const getAvailableModels: ChatService['getAvailableModels'] = async () => {\n return models.map(x => x.id);\n };\n\n const getConversation: ChatService['getConversation'] = async (\n options: GetConversationOptions,\n ) => {\n const { conversationId, userEntityRef } = options;\n\n const conversation = await chatStore.getChatMessages(\n conversationId,\n userEntityRef,\n );\n\n return conversation;\n };\n\n const getConversations: ChatService['getConversations'] = async ({\n userEntityRef,\n }: GetConversationsOptions) => {\n const conversations = await chatStore.getConversations(userEntityRef);\n\n return conversations;\n };\n\n const scoreMessage: ChatService['scoreMessage'] = async (\n messageId: string,\n score: number,\n ) => {\n const message = await chatStore.getMessageById(messageId);\n\n if (!message) {\n throw new Error(`Message with id ${messageId} not found`);\n }\n\n if (langfuseEnabled && message.traceId) {\n langfuseClient!.score.create({\n traceId: message.traceId,\n name: 'helpfulness',\n value: score,\n });\n logger.info(\n `Scored message ${messageId} on Langfuse with trace ID ${message.traceId} - ${score} for helpfulness`,\n );\n } else if (langfuseEnabled && !message.traceId) {\n logger.warn(\n `Message ${messageId} does not have a traceId, cannot score on Langfuse`,\n );\n }\n\n const updatedMessage: Required<Message> = {\n ...message,\n score,\n };\n\n await chatStore.updateMessage(updatedMessage);\n logger.info(`Message ${messageId} scored ${score}`);\n };\n\n return {\n prompt,\n getAvailableModels,\n getConversation,\n getConversations,\n addMessages,\n scoreMessage,\n };\n};\n\nasync function getUser(\n cache: CacheService,\n userEntityRef: string,\n credentials: BackstageCredentials,\n catalog: CatalogService,\n) {\n const cached = await cache.get(userEntityRef);\n\n if (cached) {\n return JSON.parse(String(cached));\n }\n\n const user = (await catalog.getEntityByRef(userEntityRef, {\n credentials,\n })) as UserEntity | undefined;\n await cache.set(userEntityRef, JSON.stringify(user));\n\n return user;\n}\n"],"names":["tools","DEFAULT_IDENTITY_PROMPT","DEFAULT_FORMATTING_PROMPT","DEFAULT_SYSTEM_PROMPT","DEFAULT_TOOL_GUIDELINE","chatStore","ChatStore","summarizer","createSummarizerService","SystemMessagePromptTemplate","conversation","DynamicStructuredTool","createReactAgent","CallbackHandler","uuid"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chat.cjs.js","sources":["../../src/services/chat.ts"],"sourcesContent":["import { Model } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { UserEntity } from '@backstage/catalog-model';\nimport {\n LoggerService,\n RootConfigService,\n DatabaseService,\n AuthService,\n} from '@backstage/backend-plugin-api';\nimport { ChatStore } from '../database/chat-store';\nimport {\n Conversation,\n Message,\n JsonObject,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { SignalsService } from '@backstage/plugin-signals-node';\nimport {\n DEFAULT_FORMATTING_PROMPT,\n DEFAULT_IDENTITY_PROMPT,\n DEFAULT_SYSTEM_PROMPT,\n DEFAULT_TOOL_GUIDELINE,\n} from '../constants/prompts';\nimport { Tool } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport { DynamicStructuredTool } from '@langchain/core/tools';\nimport { createReactAgent } from '@langchain/langgraph/prebuilt';\nimport { SystemMessagePromptTemplate } from '@langchain/core/prompts';\nimport { createSummarizerService } from './summarizer';\nimport { v4 as uuid } from 'uuid';\nimport type {\n BackstageCredentials,\n CacheService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { AIMessage, ToolMessage } from '@langchain/core/messages';\nimport { McpService } from './mcp';\nimport { CallbackService } from './callbacks';\n\nexport type ChatServiceOptions = {\n models: Model[];\n tools: Tool[];\n logger: LoggerService;\n config: RootConfigService;\n database: DatabaseService;\n signals: SignalsService;\n catalog: CatalogService;\n cache: CacheService;\n auth: AuthService;\n mcp: McpService;\n userInfo: UserInfoService;\n callback: CallbackService;\n};\n\ntype PromptOptions = {\n modelId: string;\n messages: Message[];\n conversationId: string;\n stream?: boolean;\n userCredentials: BackstageCredentials;\n};\n\ntype GetConversationOptions = {\n conversationId: string;\n userEntityRef: string;\n};\n\ntype GetConversationsOptions = {\n userEntityRef: string;\n};\n\n// Helper type for messages with required fields except traceId which remains optional\ntype MessageWithRequiredFields = Required<Omit<Message, 'traceId'>> &\n Pick<Message, 'traceId'>;\n\nexport type ChatService = {\n prompt: (options: PromptOptions) => Promise<MessageWithRequiredFields[]>;\n getAvailableModels: () => Promise<string[]>;\n getConversation: (\n options: GetConversationOptions,\n ) => Promise<MessageWithRequiredFields[]>;\n getConversations: (\n options: GetConversationsOptions,\n ) => Promise<Conversation[]>;\n addMessages: (\n messages: Message[],\n userRef: string,\n conversationId: string,\n recentConversationMessages?: Message[],\n ) => Promise<void>;\n scoreMessage: (messageId: string, score: number) => Promise<void>;\n};\n\nexport const createChatService = async ({\n models,\n tools,\n logger,\n database,\n signals,\n config,\n catalog,\n cache,\n auth,\n mcp,\n userInfo,\n callback,\n}: ChatServiceOptions): Promise<ChatService> => {\n logger.info(`Available models: ${models.map(m => m.id).join(', ')}`);\n logger.info(`Available tools: ${tools.map(t => t.name).join(', ')}`);\n\n const identityPrompt =\n config.getOptionalString('aiAssistant.prompt.identity') ||\n DEFAULT_IDENTITY_PROMPT;\n\n const formattingPrompt =\n config.getOptionalString('aiAssistant.prompt.formatting') ||\n DEFAULT_FORMATTING_PROMPT;\n\n const contentPrompt =\n config.getOptionalString('aiAssistant.prompt.content') ||\n DEFAULT_SYSTEM_PROMPT;\n\n const combinedBasePrompt = `${identityPrompt}\\n\\n${formattingPrompt}\\n\\n${contentPrompt}`;\n\n const toolGuideline =\n config.getOptionalString('aiAssistant.prompt.toolGuideline') ||\n DEFAULT_TOOL_GUIDELINE;\n\n const chatStore = await ChatStore.fromConfig({ database });\n const summarizer = await createSummarizerService({\n config,\n models,\n callback,\n });\n\n const systemPromptTemplate = SystemMessagePromptTemplate.fromTemplate(`\n PURPOSE:\n {basePrompt}\n\n TOOL USAGE GUIDELINES:\n {toolGuideline}\n\n Available tools:\n {toolList}\n\n Calling User:\n {user}\n\n Context:\n {context}`);\n\n const addMessages: ChatService['addMessages'] = async (\n messages,\n userRef,\n conversationId,\n recentConversationMessages,\n ) => {\n // If we have recentConversationMessages, use them; otherwise, fetch the last 5 messages\n const recentMessages =\n recentConversationMessages ||\n (await chatStore.getChatMessages(conversationId, userRef, 5, ['tool']));\n\n const conversationSize = (recentMessages?.length ?? 0) + messages.length;\n\n if (recentMessages.length === 0) {\n const conversation: Conversation = {\n id: conversationId,\n title: 'New Conversation',\n userRef,\n };\n chatStore.createConversation(conversation);\n chatStore.addChatMessage(messages, userRef, conversationId);\n\n signals.publish({\n channel: `ai-assistant.chat.conversation-details-update`,\n message: { conversation },\n recipients: {\n type: 'user',\n entityRef: userRef,\n },\n });\n return;\n }\n\n if (conversationSize < 5) {\n chatStore.addChatMessage(messages, userRef, conversationId);\n return;\n }\n\n const conversation = await chatStore.getConversation(\n conversationId,\n userRef,\n );\n\n if (conversation.title !== 'New Conversation') {\n chatStore.addChatMessage(messages, userRef, conversationId);\n return;\n }\n\n const summary = await summarizer.summarize(recentMessages, '25 characters');\n\n conversation.title = summary;\n\n chatStore.updateConversation(conversation);\n chatStore.addChatMessage(messages, userRef, conversationId);\n\n signals.publish({\n channel: `ai-assistant.chat.conversation-details-update`,\n message: { conversation },\n recipients: {\n type: 'user',\n entityRef: userRef,\n },\n });\n };\n\n const prompt: ChatService['prompt'] = async ({\n conversationId,\n messages,\n modelId,\n stream = true,\n userCredentials,\n }: PromptOptions) => {\n const model = models.find(m => m.id === modelId)?.chatModel;\n\n if (!model) {\n throw new Error(`Model with id ${modelId} not found`);\n }\n\n const { userEntityRef } = await userInfo.getUserInfo(userCredentials);\n\n const streamFn = async () => {\n const recentConversationMessages = await chatStore.getChatMessages(\n conversationId,\n userEntityRef,\n 10,\n ['tool'],\n );\n\n const credentials = await auth.getOwnServiceCredentials();\n const user = await getUser(cache, userEntityRef, credentials, catalog);\n\n const mcpTools = await mcp.getTools(userCredentials);\n\n const agentTools = tools\n .map(tool => new DynamicStructuredTool(tool))\n .concat(mcpTools.map(tool => new DynamicStructuredTool(tool)));\n\n addMessages(\n messages,\n userEntityRef,\n conversationId,\n recentConversationMessages,\n );\n\n const systemPrompt = await systemPromptTemplate.formatMessages({\n basePrompt: combinedBasePrompt,\n toolGuideline,\n toolList: agentTools\n .map(tool => `- ${tool.name}: ${tool.description}`)\n .join('\\n'),\n context: `none`,\n user,\n });\n\n const agent = createReactAgent({\n llm: model,\n tools: agentTools,\n prompt: systemPrompt[0].text,\n });\n\n const { metadata: promptMetadata, callbacks } =\n await callback.getAgentCallbackData({\n conversationId,\n userId: userEntityRef,\n modelId,\n });\n\n const promptStream = await agent.stream(\n {\n messages: [...recentConversationMessages, ...messages],\n },\n {\n streamMode: ['values'],\n runName: 'ai-assistant-chat',\n metadata: promptMetadata,\n callbacks,\n },\n );\n\n const responseMessages: MessageWithRequiredFields[] = [];\n\n for await (const [, chunk] of promptStream) {\n const { messages: promptMessages } = chunk;\n\n const newMessages: MessageWithRequiredFields[] = promptMessages\n .filter(m => responseMessages.findIndex(rm => rm.id === m.id) === -1)\n .filter(\n m =>\n recentConversationMessages.findIndex(rm => rm.id === m.id) === -1,\n )\n .filter(m => m.getType() !== 'human')\n .map(m => {\n const id = m.id ?? '';\n const role = m.getType();\n const content =\n typeof m.content === 'string'\n ? m.content\n : JSON.stringify(m.content);\n\n const metadata: JsonObject = {};\n\n if (role === 'ai') {\n const aiMessage = m as AIMessage;\n metadata.toolCalls = aiMessage.tool_calls || [];\n metadata.finishReason =\n aiMessage.response_metadata.finish_reason || undefined;\n metadata.modelName =\n aiMessage.response_metadata.model_name || undefined;\n }\n\n if (role === 'tool') {\n const toolMessage = m as ToolMessage;\n metadata.name = toolMessage.name || '';\n }\n\n return {\n id,\n role,\n content,\n metadata,\n score: 0,\n traceId: undefined,\n };\n });\n\n // Simulate streaming until langchain messages error is better understood\n for await (const m of newMessages) {\n const words = m.content.split(' ');\n const chunkSize = 5; // Send 5 words at a time\n let messageBuilder = '';\n\n for (let i = 0; i < words.length; i += chunkSize) {\n const wordChunk = words.slice(i, i + chunkSize).join(' ');\n messageBuilder = messageBuilder.concat(wordChunk).concat(' ');\n m.content = messageBuilder;\n\n await new Promise(resolve => setTimeout(resolve, 50));\n\n signals.publish({\n channel: `ai-assistant.chat.conversation-stream:${conversationId}`,\n message: { messages: [m] },\n recipients: {\n type: 'user',\n entityRef: userEntityRef,\n },\n });\n }\n }\n\n responseMessages.push(...newMessages);\n }\n\n addMessages(\n responseMessages.map(m => ({ ...m, id: uuid() })),\n userEntityRef,\n conversationId,\n [...recentConversationMessages, ...messages],\n );\n\n return responseMessages;\n };\n\n const result = streamFn();\n\n return stream ? [] : result;\n };\n\n const getAvailableModels: ChatService['getAvailableModels'] = async () => {\n return models.map(x => x.id);\n };\n\n const getConversation: ChatService['getConversation'] = async (\n options: GetConversationOptions,\n ) => {\n const { conversationId, userEntityRef } = options;\n\n const conversation = await chatStore.getChatMessages(\n conversationId,\n userEntityRef,\n );\n\n return conversation;\n };\n\n const getConversations: ChatService['getConversations'] = async ({\n userEntityRef,\n }: GetConversationsOptions) => {\n const conversations = await chatStore.getConversations(userEntityRef);\n\n return conversations;\n };\n\n const scoreMessage: ChatService['scoreMessage'] = async (\n messageId: string,\n score: number,\n ) => {\n const message = await chatStore.getMessageById(messageId);\n\n if (!message) {\n throw new Error(`Message with id ${messageId} not found`);\n }\n\n const updatedMessage: Required<Message> = {\n ...message,\n score,\n };\n\n await chatStore.updateMessage(updatedMessage);\n logger.info(`Message ${messageId} scored ${score}`);\n };\n\n return {\n prompt,\n getAvailableModels,\n getConversation,\n getConversations,\n addMessages,\n scoreMessage,\n };\n};\n\nasync function getUser(\n cache: CacheService,\n userEntityRef: string,\n credentials: BackstageCredentials,\n catalog: CatalogService,\n) {\n const cached = await cache.get(userEntityRef);\n\n if (cached) {\n return JSON.parse(String(cached));\n }\n\n const user = (await catalog.getEntityByRef(userEntityRef, {\n credentials,\n })) as UserEntity | undefined;\n await cache.set(userEntityRef, JSON.stringify(user));\n\n return user;\n}\n"],"names":["tools","DEFAULT_IDENTITY_PROMPT","DEFAULT_FORMATTING_PROMPT","DEFAULT_SYSTEM_PROMPT","DEFAULT_TOOL_GUIDELINE","chatStore","ChatStore","summarizer","createSummarizerService","SystemMessagePromptTemplate","conversation","DynamicStructuredTool","createReactAgent","uuid"],"mappings":"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"}
|
|
@@ -4,7 +4,6 @@ var express = require('express');
|
|
|
4
4
|
var Router = require('express-promise-router');
|
|
5
5
|
var chat = require('./chat.cjs.js');
|
|
6
6
|
var models = require('./models.cjs.js');
|
|
7
|
-
var pageSummary = require('./page-summary.cjs.js');
|
|
8
7
|
var rootHttpRouter = require('@backstage/backend-defaults/rootHttpRouter');
|
|
9
8
|
var mcp = require('./mcp.cjs.js');
|
|
10
9
|
|
|
@@ -19,7 +18,6 @@ async function createRouter(options) {
|
|
|
19
18
|
router.use("/chat", await chat.createChatRouter(options));
|
|
20
19
|
router.use("/models", await models.createModelRouter(options));
|
|
21
20
|
router.use("/mcp", await mcp.createMcpRouter(options));
|
|
22
|
-
router.use("/page-summary", await pageSummary.createPageSummaryRouter(options));
|
|
23
21
|
const middleware = rootHttpRouter.MiddlewareFactory.create(options);
|
|
24
22
|
router.use(middleware.error());
|
|
25
23
|
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 {
|
|
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 { createMcpRouter, McpRouterOptions } from './mcp';\n\nexport type RouterOptions = ChatRouterOptions &\n McpRouterOptions & {\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('/mcp', await createMcpRouter(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","createMcpRouter","MiddlewareFactory"],"mappings":";;;;;;;;;;;;;;AAiBA,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,MAAA,EAAQ,MAAMC,mBAAA,CAAgB,OAAO,CAAC,CAAA;AAEjD,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,58 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
var prompts = require('../constants/prompts.cjs.js');
|
|
4
4
|
var prompts$1 = require('@langchain/core/prompts');
|
|
5
|
-
var contentCleaner = require('../utils/content-cleaner.cjs.js');
|
|
6
|
-
var langchain = require('@langfuse/langchain');
|
|
7
5
|
|
|
8
6
|
const createSummarizerService = async ({
|
|
9
7
|
config,
|
|
10
8
|
models,
|
|
11
|
-
|
|
12
|
-
logger
|
|
9
|
+
callback
|
|
13
10
|
}) => {
|
|
14
11
|
const summaryModelId = config.getOptionalString("aiAssistant.conversation.summaryModel") ?? models[0].id;
|
|
15
12
|
const summaryPrompt = config.getOptionalString("aiAssistant.conversation.summaryPrompt") ?? prompts.DEFAULT_SUMMARY_PROMPT;
|
|
16
|
-
const pageSummaryPrompt = config.getOptionalString("aiAssistant.page.summaryPrompt") ?? prompts.DEFAULT_PAGE_SUMMARY_PROMPT;
|
|
17
13
|
const model = models.find((m) => m.id === summaryModelId);
|
|
18
14
|
if (!model) {
|
|
19
15
|
throw new Error(`Summary model with id ${summaryModelId} not found`);
|
|
20
16
|
}
|
|
21
17
|
const llm = model.chatModel;
|
|
22
|
-
const langfuseHandler = langfuseEnabled ? new langchain.CallbackHandler({
|
|
23
|
-
userId: "summarizer",
|
|
24
|
-
tags: ["backstage-ai-assistant", "summarizer"]
|
|
25
|
-
}) : void 0;
|
|
26
18
|
const chatPromptTemplate = prompts$1.ChatPromptTemplate.fromMessages([
|
|
27
19
|
prompts$1.SystemMessagePromptTemplate.fromTemplate(`
|
|
28
20
|
PURPOSE:
|
|
29
21
|
{summaryPrompt}
|
|
30
|
-
|
|
22
|
+
|
|
31
23
|
Please summarize the following conversation in {summaryLength}.
|
|
32
24
|
`),
|
|
33
25
|
prompts$1.HumanMessagePromptTemplate.fromTemplate(`
|
|
34
26
|
Conversation:
|
|
35
27
|
{conversation}
|
|
36
|
-
|
|
28
|
+
|
|
37
29
|
Please provide a summary of this conversation.
|
|
38
30
|
`)
|
|
39
31
|
]);
|
|
40
|
-
const pagePromptTemplate = prompts$1.ChatPromptTemplate.fromMessages([
|
|
41
|
-
prompts$1.SystemMessagePromptTemplate.fromTemplate(`
|
|
42
|
-
PURPOSE:
|
|
43
|
-
{pageSummaryPrompt}
|
|
44
|
-
|
|
45
|
-
Please summarize the following page content in {summaryLength}.
|
|
46
|
-
`),
|
|
47
|
-
prompts$1.HumanMessagePromptTemplate.fromTemplate(`
|
|
48
|
-
Page Title: {pageTitle}
|
|
49
|
-
Page URL: {pageUrl}
|
|
50
|
-
|
|
51
|
-
Content:
|
|
52
|
-
{pageContent}
|
|
53
|
-
|
|
54
|
-
Please provide a summary of this page.
|
|
55
|
-
`)
|
|
56
|
-
]);
|
|
57
32
|
const summarize = async (messages, summaryLength = "as few words as possible") => {
|
|
58
33
|
const conversationMessages = messages.filter(
|
|
59
34
|
(msg) => msg.role === "ai" || msg.role === "human"
|
|
@@ -63,59 +38,19 @@ const createSummarizerService = async ({
|
|
|
63
38
|
summaryLength,
|
|
64
39
|
conversation: conversationMessages.map((msg) => `${msg.role}: ${msg.content}`).join("\n")
|
|
65
40
|
});
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
};
|
|
76
|
-
const summarizePage = async (pageContent, pageUrl = "", pageTitle = "", summaryLength = "as few words as possible") => {
|
|
77
|
-
logger.info(
|
|
78
|
-
`[Page Summary] Starting summarization for content: ${pageContent.length} characters`
|
|
79
|
-
);
|
|
80
|
-
const cleanedContent = contentCleaner.cleanPageContent(pageContent);
|
|
81
|
-
logger.info(
|
|
82
|
-
`[Page Summary] Cleaned content: ${cleanedContent.length} characters`
|
|
83
|
-
);
|
|
84
|
-
const prompt = await pagePromptTemplate.formatMessages({
|
|
85
|
-
pageSummaryPrompt,
|
|
86
|
-
summaryLength,
|
|
87
|
-
pageTitle,
|
|
88
|
-
pageUrl,
|
|
89
|
-
pageContent: cleanedContent
|
|
41
|
+
const { callbacks, metadata } = await callback.getAgentCallbackData({
|
|
42
|
+
conversationId: "summarizer",
|
|
43
|
+
userId: "system",
|
|
44
|
+
modelId: summaryModelId
|
|
45
|
+
});
|
|
46
|
+
const { text } = await llm.invoke(prompt, {
|
|
47
|
+
callbacks,
|
|
48
|
+
runName: "summarizer",
|
|
49
|
+
metadata
|
|
90
50
|
});
|
|
91
|
-
|
|
92
|
-
runName: "page-summarizer",
|
|
93
|
-
tags: ["summarizer", "page-summary"],
|
|
94
|
-
timeout: 6e4
|
|
95
|
-
// 60 second timeout at the LLM level
|
|
96
|
-
};
|
|
97
|
-
if (langfuseEnabled) {
|
|
98
|
-
invokeOptions.callbacks = [langfuseHandler];
|
|
99
|
-
}
|
|
100
|
-
logger.info(`[Page Summary] Invoking LLM for summarization...`);
|
|
101
|
-
const startTime = Date.now();
|
|
102
|
-
try {
|
|
103
|
-
const { text } = await llm.invoke(prompt, invokeOptions);
|
|
104
|
-
const endTime = Date.now();
|
|
105
|
-
logger.info(
|
|
106
|
-
`[Page Summary] \u2705 Summarization completed in ${endTime - startTime}ms`
|
|
107
|
-
);
|
|
108
|
-
return text.trim();
|
|
109
|
-
} catch (error) {
|
|
110
|
-
const endTime = Date.now();
|
|
111
|
-
console.error(
|
|
112
|
-
`[Page Summary] \u274C Summarization failed after ${endTime - startTime}ms:`,
|
|
113
|
-
error
|
|
114
|
-
);
|
|
115
|
-
throw error;
|
|
116
|
-
}
|
|
51
|
+
return text.trim();
|
|
117
52
|
};
|
|
118
|
-
return { summarize
|
|
53
|
+
return { summarize };
|
|
119
54
|
};
|
|
120
55
|
|
|
121
56
|
exports.createSummarizerService = createSummarizerService;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summarizer.cjs.js","sources":["../../src/services/summarizer.ts"],"sourcesContent":["import {
|
|
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 { DEFAULT_SUMMARY_PROMPT } from '../constants/prompts';\nimport {\n SystemMessagePromptTemplate,\n HumanMessagePromptTemplate,\n ChatPromptTemplate,\n} from '@langchain/core/prompts';\nimport { Message } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { CallbackService } from './callbacks';\n\ntype SummarizerService = {\n summarize: (\n conversationMessages: Message[],\n summaryLength?: 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 summaryPrompt =\n config.getOptionalString('aiAssistant.conversation.summaryPrompt') ??\n DEFAULT_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 chatPromptTemplate = ChatPromptTemplate.fromMessages([\n SystemMessagePromptTemplate.fromTemplate(`\n PURPOSE:\n {summaryPrompt}\n\n Please summarize the following conversation in {summaryLength}.\n `),\n HumanMessagePromptTemplate.fromTemplate(`\n Conversation:\n {conversation}\n\n Please provide a summary of this conversation.\n `),\n ]);\n\n const summarize: SummarizerService['summarize'] = async (\n messages,\n summaryLength = 'as few words as possible',\n ) => {\n const conversationMessages = messages.filter(\n msg => msg.role === 'ai' || msg.role === 'human',\n );\n\n const prompt = await chatPromptTemplate.formatMessages({\n summaryPrompt,\n summaryLength,\n conversation: conversationMessages\n .map(msg => `${msg.role}: ${msg.content}`)\n .join('\\n'),\n });\n\n const { callbacks, metadata } = await callback.getAgentCallbackData({\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 return { summarize };\n};\n"],"names":["DEFAULT_SUMMARY_PROMPT","ChatPromptTemplate","SystemMessagePromptTemplate","HumanMessagePromptTemplate"],"mappings":";;;;;AAwBO,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,aAAA,GACJ,MAAA,CAAO,iBAAA,CAAkB,wCAAwC,CAAA,IACjEA,8BAAA;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,kBAAA,GAAqBC,6BAAmB,YAAA,CAAa;AAAA,IACzDC,sCAA4B,YAAA,CAAa;AAAA;AAAA;;AAAA;AAAA,IAAA,CAKxC,CAAA;AAAA,IACDC,qCAA2B,YAAA,CAAa;AAAA;AAAA;;AAAA;AAAA,IAAA,CAKvC;AAAA,GACF,CAAA;AAED,EAAA,MAAM,SAAA,GAA4C,OAChD,QAAA,EACA,aAAA,GAAgB,0BAAA,KACb;AACH,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,MAAA,GAAS,MAAM,kBAAA,CAAmB,cAAA,CAAe;AAAA,MACrD,aAAA;AAAA,MACA,aAAA;AAAA,MACA,YAAA,EAAc,oBAAA,CACX,GAAA,CAAI,CAAA,GAAA,KAAO,CAAA,EAAG,GAAA,CAAI,IAAI,CAAA,EAAA,EAAK,GAAA,CAAI,OAAO,CAAA,CAAE,CAAA,CACxC,KAAK,IAAI;AAAA,KACb,CAAA;AAED,IAAA,MAAM,EAAE,SAAA,EAAW,QAAA,EAAS,GAAI,MAAM,SAAS,oBAAA,CAAqB;AAAA,MAClE,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,OAAO,EAAE,SAAA,EAAU;AACrB;;;;"}
|
|
@@ -10,7 +10,7 @@ var z__default = /*#__PURE__*/_interopDefaultCompat(z);
|
|
|
10
10
|
const createSearchKnowledgeTool = ({
|
|
11
11
|
vectorStore
|
|
12
12
|
}) => {
|
|
13
|
-
|
|
13
|
+
const knowledgeTool = backstagePluginAiAssistantNode.createAssistantTool({
|
|
14
14
|
tool: {
|
|
15
15
|
name: "search-knowledge-base",
|
|
16
16
|
description: `Search the internal knowledge base containing company specific information.
|
|
@@ -40,6 +40,7 @@ Do NOT use for general knowledge that doesn't require company-specific informati
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
|
+
return knowledgeTool;
|
|
43
44
|
};
|
|
44
45
|
|
|
45
46
|
exports.createSearchKnowledgeTool = createSearchKnowledgeTool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"searchKnowledge.cjs.js","sources":["../../../src/services/tools/searchKnowledge.ts"],"sourcesContent":["import {\n createAssistantTool,\n Tool,\n VectorStore,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport z from 'zod';\n\ntype CreateSearchKnowledgeToolOptions = {\n vectorStore: VectorStore;\n};\n\nexport const createSearchKnowledgeTool = ({\n vectorStore,\n}: CreateSearchKnowledgeToolOptions): Tool => {\n
|
|
1
|
+
{"version":3,"file":"searchKnowledge.cjs.js","sources":["../../../src/services/tools/searchKnowledge.ts"],"sourcesContent":["import {\n createAssistantTool,\n Tool,\n VectorStore,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-node';\nimport z from 'zod';\n\ntype CreateSearchKnowledgeToolOptions = {\n vectorStore: VectorStore;\n};\n\nexport const createSearchKnowledgeTool = ({\n vectorStore,\n}: CreateSearchKnowledgeToolOptions): Tool => {\n const knowledgeTool = createAssistantTool({\n tool: {\n name: 'search-knowledge-base',\n description: `Search the internal knowledge base containing company specific information.\n\nUse this tool when users ask about:\n- General questions about the company or internal information\n\nDo NOT use for general knowledge that doesn't require company-specific information.`,\n schema: z.object({\n query: z.string().describe('The query to search for.'),\n filter: z\n .object({\n source: z.string().optional().describe('Source to filter by.'),\n id: z.string().optional().describe('ID to filter by.'),\n })\n .optional()\n .describe('Filters to apply to the search.'),\n amount: z\n .number()\n .min(1)\n .optional()\n .describe('The number of results to return.'),\n }),\n func: async ({ query, filter, amount }) => {\n const results = await vectorStore.similaritySearch(\n query,\n filter,\n amount,\n );\n if (results.length === 0) {\n return 'No relevant information found.';\n }\n return results.map(r => r.content).join('\\n---\\n');\n },\n },\n });\n\n return knowledgeTool;\n};\n"],"names":["createAssistantTool","z"],"mappings":";;;;;;;;;AAWO,MAAM,4BAA4B,CAAC;AAAA,EACxC;AACF,CAAA,KAA8C;AAC5C,EAAA,MAAM,gBAAgBA,kDAAA,CAAoB;AAAA,IACxC,IAAA,EAAM;AAAA,MACJ,IAAA,EAAM,uBAAA;AAAA,MACN,WAAA,EAAa,CAAA;;AAAA;AAAA;;AAAA,mFAAA,CAAA;AAAA,MAMb,MAAA,EAAQC,mBAAE,MAAA,CAAO;AAAA,QACf,KAAA,EAAOA,kBAAA,CAAE,MAAA,EAAO,CAAE,SAAS,0BAA0B,CAAA;AAAA,QACrD,MAAA,EAAQA,mBACL,MAAA,CAAO;AAAA,UACN,QAAQA,kBAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,sBAAsB,CAAA;AAAA,UAC7D,IAAIA,kBAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,SAAS,kBAAkB;AAAA,SACtD,CAAA,CACA,QAAA,EAAS,CACT,SAAS,iCAAiC,CAAA;AAAA,QAC7C,MAAA,EAAQA,kBAAA,CACL,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA,EAAS,CACT,QAAA,CAAS,kCAAkC;AAAA,OAC/C,CAAA;AAAA,MACD,MAAM,OAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,QAAO,KAAM;AACzC,QAAA,MAAM,OAAA,GAAU,MAAM,WAAA,CAAY,gBAAA;AAAA,UAChC,KAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACF;AACA,QAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,UAAA,OAAO,gCAAA;AAAA,QACT;AACA,QAAA,OAAO,QAAQ,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,OAAO,CAAA,CAAE,KAAK,SAAS,CAAA;AAAA,MACnD;AAAA;AACF,GACD,CAAA;AAED,EAAA,OAAO,aAAA;AACT;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sweetoburrito/backstage-plugin-ai-assistant-backend",
|
|
3
|
-
"version": "0.0.0-snapshot-
|
|
3
|
+
"version": "0.0.0-snapshot-20251113134620",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -43,14 +43,8 @@
|
|
|
43
43
|
"@langchain/langgraph": "^0.4.9",
|
|
44
44
|
"@langchain/mcp-adapters": "^1.0.0",
|
|
45
45
|
"@langchain/textsplitters": "^0.1.0",
|
|
46
|
-
"@
|
|
47
|
-
"@
|
|
48
|
-
"@langfuse/langchain": "^4.0.0",
|
|
49
|
-
"@langfuse/otel": "^4.3.0",
|
|
50
|
-
"@opentelemetry/api": "^1.9.0",
|
|
51
|
-
"@opentelemetry/sdk-node": "^0.207.0",
|
|
52
|
-
"@sweetoburrito/backstage-plugin-ai-assistant-common": "0.0.0-snapshot-20251030154701",
|
|
53
|
-
"@sweetoburrito/backstage-plugin-ai-assistant-node": "0.0.0-snapshot-20251030154701",
|
|
46
|
+
"@sweetoburrito/backstage-plugin-ai-assistant-common": "0.0.0-snapshot-20251113134620",
|
|
47
|
+
"@sweetoburrito/backstage-plugin-ai-assistant-node": "0.0.0-snapshot-20251113134620",
|
|
54
48
|
"express": "^4.17.1",
|
|
55
49
|
"express-promise-router": "^4.1.0",
|
|
56
50
|
"knex": "^3.1.0",
|
|
@@ -66,6 +60,7 @@
|
|
|
66
60
|
"@backstage/plugin-events-backend": "backstage:^",
|
|
67
61
|
"@backstage/plugin-signals-backend": "backstage:^",
|
|
68
62
|
"@backstage/types": "backstage:^",
|
|
63
|
+
"@sweetoburrito/backstage-plugin-ai-assistant-backend-module-callback-provider-langfuse": "workspace:^",
|
|
69
64
|
"@sweetoburrito/backstage-plugin-ai-assistant-backend-module-embeddings-provider-azure-open-ai": "workspace:^",
|
|
70
65
|
"@sweetoburrito/backstage-plugin-ai-assistant-backend-module-embeddings-provider-ollama": "workspace:^",
|
|
71
66
|
"@sweetoburrito/backstage-plugin-ai-assistant-backend-module-ingestor-azure-devops": "workspace:^",
|
|
@@ -74,7 +69,6 @@
|
|
|
74
69
|
"@sweetoburrito/backstage-plugin-ai-assistant-backend-module-model-provider-azure-ai": "workspace:^",
|
|
75
70
|
"@sweetoburrito/backstage-plugin-ai-assistant-backend-module-model-provider-google-vertex-ai": "workspace:^",
|
|
76
71
|
"@sweetoburrito/backstage-plugin-ai-assistant-backend-module-model-provider-ollama": "workspace:^",
|
|
77
|
-
"@sweetoburrito/backstage-plugin-ai-assistant-backend-module-realtime-voice-azure": "workspace:^",
|
|
78
72
|
"@sweetoburrito/backstage-plugin-ai-assistant-backend-module-tool-provider-backstage": "workspace:^",
|
|
79
73
|
"@types/express": "^4.0.0",
|
|
80
74
|
"@types/supertest": "^2.0.12",
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var otel = require('@langfuse/otel');
|
|
4
|
-
var sdkNode = require('@opentelemetry/sdk-node');
|
|
5
|
-
var client = require('@langfuse/client');
|
|
6
|
-
|
|
7
|
-
function initLangfuse(config, logger) {
|
|
8
|
-
const langfuseSecret = config.getOptionalString(
|
|
9
|
-
"aiAssistant.langfuse.secretKey"
|
|
10
|
-
);
|
|
11
|
-
const langfusePublic = config.getOptionalString(
|
|
12
|
-
"aiAssistant.langfuse.publicKey"
|
|
13
|
-
);
|
|
14
|
-
const langfuseBaseUrl = config.getOptionalString(
|
|
15
|
-
"aiAssistant.langfuse.baseUrl"
|
|
16
|
-
);
|
|
17
|
-
let langfuseSpanProcessor = void 0;
|
|
18
|
-
if (langfuseSecret && langfusePublic && langfuseBaseUrl) {
|
|
19
|
-
langfuseSpanProcessor = new otel.LangfuseSpanProcessor({
|
|
20
|
-
secretKey: langfuseSecret,
|
|
21
|
-
publicKey: langfusePublic,
|
|
22
|
-
baseUrl: langfuseBaseUrl
|
|
23
|
-
});
|
|
24
|
-
const sdk = new sdkNode.NodeSDK({
|
|
25
|
-
spanProcessors: [langfuseSpanProcessor]
|
|
26
|
-
});
|
|
27
|
-
const langfuseClient = new client.LangfuseClient({
|
|
28
|
-
secretKey: langfuseSecret,
|
|
29
|
-
publicKey: langfusePublic,
|
|
30
|
-
baseUrl: langfuseBaseUrl
|
|
31
|
-
});
|
|
32
|
-
logger.info(
|
|
33
|
-
"Langfuse: Starting OpenTelemetry SDK with LangfuseSpanProcessor"
|
|
34
|
-
);
|
|
35
|
-
sdk.start();
|
|
36
|
-
return { langfuseEnabled: true, langfuseClient };
|
|
37
|
-
}
|
|
38
|
-
logger.info(
|
|
39
|
-
"Langfuse: Skipping Langfuse initialization, credentials not found."
|
|
40
|
-
);
|
|
41
|
-
return {
|
|
42
|
-
langfuseEnabled: false,
|
|
43
|
-
langfuseClient: void 0
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
exports.initLangfuse = initLangfuse;
|
|
48
|
-
//# sourceMappingURL=langfuse.cjs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"langfuse.cjs.js","sources":["../../src/services/langfuse.ts"],"sourcesContent":["import {\n RootConfigService,\n RootLoggerService,\n} from '@backstage/backend-plugin-api';\nimport { LangfuseSpanProcessor } from '@langfuse/otel';\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport { LangfuseClient } from '@langfuse/client';\n\nexport function initLangfuse(\n config: RootConfigService,\n logger: RootLoggerService,\n): { langfuseEnabled: boolean; langfuseClient: LangfuseClient | undefined } {\n const langfuseSecret = config.getOptionalString(\n 'aiAssistant.langfuse.secretKey',\n );\n const langfusePublic = config.getOptionalString(\n 'aiAssistant.langfuse.publicKey',\n );\n const langfuseBaseUrl = config.getOptionalString(\n 'aiAssistant.langfuse.baseUrl',\n );\n\n let langfuseSpanProcessor: LangfuseSpanProcessor | undefined = undefined;\n if (langfuseSecret && langfusePublic && langfuseBaseUrl) {\n langfuseSpanProcessor = new LangfuseSpanProcessor({\n secretKey: langfuseSecret,\n publicKey: langfusePublic,\n baseUrl: langfuseBaseUrl,\n });\n\n const sdk = new NodeSDK({\n spanProcessors: [langfuseSpanProcessor],\n });\n\n const langfuseClient = new LangfuseClient({\n secretKey: langfuseSecret,\n publicKey: langfusePublic,\n baseUrl: langfuseBaseUrl,\n });\n\n logger.info(\n 'Langfuse: Starting OpenTelemetry SDK with LangfuseSpanProcessor',\n );\n sdk.start();\n return { langfuseEnabled: true, langfuseClient };\n }\n logger.info(\n 'Langfuse: Skipping Langfuse initialization, credentials not found.',\n );\n return {\n langfuseEnabled: false,\n langfuseClient: undefined,\n };\n}\n"],"names":["LangfuseSpanProcessor","NodeSDK","LangfuseClient"],"mappings":";;;;;;AAQO,SAAS,YAAA,CACd,QACA,MAAA,EAC0E;AAC1E,EAAA,MAAM,iBAAiB,MAAA,CAAO,iBAAA;AAAA,IAC5B;AAAA,GACF;AACA,EAAA,MAAM,iBAAiB,MAAA,CAAO,iBAAA;AAAA,IAC5B;AAAA,GACF;AACA,EAAA,MAAM,kBAAkB,MAAA,CAAO,iBAAA;AAAA,IAC7B;AAAA,GACF;AAEA,EAAA,IAAI,qBAAA,GAA2D,MAAA;AAC/D,EAAA,IAAI,cAAA,IAAkB,kBAAkB,eAAA,EAAiB;AACvD,IAAA,qBAAA,GAAwB,IAAIA,0BAAA,CAAsB;AAAA,MAChD,SAAA,EAAW,cAAA;AAAA,MACX,SAAA,EAAW,cAAA;AAAA,MACX,OAAA,EAAS;AAAA,KACV,CAAA;AAED,IAAA,MAAM,GAAA,GAAM,IAAIC,eAAA,CAAQ;AAAA,MACtB,cAAA,EAAgB,CAAC,qBAAqB;AAAA,KACvC,CAAA;AAED,IAAA,MAAM,cAAA,GAAiB,IAAIC,qBAAA,CAAe;AAAA,MACxC,SAAA,EAAW,cAAA;AAAA,MACX,SAAA,EAAW,cAAA;AAAA,MACX,OAAA,EAAS;AAAA,KACV,CAAA;AAED,IAAA,MAAA,CAAO,IAAA;AAAA,MACL;AAAA,KACF;AACA,IAAA,GAAA,CAAI,KAAA,EAAM;AACV,IAAA,OAAO,EAAE,eAAA,EAAiB,IAAA,EAAM,cAAA,EAAe;AAAA,EACjD;AACA,EAAA,MAAA,CAAO,IAAA;AAAA,IACL;AAAA,GACF;AACA,EAAA,OAAO;AAAA,IACL,eAAA,EAAiB,KAAA;AAAA,IACjB,cAAA,EAAgB;AAAA,GAClB;AACF;;;;"}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var Router = require('express-promise-router');
|
|
4
|
-
var z = require('zod');
|
|
5
|
-
var validation = require('./middleware/validation.cjs.js');
|
|
6
|
-
var summarizer = require('../summarizer.cjs.js');
|
|
7
|
-
|
|
8
|
-
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
9
|
-
|
|
10
|
-
var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
11
|
-
var z__default = /*#__PURE__*/_interopDefaultCompat(z);
|
|
12
|
-
|
|
13
|
-
function preprocessContentForSummarization(content) {
|
|
14
|
-
let processed = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
15
|
-
processed = processed.replace(/[ \t]{2,}/g, " ");
|
|
16
|
-
processed = processed.replace(/\n{3,}/g, "\n\n");
|
|
17
|
-
if (processed.length > 4e4) {
|
|
18
|
-
const lines = processed.split("\n");
|
|
19
|
-
const importantLines = [];
|
|
20
|
-
for (const line of lines) {
|
|
21
|
-
const trimmed = line.trim();
|
|
22
|
-
if (trimmed.length > 10 && !trimmed.match(/^[^\w]*$/) && !importantLines.some(
|
|
23
|
-
(existing) => existing.toLowerCase().includes(trimmed.toLowerCase().substring(0, 50))
|
|
24
|
-
)) {
|
|
25
|
-
importantLines.push(line);
|
|
26
|
-
}
|
|
27
|
-
if (importantLines.join("\n").length > 35e3) {
|
|
28
|
-
break;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
processed = importantLines.join("\n");
|
|
32
|
-
}
|
|
33
|
-
return processed;
|
|
34
|
-
}
|
|
35
|
-
async function createPageSummaryRouter(options) {
|
|
36
|
-
const { httpAuth, userInfo, config, models, langfuseEnabled, logger } = options;
|
|
37
|
-
const router = Router__default.default();
|
|
38
|
-
const summarizer$1 = await summarizer.createSummarizerService({
|
|
39
|
-
config,
|
|
40
|
-
models,
|
|
41
|
-
langfuseEnabled,
|
|
42
|
-
logger
|
|
43
|
-
});
|
|
44
|
-
const pageSummarySchema = z__default.default.object({
|
|
45
|
-
pageContent: z__default.default.string().describe("The HTML/text content of the page to summarize"),
|
|
46
|
-
pageUrl: z__default.default.string().optional().describe("The URL of the page"),
|
|
47
|
-
pageTitle: z__default.default.string().optional().describe("The title of the page"),
|
|
48
|
-
summaryLength: z__default.default.string().optional().describe("Desired summary length instruction")
|
|
49
|
-
});
|
|
50
|
-
router.post(
|
|
51
|
-
"/summarize",
|
|
52
|
-
validation.validation(pageSummarySchema, "body"),
|
|
53
|
-
async (req, res) => {
|
|
54
|
-
try {
|
|
55
|
-
const { pageContent, pageUrl, pageTitle, summaryLength } = req.body;
|
|
56
|
-
req.setTimeout(12e4, () => {
|
|
57
|
-
console.error("Request timeout for page summary");
|
|
58
|
-
if (!res.headersSent) {
|
|
59
|
-
res.status(504).json({
|
|
60
|
-
success: false,
|
|
61
|
-
error: "Request timeout - summarization took too long"
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
const credentials = await httpAuth.credentials(req);
|
|
66
|
-
const { userEntityRef } = await userInfo.getUserInfo(credentials);
|
|
67
|
-
logger.info(`Page summary request from user: ${userEntityRef}`);
|
|
68
|
-
logger.info(`Page URL: ${pageUrl || "N/A"}`);
|
|
69
|
-
logger.info(`Page Title: ${pageTitle || "N/A"}`);
|
|
70
|
-
logger.info(`Content length: ${pageContent.length} characters`);
|
|
71
|
-
const maxContentLength = 5e4;
|
|
72
|
-
let processedContent = preprocessContentForSummarization(pageContent);
|
|
73
|
-
if (processedContent.length > maxContentLength) {
|
|
74
|
-
logger.info(
|
|
75
|
-
`Content too large (${processedContent.length} chars), truncating to ${maxContentLength} chars`
|
|
76
|
-
);
|
|
77
|
-
processedContent = `${processedContent.substring(
|
|
78
|
-
0,
|
|
79
|
-
maxContentLength
|
|
80
|
-
)}
|
|
81
|
-
|
|
82
|
-
[Content truncated due to length]`;
|
|
83
|
-
}
|
|
84
|
-
logger.info(
|
|
85
|
-
`Content preprocessing: ${pageContent.length} -> ${processedContent.length} chars`
|
|
86
|
-
);
|
|
87
|
-
const summaryPromise = summarizer$1.summarizePage(
|
|
88
|
-
processedContent,
|
|
89
|
-
pageUrl,
|
|
90
|
-
pageTitle,
|
|
91
|
-
summaryLength || "in 2-3 sentences"
|
|
92
|
-
);
|
|
93
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
94
|
-
setTimeout(() => reject(new Error("Summarization timeout")), 9e4);
|
|
95
|
-
});
|
|
96
|
-
const summary = await Promise.race([summaryPromise, timeoutPromise]);
|
|
97
|
-
res.json({
|
|
98
|
-
success: true,
|
|
99
|
-
summary,
|
|
100
|
-
originalContentLength: pageContent.length,
|
|
101
|
-
processedContentLength: processedContent.length,
|
|
102
|
-
contentTruncated: pageContent.length > maxContentLength,
|
|
103
|
-
pageUrl,
|
|
104
|
-
pageTitle
|
|
105
|
-
});
|
|
106
|
-
} catch (error) {
|
|
107
|
-
console.error("Error generating page summary:", error);
|
|
108
|
-
if (!res.headersSent) {
|
|
109
|
-
if (error instanceof Error && error.message.includes("timeout")) {
|
|
110
|
-
res.status(504).json({
|
|
111
|
-
success: false,
|
|
112
|
-
error: "Summarization request timed out. Please try with shorter content."
|
|
113
|
-
});
|
|
114
|
-
} else {
|
|
115
|
-
res.status(500).json({
|
|
116
|
-
success: false,
|
|
117
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
);
|
|
124
|
-
return router;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
exports.createPageSummaryRouter = createPageSummaryRouter;
|
|
128
|
-
//# sourceMappingURL=page-summary.cjs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"page-summary.cjs.js","sources":["../../../src/services/router/page-summary.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport z from 'zod';\nimport { validation } from './middleware/validation';\nimport {\n DatabaseService,\n HttpAuthService,\n UserInfoService,\n RootConfigService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { createSummarizerService } from '../summarizer';\nimport { Model } from '@sweetoburrito/backstage-plugin-ai-assistant-node';\n\n/**\n * Preprocess content for summarization to ensure optimal performance\n */\nfunction preprocessContentForSummarization(content: string): string {\n // Remove excessive whitespace and normalize line endings\n let processed = content.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n\n // Remove repeated whitespace patterns\n processed = processed.replace(/[ \\t]{2,}/g, ' ');\n processed = processed.replace(/\\n{3,}/g, '\\n\\n');\n\n // If content is still very large, focus on the most important parts\n if (processed.length > 40000) {\n const lines = processed.split('\\n');\n const importantLines: string[] = [];\n\n for (const line of lines) {\n const trimmed = line.trim();\n // Skip very short lines, repeated content, or obvious noise\n if (\n trimmed.length > 10 &&\n !trimmed.match(/^[^\\w]*$/) &&\n !importantLines.some(existing =>\n existing\n .toLowerCase()\n .includes(trimmed.toLowerCase().substring(0, 50)),\n )\n ) {\n importantLines.push(line);\n }\n\n // Stop if we have enough content\n if (importantLines.join('\\n').length > 35000) {\n break;\n }\n }\n\n processed = importantLines.join('\\n');\n }\n\n return processed;\n}\n\nexport type PageSummaryRouterOptions = {\n database: DatabaseService;\n httpAuth: HttpAuthService;\n userInfo: UserInfoService;\n config: RootConfigService;\n logger: LoggerService;\n models: Model[];\n langfuseEnabled: boolean;\n};\n\nexport async function createPageSummaryRouter(\n options: PageSummaryRouterOptions,\n): Promise<express.Router> {\n const { httpAuth, userInfo, config, models, langfuseEnabled, logger } =\n options;\n const router = Router();\n\n const summarizer = await createSummarizerService({\n config,\n models,\n langfuseEnabled,\n logger,\n });\n\n const pageSummarySchema = z.object({\n pageContent: z\n .string()\n .describe('The HTML/text content of the page to summarize'),\n pageUrl: z.string().optional().describe('The URL of the page'),\n pageTitle: z.string().optional().describe('The title of the page'),\n summaryLength: z\n .string()\n .optional()\n .describe('Desired summary length instruction'),\n });\n\n router.post(\n '/summarize',\n validation(pageSummarySchema, 'body'),\n async (req, res) => {\n try {\n const { pageContent, pageUrl, pageTitle, summaryLength } = req.body;\n\n // Set a longer timeout for this endpoint (2 minutes)\n req.setTimeout(120000, () => {\n console.error('Request timeout for page summary');\n if (!res.headersSent) {\n res.status(504).json({\n success: false,\n error: 'Request timeout - summarization took too long',\n });\n }\n });\n\n // Authenticate the request\n const credentials = await httpAuth.credentials(req);\n const { userEntityRef } = await userInfo.getUserInfo(credentials);\n\n logger.info(`Page summary request from user: ${userEntityRef}`);\n logger.info(`Page URL: ${pageUrl || 'N/A'}`);\n logger.info(`Page Title: ${pageTitle || 'N/A'}`);\n logger.info(`Content length: ${pageContent.length} characters`);\n\n // Preprocess and check content length\n const maxContentLength = 50000; // 50k characters\n let processedContent = preprocessContentForSummarization(pageContent);\n\n if (processedContent.length > maxContentLength) {\n logger.info(\n `Content too large (${processedContent.length} chars), truncating to ${maxContentLength} chars`,\n );\n processedContent = `${processedContent.substring(\n 0,\n maxContentLength,\n )}\\n\\n[Content truncated due to length]`;\n }\n\n logger.info(\n `Content preprocessing: ${pageContent.length} -> ${processedContent.length} chars`,\n );\n\n // Add timeout protection for the summarization call\n const summaryPromise = summarizer.summarizePage(\n processedContent,\n pageUrl,\n pageTitle,\n summaryLength || 'in 2-3 sentences',\n );\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => reject(new Error('Summarization timeout')), 90000); // 90 seconds\n });\n\n const summary = await Promise.race([summaryPromise, timeoutPromise]);\n\n res.json({\n success: true,\n summary,\n originalContentLength: pageContent.length,\n processedContentLength: processedContent.length,\n contentTruncated: pageContent.length > maxContentLength,\n pageUrl,\n pageTitle,\n });\n } catch (error) {\n console.error('Error generating page summary:', error);\n\n if (!res.headersSent) {\n if (error instanceof Error && error.message.includes('timeout')) {\n res.status(504).json({\n success: false,\n error:\n 'Summarization request timed out. Please try with shorter content.',\n });\n } else {\n res.status(500).json({\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n });\n }\n }\n }\n },\n );\n\n return router;\n}\n"],"names":["Router","summarizer","createSummarizerService","z","validation"],"mappings":"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"}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
function cleanPageContent(content) {
|
|
4
|
-
if (content.length < 100) {
|
|
5
|
-
return content.trim();
|
|
6
|
-
}
|
|
7
|
-
let cleanedContent = content;
|
|
8
|
-
cleanedContent = cleanedContent.replace(
|
|
9
|
-
/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
|
|
10
|
-
""
|
|
11
|
-
);
|
|
12
|
-
cleanedContent = cleanedContent.replace(
|
|
13
|
-
/\s*on\w+\s*=\s*["'][^"']*["']/gi,
|
|
14
|
-
""
|
|
15
|
-
);
|
|
16
|
-
cleanedContent = cleanedContent.replace(/javascript:[^"'\s>]*/gi, "");
|
|
17
|
-
cleanedContent = cleanedContent.replace(
|
|
18
|
-
/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi,
|
|
19
|
-
""
|
|
20
|
-
);
|
|
21
|
-
cleanedContent = cleanedContent.replace(
|
|
22
|
-
/\s*style\s*=\s*["'][^"']*["']/gi,
|
|
23
|
-
""
|
|
24
|
-
);
|
|
25
|
-
cleanedContent = cleanedContent.replace(
|
|
26
|
-
/\s*class\s*=\s*["'][^"']*["']/gi,
|
|
27
|
-
""
|
|
28
|
-
);
|
|
29
|
-
cleanedContent = cleanedContent.replace(
|
|
30
|
-
/<(nav|header|footer|aside|script|style|noscript)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
31
|
-
""
|
|
32
|
-
);
|
|
33
|
-
cleanedContent = convertHtmlToStructuredText(cleanedContent);
|
|
34
|
-
cleanedContent = normalizeContentWhitespace(cleanedContent);
|
|
35
|
-
const maxFinalLength = 3e4;
|
|
36
|
-
if (cleanedContent.length > maxFinalLength) {
|
|
37
|
-
cleanedContent = `${cleanedContent.substring(
|
|
38
|
-
0,
|
|
39
|
-
maxFinalLength
|
|
40
|
-
)}
|
|
41
|
-
|
|
42
|
-
[Content truncated for processing efficiency]`;
|
|
43
|
-
}
|
|
44
|
-
return cleanedContent.trim();
|
|
45
|
-
}
|
|
46
|
-
function convertHtmlToStructuredText(html) {
|
|
47
|
-
let text = html;
|
|
48
|
-
text = text.replace(/<h([1-6])[^>]*>/gi, (_, level) => {
|
|
49
|
-
const hashes = "#".repeat(parseInt(level, 10));
|
|
50
|
-
return `
|
|
51
|
-
${hashes} `;
|
|
52
|
-
});
|
|
53
|
-
text = text.replace(/<\/h[1-6]>/gi, "\n\n");
|
|
54
|
-
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
55
|
-
text = text.replace(/<\/p>/gi, "\n\n");
|
|
56
|
-
text = text.replace(/<br[^>]*>/gi, "\n");
|
|
57
|
-
text = text.replace(/<ul[^>]*>/gi, "\n");
|
|
58
|
-
text = text.replace(/<\/ul>/gi, "\n");
|
|
59
|
-
text = text.replace(/<ol[^>]*>/gi, "\n");
|
|
60
|
-
text = text.replace(/<\/ol>/gi, "\n");
|
|
61
|
-
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
62
|
-
text = text.replace(/<\/li>/gi, "");
|
|
63
|
-
text = text.replace(/<(strong|b)[^>]*>/gi, "**");
|
|
64
|
-
text = text.replace(/<\/(strong|b)>/gi, "**");
|
|
65
|
-
text = text.replace(/<(em|i)[^>]*>/gi, "*");
|
|
66
|
-
text = text.replace(/<\/(em|i)>/gi, "*");
|
|
67
|
-
text = text.replace(/<a[^>]*href\s*=\s*["']([^"']*)["'][^>]*>/gi, "[");
|
|
68
|
-
text = text.replace(/<\/a>/gi, "]");
|
|
69
|
-
text = stripHtmlTags(text);
|
|
70
|
-
return text;
|
|
71
|
-
}
|
|
72
|
-
function stripHtmlTags(html) {
|
|
73
|
-
return html.replace(/<[^>]*>/g, "");
|
|
74
|
-
}
|
|
75
|
-
function normalizeContentWhitespace(text) {
|
|
76
|
-
return text.replace(/[ \t]+/g, " ").replace(/\n{3,}/g, "\n\n").replace(/^[ \t]+|[ \t]+$/gm, "").replace(/^\s*$/gm, "");
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
exports.cleanPageContent = cleanPageContent;
|
|
80
|
-
//# sourceMappingURL=content-cleaner.cjs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"content-cleaner.cjs.js","sources":["../../src/utils/content-cleaner.ts"],"sourcesContent":["/**\n * Content cleaning utilities for processing page content before summarization\n */\n\n/**\n * Clean page content by removing HTML markup, styling, and scripts\n * while preserving meaningful text content and structure.\n */\nexport function cleanPageContent(content: string): string {\n // Early return for very small content\n if (content.length < 100) {\n return content.trim();\n }\n\n let cleanedContent = content;\n\n // Remove JavaScript blocks and inline scripts (optimized for performance)\n cleanedContent = cleanedContent.replace(\n /<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi,\n '',\n );\n cleanedContent = cleanedContent.replace(\n /\\s*on\\w+\\s*=\\s*[\"'][^\"']*[\"']/gi,\n '',\n );\n cleanedContent = cleanedContent.replace(/javascript:[^\"'\\s>]*/gi, '');\n\n // Remove CSS style blocks and inline styles\n cleanedContent = cleanedContent.replace(\n /<style\\b[^<]*(?:(?!<\\/style>)<[^<]*)*<\\/style>/gi,\n '',\n );\n cleanedContent = cleanedContent.replace(\n /\\s*style\\s*=\\s*[\"'][^\"']*[\"']/gi,\n '',\n );\n cleanedContent = cleanedContent.replace(\n /\\s*class\\s*=\\s*[\"'][^\"']*[\"']/gi,\n '',\n );\n\n // Remove common noise elements early to reduce processing load\n cleanedContent = cleanedContent.replace(\n /<(nav|header|footer|aside|script|style|noscript)[^>]*>[\\s\\S]*?<\\/\\1>/gi,\n '',\n );\n\n // Convert HTML to structured text preserving important elements\n cleanedContent = convertHtmlToStructuredText(cleanedContent);\n\n // Normalize whitespace\n cleanedContent = normalizeContentWhitespace(cleanedContent);\n\n // Limit final content length to prevent excessive token usage\n const maxFinalLength = 30000; // 30k characters\n if (cleanedContent.length > maxFinalLength) {\n cleanedContent = `${cleanedContent.substring(\n 0,\n maxFinalLength,\n )}\\n\\n[Content truncated for processing efficiency]`;\n }\n\n return cleanedContent.trim();\n}\n\n/**\n * Convert HTML to structured text preserving headings, lists, and paragraphs\n */\nfunction convertHtmlToStructuredText(html: string): string {\n let text = html;\n\n // Convert headings\n text = text.replace(/<h([1-6])[^>]*>/gi, (_, level) => {\n const hashes = '#'.repeat(parseInt(level, 10));\n return `\\n${hashes} `;\n });\n text = text.replace(/<\\/h[1-6]>/gi, '\\n\\n');\n\n // Convert paragraphs\n text = text.replace(/<p[^>]*>/gi, '\\n');\n text = text.replace(/<\\/p>/gi, '\\n\\n');\n\n // Convert line breaks\n text = text.replace(/<br[^>]*>/gi, '\\n');\n\n // Convert lists\n text = text.replace(/<ul[^>]*>/gi, '\\n');\n text = text.replace(/<\\/ul>/gi, '\\n');\n text = text.replace(/<ol[^>]*>/gi, '\\n');\n text = text.replace(/<\\/ol>/gi, '\\n');\n text = text.replace(/<li[^>]*>/gi, '\\n- ');\n text = text.replace(/<\\/li>/gi, '');\n\n // Convert emphasis\n text = text.replace(/<(strong|b)[^>]*>/gi, '**');\n text = text.replace(/<\\/(strong|b)>/gi, '**');\n text = text.replace(/<(em|i)[^>]*>/gi, '*');\n text = text.replace(/<\\/(em|i)>/gi, '*');\n\n // Convert links (preserve URL in brackets)\n text = text.replace(/<a[^>]*href\\s*=\\s*[\"']([^\"']*)[\"'][^>]*>/gi, '[');\n text = text.replace(/<\\/a>/gi, ']');\n\n // Remove remaining HTML tags\n text = stripHtmlTags(text);\n\n return text;\n}\n\n/**\n * Simple HTML tag removal\n */\nfunction stripHtmlTags(html: string): string {\n return html.replace(/<[^>]*>/g, '');\n}\n\n/**\n * Normalize whitespace, removing excess spaces and line breaks\n */\nfunction normalizeContentWhitespace(text: string): string {\n return (\n text\n // Replace multiple spaces with single space\n .replace(/[ \\t]+/g, ' ')\n // Replace multiple line breaks with double line break (paragraph separation)\n .replace(/\\n{3,}/g, '\\n\\n')\n // Remove spaces at the beginning and end of lines\n .replace(/^[ \\t]+|[ \\t]+$/gm, '')\n // Remove empty lines that only contain whitespace\n .replace(/^\\s*$/gm, '')\n );\n}\n\n/**\n * Extract meaningful text content from HTML while removing noise\n */\nexport function extractTextContent(html: string): string {\n let text = html;\n\n // Remove comments\n text = text.replace(/<!--[\\s\\S]*?-->/g, '');\n\n // Remove common non-content elements by tag name\n text = text.replace(/<(nav|header|footer|aside)[^>]*>[\\s\\S]*?<\\/\\1>/gi, '');\n\n return cleanPageContent(text);\n}\n"],"names":[],"mappings":"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"}
|