Files
openclaw/extensions/baseten/models.test.ts
Peter Steinberger 603f839058 refactor(providers): collapse live model discovery onto shared projection hook (#113903)
* feat(plugin-sdk): add live catalog row projection

* refactor(providers): share live catalog projection

* fix(venice): keep live projection internal
2026-07-25 16:17:27 -07:00

217 lines
7.2 KiB
TypeScript

import {
buildOpenAICompatibleLiveModelProviderConfig,
clearLiveCatalogCacheForTests,
type LiveModelCatalogFetchGuard,
} from "openclaw/plugin-sdk/provider-catalog-live-runtime";
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
BASETEN_DEFAULT_MODEL_REF,
BASETEN_MODEL_CATALOG,
buildStaticBasetenModels,
projectBasetenLiveModels,
resolveBasetenDynamicModel,
} from "./models.js";
const TEST_VALUE = "fixture";
async function buildLiveBasetenModels(params: {
discoveryApiKey: string;
fetchGuard: LiveModelCatalogFetchGuard;
}) {
const provider = await buildOpenAICompatibleLiveModelProviderConfig({
providerId: "baseten",
providerConfig: {
baseUrl: "https://inference.baseten.co/v1",
api: "openai-completions",
models: buildStaticBasetenModels(),
},
discoveryApiKey: params.discoveryApiKey,
fetchGuard: params.fetchGuard,
modelDiscovery: {
timeoutMs: 10_000,
ttlMs: 5 * 60 * 1000,
projectRows: projectBasetenLiveModels,
},
});
return provider.models;
}
describe("Baseten model catalog", () => {
beforeEach(() => {
clearLiveCatalogCacheForTests();
});
it("ships every current Baseten Model API with Inkling as the default", () => {
const models = buildStaticBasetenModels();
expect(BASETEN_DEFAULT_MODEL_REF).toBe("baseten/thinkingmachines/inkling");
expect(models).toHaveLength(9);
expect(models.map((model) => model.id)).toEqual(BASETEN_MODEL_CATALOG.map((model) => model.id));
expect(models.find((model) => model.id === "zai-org/GLM-5.2")).toMatchObject({
contextWindow: 524_000,
maxTokens: 262_000,
cost: { input: 1.4, output: 4.4, cacheRead: 0.14, cacheWrite: 0 },
});
expect(models.find((model) => model.id === "zai-org/GLM-5.2-Fast")).toMatchObject({
reasoning: true,
input: ["text"],
contextWindow: 524_000,
maxTokens: 262_000,
cost: { input: 2.1, output: 6.6, cacheRead: 0.21, cacheWrite: 0 },
});
expect(models.find((model) => model.id === "thinkingmachines/inkling")).toMatchObject({
reasoning: true,
input: ["text", "image"],
contextWindow: 1_048_000,
maxTokens: 32_000,
cost: { input: 1, output: 4.05, cacheRead: 0.17, cacheWrite: 0 },
compat: {
supportsStore: false,
supportsDeveloperRole: false,
supportsUsageInStreaming: true,
supportsStrictMode: true,
supportsTools: true,
supportsReasoningEffort: true,
supportedReasoningEfforts: ["none", "minimal", "low", "medium", "high", "xhigh"],
maxTokensField: "max_tokens",
},
});
});
it("projects authenticated live rows while retaining curated capability metadata", () => {
const models = projectBasetenLiveModels([
{
id: "thinkingmachines/inkling",
object: "model",
name: "Inkling live",
context_length: 1_048_576,
max_completion_tokens: 32_768,
pricing: {
prompt: "0.0000011",
completion: "0.0000042",
input_cache_read: "0.00000018",
},
supported_features: ["vision", "reasoning", "reasoning_effort"],
},
{
id: "future/model",
object: "model",
context_length: 64_000,
max_completion_tokens: 4_000,
pricing: { prompt: 0.0000002, completion: 0.0000008, input_cache_read: 0.00000004 },
supported_features: ["vision", "reasoning_effort"],
},
{ id: "future/model", object: "model" },
{ id: "ignored", object: "not-a-model" },
]);
expect(models).toHaveLength(2);
expect(models[0]).toMatchObject({
id: "thinkingmachines/inkling",
name: "Inkling live",
contextWindow: 1_048_576,
maxTokens: 32_768,
cost: { input: 1.1, output: 4.2, cacheRead: 0.18, cacheWrite: 0 },
compat: { supportsReasoningEffort: true, maxTokensField: "max_tokens" },
});
expect(models[1]).toMatchObject({
id: "future/model",
reasoning: true,
input: ["text", "image"],
contextWindow: 64_000,
maxTokens: 4_000,
cost: { input: 0.2, output: 0.8, cacheRead: 0.04, cacheWrite: 0 },
compat: {
supportsReasoningEffort: true,
supportsTools: true,
supportsStrictMode: true,
},
});
});
it("uses live capability metadata when present and curated metadata when absent", () => {
const liveCapabilities = projectBasetenLiveModels([
{
id: "thinkingmachines/inkling",
object: "model",
supported_features: [],
},
])[0];
const curatedCapabilities = projectBasetenLiveModels([
{
id: "thinkingmachines/inkling",
object: "model",
},
])[0];
expect(liveCapabilities).toMatchObject({ reasoning: false, input: ["text"] });
expect(liveCapabilities?.compat?.supportsReasoningEffort).toBeUndefined();
expect(liveCapabilities?.compat?.supportedReasoningEfforts).toBeUndefined();
expect(liveCapabilities?.compat?.reasoningEffortMap).toBeUndefined();
expect(curatedCapabilities).toMatchObject({
reasoning: true,
input: ["text", "image"],
compat: { supportsReasoningEffort: true },
});
});
it("authenticates live discovery and does not cache unusable rows", async () => {
const release = vi.fn(async () => undefined);
const fetchGuard: LiveModelCatalogFetchGuard = vi
.fn()
.mockImplementationOnce(async () => ({
response: Response.json({ data: [{ object: "not-a-model" }] }),
finalUrl: "https://inference.baseten.co/v1/models",
release,
}))
.mockImplementationOnce(async () => ({
response: Response.json({
data: [
{
id: "thinkingmachines/inkling",
object: "model",
context_length: 1_048_576,
max_completion_tokens: 32_768,
supported_features: ["vision", "reasoning", "reasoning_effort"],
},
],
}),
finalUrl: "https://inference.baseten.co/v1/models",
release,
}));
await expect(
buildLiveBasetenModels({ discoveryApiKey: TEST_VALUE, fetchGuard }),
).resolves.toHaveLength(9);
await expect(
buildLiveBasetenModels({ discoveryApiKey: TEST_VALUE, fetchGuard }),
).resolves.toEqual([
expect.objectContaining({
id: "thinkingmachines/inkling",
contextWindow: 1_048_576,
maxTokens: 32_768,
}),
]);
expect(fetchGuard).toHaveBeenCalledTimes(2);
const headers = vi.mocked(fetchGuard).mock.calls[0]?.[0].init?.headers;
expect(headers).toBeInstanceOf(Headers);
if (!(headers instanceof Headers)) {
throw new Error("expected fetch headers");
}
expect(headers.get("Authorization")).toBe(`Bearer ${TEST_VALUE}`);
expect(release).toHaveBeenCalledTimes(2);
});
it("resolves future model ids without shadowing bundled rows", () => {
expect(resolveBasetenDynamicModel("thinkingmachines/inkling")).toBeUndefined();
expect(resolveBasetenDynamicModel("future/model")).toMatchObject({
id: "future/model",
provider: "baseten",
api: "openai-completions",
baseUrl: "https://inference.baseten.co/v1",
compat: { supportsTools: true, maxTokensField: "max_tokens" },
});
});
});