Jelajahi Sumber

refac: styling

Timothy Jaeryang Baek 3 bulan lalu
induk
melakukan
58d2fd9ac1

+ 10 - 5
backend/open_webui/utils/middleware.py

@@ -7,6 +7,7 @@ from aiocache import cached
 from typing import Any, Optional
 import random
 import json
+import html
 import inspect
 import re
 
@@ -1082,7 +1083,7 @@ async def process_chat_response(
 
         # Handle as a background task
         async def post_response_handler(response, events):
-            def serialize_content_blocks(content_blocks):
+            def serialize_content_blocks(content_blocks, raw=False):
                 content = ""
 
                 for block in content_blocks:
@@ -1103,12 +1104,16 @@ async def process_chat_response(
 
                     elif block["type"] == "code_interpreter":
                         attributes = block.get("attributes", {})
-                        output = block.get("output", {})
-
+                        output = block.get("output", None)
                         lang = attributes.get("lang", "")
 
                         if output:
-                            content = f'{content}<details type="code_interpreter" done="true">\n<summary>Analyzed</summary>\n```{lang}\n{block["content"]}\n```\n```output\n{output}\n```\n</details>\n'
+                            output = html.escape(json.dumps(output))
+
+                            if raw:
+                                content = f'{content}<details type="code_interpreter" done="true" output="{output}">\n<summary>Analyzed</summary>\n```{lang}\n{block["content"]}\n```\n```output\n{output}\n```\n</details>\n'
+                            else:
+                                content = f'{content}<details type="code_interpreter" done="true" output="{output}">\n<summary>Analyzed</summary>\n```{lang}\n{block["content"]}\n```\n</details>\n'
                         else:
                             content = f'{content}<details type="code_interpreter" done="false">\n<summary>Analyzing...</summary>\n```{lang}\n{block["content"]}\n```\n</details>\n'
 
@@ -1388,7 +1393,7 @@ async def process_chat_response(
                                     {
                                         "role": "assistant",
                                         "content": serialize_content_blocks(
-                                            content_blocks
+                                            content_blocks, raw=True
                                         ),
                                     },
                                 ],

+ 31 - 0
src/lib/components/chat/Messages/CodeBlock.svelte

@@ -25,6 +25,7 @@
 	export let token;
 	export let lang = '';
 	export let code = '';
+	export let attributes = {};
 
 	export let className = 'my-2';
 	export let editorClassName = '';
@@ -279,6 +280,36 @@ __builtins__.input = input`);
 
 	$: dispatch('code', { lang, code });
 
+	$: if (attributes) {
+		onAttributesUpdate();
+	}
+
+	const onAttributesUpdate = () => {
+		if (attributes?.output) {
+			// Create a helper function to unescape HTML entities
+			const unescapeHtml = (html) => {
+				const textArea = document.createElement('textarea');
+				textArea.innerHTML = html;
+				return textArea.value;
+			};
+
+			try {
+				// Unescape the HTML-encoded string
+				const unescapedOutput = unescapeHtml(attributes.output);
+
+				// Parse the unescaped string into JSON
+				const output = JSON.parse(unescapedOutput);
+
+				// Assign the parsed values to variables
+				stdout = output.stdout;
+				stderr = output.stderr;
+				result = output.result;
+			} catch (error) {
+				console.error('Error:', error);
+			}
+		}
+	};
+
 	onMount(async () => {
 		console.log('codeblock', lang, code);
 

+ 7 - 1
src/lib/components/chat/Messages/Markdown/MarkdownTokens.svelte

@@ -23,6 +23,7 @@
 	export let id: string;
 	export let tokens: Token[];
 	export let top = true;
+	export let attributes = {};
 
 	export let save = false;
 	export let onSourceClick: Function = () => {};
@@ -83,6 +84,7 @@
 				{token}
 				lang={token?.lang ?? ''}
 				code={token?.text ?? ''}
+				{attributes}
 				{save}
 				on:code={(e) => {
 					dispatch('code', e.detail);
@@ -197,7 +199,11 @@
 	{:else if token.type === 'details'}
 		<Collapsible title={token.summary} attributes={token?.attributes} className="w-full space-y-1">
 			<div class=" mb-1.5" slot="content">
-				<svelte:self id={`${id}-${tokenIdx}-d`} tokens={marked.lexer(token.text)} />
+				<svelte:self
+					id={`${id}-${tokenIdx}-d`}
+					tokens={marked.lexer(token.text)}
+					attributes={token?.attributes}
+				/>
 			</div>
 		</Collapsible>
 	{:else if token.type === 'html'}

+ 6 - 0
src/lib/components/common/Collapsible.svelte

@@ -80,6 +80,12 @@
 						{:else}
 							{$i18n.t('Thinking...')}
 						{/if}
+					{:else if attributes?.type === 'code_interpreter'}
+						{#if attributes?.done === 'true'}
+							{$i18n.t('Analyzed')}
+						{:else}
+							{$i18n.t('Analyzing...')}
+						{/if}
 					{:else}
 						{title}
 					{/if}

+ 5 - 1
src/lib/workers/pyodide.worker.ts

@@ -84,6 +84,10 @@ function processResult(result: any): any {
 			// Handle primitive types directly
 			return result;
 		}
+		if (typeof result === 'bigint') {
+			// Convert BigInt to a string for JSON-safe representation
+			return result.toString();
+		}
 		if (Array.isArray(result)) {
 			// If it's an array, recursively process items
 			return result.map((item) => processResult(item));
@@ -106,7 +110,7 @@ function processResult(result: any): any {
 		return JSON.stringify(result);
 	} catch (err) {
 		// In case something unexpected happens, we return a stringified fallback
-		return `[processResult error]: ${err.toString()}`;
+		return `[processResult error]: ${err.message || err.toString()}`;
 	}
 }