Support explicit platform provider labels #1847

Merged
hongming merged 1 commits from fix/platform-us-default-provider into main 2026-05-25 14:26:37 +00:00
5 changed files with 37 additions and 0 deletions
@@ -28,6 +28,7 @@ import { useId, useMemo } from "react";
export interface SelectorModel {
id: string;
name?: string;
provider?: string;
required_env?: string[];
}
@@ -88,6 +89,7 @@ interface Props {
/** Vendor keys → human label. Add new vendors here when templates pick
* up new model families. */
const VENDOR_LABELS: Record<string, string> = {
"platform": "Platform",
"anthropic-oauth": "Claude Code subscription",
anthropic: "Anthropic API",
minimax: "MiniMax",
@@ -118,6 +120,8 @@ const VENDOR_LABELS: Record<string, string> = {
/** Optional per-vendor tooltip shown on hover. */
const VENDOR_TOOLTIPS: Record<string, string> = {
"platform":
"Use the Molecule platform-managed LLM proxy. No vendor API key is required.",
"anthropic-oauth":
"Use your Claude.ai (Pro/Max/Team) subscription via OAuth. Run `claude login` in the workspace terminal to mint the token, then paste it here. No API spend.",
anthropic:
@@ -165,6 +169,9 @@ const BARE_VENDOR_PATTERNS: Array<{ test: (id: string) => boolean; vendor: strin
/** Infer a vendor key from a model spec. Combines id-prefix and env
* signals. Exported for tests. */
export function inferVendor(model: SelectorModel): string {
const explicitProvider = model.provider?.trim().toLowerCase();
if (explicitProvider) return explicitProvider;
const id = model.id || "";
const envSet = new Set(model.required_env ?? []);
@@ -44,6 +44,14 @@ const HERMES_MODELS: SelectorModel[] = [
];
describe("inferVendor", () => {
it("uses explicit provider metadata before slug heuristics", () => {
expect(inferVendor({
id: "moonshot/kimi-k2.6",
provider: "platform",
required_env: [],
})).toBe("platform");
});
it("uses slash prefix when present", () => {
expect(inferVendor({ id: "nousresearch/hermes-4-70b", required_env: ["HERMES_API_KEY"] }))
.toBe("nousresearch");
@@ -105,6 +113,22 @@ describe("buildProviderCatalog", () => {
expect(oauth!.models.map((m) => m.id).sort()).toEqual(["haiku", "opus", "sonnet"]);
});
it("labels explicit platform-managed providers", () => {
const catalog = buildProviderCatalog([
{
id: "moonshot/kimi-k2.6",
name: "Kimi K2.6",
provider: "platform",
required_env: [],
},
]);
expect(catalog[0]).toMatchObject({
vendor: "platform",
label: "Platform",
envVars: [],
});
});
it("flags wildcard providers", () => {
const catalog = buildProviderCatalog(HERMES_MODELS);
const hf = catalog.find((p) => p.vendor === "huggingface");
+1
View File
@@ -21,6 +21,7 @@ import { api } from "./api";
export interface ModelSpec {
id: string;
name?: string;
provider?: string;
required_env?: string[];
}
@@ -77,6 +77,7 @@ func NewTemplatesHandler(configsDir string, dockerCli *client.Client, wh *Worksp
type modelSpec struct {
ID string `json:"id" yaml:"id"`
Name string `json:"name,omitempty" yaml:"name"`
Provider string `json:"provider,omitempty" yaml:"provider"`
RequiredEnv []string `json:"required_env,omitempty" yaml:"required_env"`
}
@@ -155,6 +155,7 @@ runtime_config:
required_env: [HERMES_API_KEY]
- id: minimax/minimax-m2.7
name: MiniMax M2.7 (via OpenRouter)
provider: platform
required_env: [OPENROUTER_API_KEY]
skills: []
`
@@ -197,6 +198,9 @@ skills: []
if got.Models[1].ID != "minimax/minimax-m2.7" {
t.Errorf("Models[1].ID: got %q", got.Models[1].ID)
}
if got.Models[1].Provider != "platform" {
t.Errorf("Models[1].Provider: got %q", got.Models[1].Provider)
}
if len(got.Models[1].RequiredEnv) != 1 || got.Models[1].RequiredEnv[0] != "OPENROUTER_API_KEY" {
t.Errorf("Models[1] required_env: want [OPENROUTER_API_KEY], got %+v", got.Models[1].RequiredEnv)
}