"use client"; import { useState, useEffect } from "react"; import { Card } from "@/shared/components"; import { getProviderAlias } from "@/shared/constants/providers"; import { getModelKind } from "@/shared/constants/models"; import { getModelsByProviderId } from "@/shared/constants/models"; import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard"; import { Row } from "./exampleShared"; export function SttExampleCard({ providerId }) { const providerAlias = getProviderAlias(providerId); const builtinSttModels = getModelsByProviderId(providerId).filter((m) => getModelKind(m) === "stt"); const [customSttModels, setCustomSttModels] = useState([]); const sttModels = [...builtinSttModels, ...customSttModels]; const [selectedModel, setSelectedModel] = useState(builtinSttModels[0]?.id ?? ""); const selectedModelObj = sttModels.find((m) => m.id === selectedModel); const allowedParams = Array.isArray(selectedModelObj?.params) ? selectedModelObj.params : []; const [audioFile, setAudioFile] = useState(null); const [language, setLanguage] = useState(""); const [prompt, setPrompt] = useState(""); const [responseFormat, setResponseFormat] = useState("json"); const [temperature, setTemperature] = useState(""); const [apiKey, setApiKey] = useState(""); const [useTunnel, setUseTunnel] = useState(false); const [localEndpoint, setLocalEndpoint] = useState(""); const [tunnelEndpoint, setTunnelEndpoint] = useState(""); const [result, setResult] = useState(null); const [latency, setLatency] = useState(null); const [running, setRunning] = useState(false); const [error, setError] = useState(""); const { copied: copiedCurl, copy: copyCurl } = useCopyToClipboard(); const { copied: copiedRes, copy: copyRes } = useCopyToClipboard(); useEffect(() => { setLocalEndpoint(window.location.origin); fetch("/api/keys") .then((r) => r.json()) .then((d) => { setApiKey((d.keys || []).find((k) => k.isActive !== false)?.key || ""); }) .catch(() => {}); fetch("/api/tunnel/status") .then((r) => r.json()) .then((d) => { if (d.publicUrl) setTunnelEndpoint(d.publicUrl); }) .catch(() => {}); const loadCustom = () => { fetch("/api/models/custom", { cache: "no-store" }) .then((r) => r.json()) .then((d) => { const list = (d.models || []).filter((m) => getModelKind(m) === "stt" && m.providerAlias === providerAlias); setCustomSttModels(list); }) .catch(() => {}); }; loadCustom(); window.addEventListener("focus", loadCustom); window.addEventListener("customModelChanged", loadCustom); return () => { window.removeEventListener("focus", loadCustom); window.removeEventListener("customModelChanged", loadCustom); }; }, [providerAlias]); const endpoint = useTunnel ? tunnelEndpoint : localEndpoint; const modelFull = selectedModel ? `${providerAlias}/${selectedModel}` : ""; const curlSnippet = `curl -X POST ${endpoint}/v1/audio/transcriptions \\ -H "Authorization: Bearer ${apiKey || "YOUR_KEY"}" \\ -F "file=@${audioFile?.name || "audio.mp3"}" \\ -F "model=${modelFull}"${allowedParams.includes("language") && language ? ` \\\n -F "language=${language}"` : ""}${allowedParams.includes("response_format") ? ` \\\n -F "response_format=${responseFormat}"` : ""}${allowedParams.includes("temperature") && temperature ? ` \\\n -F "temperature=${temperature}"` : ""}${allowedParams.includes("prompt") && prompt ? ` \\\n -F "prompt=${prompt}"` : ""}`; const handleRun = async () => { if (!audioFile || !modelFull) return; setRunning(true); setError(""); setResult(null); const start = Date.now(); try { const fd = new FormData(); fd.append("file", audioFile); fd.append("model", modelFull); if (allowedParams.includes("language") && language) fd.append("language", language); if (allowedParams.includes("response_format")) fd.append("response_format", responseFormat); if (allowedParams.includes("temperature") && temperature) fd.append("temperature", temperature); if (allowedParams.includes("prompt") && prompt) fd.append("prompt", prompt); const headers = {}; if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`; const res = await fetch("/api/v1/audio/transcriptions", { method: "POST", headers, body: fd }); setLatency(Date.now() - start); const ct = res.headers.get("content-type") || ""; const data = ct.includes("application/json") ? await res.json() : await res.text(); if (!res.ok) { setError(data?.error?.message || data?.error || data || `HTTP ${res.status}`); return; } setResult(data); } catch (e) { setError(e.message || "Network error"); } finally { setRunning(false); } }; const resultStr = typeof result === "string" ? result : (result ? JSON.stringify(result, null, 2) : `{\n "text": "Hello world..."\n}`); return (

Example

{/* Model */} {sttModels.length > 0 ? ( ) : ( setSelectedModel(e.target.value)} placeholder="Enter model id" className="w-full px-3 py-1.5 text-sm border border-border rounded-lg bg-background focus:outline-none focus:border-primary font-mono" /> )} {/* Endpoint */}
{endpoint}/v1/audio/transcriptions {tunnelEndpoint && ( )}
{/* API Key */} {apiKey ? `${apiKey.slice(0, 8)}${"\u2022".repeat(Math.min(20, Math.max(0, apiKey.length - 8)))}` : No key configured} {/* Audio file */}
setAudioFile(e.target.files?.[0] || null)} className="w-full text-xs text-text-muted file:mr-2 file:py-1 file:px-2.5 file:rounded-lg file:border file:border-border file:bg-background file:text-text-main hover:file:bg-sidebar file:cursor-pointer" /> {audioFile && ( {audioFile.name} ยท {(audioFile.size / 1024).toFixed(1)} KB )}
{/* Language (if model supports) */} {allowedParams.includes("language") && ( setLanguage(e.target.value)} placeholder="e.g. en, vi, ja (auto-detect if empty)" className="w-full px-3 py-1.5 text-sm border border-border rounded-lg bg-background focus:outline-none focus:border-primary font-mono" /> )} {/* Prompt (if model supports) */} {allowedParams.includes("prompt") && ( setPrompt(e.target.value)} placeholder="optional context to improve accuracy" className="w-full px-3 py-1.5 text-sm border border-border rounded-lg bg-background focus:outline-none focus:border-primary" /> )} {/* Temperature (if model supports) */} {allowedParams.includes("temperature") && ( setTemperature(e.target.value)} placeholder="0 - 1 (default 0)" className="w-full px-3 py-1.5 text-sm border border-border rounded-lg bg-background focus:outline-none focus:border-primary" /> )} {/* Response format (if model supports) */} {allowedParams.includes("response_format") && ( )} {/* Curl + Run */}
Request
{curlSnippet}
{error &&

{error}

} {/* Response */}
Response {result && latency && ⚡ {latency}ms} {result && ( )}
            {resultStr}
          
); }