diff --git a/studio/backend/core/inference/tools.py b/studio/backend/core/inference/tools.py index 159facabe9..90c0933dc0 100644 --- a/studio/backend/core/inference/tools.py +++ b/studio/backend/core/inference/tools.py @@ -17,7 +17,6 @@ import tempfile import threading from loggers import get_logger -from unsloth_zoo.rl_environments import check_signal_escape_patterns logger = get_logger(__name__) @@ -171,6 +170,11 @@ def _check_code_safety(code: str) -> str | None: Returns an error message string if the code is unsafe, or None if OK. """ # Check for signal/timeout escape patterns + try: + from unsloth_zoo.rl_environments import check_signal_escape_patterns + except (ImportError, NotImplementedError): + # unsloth_zoo may not be available or may fail on non-GPU platforms (e.g. Mac) + return None safe, info = check_signal_escape_patterns(code) if not safe: reasons = [ diff --git a/studio/frontend/src/components/assistant-ui/tool-ui-python.tsx b/studio/frontend/src/components/assistant-ui/tool-ui-python.tsx index 760efc8db4..28468a10ad 100644 --- a/studio/frontend/src/components/assistant-ui/tool-ui-python.tsx +++ b/studio/frontend/src/components/assistant-ui/tool-ui-python.tsx @@ -17,7 +17,7 @@ import { const MAX_DISPLAY = 10_000; const COPY_RESET_MS = 2000; -const SHIKI_THEME = ["github-light", "github-dark"] as const; +const SHIKI_THEME = ["github-light", "github-dark"] as ["github-light", "github-dark"]; function truncate(text: string): string { return text.length <= MAX_DISPLAY diff --git a/studio/frontend/src/features/chat/api/chat-adapter.ts b/studio/frontend/src/features/chat/api/chat-adapter.ts index 0d25db2997..13ba64a5a8 100644 --- a/studio/frontend/src/features/chat/api/chat-adapter.ts +++ b/studio/frontend/src/features/chat/api/chat-adapter.ts @@ -2,7 +2,7 @@ // Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 import type { ChatModelAdapter } from "@assistant-ui/react"; -import type { MessageTiming } from "@assistant-ui/core"; +import type { MessageTiming, ToolCallMessagePart } from "@assistant-ui/core"; import { toast } from "sonner"; import { generateAudio, @@ -527,7 +527,7 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter { let reasoningDuration = 0; // Tool call content parts — accumulated and yielded cumulatively. // result is set directly on the tool-call part when tool_end arrives. - const toolCallParts: { type: "tool-call"; toolCallId: string; toolName: string; args: Record; result?: unknown }[] = []; + const toolCallParts: ToolCallMessagePart[] = []; try { const { supportsReasoning, reasoningEnabled } = runtime; @@ -582,11 +582,13 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter { if (toolEvent !== undefined) { if (toolEvent.type === "tool_start") { const id = (toolEvent.tool_call_id as string) || `${toolEvent.tool_name}_${Date.now()}`; + const toolArgs = (toolEvent.arguments ?? {}) as ToolCallMessagePart["args"]; toolCallParts.push({ type: "tool-call" as const, toolCallId: id, toolName: toolEvent.tool_name as string, - args: (toolEvent.arguments as Record) ?? {}, + argsText: JSON.stringify(toolArgs), + args: toolArgs, }); } else if (toolEvent.type === "tool_end") { const id = (toolEvent.tool_call_id as string) ||