fix(ui): use test-connection endpoint for TTS credential validation

Update CredentialsSection to call /v1/tts/test-connection instead of
/v1/tts/synthesize - allows testing unsaved credentials before save.
This commit is contained in:
viettranx committed 2026-04-16 17:55:34 +07:00
1 parent 7c5630c27b
commit 82fe289918
3 files changed
+51 -8

No files matched your search

+34 -1
View File
@@ -52,6 +52,22 @@ export interface SynthesizeParams {
model_id?: string;
}
export interface TestConnectionParams {
provider: string;
api_key?: string;
api_base?: string;
voice_id?: string;
model_id?: string;
group_id?: string;
}
export interface TestConnectionResult {
success: boolean;
provider?: string;
latency_ms?: number;
error?: string;
}
export function useTtsConfig() {
const ws = useWs();
const http = useHttp();
@@ -114,5 +130,22 @@ export function useTtsConfig() {
[http],
);
return { tts, loading, saving, error, refresh: invalidate, save, synthesize };
// Test connection with unsaved credentials — uses ephemeral provider.
const testConnection = useCallback(
async (params: TestConnectionParams): Promise<TestConnectionResult> => {
const res = await fetch("/v1/tts/test-connection", {
method: "POST",
headers: { "Content-Type": "application/json", ...http.getAuthHeaders() },
body: JSON.stringify(params),
});
const data = (await res.json()) as TestConnectionResult;
if (!res.ok || !data.success) {
throw new Error(data.error || `Test failed (${res.status})`);
}
return data;
},
[http],
);
return { tts, loading, saving, error, refresh: invalidate, save, synthesize, testConnection };
}
@@ -13,7 +13,7 @@ import { Label } from "@/components/ui/label";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { toast } from "@/stores/use-toast-store";
import { getProviderDefinition } from "@/data/tts-providers";
import type { TtsConfig, TtsProviderConfig, SynthesizeParams } from "../hooks/use-tts-config";
import type { TtsConfig, TtsProviderConfig, TestConnectionParams, TestConnectionResult } from "../hooks/use-tts-config";
interface Props {
provider: string;
@@ -22,10 +22,10 @@ interface Props {
providerKey: keyof Pick<TtsConfig, "openai" | "elevenlabs" | "edge" | "minimax">,
patch: Partial<TtsProviderConfig>,
) => void;
synthesize: (params: SynthesizeParams) => Promise<Blob>;
testConnection: (params: TestConnectionParams) => Promise<TestConnectionResult>;
}
export function CredentialsSection({ provider, draft, onUpdate, synthesize }: Props) {
export function CredentialsSection({ provider, draft, onUpdate, testConnection }: Props) {
const { t } = useTranslation("tts");
const [testing, setTesting] = useState(false);
@@ -36,8 +36,18 @@ export function CredentialsSection({ provider, draft, onUpdate, synthesize }: Pr
const handleTestConnection = async () => {
setTesting(true);
try {
await synthesize({ text: "Hello, this is a connection test.", provider });
toast.success(t("testConnection.success", "Connection successful"));
// Build params from draft credentials — test with unsaved config
const cfg = draft[provider as keyof Pick<typeof draft, "openai" | "elevenlabs" | "minimax">];
const params: TestConnectionParams = {
provider,
api_key: cfg?.api_key,
api_base: cfg?.api_base || cfg?.base_url,
voice_id: cfg?.voice_id || cfg?.voice,
model_id: cfg?.model_id || cfg?.model,
group_id: (cfg as { group_id?: string })?.group_id,
};
const result = await testConnection(params);
toast.success(t("testConnection.success", "Connection successful"), `${result.latency_ms}ms`);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
toast.error(t("testConnection.failed", "Connection failed"), msg);
+2 -2
View File
@@ -68,7 +68,7 @@ function modelPatch(provider: string, value: string): [ProviderKey, Partial<TtsP
export function TtsPage() {
const { t } = useTranslation("tts");
const { t: tc } = useTranslation("common");
const { tts, loading, saving, error, refresh, save, synthesize } = useTtsConfig();
const { tts, loading, saving, error, refresh, save, synthesize, testConnection } = useTtsConfig();
const spinning = useMinLoading(loading);
const [draft, setDraft] = useState<TtsConfig>(tts);
@@ -138,7 +138,7 @@ export function TtsPage() {
provider={draft.provider}
draft={draft}
onUpdate={updateProvider}
synthesize={synthesize}
testConnection={testConnection}
/>
)}