Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
Daniel Han
973532786a Fix studio crash on Mac by deferring unsloth_zoo import
Move `from unsloth_zoo.rl_environments import check_signal_escape_patterns`
from module-level to inside `_check_code_safety()` and wrap it in a
try/except for NotImplementedError. The top-level import triggers
`unsloth_zoo.__init__` which calls `get_device_type()` at module scope,
raising NotImplementedError on Apple Silicon. On Mac the safety check
gracefully skips since unsloth_zoo is unavailable, while GGUF inference
via llama.cpp continues to work.
2026-03-18 15:58:21 +00:00
Daniel Han
3849f5d80d Fix TypeScript build errors in studio frontend
- tool-ui-python.tsx: use explicit tuple type instead of `as const` to
  match the mutable `[BundledTheme, BundledTheme]` expected by Streamdown
- chat-adapter.ts: add missing `argsText` field required by
  ToolCallMessagePart and fix `args` type to use ReadonlyJSONObject
2026-03-18 15:42:26 +00:00
3 changed files with 11 additions and 5 deletions

View file

@ -17,7 +17,6 @@ import tempfile
import threading import threading
from loggers import get_logger from loggers import get_logger
from unsloth_zoo.rl_environments import check_signal_escape_patterns
logger = get_logger(__name__) 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. Returns an error message string if the code is unsafe, or None if OK.
""" """
# Check for signal/timeout escape patterns # 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) safe, info = check_signal_escape_patterns(code)
if not safe: if not safe:
reasons = [ reasons = [

View file

@ -17,7 +17,7 @@ import {
const MAX_DISPLAY = 10_000; const MAX_DISPLAY = 10_000;
const COPY_RESET_MS = 2000; 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 { function truncate(text: string): string {
return text.length <= MAX_DISPLAY return text.length <= MAX_DISPLAY

View file

@ -2,7 +2,7 @@
// Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 // 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 { 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 { toast } from "sonner";
import { import {
generateAudio, generateAudio,
@ -527,7 +527,7 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
let reasoningDuration = 0; let reasoningDuration = 0;
// Tool call content parts — accumulated and yielded cumulatively. // Tool call content parts — accumulated and yielded cumulatively.
// result is set directly on the tool-call part when tool_end arrives. // result is set directly on the tool-call part when tool_end arrives.
const toolCallParts: { type: "tool-call"; toolCallId: string; toolName: string; args: Record<string, unknown>; result?: unknown }[] = []; const toolCallParts: ToolCallMessagePart[] = [];
try { try {
const { supportsReasoning, reasoningEnabled } = runtime; const { supportsReasoning, reasoningEnabled } = runtime;
@ -582,11 +582,13 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
if (toolEvent !== undefined) { if (toolEvent !== undefined) {
if (toolEvent.type === "tool_start") { if (toolEvent.type === "tool_start") {
const id = (toolEvent.tool_call_id as string) || `${toolEvent.tool_name}_${Date.now()}`; const id = (toolEvent.tool_call_id as string) || `${toolEvent.tool_name}_${Date.now()}`;
const toolArgs = (toolEvent.arguments ?? {}) as ToolCallMessagePart["args"];
toolCallParts.push({ toolCallParts.push({
type: "tool-call" as const, type: "tool-call" as const,
toolCallId: id, toolCallId: id,
toolName: toolEvent.tool_name as string, toolName: toolEvent.tool_name as string,
args: (toolEvent.arguments as Record<string, unknown>) ?? {}, argsText: JSON.stringify(toolArgs),
args: toolArgs,
}); });
} else if (toolEvent.type === "tool_end") { } else if (toolEvent.type === "tool_end") {
const id = (toolEvent.tool_call_id as string) || const id = (toolEvent.tool_call_id as string) ||