mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-11 20:05:19 +00:00
transform tool calls into proper messages
This commit is contained in:
parent
453ea9b9a1
commit
fc68071e1d
2 changed files with 128 additions and 51 deletions
|
|
@ -51,7 +51,7 @@
|
||||||
getMessageContentParts,
|
getMessageContentParts,
|
||||||
createMessagesList,
|
createMessagesList,
|
||||||
getPromptVariables,
|
getPromptVariables,
|
||||||
processDetails,
|
processDetailsAndExtractToolCalls,
|
||||||
removeAllDetails,
|
removeAllDetails,
|
||||||
getCodeBlockContents
|
getCodeBlockContents
|
||||||
} from '$lib/utils';
|
} from '$lib/utils';
|
||||||
|
|
@ -1865,46 +1865,73 @@
|
||||||
$settings?.params?.stream_response ??
|
$settings?.params?.stream_response ??
|
||||||
params?.stream_response ??
|
params?.stream_response ??
|
||||||
true;
|
true;
|
||||||
|
|
||||||
let messages = [
|
let messages = [];
|
||||||
params?.system || $settings.system
|
if (params?.system || $settings.system) {
|
||||||
? {
|
messages.push({
|
||||||
role: 'system',
|
role: 'system',
|
||||||
content: `${params?.system ?? $settings?.system ?? ''}`
|
content: `${params?.system ?? $settings?.system ?? ''}`
|
||||||
}
|
});
|
||||||
: undefined,
|
}
|
||||||
..._messages.map((message) => ({
|
|
||||||
...message,
|
|
||||||
content: processDetails(message.content)
|
|
||||||
}))
|
|
||||||
].filter((message) => message);
|
|
||||||
|
|
||||||
messages = messages
|
for (const message of _messages) {
|
||||||
.map((message, idx, arr) => ({
|
let content = message?.merged?.content ?? message?.content;
|
||||||
role: message.role,
|
let processedMessages = processDetailsAndExtractToolCalls(content ?? '');
|
||||||
...((message.files?.filter((file) => file.type === 'image').length > 0 ?? false) &&
|
let nonToolMesssage = null;
|
||||||
message.role === 'user'
|
let toolCallIndex = 0;
|
||||||
? {
|
|
||||||
content: [
|
for (const processedMessage of processedMessages) {
|
||||||
{
|
|
||||||
type: 'text',
|
if (typeof processedMessage == "string") {
|
||||||
text: message?.merged?.content ?? message.content
|
nonToolMesssage = {
|
||||||
},
|
role: message?.role,
|
||||||
...message.files
|
content: message?.role === 'user' ? processedMessage : processedMessage.trim()
|
||||||
.filter((file) => file.type === 'image')
|
};
|
||||||
.map((file) => ({
|
|
||||||
type: 'image_url',
|
if (message?.role === 'user' && (message.files?.filter((file) => file.type === 'image').length > 0 ?? false)) {
|
||||||
image_url: {
|
nonToolMesssage = {
|
||||||
url: file.url
|
...nonToolMesssage,
|
||||||
}
|
...message.files
|
||||||
}))
|
.filter((file) => file.type === 'image')
|
||||||
]
|
.map((file) => ({
|
||||||
|
type: 'image_url',
|
||||||
|
image_url: {
|
||||||
|
url: file.url
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
: {
|
}
|
||||||
content: message?.merged?.content ?? message.content
|
|
||||||
})
|
messages.push(nonToolMesssage);
|
||||||
}))
|
continue;
|
||||||
.filter((message) => message?.role === 'user' || message?.content?.trim());
|
}
|
||||||
|
|
||||||
|
if (!nonToolMesssage) {
|
||||||
|
nonToolMesssage = {
|
||||||
|
role: message?.role,
|
||||||
|
content: ''
|
||||||
|
};
|
||||||
|
messages.push(nonToolMesssage);
|
||||||
|
}
|
||||||
|
|
||||||
|
nonToolMesssage.tool_calls ??= [];
|
||||||
|
nonToolMesssage.tool_calls.push({
|
||||||
|
index: toolCallIndex++,
|
||||||
|
id: processedMessage.id,
|
||||||
|
type: 'function',
|
||||||
|
function: {
|
||||||
|
name: processedMessage.name,
|
||||||
|
arguments: processedMessage.arguments
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
messages.push({
|
||||||
|
role: 'tool',
|
||||||
|
tool_call_id: processedMessage.id,
|
||||||
|
content: processedMessage.result
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const toolIds = [];
|
const toolIds = [];
|
||||||
const toolServerIds = [];
|
const toolServerIds = [];
|
||||||
|
|
|
||||||
|
|
@ -856,28 +856,78 @@ export const removeAllDetails = (content) => {
|
||||||
return content;
|
return content;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const processDetails = (content) => {
|
// This regex matches <details> tags with type="tool_calls" and captures their attributes
|
||||||
content = removeDetails(content, ['reasoning', 'code_interpreter']);
|
const toolCallsDetailsRegex = /<details\s+type="tool_calls"([^>]*)>([\s\S]*?)<\/details>/gis;
|
||||||
|
const detailsAttributesRegex = /(\w+)="([^"]*)"/g;
|
||||||
|
|
||||||
// This regex matches <details> tags with type="tool_calls" and captures their attributes to convert them to a string
|
export const processDetailsAndExtractToolCalls = (content) => {
|
||||||
const detailsRegex = /<details\s+type="tool_calls"([^>]*)>([\s\S]*?)<\/details>/gis;
|
content = removeDetails(content, ['reasoning', 'code_interpreter']);
|
||||||
const matches = content.match(detailsRegex);
|
|
||||||
if (matches) {
|
// Split text and tool calls into messages array
|
||||||
|
let messages = [];
|
||||||
|
const matches = content.match(toolCallsDetailsRegex);
|
||||||
|
if (matches && matches.length > 0) {
|
||||||
|
let previousDetailsEndIndex = 0;
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
const attributesRegex = /(\w+)="([^"]*)"/g;
|
|
||||||
const attributes = {};
|
let detailsStartIndex = content.indexOf(match, previousDetailsEndIndex);
|
||||||
let attributeMatch;
|
let assistantMessage = content.substr(previousDetailsEndIndex, detailsStartIndex - previousDetailsEndIndex);
|
||||||
while ((attributeMatch = attributesRegex.exec(match)) !== null) {
|
previousDetailsEndIndex = detailsStartIndex + match.length;
|
||||||
attributes[attributeMatch[1]] = attributeMatch[2];
|
|
||||||
|
assistantMessage = assistantMessage.trim('\n');
|
||||||
|
if (assistantMessage.length > 0) {
|
||||||
|
messages.push(assistantMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
content = content.replace(match, `"${attributes.result}"`);
|
const attributes = {};
|
||||||
|
let attributeMatch;
|
||||||
|
while ((attributeMatch = detailsAttributesRegex.exec(match)) !== null) {
|
||||||
|
attributes[attributeMatch[1]] = attributeMatch[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!attributes.id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let toolCall = {
|
||||||
|
id: attributes.id,
|
||||||
|
name: attributes.name,
|
||||||
|
arguments: unescapeHtml(attributes.arguments ?? ''),
|
||||||
|
result: unescapeHtml(attributes.result ?? '')
|
||||||
|
}
|
||||||
|
|
||||||
|
toolCall.arguments = parseDoubleEncodedString(toolCall.arguments);
|
||||||
|
toolCall.result = parseDoubleEncodedString(toolCall.result);
|
||||||
|
|
||||||
|
messages.push(toolCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
let finalAssistantMessage = content.substr(previousDetailsEndIndex);
|
||||||
|
finalAssistantMessage = finalAssistantMessage.trim('\n');
|
||||||
|
if (finalAssistantMessage.length > 0) {
|
||||||
|
messages.push(finalAssistantMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (content.length > 0) {
|
||||||
return content;
|
messages.push(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function parseDoubleEncodedString(value) {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
let parsedValue = JSON.parse(value);
|
||||||
|
if (typeof value == "string") {
|
||||||
|
return parsedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
// This regular expression matches code blocks marked by triple backticks
|
// This regular expression matches code blocks marked by triple backticks
|
||||||
const codeBlockRegex = /```[\s\S]*?```/g;
|
const codeBlockRegex = /```[\s\S]*?```/g;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue