webui: Agentic Loop + MCP Client with support for Tools, Resources and Prompts (#18655)

This commit is contained in:
Aleksander Grygier
2026-03-06 10:00:39 +01:00
committed by GitHub
parent 2850bc6a13
commit f6235a41ef
147 changed files with 15285 additions and 366 deletions
@@ -1,13 +1,19 @@
import { getJsonHeaders, formatAttachmentText, isAbortError } from '$lib/utils';
import { AGENTIC_REGEX, ATTACHMENT_LABEL_PDF_FILE } from '$lib/constants';
import {
AGENTIC_REGEX,
ATTACHMENT_LABEL_PDF_FILE,
ATTACHMENT_LABEL_MCP_PROMPT,
ATTACHMENT_LABEL_MCP_RESOURCE
} from '$lib/constants';
import {
AttachmentType,
ContentPartType,
MessageRole,
ReasoningFormat,
UrlPrefix
UrlProtocol
} from '$lib/enums';
import type { ApiChatMessageContentPart, ApiChatCompletionToolCall } from '$lib/types/api';
import type { DatabaseMessageExtraMcpPrompt, DatabaseMessageExtraMcpResource } from '$lib/types';
import { modelsStore } from '$lib/stores/models.svelte';
export class ChatService {
@@ -21,7 +27,9 @@ export class ChatService {
if (typeof content === 'string') {
return content
.replace(AGENTIC_REGEX.REASONING_BLOCK, '')
.replace(AGENTIC_REGEX.REASONING_OPEN, '');
.replace(AGENTIC_REGEX.REASONING_OPEN, '')
.replace(AGENTIC_REGEX.AGENTIC_TOOL_CALL_BLOCK, '')
.replace(AGENTIC_REGEX.AGENTIC_TOOL_CALL_OPEN, '');
}
if (!Array.isArray(content)) {
@@ -35,6 +43,8 @@ export class ChatService {
text: part.text
.replace(AGENTIC_REGEX.REASONING_BLOCK, '')
.replace(AGENTIC_REGEX.REASONING_OPEN, '')
.replace(AGENTIC_REGEX.AGENTIC_TOOL_CALL_BLOCK, '')
.replace(AGENTIC_REGEX.AGENTIC_TOOL_CALL_OPEN, '')
};
});
}
@@ -404,7 +414,7 @@ export class ChatService {
for (const line of lines) {
if (abortSignal?.aborted) break;
if (line.startsWith(UrlPrefix.DATA)) {
if (line.startsWith(UrlProtocol.DATA)) {
const data = line.slice(6);
if (data === '[DONE]') {
streamFinished = true;
@@ -755,11 +765,47 @@ export class ChatService {
}
}
const mcpPrompts = message.extra.filter(
(extra: DatabaseMessageExtra): extra is DatabaseMessageExtraMcpPrompt =>
extra.type === AttachmentType.MCP_PROMPT
);
for (const mcpPrompt of mcpPrompts) {
contentParts.push({
type: ContentPartType.TEXT,
text: formatAttachmentText(
ATTACHMENT_LABEL_MCP_PROMPT,
mcpPrompt.name,
mcpPrompt.content,
mcpPrompt.serverName
)
});
}
const mcpResources = message.extra.filter(
(extra: DatabaseMessageExtra): extra is DatabaseMessageExtraMcpResource =>
extra.type === AttachmentType.MCP_RESOURCE
);
for (const mcpResource of mcpResources) {
contentParts.push({
type: ContentPartType.TEXT,
text: formatAttachmentText(
ATTACHMENT_LABEL_MCP_RESOURCE,
mcpResource.name,
mcpResource.content,
mcpResource.serverName
)
});
}
const result: ApiChatMessageData = {
role: message.role as MessageRole,
content: contentParts
};
if (toolCalls && toolCalls.length > 0) {
result.tool_calls = toolCalls;
}
return result;
}