feat: configure opencode dsflash-go provider

Refs #2268
This commit is contained in:
UniDesk Codex
2026-06-30 06:17:40 +08:00
parent c1665a0318
commit 2f1d34b36a
4 changed files with 166 additions and 18 deletions
+25
View File
@@ -337,6 +337,7 @@
"imageTagMode": { "type": "string", "enum": ["short", "full"] },
"workbench": { "$ref": "#/$defs/workbenchUiPolicy" },
"observability": { "$ref": "#/$defs/runtimeObservabilityPolicy" },
"opencode": { "$ref": "#/$defs/opencodeRuntime" },
"runtimeStore": { "$ref": "#/$defs/runtimeStore" },
"workbenchRuntime": { "$ref": "#/$defs/workbenchRuntime" },
"sourceRepo": { "type": "string" },
@@ -368,6 +369,30 @@
},
"additionalProperties": false
},
"opencodeRuntime": {
"type": "object",
"properties": {
"providerProfile": { "type": "string", "minLength": 1 },
"providerId": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$" },
"displayName": { "type": "string", "minLength": 1 },
"model": { "type": "string", "minLength": 1 },
"smallModel": { "type": "string", "minLength": 1 },
"baseURL": { "type": "string", "minLength": 1 },
"npm": { "type": "string", "minLength": 1 },
"apiKeyEnv": { "type": "string", "pattern": "^[A-Z0-9_]+$" },
"apiKeySecretRef": { "type": "string", "pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$" },
"contextLimit": { "type": "integer", "minimum": 1 },
"outputLimit": { "type": "integer", "minimum": 1 },
"agentrunSecretNamespace": { "type": "string", "minLength": 1 },
"agentrunSecretName": { "type": "string", "minLength": 1 },
"agentrunSecretKeys": {
"type": "array",
"items": { "type": "string", "minLength": 1 },
"uniqueItems": true
}
},
"additionalProperties": false
},
"runtimeObservabilityPolicy": {
"type": "object",
"properties": {
+18
View File
@@ -335,6 +335,24 @@ lanes:
autoCollapseTerminal: false
observability:
traceExplorerUrlTemplate: /v1/workbench/traces/{trace_id}/events
opencode:
providerProfile: dsflash-go
providerId: dsflash-go
displayName: AgentRun dsflash-go
model: deepseek-v4-flash
smallModel: deepseek-v4-flash
baseURL: https://opencode.ai/zen/go/v1
npm: '@ai-sdk/openai-compatible'
apiKeyEnv: OPENCODE_DSFLASH_GO_API_KEY
apiKeySecretRef: hwlab-code-agent-provider/opencode-api-key
contextLimit: 1000000
outputLimit: 384000
agentrunSecretNamespace: agentrun-v02
agentrunSecretName: agentrun-v01-provider-dsflash-go
agentrunSecretKeys:
- auth.json
- config.toml
- model-catalog.json
externalPostgres:
enabled: true
serviceName: g14-platform-postgres
+104 -18
View File
@@ -415,6 +415,10 @@ function requiredAccessConfigString(value, label) {
return text;
}
function optionalObject(value) {
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
}
async function gitValue(args) {
const result = await execFileAsync("git", args, { cwd: repoRoot, timeout: 10000, maxBuffer: 1024 * 1024 });
return result.stdout.trim();
@@ -987,6 +991,13 @@ function secretNameForProfile(secretName, profile) {
return secretName;
}
function secretRefObjectForProfile(secretRef, profile) {
const ref = configString(secretRef);
const slashIndex = ref.indexOf("/");
assert.ok(slashIndex > 0 && slashIndex < ref.length - 1, `secretRef ${JSON.stringify(secretRef)} must be name/key`);
return { name: secretNameForProfile(ref.slice(0, slashIndex), profile), key: ref.slice(slashIndex + 1), optional: true };
}
function rewritePodSecretRefs(podSpec, profile) {
if (!podSpec || !isRuntimeLane(profile)) return;
for (const volume of podSpec.volumes ?? []) {
@@ -996,20 +1007,7 @@ function rewritePodSecretRefs(podSpec, profile) {
function deployEnvEntry(name, value, namespace, profile = "dev") {
if (typeof value === "string" && value.startsWith("secretRef:")) {
const ref = value.slice("secretRef:".length);
const slashIndex = ref.indexOf("/");
if (slashIndex > 0 && slashIndex < ref.length - 1) {
return {
name,
valueFrom: {
secretKeyRef: {
name: secretNameForProfile(ref.slice(0, slashIndex), profile),
key: ref.slice(slashIndex + 1),
optional: true
}
}
};
}
return { name, valueFrom: { secretKeyRef: secretRefObjectForProfile(value.slice("secretRef:".length), profile) } };
}
return { name, value: namespaceScopedHost(value, namespace) };
}
@@ -5638,7 +5636,81 @@ function secretPlaneIssueLabelValue(issueRef) {
return label;
}
function opencodeServerManifest({ profile = "v03", source }) {
function opencodeRuntimeConfigForProfile(deploy, profile) {
const config = optionalObject(deploy?.lanes?.[profile]?.opencode);
const providerProfile = configString(config.providerProfile) || "dsflash-go";
const providerId = configString(config.providerId) || providerProfile;
const model = configString(config.model) || "deepseek-v4-flash";
const smallModel = configString(config.smallModel) || model;
const apiKeyEnv = configString(config.apiKeyEnv) || opencodeApiKeyEnvName(providerId);
const contextLimit = opencodePositiveInteger(config.contextLimit, 1000000, `deploy.lanes.${profile}.opencode.contextLimit`);
const outputLimit = opencodePositiveInteger(config.outputLimit, 384000, `deploy.lanes.${profile}.opencode.outputLimit`);
return {
providerProfile,
providerId,
displayName: configString(config.displayName) || `AgentRun ${providerProfile}`,
model,
smallModel,
baseURL: configString(config.baseURL) || "https://opencode.ai/zen/go/v1",
npm: configString(config.npm) || "@ai-sdk/openai-compatible",
apiKeyEnv,
apiKeySecretRef: configString(config.apiKeySecretRef) || "hwlab-code-agent-provider/opencode-api-key",
contextLimit,
outputLimit,
agentrunSecretNamespace: configString(config.agentrunSecretNamespace) || "agentrun-v02",
agentrunSecretName: configString(config.agentrunSecretName) || `agentrun-v01-provider-${providerProfile}`,
agentrunSecretKeys: Array.isArray(config.agentrunSecretKeys) && config.agentrunSecretKeys.length > 0
? config.agentrunSecretKeys.map(configString).filter(Boolean)
: ["auth.json", "config.toml", "model-catalog.json"]
};
}
function opencodePositiveInteger(value, fallback, label) {
if (value === undefined || value === null) return fallback;
assert.ok(Number.isInteger(value) && value > 0, `${label} must be a positive integer`);
return value;
}
function opencodeApiKeyEnvName(providerId) {
const normalized = String(providerId || "provider").toUpperCase().replace(/[^A-Z0-9]+/gu, "_").replace(/^_+|_+$/gu, "");
return `OPENCODE_${normalized || "PROVIDER"}_API_KEY`;
}
function opencodeConfigContent(config) {
return JSON.stringify({
$schema: "https://opencode.ai/config.json",
autoupdate: false,
share: "disabled",
model: `${config.providerId}/${config.model}`,
small_model: `${config.providerId}/${config.smallModel}`,
provider: {
[config.providerId]: {
npm: config.npm,
name: config.displayName,
options: {
baseURL: config.baseURL,
apiKey: `{env:${config.apiKeyEnv}}`,
timeout: 600000,
headerTimeout: 600000,
chunkTimeout: 300000
},
models: {
[config.model]: {
name: config.model,
reasoning: true,
tool_call: true,
limit: {
context: config.contextLimit,
output: config.outputLimit
}
}
}
}
}
});
}
function opencodeServerManifest({ profile = "v03", source, deploy = null }) {
assert.ok(isRuntimeLane(profile), `opencode-server profile must be a runtime lane, got ${profile}`);
const namespace = namespaceNameForProfile(profile);
const name = "opencode-server";
@@ -5655,9 +5727,22 @@ function opencodeServerManifest({ profile = "v03", source }) {
"hwlab.pikastech.local/service-id": name,
"hwlab.pikastech.local/source-commit": source.full
};
const annotations = { "hwlab.pikastech.local/rendered-by": "scripts/gitops-render.mjs" };
const opencodeConfig = opencodeRuntimeConfigForProfile(deploy, profile);
const configContent = opencodeConfigContent(opencodeConfig);
const configSha256 = createHash("sha256").update(configContent).digest("hex");
const apiKeySecretRef = secretRefObjectForProfile(opencodeConfig.apiKeySecretRef, profile);
const annotations = {
"hwlab.pikastech.local/rendered-by": "scripts/gitops-render.mjs",
"hwlab.pikastech.local/opencode-provider-profile": opencodeConfig.providerProfile,
"hwlab.pikastech.local/opencode-provider-id": opencodeConfig.providerId,
"hwlab.pikastech.local/opencode-model": opencodeConfig.model,
"hwlab.pikastech.local/opencode-config-sha256": configSha256,
"hwlab.pikastech.local/opencode-api-key-secret-ref": `${apiKeySecretRef.name}/${apiKeySecretRef.key}`,
"hwlab.pikastech.local/agentrun-provider-secret-ref": `${opencodeConfig.agentrunSecretNamespace}/${opencodeConfig.agentrunSecretName}`,
"hwlab.pikastech.local/agentrun-provider-secret-keys": opencodeConfig.agentrunSecretKeys.join(","),
"hwlab.pikastech.local/values-printed": "false"
};
const authSecretName = secretNameForProfile("hwlab-opencode-server-auth", profile);
const configContent = JSON.stringify({ autoupdate: false, share: "disabled" });
return {
apiVersion: "v1",
kind: "List",
@@ -5711,6 +5796,7 @@ function opencodeServerManifest({ profile = "v03", source }) {
{ name: "XDG_CONFIG_HOME", value: "/workspace/.config" },
{ name: "XDG_DATA_HOME", value: "/workspace/.local/share" },
{ name: "OPENCODE_CONFIG_CONTENT", value: configContent },
{ name: opencodeConfig.apiKeyEnv, valueFrom: { secretKeyRef: apiKeySecretRef } },
{ name: "OPENCODE_SERVER_USERNAME", valueFrom: { secretKeyRef: { name: authSecretName, key: "username" } } },
{ name: "OPENCODE_SERVER_PASSWORD", valueFrom: { secretKeyRef: { name: authSecretName, key: "password" } } }
],
@@ -5905,7 +5991,7 @@ async function plannedFiles(args) {
if (workbenchRedis) putJson(`${runtimePath}/workbench-redis.yaml`, workbenchRuntimeRedisManifest({ profile, config: workbenchRedis, source }));
if (isRuntimeLane(profile)) putJson(`${runtimePath}/openfga.yaml`, v02OpenFgaManifest({ profile, source }));
if (isRuntimeLane(profile)) putJson(`${runtimePath}/observability.yaml`, observabilityManifest({ deploy, profile, namespace, labels: profileLabels, annotations, metricsSidecarScript }));
if (isRuntimeLane(profile)) putJson(`${runtimePath}/opencode.yaml`, opencodeServerManifest({ profile, source }));
if (isRuntimeLane(profile)) putJson(`${runtimePath}/opencode.yaml`, opencodeServerManifest({ profile, source, deploy }));
if (includeDeviceAgent) putJson(`${runtimePath}/device-agent-71-freq.yaml`, deviceAgent71FreqManifest({ profile, source, registryPrefix: args.registryPrefix, catalog: artifactCatalog, useDeployImages: args.useDeployImages }));
putJson(`${runtimePath}/node-frpc.yaml`, nodeFrpcManifest({ profile, source }));
}
+19
View File
@@ -691,6 +691,7 @@ test("v03 render includes D518 secret-plane smoke on D518 gitops root", async ()
const generatedPaths = generatedFiles.map((filePath) => path.relative(outDir, filePath));
assert.ok(generatedPaths.includes("runtime-v03/kustomization.yaml"));
assert.ok(generatedPaths.includes("runtime-v03/workloads.yaml"));
assert.ok(generatedPaths.includes("runtime-v03/opencode.yaml"));
assert.equal(generatedPaths.includes("runtime-v03/external-secrets.yaml"), true);
const kustomization = await readFile(path.join(outDir, "runtime-v03", "kustomization.yaml"), "utf8");
assert.match(kustomization, /external-secrets\.yaml/u);
@@ -716,6 +717,24 @@ test("v03 render includes D518 secret-plane smoke on D518 gitops root", async ()
.filter((entry) => entry.name === "HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID");
assert.ok(agentRunNodeEnv.length > 0, "expected D518 node id to be inferred from gitops root");
assert.deepEqual([...new Set(agentRunNodeEnv.map((entry) => entry.value))], ["D518"]);
const opencode = JSON.parse(await readFile(path.join(outDir, "runtime-v03", "opencode.yaml"), "utf8"));
const opencodeDeployment = (opencode.items ?? []).find((item) => item.kind === "Deployment" && item.metadata?.name === "opencode-server");
assert.ok(opencodeDeployment, "expected opencode-server deployment");
assert.equal(opencodeDeployment.metadata?.annotations?.["hwlab.pikastech.local/opencode-provider-profile"], "dsflash-go");
assert.equal(opencodeDeployment.metadata?.annotations?.["hwlab.pikastech.local/agentrun-provider-secret-ref"], "agentrun-v02/agentrun-v01-provider-dsflash-go");
assert.equal(opencodeDeployment.metadata?.annotations?.["hwlab.pikastech.local/values-printed"], "false");
assert.match(opencodeDeployment.spec?.template?.metadata?.annotations?.["hwlab.pikastech.local/opencode-config-sha256"] ?? "", /^[a-f0-9]{64}$/u);
const opencodeContainer = collectContainersFromItem(opencodeDeployment).find((container) => container.name === "opencode-server");
const opencodeEnvEntries = new Map((opencodeContainer?.env ?? []).map((entry) => [entry.name, entry]));
assert.deepEqual(opencodeEnvEntries.get("OPENCODE_DSFLASH_GO_API_KEY")?.valueFrom?.secretKeyRef, { name: "hwlab-v03-code-agent-provider", key: "opencode-api-key", optional: true });
const opencodeConfig = JSON.parse(opencodeEnvEntries.get("OPENCODE_CONFIG_CONTENT")?.value ?? "{}");
assert.equal(opencodeConfig.model, "dsflash-go/deepseek-v4-flash");
assert.equal(opencodeConfig.small_model, "dsflash-go/deepseek-v4-flash");
assert.equal(opencodeConfig.provider["dsflash-go"].npm, "@ai-sdk/openai-compatible");
assert.equal(opencodeConfig.provider["dsflash-go"].options.baseURL, "https://opencode.ai/zen/go/v1");
assert.equal(opencodeConfig.provider["dsflash-go"].options.apiKey, "{env:OPENCODE_DSFLASH_GO_API_KEY}");
assert.deepEqual(opencodeConfig.provider["dsflash-go"].models["deepseek-v4-flash"].limit, { context: 1000000, output: 384000 });
} finally {
await rm(outDir, { recursive: true, force: true });
await rm(scratchDir, { recursive: true, force: true });