fix(opencode): add SiliconFlow provider presets (#4878)

This commit is contained in:
xie well authored and GitHub committed 2026-10-14 17:30:32 +08:00
1 parent cc81627a8e
commit 23dc945e89
2 files changed
+162

No files matched your search

+77
View File
@@ -3110,6 +3110,83 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [
},
},
},
{
name: "SiliconFlow",
family: "siliconflow",
regionKey: "cn",
websiteUrl: "https://siliconflow.cn",
apiKeyUrl: "https://cloud.siliconflow.cn/i/YflgU2Ve",
settingsConfig: {
npm: "@ai-sdk/openai-compatible",
name: "SiliconFlow",
options: {
baseURL: "https://api.siliconflow.cn/v1",
apiKey: "",
setCacheKey: true,
},
models: {
"deepseek-ai/DeepSeek-V4-Flash": {
name: "DeepSeek V4 Flash",
reasoning: true,
limit: { context: 1048576, output: 384000 },
},
},
},
category: "aggregator",
isPartner: true,
partnerPromotionKey: "siliconflow",
icon: "siliconflow",
iconColor: "#6E29F6",
templateValues: {
apiKey: {
label: "API Key",
placeholder: "sk-...",
editorValue: "",
},
},
},
{
name: "SiliconFlow en",
family: "siliconflow",
regionKey: "intl",
websiteUrl: "https://siliconflow.com",
apiKeyUrl: "https://cloud.siliconflow.cn/i/YflgU2Ve",
settingsConfig: {
npm: "@ai-sdk/openai-compatible",
name: "SiliconFlow en",
options: {
baseURL: "https://api.siliconflow.com/v1",
apiKey: "",
setCacheKey: true,
},
models: {
"MiniMaxAI/MiniMax-M3": {
name: "MiniMax M3",
reasoning: true,
limit: { context: 1048576, output: 131072 },
modalities: { input: ["text", "image"], output: ["text"] },
},
"deepseek-ai/DeepSeek-V4.1-Flash": {
name: "DeepSeek V4.1 Flash",
reasoning: true,
limit: { context: 1048576, output: 384000 },
modalities: { input: ["text", "image"], output: ["text"] },
},
},
},
category: "aggregator",
isPartner: true,
partnerPromotionKey: "siliconflow",
icon: "siliconflow",
iconColor: "#000000",
templateValues: {
apiKey: {
label: "API Key",
placeholder: "sk-...",
editorValue: "",
},
},
},
{
name: "AiHubMix",
websiteUrl: "https://aihubmix.com",
@@ -4,6 +4,12 @@ import {
opencodeNpmPackages,
OPENCODE_PRESET_MODEL_VARIANTS,
} from "@/config/opencodeProviderPresets";
import { openclawProviderPresets } from "@/config/openclawProviderPresets";
import { codexProviderPresets } from "@/config/codexProviderPresets";
import {
groupPresetRows,
presetVersionLayout,
} from "@/components/providers/forms/presetGroups";
describe("AWS Bedrock OpenCode Provider Presets", () => {
it("should include @ai-sdk/amazon-bedrock in npm packages", () => {
@@ -161,3 +167,82 @@ describe("OpenCode preset reasoning flags", () => {
}
});
});
describe("OpenCode preset model limits", () => {
// 预设走 V1 格式(npm),OpenCode 要求 limit 同时带 context 和 output,
// 缺一个整份 opencode.json 报 Configuration is invalid,所有供应商一起不可用。
it("declares both context and output whenever a limit is set", () => {
for (const preset of opencodeProviderPresets) {
for (const [id, model] of Object.entries(
preset.settingsConfig.models ?? {},
)) {
if (model.limit === undefined) continue;
expect(model.limit.context, `${preset.name} / ${id}`).toBeTypeOf(
"number",
);
expect(model.limit.output, `${preset.name} / ${id}`).toBeTypeOf(
"number",
);
}
}
});
});
describe("SiliconFlow OpenCode provider presets", () => {
it.each(["SiliconFlow", "SiliconFlow en"])(
"%s includes the current Codex preset models at the matching endpoint",
(name) => {
const preset = opencodeProviderPresets.find(
(item) => item.name === name,
)!;
const openclaw = openclawProviderPresets.find(
(item) => item.name === name,
)!;
const codex = codexProviderPresets.find((item) => item.name === name)!;
expect(preset).toBeDefined();
expect(openclaw).toBeDefined();
expect(codex).toBeDefined();
expect(preset.settingsConfig.npm).toBe("@ai-sdk/openai-compatible");
expect(preset.settingsConfig.options.baseURL).toBe(
openclaw.settingsConfig.baseUrl,
);
expect(preset.settingsConfig.options.apiKey).toBe("");
const codexModels = codex.modelCatalog ?? [];
expect(codexModels.length).toBeGreaterThan(0);
expect(Object.keys(preset.settingsConfig.models)).toEqual(
expect.arrayContaining(codexModels.map((model) => model.model)),
);
for (const model of codexModels) {
expect(preset.settingsConfig.models[model.model].limit?.context).toBe(
model.contextWindow,
);
expect(preset.settingsConfig.models[model.model].reasoning).toBe(true);
}
expect(preset.category).toBe("aggregator");
expect(preset.partnerPromotionKey).toBe(openclaw.partnerPromotionKey);
expect(preset.icon).toBe(openclaw.icon);
},
);
it("groups both regions into one provider with a region selector", () => {
const entries = opencodeProviderPresets.map((preset, index) => ({
id: `opencode-${index}`,
preset,
}));
const rows = groupPresetRows(entries).filter((row) =>
row.versions.some(({ preset }) => preset.name.startsWith("SiliconFlow")),
);
expect(rows).toHaveLength(1);
expect(rows[0].family).toBe("siliconflow");
expect(rows[0].versions.map(({ preset }) => preset.regionKey)).toEqual([
"cn",
"intl",
]);
expect(presetVersionLayout(rows[0].versions)).toEqual({
kind: "single",
dimension: "region",
});
});
});