refac: built-in tools ui component support

This commit is contained in:
Timothy Jaeryang Baek 2025-09-19 01:38:44 -05:00
parent 7528f24b61
commit 60db9ec8ef
3 changed files with 46 additions and 2 deletions

View file

@ -20,6 +20,7 @@ from concurrent.futures import ThreadPoolExecutor
from fastapi import Request, HTTPException from fastapi import Request, HTTPException
from fastapi.responses import HTMLResponse
from starlette.responses import Response, StreamingResponse, JSONResponse from starlette.responses import Response, StreamingResponse, JSONResponse
@ -2404,10 +2405,45 @@ async def process_chat_response(
tool_result = str(e) tool_result = str(e)
tool_result_embeds = [] 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, tuple
): ):
tool_result, tool_response_headers = tool_result tool_result, tool_response_headers = tool_result
if tool_response_headers: if tool_response_headers:

View file

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

View file

@ -6,6 +6,8 @@
export let title = 'Embedded Content'; export let title = 'Embedded Content';
export let initialHeight: number | null = null; // initial height in px, null = auto export let initialHeight: number | null = null; // initial height in px, null = auto
export let args = null;
export let allowScripts = true; export let allowScripts = true;
export let allowForms = false; export let allowForms = false;
@ -63,6 +65,11 @@
const onLoad = async () => { const onLoad = async () => {
requestAnimationFrame(resizeSameOrigin); 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 we're injecting Alpine into srcdoc iframe and sameOrigin allowed
if (iframeDoc && allowSameOrigin && iframe?.contentWindow) { if (iframeDoc && allowSameOrigin && iframe?.contentWindow) {
const alpineDirectives = [ const alpineDirectives = [