


























@@ -7,6 +7,32 @@ import { expectPassthroughReplayPolicy } from "openclaw/plugin-sdk/provider-test
77import { describe, expect, it } from "vitest";
88import plugin from "./index.js";
9910+function requireRecord(value: unknown, label: string): Record<string, unknown> {
11+if (value === null || typeof value !== "object" || Array.isArray(value)) {
12+throw new Error(`expected ${label} to be a record`);
13+}
14+return value as Record<string, unknown>;
15+}
16+17+function requireMapEntry<T>(map: Map<string, T>, id: string): T {
18+const entry = map.get(id);
19+if (!entry) {
20+throw new Error(`expected model ${id}`);
21+}
22+return entry;
23+}
24+25+function requireCatalogEntry(entries: readonly unknown[] | null | undefined, id: string) {
26+if (!entries) {
27+throw new Error("expected supplemental catalog entries");
28+}
29+const entry = entries.find((candidate) => requireRecord(candidate, "catalog entry").id === id);
30+if (!entry) {
31+throw new Error(`expected supplemental catalog entry ${id}`);
32+}
33+return requireRecord(entry, `supplemental catalog entry ${id}`);
34+}
35+1036describe("opencode-go provider plugin", () => {
1137it("registers image media understanding through the OpenCode Go plugin", async () => {
1238const { mediaProviders } = await registerProviderPlugin({
@@ -52,6 +78,8 @@ describe("opencode-go provider plugin", () => {
5278"glm-5.1",
5379"kimi-k2.5",
5480"kimi-k2.6",
81+"mimo-v2-omni",
82+"mimo-v2-pro",
5583"mimo-v2.5",
5684"mimo-v2.5-pro",
5785"minimax-m2.5",
@@ -66,68 +94,71 @@ describe("opencode-go provider plugin", () => {
6694name: model.name,
6795})),
6896} as never);
69-expect(supplemental).toEqual(
70-expect.arrayContaining([
71-expect.objectContaining({
72-provider: "opencode-go",
73-id: "deepseek-v4-pro",
74-name: "DeepSeek V4 Pro",
75-}),
76-expect.objectContaining({
77-provider: "opencode-go",
78-id: "deepseek-v4-flash",
79-name: "DeepSeek V4 Flash",
80-}),
81-]),
82-);
97+const deepSeekPro = requireCatalogEntry(supplemental, "deepseek-v4-pro");
98+expect(deepSeekPro.provider).toBe("opencode-go");
99+expect(deepSeekPro.name).toBe("DeepSeek V4 Pro");
100+const deepSeekFlash = requireCatalogEntry(supplemental, "deepseek-v4-flash");
101+expect(deepSeekFlash.provider).toBe("opencode-go");
102+expect(deepSeekFlash.name).toBe("DeepSeek V4 Flash");
8310384-expect(models.get("kimi-k2.6")).toMatchObject({
85-api: "openai-completions",
86-baseUrl: "https://opencode.ai/zen/go/v1",
87-input: ["text", "image"],
88-reasoning: true,
89-contextWindow: 262_144,
90-maxTokens: 65_536,
91-});
92-expect(models.get("minimax-m2.7")).toMatchObject({
93-api: "openai-completions",
94-baseUrl: "https://opencode.ai/zen/go/v1",
95-reasoning: true,
96-contextWindow: 204_800,
97-maxTokens: 131_072,
98-});
99-expect(models.get("mimo-v2.5-pro")).toMatchObject({
100-api: "openai-completions",
101-baseUrl: "https://opencode.ai/zen/go/v1",
102-input: ["text"],
103-reasoning: true,
104-contextWindow: 1_048_576,
105-maxTokens: 128_000,
106-});
107-expect(models.get("mimo-v2.5")).toMatchObject({
108-input: ["text", "image"],
109-reasoning: true,
110-contextWindow: 1_000_000,
111-maxTokens: 128_000,
112-});
113-expect(
104+const kimi = requireMapEntry(models, "kimi-k2.6");
105+expect(kimi.api).toBe("openai-completions");
106+expect(kimi.baseUrl).toBe("https://opencode.ai/zen/go/v1");
107+expect(kimi.input).toEqual(["text", "image"]);
108+expect(kimi.reasoning).toBe(true);
109+expect(kimi.contextWindow).toBe(262_144);
110+expect(kimi.maxTokens).toBe(65_536);
111+112+const minimax = requireMapEntry(models, "minimax-m2.7");
113+expect(minimax.api).toBe("openai-completions");
114+expect(minimax.baseUrl).toBe("https://opencode.ai/zen/go/v1");
115+expect(minimax.reasoning).toBe(true);
116+expect(minimax.contextWindow).toBe(204_800);
117+expect(minimax.maxTokens).toBe(131_072);
118+119+const mimoPro = requireMapEntry(models, "mimo-v2.5-pro");
120+expect(mimoPro.api).toBe("openai-completions");
121+expect(mimoPro.baseUrl).toBe("https://opencode.ai/zen/go/v1");
122+expect(mimoPro.input).toEqual(["text"]);
123+expect(mimoPro.reasoning).toBe(true);
124+expect(mimoPro.contextWindow).toBe(1_048_576);
125+expect(mimoPro.maxTokens).toBe(128_000);
126+127+const mimo = requireMapEntry(models, "mimo-v2.5");
128+expect(mimo.input).toEqual(["text", "image"]);
129+expect(mimo.reasoning).toBe(true);
130+expect(mimo.contextWindow).toBe(1_000_000);
131+expect(mimo.maxTokens).toBe(128_000);
132+133+const mimoOmni = requireMapEntry(models, "mimo-v2-omni");
134+expect(mimoOmni.input).toEqual(["text", "image"]);
135+expect(mimoOmni.reasoning).toBe(true);
136+expect(mimoOmni.contextWindow).toBe(262_144);
137+expect(mimoOmni.maxTokens).toBe(128_000);
138+139+const mimoV2Pro = requireMapEntry(models, "mimo-v2-pro");
140+expect(mimoV2Pro.input).toEqual(["text"]);
141+expect(mimoV2Pro.reasoning).toBe(true);
142+expect(mimoV2Pro.contextWindow).toBe(1_048_576);
143+expect(mimoV2Pro.maxTokens).toBe(128_000);
144+145+const dynamicModel = requireRecord(
114146provider.resolveDynamicModel?.({
115147modelId: "deepseek-v4-pro",
116148} as never),
117-).toMatchObject({
118-id: "deepseek-v4-pro",
119-api: "openai-completions",
120-provider: "opencode-go",
121-baseUrl: "https://opencode.ai/zen/go/v1",
122-reasoning: true,
123-contextWindow: 1_000_000,
124-maxTokens: 384_000,
125-compat: {
126-supportsUsageInStreaming: true,
127-supportsReasoningEffort: true,
128-maxTokensField: "max_tokens",
129-},
130-});
149+"dynamic model",
150+);
151+expect(dynamicModel.id).toBe("deepseek-v4-pro");
152+expect(dynamicModel.api).toBe("openai-completions");
153+expect(dynamicModel.provider).toBe("opencode-go");
154+expect(dynamicModel.baseUrl).toBe("https://opencode.ai/zen/go/v1");
155+expect(dynamicModel.reasoning).toBe(true);
156+expect(dynamicModel.contextWindow).toBe(1_000_000);
157+expect(dynamicModel.maxTokens).toBe(384_000);
158+const compat = requireRecord(dynamicModel.compat, "dynamic model compat");
159+expect(compat.supportsUsageInStreaming).toBe(true);
160+expect(compat.supportsReasoningEffort).toBe(true);
161+expect(compat.maxTokensField).toBe("max_tokens");
131162});
132163133164it("disables invalid DeepSeek V4 reasoning_effort off payloads on OpenCode Go", async () => {
@@ -169,7 +200,7 @@ describe("opencode-go provider plugin", () => {
169200it("canonicalizes stale OpenCode Go base URLs", async () => {
170201const provider = await registerSingleProviderPlugin(plugin);
171202172-expect(
203+const normalizedConfig = requireRecord(
173204provider.normalizeConfig?.({
174205provider: "opencode-go",
175206providerConfig: {
@@ -178,11 +209,11 @@ describe("opencode-go provider plugin", () => {
178209models: [],
179210},
180211} as never),
181-).toMatchObject({
182- baseUrl: "https://opencode.ai/zen/go/v1",
183-});
212+ "normalized config",
213+);
214+expect(normalizedConfig.baseUrl).toBe("https://opencode.ai/zen/go/v1");
184215185-expect(
216+const normalizedModel = requireRecord(
186217provider.normalizeResolvedModel?.({
187218provider: "opencode-go",
188219model: {
@@ -198,9 +229,9 @@ describe("opencode-go provider plugin", () => {
198229maxTokens: 65_536,
199230},
200231} as never),
201-).toMatchObject({
202- baseUrl: "https://opencode.ai/zen/go/v1",
203-});
232+ "normalized model",
233+);
234+expect(normalizedModel.baseUrl).toBe("https://opencode.ai/zen/go/v1");
204235205236expect(
206237provider.normalizeTransport?.({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。