diff --git a/studio/frontend/src/components/assistant-ui/thread.tsx b/studio/frontend/src/components/assistant-ui/thread.tsx
index c0d577b12d..431e568205 100644
--- a/studio/frontend/src/components/assistant-ui/thread.tsx
+++ b/studio/frontend/src/components/assistant-ui/thread.tsx
@@ -18,6 +18,7 @@ import {
import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
import { ToolGroup } from "@/components/assistant-ui/tool-group";
import { CodeExecutionToolUI } from "@/components/assistant-ui/tool-ui-code-execution";
+import { ImageGenerationToolUI } from "@/components/assistant-ui/tool-ui-image-generation";
import { PythonToolUI } from "@/components/assistant-ui/tool-ui-python";
import { TerminalToolUI } from "@/components/assistant-ui/tool-ui-terminal";
import { WebSearchToolUI } from "@/components/assistant-ui/tool-ui-web-search";
@@ -68,6 +69,7 @@ import {
DownloadIcon,
GlobeIcon,
HeadphonesIcon,
+ ImageIcon,
LightbulbIcon,
LightbulbOffIcon,
MicIcon,
@@ -870,6 +872,42 @@ const CodeToolsToggle: FC = () => {
);
};
+const ImagesToggle: FC = () => {
+ const modelLoaded = useChatRuntimeStore(
+ (s) => !!s.params.checkpoint && !s.modelLoading,
+ );
+ // OpenAI cloud Responses-API models advertise image_generation as a
+ // server-side tool; no local runtime fallback exists. Mirror of
+ // shared-composer's imageDisabled / showImagePill so the in-thread
+ // composer surfaces the same control as the empty-state composer.
+ const supportsBuiltinImageGeneration = useChatRuntimeStore(
+ (s) => s.supportsBuiltinImageGeneration,
+ );
+ const imageToolsEnabled = useChatRuntimeStore((s) => s.imageToolsEnabled);
+ const setImageToolsEnabled = useChatRuntimeStore(
+ (s) => s.setImageToolsEnabled,
+ );
+ if (!supportsBuiltinImageGeneration) {
+ return null;
+ }
+ const disabled = !modelLoaded;
+ return (
+
+ );
+};
+
const ToolStatusDisplay: FC = () => {
const toolStatus = useChatRuntimeStore((s) => s.toolStatus);
const isThreadRunning = useAuiState(({ thread }) => thread.isRunning);
@@ -941,6 +979,7 @@ const ComposerAction: FC<{ disabled?: boolean; blockSend?: () => boolean }> = ({
+
@@ -1067,6 +1106,7 @@ const AssistantMessage: FC = () => {
python: PythonToolUI,
terminal: TerminalToolUI,
code_execution: CodeExecutionToolUI,
+ image_generation: ImageGenerationToolUI,
},
Fallback: ToolFallback,
},
diff --git a/studio/frontend/src/components/assistant-ui/tool-ui-image-generation.tsx b/studio/frontend/src/components/assistant-ui/tool-ui-image-generation.tsx
new file mode 100644
index 0000000000..246d8b6978
--- /dev/null
+++ b/studio/frontend/src/components/assistant-ui/tool-ui-image-generation.tsx
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+// Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
+
+"use client";
+
+import { type ToolCallMessagePartComponent, useAuiState } from "@assistant-ui/react";
+import { ImageIcon, LoaderIcon } from "lucide-react";
+import { memo, useEffect, useState } from "react";
+import {
+ ToolFallbackContent,
+ ToolFallbackRoot,
+ ToolFallbackTrigger,
+} from "./tool-fallback";
+
+/**
+ * Renders the synthetic `_toolEvent` chunks emitted by
+ * `_stream_openai_responses` when OpenAI's Responses-API
+ * `image_generation` tool fires. The backend stashes the base64
+ * PNG/WebP/JPEG (the gpt-image backbone output) on an `image_b64`
+ * field of the tool_end event so the JSON result stays small, and the
+ * adapter repackages it into a structured `result` shape:
+ *
+ * {
+ * image_b64: string,
+ * image_mime: string, // e.g. "image/png"
+ * size?: string, // "1024x1024" etc
+ * quality?: string,
+ * background?: string,
+ * }
+ *
+ * The corresponding `tool_start` carries the prompt as
+ * `args.prompt` (after gpt-image's revision pass) plus `args.kind:
+ * "image"`. Without this component the generic ToolFallback would
+ * print the prompt as JSON args text with an empty Result block --
+ * which is exactly the "no image" symptom users hit before this UI
+ * landed.
+ */
+interface ImageGenerationArgs {
+ prompt?: string;
+ kind?: string;
+}
+
+interface ImageGenerationResult {
+ image_b64?: string;
+ image_mime?: string;
+ size?: string;
+ quality?: string;
+ background?: string;
+}
+
+const ImageGenerationToolUIImpl: ToolCallMessagePartComponent = ({
+ args,
+ result,
+ status,
+}) => {
+ const parsedArgs = (args as ImageGenerationArgs) ?? {};
+ const prompt = parsedArgs.prompt ?? "";
+ const isRunning = status?.type === "running";
+
+ const isImageResult =
+ !!result &&
+ typeof result === "object" &&
+ typeof (result as ImageGenerationResult).image_b64 === "string";
+ const imageResult = isImageResult ? (result as ImageGenerationResult) : null;
+ const mime = imageResult?.image_mime || "image/png";
+ const imageSrc = imageResult?.image_b64
+ ? `data:${mime};base64,${imageResult.image_b64}`
+ : null;
+
+ // Collapse the card once the model has resumed streaming prose
+ // after the image. Mirrors CodeExecutionToolUI so the inline image
+ // doesn't collapse mid-stream and the user can click to re-expand.
+ const hasText = useAuiState(({ message }) =>
+ message.content.some(
+ (p) =>
+ p.type === "text" &&
+ "text" in p &&
+ (p as { text: string }).text.length > 0,
+ ),
+ );
+ const [open, setOpen] = useState(true);
+ useEffect(() => {
+ if (isRunning) {
+ setOpen(true);
+ } else if (hasText && !imageSrc) {
+ setOpen(false);
+ }
+ }, [isRunning, hasText, imageSrc]);
+
+ const runningLabel = "Generating image…";
+ const completedLabel = prompt
+ ? prompt.length > 80
+ ? `Generated image: ${prompt.slice(0, 80)}…`
+ : `Generated image: ${prompt}`
+ : "Generated image";
+
+ return (
+
+
+
+ {isRunning && !imageSrc ? (
+
+
+ {runningLabel}
+
+ ) : imageSrc ? (
+
+
+ {prompt ? (
+
+ {prompt}
+
+ ) : null}
+
+ ) : null}
+
+
+ );
+};
+
+export const ImageGenerationToolUI = memo(
+ ImageGenerationToolUIImpl,
+) as unknown as ToolCallMessagePartComponent;
+ImageGenerationToolUI.displayName = "ImageGenerationToolUI";
diff --git a/studio/frontend/src/features/chat/api/chat-adapter.ts b/studio/frontend/src/features/chat/api/chat-adapter.ts
index c214c17c0a..0521ca2e10 100644
--- a/studio/frontend/src/features/chat/api/chat-adapter.ts
+++ b/studio/frontend/src/features/chat/api/chat-adapter.ts
@@ -23,6 +23,7 @@ import {
getExternalReasoningCapabilities,
getProviderCapabilities,
providerSupportsBuiltinCodeExecution,
+ providerSupportsBuiltinImageGeneration,
providerSupportsBuiltinWebFetch,
providerSupportsBuiltinWebSearch,
} from "../provider-capabilities";
@@ -830,7 +831,7 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
// Re-read store after potential auto-load / model ready wait
runtime = useChatRuntimeStore.getState();
const { params } = runtime;
- const { supportsTools, toolsEnabled, codeToolsEnabled } = runtime;
+ const { supportsTools, toolsEnabled, codeToolsEnabled, imageToolsEnabled } = runtime;
const externalSelection = parseExternalModelId(params.checkpoint);
const isExternalRequest = externalSelection !== null;
if (
@@ -900,6 +901,20 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
externalProvider &&
providerSupportsBuiltinWebFetch(externalProvider.providerType),
);
+ // OpenAI Responses-API image_generation server tool. Pill is
+ // gated on OpenAI cloud + a Responses-API model id; the backend
+ // additionally re-checks is_openai_cloud before appending
+ // {type:"image_generation"} to the request tools array.
+ const imageGenerationEnabledForThisTurn = Boolean(
+ externalProvider &&
+ externalSelection &&
+ imageToolsEnabled &&
+ providerSupportsBuiltinImageGeneration(
+ externalProvider.providerType,
+ externalSelection.modelId,
+ externalProvider.baseUrl,
+ ),
+ );
const outboundMessages = messages
.map(toOpenAIMessage)
@@ -1396,7 +1411,8 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
// body["tools"] inside _stream_anthropic.
...(webSearchEnabledForThisTurn ||
webFetchEnabledForThisTurn ||
- codeExecEnabledForThisTurn
+ codeExecEnabledForThisTurn ||
+ imageGenerationEnabledForThisTurn
? {
enable_tools: true,
enabled_tools: [
@@ -1410,6 +1426,13 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
// tool. There is no separate UI toggle yet.
...(webFetchEnabledForThisTurn ? ["web_fetch"] : []),
...(codeExecEnabledForThisTurn ? ["code_execution"] : []),
+ // OpenAI Responses-API only: `image_generation`
+ // returns inline image_generation_call output
+ // items; the backend's _stream_openai_responses
+ // path translates them to assistant tool events.
+ ...(imageGenerationEnabledForThisTurn
+ ? ["image_generation"]
+ : []),
],
}
: {}),
@@ -1587,8 +1610,36 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
const imgIdx = rawResult.lastIndexOf(imgMarker);
let parsedResult:
| string
- | { text: string; images: string[]; sessionId: string };
- if (imgIdx !== -1) {
+ | { text: string; images: string[]; sessionId: string }
+ | {
+ image_b64: string;
+ image_mime: string;
+ size?: string;
+ quality?: string;
+ background?: string;
+ };
+ const imageB64 = toolEvent.image_b64 as string | undefined;
+ if (
+ toolCallParts[idx].toolName === "image_generation" &&
+ typeof imageB64 === "string" &&
+ imageB64
+ ) {
+ // OpenAI Responses image_generation_call: the
+ // backend stashes the base64 PNG/WebP/JPEG on
+ // separate `image_b64` / `image_mime` fields on
+ // the synthetic _toolEvent so the JSON result
+ // string stays small enough to log. Repackage as
+ // a structured result for the dedicated tool UI.
+ parsedResult = {
+ image_b64: imageB64,
+ image_mime:
+ (toolEvent.image_mime as string | undefined) ??
+ "image/png",
+ size: toolEvent.size as string | undefined,
+ quality: toolEvent.quality as string | undefined,
+ background: toolEvent.background as string | undefined,
+ };
+ } else if (imgIdx !== -1) {
const text = rawResult.slice(0, imgIdx);
// Fall back to "_default" to match the backend sandbox directory
// used when no session_id is provided (see tools.py _get_workdir).
diff --git a/studio/frontend/src/features/chat/chat-page.tsx b/studio/frontend/src/features/chat/chat-page.tsx
index 2f062568dd..0943e26313 100644
--- a/studio/frontend/src/features/chat/chat-page.tsx
+++ b/studio/frontend/src/features/chat/chat-page.tsx
@@ -56,6 +56,7 @@ import {
getExternalReasoningCapabilities,
getProviderCapabilities,
providerSupportsBuiltinCodeExecution,
+ providerSupportsBuiltinImageGeneration,
providerSupportsBuiltinWebSearch,
} from "./provider-capabilities";
import { ChatRuntimeProvider } from "./runtime-provider";
@@ -68,6 +69,7 @@ import {
} from "./shared-composer";
import {
CHAT_CODE_TOOLS_ENABLED_KEY,
+ CHAT_IMAGE_TOOLS_ENABLED_KEY,
CHAT_TOOLS_ENABLED_KEY,
loadOptionalBool,
useChatRuntimeStore,
@@ -771,6 +773,12 @@ export function ChatPage(): ReactElement {
selection.modelId,
provider?.baseUrl,
);
+ const supportsBuiltinImageGeneration =
+ providerSupportsBuiltinImageGeneration(
+ provider?.providerType,
+ selection.modelId,
+ provider?.baseUrl,
+ );
// Kimi's k2.6/k2.5 default to thinking enabled on the server side
// (per https://platform.kimi.ai/docs/models). Mirror that default
// in the UI so the Think pill comes up clicked when the user picks
@@ -790,6 +798,9 @@ export function ChatPage(): ReactElement {
provider?.providerType === "openai");
const storedToolsEnabled = loadOptionalBool(CHAT_TOOLS_ENABLED_KEY);
const storedCodeToolsEnabled = loadOptionalBool(CHAT_CODE_TOOLS_ENABLED_KEY);
+ const storedImageToolsEnabled = loadOptionalBool(
+ CHAT_IMAGE_TOOLS_ENABLED_KEY,
+ );
const nextToolsEnabled = supportsBuiltinWebSearch
? isKimi
? false
@@ -811,19 +822,25 @@ export function ChatPage(): ReactElement {
: state.reasoningEnabled,
supportsPreserveThinking: false,
// External models never give us a local tool runtime (no
- // python sandbox), so `supportsTools` must be false. The two
+ // python sandbox), so `supportsTools` must be false. The three
// `supportsBuiltin*` flags pick up the slack for providers that
// run the tool server-side: `supportsBuiltinWebSearch` lights
// up the Search pill (OpenAI / Anthropic / OpenRouter / Kimi),
// `supportsBuiltinCodeExecution` lights up the Code pill
- // (Anthropic Claude 4.x only, today).
+ // (Anthropic Claude 4.x and OpenAI gpt-5.5), and
+ // `supportsBuiltinImageGeneration` lights up the Images pill
+ // (OpenAI cloud Responses-API models only).
supportsTools: false,
supportsBuiltinWebSearch,
supportsBuiltinCodeExecution,
+ supportsBuiltinImageGeneration,
toolsEnabled: nextToolsEnabled,
codeToolsEnabled: supportsBuiltinCodeExecution
? (storedCodeToolsEnabled ?? false)
: false,
+ imageToolsEnabled: supportsBuiltinImageGeneration
+ ? (storedImageToolsEnabled ?? false)
+ : false,
});
}, [externalProvidersForChat, inferenceParams.checkpoint]);
const canCompare = useMemo(() => {
@@ -988,6 +1005,12 @@ export function ChatPage(): ReactElement {
selectedExternal?.modelId,
selectedProvider?.baseUrl,
);
+ const supportsBuiltinImageGeneration =
+ providerSupportsBuiltinImageGeneration(
+ selectedProvider?.providerType,
+ selectedExternal?.modelId,
+ selectedProvider?.baseUrl,
+ );
// See sibling useEffect above: Kimi's k2.x default to thinking
// enabled, so the Think pill comes up clicked. Search pill stays
// off by default; mutual exclusion flips them via the composer.
@@ -1003,6 +1026,9 @@ export function ChatPage(): ReactElement {
const storedCodeToolsEnabled = loadOptionalBool(
CHAT_CODE_TOOLS_ENABLED_KEY,
);
+ const storedImageToolsEnabled = loadOptionalBool(
+ CHAT_IMAGE_TOOLS_ENABLED_KEY,
+ );
const nextToolsEnabled = supportsBuiltinWebSearch
? isKimi
? false
@@ -1029,18 +1055,24 @@ export function ChatPage(): ReactElement {
: store.reasoningEnabled,
supportsPreserveThinking: false,
// External models have no local tool runtime → supportsTools
- // stays false. The two supportsBuiltin* flags carry the
+ // stays false. The three supportsBuiltin* flags carry the
// server-side capability info for each pill:
// - Search → providerSupportsBuiltinWebSearch
// - Code → providerSupportsBuiltinCodeExecution
- // (Anthropic Claude 4.x only, today)
+ // (Anthropic Claude 4.x + OpenAI gpt-5.5)
+ // - Images → providerSupportsBuiltinImageGeneration
+ // (OpenAI cloud Responses-API models)
supportsTools: false,
supportsBuiltinWebSearch,
supportsBuiltinCodeExecution,
+ supportsBuiltinImageGeneration,
toolsEnabled: nextToolsEnabled,
codeToolsEnabled: supportsBuiltinCodeExecution
? (storedCodeToolsEnabled ?? false)
: false,
+ imageToolsEnabled: supportsBuiltinImageGeneration
+ ? (storedImageToolsEnabled ?? false)
+ : false,
...(stillOnOpenRouterFree ? {} : { lastOpenRouterChosenModel: null }),
});
return;
diff --git a/studio/frontend/src/features/chat/provider-capabilities.ts b/studio/frontend/src/features/chat/provider-capabilities.ts
index 8b16c2c85f..da1d6e3431 100644
--- a/studio/frontend/src/features/chat/provider-capabilities.ts
+++ b/studio/frontend/src/features/chat/provider-capabilities.ts
@@ -217,6 +217,44 @@ export function providerSupportsBuiltinCodeExecution(
return false;
}
+/**
+ * Whether the selected external provider/model exposes OpenAI's
+ * Responses-API server-side image_generation tool. Lit on for OpenAI
+ * cloud (`api.openai.com`) when the picked model is a Responses-API
+ * family id (gpt-5.x today). The backend additionally gates on
+ * `is_openai_cloud`; mirror that here so the pill is hidden on custom
+ * OpenAI-compat backends (ollama / llama.cpp / vLLM) that report
+ * `provider_type="openai"` but would 400 on a `{type:"image_generation"}`
+ * tool. See backend/core/inference/external_provider.py near line 2770
+ * for the dispatch and backend/tests/test_openai_image_generation.py
+ * for the round-trip coverage.
+ */
+const OPENAI_IMAGE_GENERATION_MODEL_PREFIXES = [
+ "gpt-5.5-pro",
+ "gpt-5.5",
+ "gpt-5.4-pro",
+ "gpt-5.4",
+ "gpt-5.3",
+ "gpt-5.2",
+ "gpt-5.1",
+ "gpt-5",
+ "o3",
+] as const;
+
+export function providerSupportsBuiltinImageGeneration(
+ providerType: string | null | undefined,
+ modelId: string | null | undefined,
+ baseUrl?: string | null,
+): boolean {
+ if (providerType !== "openai") return false;
+ if (!isOpenAICloudBaseUrl(baseUrl)) return false;
+ const normalized = modelId?.trim().toLowerCase() ?? "";
+ if (!normalized) return false;
+ return OPENAI_IMAGE_GENERATION_MODEL_PREFIXES.some((prefix) =>
+ normalized.startsWith(prefix),
+ );
+}
+
/**
* Per-provider minimum on the outbound max_tokens. Kimi's docs require
* `max_tokens >= 16000` whenever a thinking model is in use so the
diff --git a/studio/frontend/src/features/chat/shared-composer.tsx b/studio/frontend/src/features/chat/shared-composer.tsx
index 57a8a132d7..246fd81510 100644
--- a/studio/frontend/src/features/chat/shared-composer.tsx
+++ b/studio/frontend/src/features/chat/shared-composer.tsx
@@ -21,7 +21,7 @@ import { isTauri } from "@/lib/api-base";
import { isMultimodalResponse } from "./types/api";
import { getImageInputUnavailableReason } from "./utils/image-input-support";
import { useAui } from "@assistant-ui/react";
-import { ArrowUpIcon, GlobeIcon, HeadphonesIcon, LightbulbIcon, LightbulbOffIcon, MicIcon, PlusIcon, SquareIcon, XIcon } from "lucide-react";
+import { ArrowUpIcon, GlobeIcon, HeadphonesIcon, ImageIcon, LightbulbIcon, LightbulbOffIcon, MicIcon, PlusIcon, SquareIcon, XIcon } from "lucide-react";
import { toast } from "@/lib/toast";
import { loadModel, validateModel } from "./api/chat-api";
import { parseExternalModelId, providerTypeSupportsVision } from "./external-providers";
@@ -33,6 +33,7 @@ import {
import {
getExternalReasoningCapabilities,
providerSupportsBuiltinCodeExecution,
+ providerSupportsBuiltinImageGeneration,
} from "./provider-capabilities";
import {
type CompositionEvent,
@@ -331,6 +332,10 @@ export function SharedComposer({
const setToolsEnabled = useChatRuntimeStore((s) => s.setToolsEnabled);
const codeToolsEnabled = useChatRuntimeStore((s) => s.codeToolsEnabled);
const setCodeToolsEnabled = useChatRuntimeStore((s) => s.setCodeToolsEnabled);
+ const imageToolsEnabled = useChatRuntimeStore((s) => s.imageToolsEnabled);
+ const setImageToolsEnabled = useChatRuntimeStore(
+ (s) => s.setImageToolsEnabled,
+ );
const lastOpenRouterChosenModel = useChatRuntimeStore(
(s) => s.lastOpenRouterChosenModel,
);
@@ -416,10 +421,22 @@ export function SharedComposer({
effectiveExternalModelId,
selectedExternalProvider?.baseUrl,
);
+ const supportsBuiltinImageGeneration = providerSupportsBuiltinImageGeneration(
+ selectedExternalProvider?.providerType,
+ effectiveExternalModelId,
+ selectedExternalProvider?.baseUrl,
+ );
const searchDisabled =
!modelLoaded || !(supportsTools || supportsBuiltinWebSearch);
const codeDisabled =
!modelLoaded || !(supportsTools || supportsBuiltinCodeExecution);
+ // Images pill is only ever lit on OpenAI cloud's Responses-API models.
+ // No local tool runtime fallback because the only image-generation
+ // server tool we wire today is OpenAI's; local models cannot dispatch
+ // it. Hidden entirely when the active model does not advertise it so
+ // the pill row stays compact for providers without the capability.
+ const imageDisabled = !modelLoaded || !supportsBuiltinImageGeneration;
+ const showImagePill = supportsBuiltinImageGeneration;
// Backwards-compatible alias for any other call site that may still
// reference `toolsDisabled` (rare; both pills used it before).
const toolsDisabled = codeDisabled;
@@ -1074,6 +1091,21 @@ export function SharedComposer({
Code
+ {showImagePill && (
+
+ )}
{dictationSupported && (
diff --git a/studio/frontend/src/features/chat/stores/chat-runtime-store.ts b/studio/frontend/src/features/chat/stores/chat-runtime-store.ts
index 287dd1fa8b..a00b53a44c 100644
--- a/studio/frontend/src/features/chat/stores/chat-runtime-store.ts
+++ b/studio/frontend/src/features/chat/stores/chat-runtime-store.ts
@@ -24,6 +24,8 @@ const HF_TOKEN_KEY = "unsloth_hf_token";
export const CHAT_REASONING_ENABLED_KEY = "unsloth_chat_reasoning_enabled";
export const CHAT_TOOLS_ENABLED_KEY = "unsloth_chat_tools_enabled";
export const CHAT_CODE_TOOLS_ENABLED_KEY = "unsloth_chat_code_tools_enabled";
+export const CHAT_IMAGE_TOOLS_ENABLED_KEY = "unsloth_chat_image_tools_enabled";
+
// External provider selection is encoded into `params.checkpoint` as
// `external::
::`. PersistedChatSettings deliberately
// Omits `checkpoint` because the local-model side is mirrored by the
@@ -253,8 +255,16 @@ type ChatRuntimeStore = {
* execution server-side. Read by both composers' Code pill gate.
*/
supportsBuiltinCodeExecution: boolean;
+ /**
+ * Whether the active external provider exposes a server-side
+ * image-generation tool (OpenAI's Responses-API `image_generation`
+ * today). Gates the chat composer's Images pill. Local models never
+ * receive the tool because their runtime cannot dispatch it.
+ */
+ supportsBuiltinImageGeneration: boolean;
toolsEnabled: boolean;
codeToolsEnabled: boolean;
+ imageToolsEnabled: boolean;
toolStatus: string | null;
generatingStatus: string | null;
autoHealToolCalls: boolean;
@@ -313,6 +323,7 @@ type ChatRuntimeStore = {
setPreserveThinking: (value: boolean) => void;
setToolsEnabled: (enabled: boolean, options?: { persist?: boolean }) => void;
setCodeToolsEnabled: (enabled: boolean) => void;
+ setImageToolsEnabled: (enabled: boolean) => void;
setToolStatus: (status: string | null) => void;
setGeneratingStatus: (status: string | null) => void;
setAutoHealToolCalls: (enabled: boolean) => void;
@@ -552,8 +563,10 @@ export const useChatRuntimeStore = create((set, get) => ({
supportsTools: false,
supportsBuiltinWebSearch: false,
supportsBuiltinCodeExecution: false,
+ supportsBuiltinImageGeneration: false,
toolsEnabled: loadBool(CHAT_TOOLS_ENABLED_KEY, false),
codeToolsEnabled: loadBool(CHAT_CODE_TOOLS_ENABLED_KEY, false),
+ imageToolsEnabled: loadBool(CHAT_IMAGE_TOOLS_ENABLED_KEY, false),
toolStatus: null,
generatingStatus: null,
autoHealToolCalls: true,
@@ -730,8 +743,10 @@ export const useChatRuntimeStore = create((set, get) => ({
supportsTools: false,
supportsBuiltinWebSearch: false,
supportsBuiltinCodeExecution: false,
+ supportsBuiltinImageGeneration: false,
toolsEnabled: false,
codeToolsEnabled: false,
+ imageToolsEnabled: false,
toolStatus: null,
kvCacheDtype: null,
loadedKvCacheDtype: null,
@@ -786,6 +801,11 @@ export const useChatRuntimeStore = create((set, get) => ({
saveBool(CHAT_CODE_TOOLS_ENABLED_KEY, codeToolsEnabled);
return { codeToolsEnabled };
}),
+ setImageToolsEnabled: (imageToolsEnabled) =>
+ set(() => {
+ saveBool(CHAT_IMAGE_TOOLS_ENABLED_KEY, imageToolsEnabled);
+ return { imageToolsEnabled };
+ }),
setToolStatus: (toolStatus) => set({ toolStatus }),
setGeneratingStatus: (generatingStatus) => set({ generatingStatus }),
setAutoHealToolCalls: (autoHealToolCalls) =>