"use client"; import * as React from "react"; import { Braces } from "lucide-react"; import { ResponsiveDialog } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/input"; import { Segmented } from "@/components/ui/segmented"; import { Switch } from "@/components/ui/switch"; import type { ChatSettings } from "./model-config"; import { cn } from "@/lib/utils"; type FormatMode = "text" | "json" | "json_schema"; const DEFAULT_SCHEMA = '{\n "type": "object",\n "properties": {\n "answer": { "type": "string" }\n },\n "required": ["answer"],\n "additionalProperties": false\n}'; /** Structured output: JSON object / JSON schema (with an editor) — only offered when the model supports it. */ export function StructuredOutputSheet({ open, onOpenChange, settings, onChange }: { open: boolean; onOpenChange: (o: boolean) => void; settings: ChatSettings; onChange: (s: ChatSettings) => void }) { const [mode, setMode] = React.useState(settings.responseFormat?.type ?? "text"); const [schemaText, setSchemaText] = React.useState(() => (settings.responseFormat?.schema ? JSON.stringify(settings.responseFormat.schema, null, 2) : DEFAULT_SCHEMA)); const [error, setError] = React.useState(null); React.useEffect(() => { if (open) { // eslint-disable-next-line react-hooks/set-state-in-effect setMode(settings.responseFormat?.type ?? "text"); setSchemaText(settings.responseFormat?.schema ? JSON.stringify(settings.responseFormat.schema, null, 2) : DEFAULT_SCHEMA); setError(null); } }, [open, settings.responseFormat]); const apply = () => { const next = { ...settings }; if (mode === "text") { delete next.responseFormat; } else if (mode === "json") { next.responseFormat = { type: "json" }; } else { try { const parsed = JSON.parse(schemaText); if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("Schema must be a JSON object"); next.responseFormat = { type: "json_schema", schema: parsed, strict: true }; } catch (e) { setError((e as Error).message); return; } } onChange(next); onOpenChange(false); }; return ( Structured output } description="Ask the model to answer with JSON. Strict schema mode validates the shape when the provider supports it." size="md" footer={
} >
value={mode} onChange={setMode} fill ariaLabel="Response format" options={[{ value: "text", label: "Text" }, { value: "json", label: "JSON object" }, { value: "json_schema", label: "JSON schema" }]} /> {mode === "json_schema" ? (