Просмотр исходного кода

refac: built-in tools ui component support

Timothy Jaeryang Baek 2 недель назад
Родитель
Сommit
60db9ec8ef

+ 38 - 2
backend/open_webui/utils/middleware.py

@@ -20,6 +20,7 @@ from concurrent.futures import ThreadPoolExecutor
 
 
 from fastapi import Request, HTTPException
+from fastapi.responses import HTMLResponse
 from starlette.responses import Response, StreamingResponse, JSONResponse
 
 
@@ -2404,10 +2405,45 @@ async def process_chat_response(
                                 tool_result = str(e)
 
                         tool_result_embeds = []
-                        if tool.get("type") == "external" and isinstance(
+
+                        if isinstance(tool_result, HTMLResponse):
+                            content_disposition = tool_result.headers.get(
+                                "Content-Disposition", ""
+                            )
+                            if "inline" in content_disposition:
+                                content = tool_result.body.decode("utf-8")
+                                tool_result_embeds.append(content)
+
+                                if 200 <= tool_result.status_code < 300:
+                                    tool_result = {
+                                        "status": "success",
+                                        "code": "ui_component",
+                                        "message": "Embedded UI result is active and visible to the user.",
+                                    }
+                                elif 400 <= tool_result.status_code < 500:
+                                    tool_result = {
+                                        "status": "error",
+                                        "code": "ui_component",
+                                        "message": f"Client error {tool_result.status_code} from embedded UI result.",
+                                    }
+                                elif 500 <= tool_result.status_code < 600:
+                                    tool_result = {
+                                        "status": "error",
+                                        "code": "ui_component",
+                                        "message": f"Server error {tool_result.status_code} from embedded UI result.",
+                                    }
+                                else:
+                                    tool_result = {
+                                        "status": "error",
+                                        "code": "ui_component",
+                                        "message": f"Unexpected status code {tool_result.status_code} from embedded UI result.",
+                                    }
+                            else:
+                                tool_result = tool_result.body.decode("utf-8")
+
+                        elif tool.get("type") == "external" and isinstance(
                             tool_result, tuple
                         ):
-
                             tool_result, tool_response_headers = tool_result
 
                             if tool_response_headers:

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

@@ -107,6 +107,7 @@
 					<div class="my-2" id={`${collapsibleId}-tool-calls-${attributes?.id}-embed-${idx}`}>
 						<FullHeightIframe
 							src={embed}
+							{args}
 							allowScripts={true}
 							allowForms={true}
 							allowSameOrigin={true}

+ 7 - 0
src/lib/components/common/FullHeightIframe.svelte

@@ -6,6 +6,8 @@
 	export let title = 'Embedded Content';
 	export let initialHeight: number | null = null; // initial height in px, null = auto
 
+	export let args = null;
+
 	export let allowScripts = true;
 	export let allowForms = false;
 
@@ -63,6 +65,11 @@
 	const onLoad = async () => {
 		requestAnimationFrame(resizeSameOrigin);
 
+		// if arguments are provided, inject them into the iframe window
+		if (args && iframe?.contentWindow) {
+			(iframe.contentWindow as any).args = args;
+		}
+
 		// If we're injecting Alpine into srcdoc iframe and sameOrigin allowed
 		if (iframeDoc && allowSameOrigin && iframe?.contentWindow) {
 			const alpineDirectives = [