diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py
index 35ff61231..653742e46 100644
--- a/backend/open_webui/utils/middleware.py
+++ b/backend/open_webui/utils/middleware.py
@@ -1201,13 +1201,15 @@ async def process_chat_response(
)
tool_result = None
+ tool_result_files = None
for result in results:
if tool_call_id == result.get("tool_call_id", ""):
tool_result = result.get("content", None)
+ tool_result_files = result.get("files", None)
break
if tool_result:
- tool_calls_display_content = f'{tool_calls_display_content}\n\nTool Executed
\n '
+ tool_calls_display_content = f'{tool_calls_display_content}\n\nTool Executed
\n \n'
else:
tool_calls_display_content = f'{tool_calls_display_content}\n\nExecuting...
\n '
@@ -1901,6 +1903,13 @@ async def process_chat_response(
except Exception as e:
tool_result = str(e)
+ tool_result_files = []
+ if isinstance(tool_result, list):
+ for item in tool_result:
+ if item.startswith("data:"):
+ tool_result_files.append(item)
+ tool_result.remove(item)
+
if isinstance(tool_result, dict) or isinstance(
tool_result, list
):
@@ -1910,6 +1919,11 @@ async def process_chat_response(
{
"tool_call_id": tool_call_id,
"content": tool_result,
+ **(
+ {"files": tool_result_files}
+ if tool_result_files
+ else {}
+ ),
}
)
diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte
index 9b4c04570..5d7a98866 100644
--- a/src/lib/components/chat/Chat.svelte
+++ b/src/lib/components/chat/Chat.svelte
@@ -48,7 +48,8 @@
splitStream,
sleep,
removeDetails,
- getPromptVariables
+ getPromptVariables,
+ processDetails
} from '$lib/utils';
import { generateChatCompletion } from '$lib/apis/ollama';
@@ -1514,7 +1515,7 @@
: undefined,
...createMessagesList(_history, responseMessageId).map((message) => ({
...message,
- content: removeDetails(message.content, ['reasoning', 'code_interpreter'])
+ content: processDetails(message.content)
}))
].filter((message) => message);
diff --git a/src/lib/components/common/Collapsible.svelte b/src/lib/components/common/Collapsible.svelte
index dfed04acf..aa841a4f7 100644
--- a/src/lib/components/common/Collapsible.svelte
+++ b/src/lib/components/common/Collapsible.svelte
@@ -36,6 +36,7 @@
import Spinner from './Spinner.svelte';
import CodeBlock from '../chat/Messages/CodeBlock.svelte';
import Markdown from '../chat/Messages/Markdown.svelte';
+ import Image from './Image.svelte';
export let open = false;
@@ -53,9 +54,17 @@
export let disabled = false;
export let hide = false;
- function formatJSONString(obj) {
+ function parseJSONString(str) {
try {
- const parsed = JSON.parse(JSON.parse(obj));
+ return parseJSONString(JSON.parse(str));
+ } catch (e) {
+ return str;
+ }
+ }
+
+ function formatJSONString(str) {
+ try {
+ const parsed = parseJSONString(str);
// If parsed is an object/array, then it's valid JSON
if (typeof parsed === 'object') {
return JSON.stringify(parsed, null, 2);
@@ -65,7 +74,7 @@
}
} catch (e) {
// Not valid JSON, return as-is
- return obj;
+ return str;
}
}
@@ -120,14 +129,14 @@
{#if attributes?.done === 'true'}
{:else}
@@ -194,6 +203,7 @@
{#if attributes?.type === 'tool_calls'}
{@const args = decode(attributes?.arguments)}
{@const result = decode(attributes?.result ?? '')}
+ {@const files = parseJSONString(decode(attributes?.files ?? ''))}
{#if attributes?.done === 'true'}
${formatJSONString(result)}
> \`\`\``}
/>
+
+ {#if typeof files === 'object'}
+ {#each files ?? [] as file, idx}
+ {#if file.startsWith('data:image/')}
+
+ {/if}
+ {/each}
+ {/if}
{:else}
{
return content;
};
+export const processDetails = (content) => {
+ content = removeDetails(content, ['reasoning', 'code_interpreter']);
+
+ // This regex matches tags with type="tool_calls" and captures their attributes to convert them to tags
+ const detailsRegex = /]*)>([\s\S]*?)<\/details>/gis;
+ const matches = content.match(detailsRegex);
+ if (matches) {
+ for (const match of matches) {
+ const attributesRegex = /(\w+)="([^"]*)"/g;
+ const attributes = {};
+ let attributeMatch;
+ while ((attributeMatch = attributesRegex.exec(match)) !== null) {
+ attributes[attributeMatch[1]] = attributeMatch[2];
+ }
+
+ content = content.replace(
+ match,
+ ``
+ );
+ }
+ }
+
+ return content;
+};
+
// This regular expression matches code blocks marked by triple backticks
const codeBlockRegex = /```[\s\S]*?```/g;