fix(dashboard): label audio/embeddings/image compatible providers by kind on ProviderCard (#6936) (#6961)

ProviderCard's compatibility badge used a binary apiType ternary
(responses vs everything-else -> "Chat"), so audio-transcriptions,
audio-speech, images-generations and embeddings compatible providers
(e.g. a locally-hosted speaches TTS/STT server) were mislabeled as
"Chat". Reuse the existing KIND_LABEL map (stt/tts/image/embedding)
instead of adding new i18n keys.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-12 10:23:47 -03:00
committed by GitHub
parent 86af4765f5
commit 152d77cf4b
3 changed files with 108 additions and 1 deletions

View File

@@ -0,0 +1 @@
- fix(dashboard): label audio/embeddings/image compatible providers by kind instead of "Chat" on ProviderCard (#6936)

View File

@@ -42,6 +42,15 @@ const KIND_LABEL: Record<string, string> = {
music: "Music",
};
/** Maps a compatible-provider `apiType` to its `KIND_LABEL` key (#6936: non-chat
* apiTypes — audio/embeddings/image — were falling through to the "Chat" badge). */
const COMPATIBLE_API_TYPE_KIND: Record<string, string> = {
"audio-transcriptions": "stt",
"audio-speech": "tts",
"images-generations": "image",
embeddings: "embedding",
};
interface ProviderCardProps {
providerId: string;
provider: {
@@ -319,7 +328,10 @@ export default function ProviderCard({
))}
{isCompatible && (
<Badge variant="default" size="sm">
{provider.apiType === "responses" ? t("responses") : t("chat")}
{provider.apiType === "responses"
? t("responses")
: (KIND_LABEL[COMPATIBLE_API_TYPE_KIND[provider.apiType ?? ""] ?? ""] ??
t("chat"))}
</Badge>
)}
{isCcCompatible && (

View File

@@ -0,0 +1,94 @@
import React from "react";
import { createRoot } from "react-dom/client";
import { act } from "react";
import { afterEach, describe, expect, it, vi } from "vitest";
import ProviderCard from "../ProviderCard";
vi.mock("next-intl", () => ({ useTranslations: () => (k: string) => k }));
vi.mock("@/shared/components/ProviderTestSlideOver", () => ({ default: () => null }));
vi.mock("@/shared/components/ProviderIcon", () => ({ default: () => null }));
describe("ProviderCard — #6936 audio-transcriptions provider badge", () => {
let container: HTMLDivElement | null = null;
afterEach(() => {
if (container) {
document.body.removeChild(container);
container = null;
}
});
it("does NOT label an audio-transcriptions compatible node as Chat", () => {
container = document.createElement("div");
document.body.appendChild(container);
const root = createRoot(container);
act(() => {
root.render(
<ProviderCard
providerId="openai-compatible-speaches-stt"
provider={{
id: "openai-compatible-speaches-stt",
name: "Speaches-stt",
apiType: "audio-transcriptions",
serviceKinds: [],
}}
stats={{ total: 1, connected: 1, error: 0, warning: 0 }}
authType="apikey"
onToggle={() => {}}
/>
);
});
const text = (container.textContent || "").toLowerCase();
expect(text).not.toContain("chat");
expect(text).toContain("stt");
});
it("labels an audio-speech compatible node as TTS", () => {
container = document.createElement("div");
document.body.appendChild(container);
const root = createRoot(container);
act(() => {
root.render(
<ProviderCard
providerId="openai-compatible-speaches-tts"
provider={{
id: "openai-compatible-speaches-tts",
name: "Speaches-tts",
apiType: "audio-speech",
serviceKinds: [],
}}
stats={{ total: 1, connected: 1, error: 0, warning: 0 }}
authType="apikey"
onToggle={() => {}}
/>
);
});
const text = (container.textContent || "").toLowerCase();
expect(text).not.toContain("chat");
expect(text).toContain("tts");
});
it("still labels a plain chat compatible node as Chat", () => {
container = document.createElement("div");
document.body.appendChild(container);
const root = createRoot(container);
act(() => {
root.render(
<ProviderCard
providerId="openai-compatible-plain"
provider={{
id: "openai-compatible-plain",
name: "Plain-chat",
apiType: "chat",
serviceKinds: [],
}}
stats={{ total: 1, connected: 1, error: 0, warning: 0 }}
authType="apikey"
onToggle={() => {}}
/>
);
});
const text = (container.textContent || "").toLowerCase();
expect(text).toContain("chat");
});
});