feat(providers): live discovery for opencode, opencode-go, minimax
Some checks are pending
CI / detect-changes (push) Waiting to run
CI / docs-check (push) Blocked by required conditions
CI / lint (push) Blocked by required conditions
CI / build (push) Blocked by required conditions
CI / integration-tests (push) Blocked by required conditions
CI / windows-portability (push) Blocked by required conditions
CI / rtk-portability (linux, blacksmith-4vcpu-ubuntu-2404) (push) Blocked by required conditions
CI / rtk-portability (macos, macos-15) (push) Blocked by required conditions
CI / rtk-portability (windows, blacksmith-4vcpu-windows-2025) (push) Blocked by required conditions
Some checks are pending
CI / detect-changes (push) Waiting to run
CI / docs-check (push) Blocked by required conditions
CI / lint (push) Blocked by required conditions
CI / build (push) Blocked by required conditions
CI / integration-tests (push) Blocked by required conditions
CI / windows-portability (push) Blocked by required conditions
CI / rtk-portability (linux, blacksmith-4vcpu-ubuntu-2404) (push) Blocked by required conditions
CI / rtk-portability (macos, macos-15) (push) Blocked by required conditions
CI / rtk-portability (windows, blacksmith-4vcpu-windows-2025) (push) Blocked by required conditions
Three providers were missing from PROVIDER_CATALOG_CONFIG so their
model lists couldn't be auto-discovered. Their wire ids only existed
in packages/ai/src/models.generated.ts as hand-coded entries, meaning
new model variants from these providers required manual catalog edits.
Verified live endpoints respond to /v1/models with bearer auth:
- opencode → https://opencode.ai/zen/v1/models (6 free models)
- opencode-go → https://opencode.ai/zen/go/v1/models (15 models)
- minimax → https://api.minimax.io/v1/models (works)
Added entries:
opencode: baseUrl https://opencode.ai/zen, modelsPath /v1/models
opencode-go: baseUrl https://opencode.ai/zen/go, modelsPath /v1/models
minimax: baseUrl https://api.minimax.io, modelsPath /v1/models
(international endpoint; Chinese-network api.minimaxi.com
still handled separately in the SDK)
Auth keys already wired: OPENCODE_API_KEY, OPENCODE_GO_API_KEY (with
OPENCODE_API_KEY fallback), MINIMAX_API_KEY. No env-api-keys.ts changes.
Combined with 385e0b448 (dynamic canonicalIdFor resolver), new model
variants from these three providers will be auto-grouped in
.sf/model-performance.json without hand-editing CANONICAL_BY_ROUTE.
Live counts after fresh discovery will reveal experimental models
absent from static catalog (e.g. opencode's "big-pickle", opencode-go's
deepseek-v4-pro, mimo-v2.5-pro, hy3-preview). The model-router
tolerates unconventional wire IDs — no naming constraints.
To populate cache: rm -rf ~/.sf/runtime/model-catalog/ + relaunch sf.
Tests: 13 new in provider-catalog-discovery.test.mjs (catalog shape,
modelsPath presence, DISCOVERABLE_PROVIDER_IDS inclusion). Full suite
183 files / 1940 tests pass, zero regressions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
db3525b933
commit
1b5348e28e
2 changed files with 151 additions and 0 deletions
|
|
@ -141,6 +141,49 @@ export const PROVIDER_CATALOG_CONFIG = {
|
|||
auth: { type: "bearer" },
|
||||
rateLimits: { scope: "model" },
|
||||
},
|
||||
|
||||
// ─── MiniMax ─────────────────────────────────────────────────────────────
|
||||
// International endpoint (api.minimax.io) — Chinese-network endpoint
|
||||
// (api.minimaxi.com/anthropic) is handled separately in the SDK layer.
|
||||
minimax: {
|
||||
type: "openai",
|
||||
baseUrl: "https://api.minimax.io",
|
||||
modelsPath: "/v1/models",
|
||||
auth: { type: "bearer" },
|
||||
rateLimits: { scope: "provider" },
|
||||
modelFilter: {
|
||||
excludePatterns: [],
|
||||
},
|
||||
},
|
||||
|
||||
// ─── OpenCode Zen ─────────────────────────────────────────────────────────
|
||||
// Free-tier routing layer — all models are zero-cost. IDs may use "-free"
|
||||
// suffix or unconventional names (e.g. "big-pickle"); no additional filter.
|
||||
opencode: {
|
||||
type: "openai",
|
||||
baseUrl: "https://opencode.ai/zen",
|
||||
modelsPath: "/v1/models",
|
||||
auth: { type: "bearer" },
|
||||
rateLimits: { scope: "provider" },
|
||||
modelFilter: {
|
||||
// All opencode entries are intended to be free-tier; the wire IDs
|
||||
// typically end in "-free" but also include experimental zero-cost
|
||||
// models (e.g. big-pickle). No additional filtering needed.
|
||||
excludePatterns: [],
|
||||
},
|
||||
},
|
||||
|
||||
// ─── OpenCode Go ─────────────────────────────────────────────────────────
|
||||
// Paid/commercial tier at /zen/go — exposes upstream model IDs directly
|
||||
// (minimax-m2.7, kimi-k2.6, deepseek-v4-pro, etc.) without a "-free" suffix.
|
||||
"opencode-go": {
|
||||
type: "openai",
|
||||
baseUrl: "https://opencode.ai/zen/go",
|
||||
modelsPath: "/v1/models",
|
||||
auth: { type: "bearer" },
|
||||
rateLimits: { scope: "provider" },
|
||||
modelFilter: { excludePatterns: [] },
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* provider-catalog-discovery.test.mjs
|
||||
*
|
||||
* Regression tests for live-discovery entries in provider-catalog-config.js.
|
||||
* Covers: opencode, opencode-go, and minimax baseline fields.
|
||||
*/
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, test } from "vitest";
|
||||
import {
|
||||
DISCOVERABLE_PROVIDER_IDS,
|
||||
getProviderCatalogConfig,
|
||||
} from "../provider-catalog-config.js";
|
||||
|
||||
describe("opencode discovery config", () => {
|
||||
test("baseUrl points to /zen path", () => {
|
||||
const cfg = getProviderCatalogConfig("opencode");
|
||||
assert.ok(cfg, "opencode entry must exist");
|
||||
assert.equal(cfg.baseUrl, "https://opencode.ai/zen");
|
||||
});
|
||||
|
||||
test("modelsPath is /v1/models", () => {
|
||||
const cfg = getProviderCatalogConfig("opencode");
|
||||
assert.equal(cfg.modelsPath, "/v1/models");
|
||||
});
|
||||
|
||||
test("auth type is bearer", () => {
|
||||
const cfg = getProviderCatalogConfig("opencode");
|
||||
assert.equal(cfg.auth.type, "bearer");
|
||||
});
|
||||
|
||||
test("is included in DISCOVERABLE_PROVIDER_IDS", () => {
|
||||
assert.ok(
|
||||
DISCOVERABLE_PROVIDER_IDS.includes("opencode"),
|
||||
"opencode should be discoverable",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("opencode-go discovery config", () => {
|
||||
test("baseUrl points to /zen/go path", () => {
|
||||
const cfg = getProviderCatalogConfig("opencode-go");
|
||||
assert.ok(cfg, "opencode-go entry must exist");
|
||||
assert.equal(cfg.baseUrl, "https://opencode.ai/zen/go");
|
||||
});
|
||||
|
||||
test("modelsPath is /v1/models", () => {
|
||||
const cfg = getProviderCatalogConfig("opencode-go");
|
||||
assert.equal(cfg.modelsPath, "/v1/models");
|
||||
});
|
||||
|
||||
test("auth type is bearer", () => {
|
||||
const cfg = getProviderCatalogConfig("opencode-go");
|
||||
assert.equal(cfg.auth.type, "bearer");
|
||||
});
|
||||
|
||||
test("is included in DISCOVERABLE_PROVIDER_IDS", () => {
|
||||
assert.ok(
|
||||
DISCOVERABLE_PROVIDER_IDS.includes("opencode-go"),
|
||||
"opencode-go should be discoverable",
|
||||
);
|
||||
});
|
||||
|
||||
test("baseUrl has /go suffix (distinct from opencode)", () => {
|
||||
const opencodeBase = getProviderCatalogConfig("opencode").baseUrl;
|
||||
const goBase = getProviderCatalogConfig("opencode-go").baseUrl;
|
||||
assert.ok(
|
||||
goBase.endsWith("/go"),
|
||||
"opencode-go baseUrl must end in /go",
|
||||
);
|
||||
assert.notEqual(goBase, opencodeBase, "opencode-go baseUrl must differ from opencode");
|
||||
});
|
||||
});
|
||||
|
||||
describe("minimax discovery config", () => {
|
||||
test("baseUrl uses international api.minimax.io endpoint", () => {
|
||||
const cfg = getProviderCatalogConfig("minimax");
|
||||
assert.ok(cfg, "minimax entry must exist");
|
||||
assert.ok(
|
||||
cfg.baseUrl.includes("api.minimax.io"),
|
||||
`expected api.minimax.io, got ${cfg.baseUrl}`,
|
||||
);
|
||||
assert.ok(
|
||||
!cfg.baseUrl.includes("minimaxi.com"),
|
||||
"baseUrl must not use minimaxi.com (Chinese-network endpoint)",
|
||||
);
|
||||
});
|
||||
|
||||
test("modelsPath is /v1/models", () => {
|
||||
const cfg = getProviderCatalogConfig("minimax");
|
||||
assert.equal(cfg.modelsPath, "/v1/models");
|
||||
});
|
||||
|
||||
test("is included in DISCOVERABLE_PROVIDER_IDS", () => {
|
||||
assert.ok(
|
||||
DISCOVERABLE_PROVIDER_IDS.includes("minimax"),
|
||||
"minimax should be discoverable",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("DISCOVERABLE_PROVIDER_IDS completeness", () => {
|
||||
test("includes all three newly added providers", () => {
|
||||
const ids = DISCOVERABLE_PROVIDER_IDS;
|
||||
assert.ok(ids.includes("opencode"), "missing opencode");
|
||||
assert.ok(ids.includes("opencode-go"), "missing opencode-go");
|
||||
assert.ok(ids.includes("minimax"), "missing minimax");
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue