Files
jiang d58bd19039 feat(agent): unify models and validate OpenCode providers
Generate chat, task and learning models from agent-models.json.
Use WaterNetwork fast/expert models with 272k context and validate
upstream names before readiness. Add an isolated model check command
with signal-safe cleanup and plugin, Skill and MCP management docs.

Verified in isolation: type checks, 26 focused tests and both model
catalog checks; no model inference requests.
2026-10-09 11:28:37 +08:00

74 lines
4.7 KiB
TypeScript

import { describe, expect, it } from "bun:test";
import { checkOpencodeModels } from "../../.opencode/model-check.js";
import { assertModelCheckPassed, type ModelCheckInput } from "../../src/runtime/model-check.js";
const targets: ModelCheckInput = { models: [
{ id: "custom/alias", provider_id: "custom", model_id: "alias", wire_id: "org/model", requires_tools: true },
{ id: "custom/typo", provider_id: "custom", model_id: "typo", wire_id: "does-not-exist", requires_tools: true },
] };
describe("OpenCode model validation", () => {
it("rejects a manually registered invalid name and validates the actual upstream alias", async () => {
const originalFetch = globalThis.fetch;
let queries = 0;
globalThis.fetch = (async (input: unknown, init: RequestInit) => {
queries++;
expect(String(input)).toBe("https://example.com/v1/models");
expect(new Headers(init.headers).get("Authorization")).toBe("Bearer TEST_ONLY_SECRET");
expect(init.redirect).toBe("error");
return Response.json({ data: [{ id: "org/model" }] });
}) as typeof fetch;
try {
const report = await checkOpencodeModels({
model: { list: async () => ({ data: targets.models.map((model) => ({ id: model.model_id, modelID: model.wire_id, providerID: "custom", enabled: true, capabilities: { tools: true } })) }) },
provider: { get: async () => ({ data: { package: "@opencode/ai/providers/openai-compatible", settings: { baseURL: "https://example.com/v1", apiKey: "TEST_ONLY_SECRET" } } }) },
} as never, targets, new AbortController().signal);
expect(report.models.map((model) => model.status)).toEqual(["valid", "not_advertised"]);
expect(queries).toBe(1);
expect(() => assertModelCheckPassed(report)).toThrow("custom/typo: not_advertised");
expect(JSON.stringify(report)).not.toContain("TEST_ONLY_SECRET");
expect(JSON.stringify(report)).not.toContain("example.com");
} finally { globalThis.fetch = originalFetch; }
});
it("reports unavailable, non-tool and mismatched native models", async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = (async () => Response.json({ data: [{ id: "org/model" }, { id: "does-not-exist" }] })) as unknown as typeof fetch;
try {
for (const [native, expected] of [
[undefined, "not_registered"],
[{ enabled: false, modelID: "org/model", capabilities: { tools: true } }, "not_enabled"],
[{ enabled: true, modelID: "org/model", capabilities: { tools: false } }, "no_tools"],
[{ enabled: true, modelID: "different", capabilities: { tools: true } }, "wire_id_mismatch"],
] as const) {
const report = await checkOpencodeModels({
model: { list: async () => ({ data: native ? [{ id: "alias", providerID: "custom", ...native }] : [] }) },
provider: { get: async () => ({ data: { package: "@opencode/ai/providers/openai-compatible", settings: { baseURL: "https://example.com/v1", apiKey: "TEST_ONLY_SECRET" } } }) },
} as never, { models: [targets.models[0]!] }, new AbortController().signal);
expect(report.models[0]!.status).toBe(expected);
expect(() => assertModelCheckPassed(report)).toThrow();
}
} finally { globalThis.fetch = originalFetch; }
});
it("fails closed on auth, empty/invalid inventory and cancellation without exposing upstream errors", async () => {
const originalFetch = globalThis.fetch;
const context = {
model: { list: async () => ({ data: [{ id: "alias", modelID: "org/model", providerID: "custom", enabled: true, capabilities: { tools: true } }] }) },
provider: { get: async () => ({ data: { package: "@opencode/ai/providers/openai-compatible", settings: { baseURL: "https://example.com/v1", apiKey: "TEST_ONLY_SECRET" } } }) },
} as never;
try {
for (const response of [Response.json({ secret: "TEST_ONLY_SECRET" }, { status: 401 }), Response.json({ data: [] }), Response.json({ data: [{ wrong: true }] })]) {
globalThis.fetch = (async () => response) as unknown as typeof fetch;
const report = await checkOpencodeModels(context, { models: [targets.models[0]!] }, new AbortController().signal);
expect(() => assertModelCheckPassed(report)).toThrow();
expect(JSON.stringify(report)).not.toContain("TEST_ONLY_SECRET");
}
globalThis.fetch = (async (_input: unknown, init: RequestInit) => { init.signal!.throwIfAborted(); throw new Error("should not query"); }) as unknown as typeof fetch;
const controller = new AbortController(); controller.abort();
const report = await checkOpencodeModels(context, { models: [targets.models[0]!] }, controller.signal);
expect(report.providers[0]!.status).toBe("catalog_unavailable");
} finally { globalThis.fetch = originalFetch; }
});
});