feat: 将 ofcx-go backend 收敛为 dsflash-go

This commit is contained in:
Codex
2026-06-08 01:38:50 +08:00
parent eef59c74ee
commit 41886aef80
14 changed files with 115 additions and 36 deletions
+3 -3
View File
@@ -51,16 +51,16 @@ export const backendProfileSpecs: readonly BackendProfileSpec[] = [
description: "MiniMax M3 OpenAI-compatible profile through Codex app-server stdio",
},
{
profile: "ofcx-go",
profile: "dsflash-go",
backendKind: "codex-app-server-stdio",
protocol: "codex-app-server-jsonrpc-stdio",
transport: "stdio",
command: "codex app-server --listen stdio://",
status: "registered",
requiredSecretKeys: ["auth.json", "config.toml"],
defaultSecretName: "agentrun-v01-provider-ofcx-go",
defaultSecretName: "agentrun-v01-provider-dsflash-go",
profileIsolation: "profile-scoped-codex-home",
description: "OpenCode Zen Go DeepSeek V4 Flash profile through Moon Bridge",
description: "DeepSeek V4 Flash profile through OpenCode Zen Go Moon Bridge",
},
];
+1 -1
View File
@@ -28,7 +28,7 @@ export type FailureKind =
export type RunStatus = "pending" | "claimed" | "running" | "completed" | "failed" | "blocked" | "cancelled";
export type CommandState = "pending" | "acknowledged" | "completed" | "failed" | "cancelled";
export type TerminalStatus = "completed" | "failed" | "blocked" | "cancelled";
export type BackendProfile = "codex" | "deepseek" | "minimax-m3" | "ofcx-go";
export type BackendProfile = "codex" | "deepseek" | "minimax-m3" | "dsflash-go";
export type QueueTaskState = "pending" | "running" | "completed" | "failed" | "blocked" | "cancelled";
export type SessionExecutionState = "idle" | "running" | "terminal";
export type SessionAttentionState = "active" | "unread" | "read";
+38
View File
@@ -137,6 +137,39 @@ ON CONFLICT (profile) DO UPDATE SET
updated_at = EXCLUDED.updated_at;
`;
const dsflashGoBackendProfileMigrationSql = `
UPDATE agentrun_runs SET backend_profile = 'dsflash-go' WHERE backend_profile = 'ofcx-go';
UPDATE agentrun_sessions SET backend_profile = 'dsflash-go' WHERE backend_profile = 'ofcx-go';
UPDATE agentrun_runners SET backend_profile = 'dsflash-go' WHERE backend_profile = 'ofcx-go';
UPDATE agentrun_queue_tasks SET backend_profile = 'dsflash-go' WHERE backend_profile = 'ofcx-go';
UPDATE agentrun_runs
SET execution_policy = replace(replace(replace(execution_policy::text,
'"profile": "ofcx-go"',
'"profile": "dsflash-go"'),
'agentrun-v01-provider-ofcx-go',
'agentrun-v01-provider-dsflash-go'),
'/home/agentrun/.codex-ofcx-go',
'/home/agentrun/.codex-dsflash-go')::jsonb
WHERE execution_policy::text LIKE '%ofcx-go%';
UPDATE agentrun_queue_tasks
SET execution_policy = replace(replace(replace(execution_policy::text,
'"profile": "ofcx-go"',
'"profile": "dsflash-go"'),
'agentrun-v01-provider-ofcx-go',
'agentrun-v01-provider-dsflash-go'),
'/home/agentrun/.codex-ofcx-go',
'/home/agentrun/.codex-dsflash-go')::jsonb
WHERE execution_policy IS NOT NULL AND execution_policy::text LIKE '%ofcx-go%';
DELETE FROM agentrun_backends WHERE profile = 'ofcx-go';
INSERT INTO agentrun_backends (profile, capabilities, capacity, health, updated_at)
VALUES ${backendCapabilitiesSqlValues(["dsflash-go"])}
ON CONFLICT (profile) DO UPDATE SET
capabilities = EXCLUDED.capabilities,
capacity = EXCLUDED.capacity,
health = EXCLUDED.health,
updated_at = EXCLUDED.updated_at;
`;
const sessionControlMigrationSql = `
ALTER TABLE agentrun_sessions ADD COLUMN IF NOT EXISTS version bigint NOT NULL DEFAULT 1;
ALTER TABLE agentrun_sessions ADD COLUMN IF NOT EXISTS execution_state text NOT NULL DEFAULT 'idle';
@@ -322,6 +355,11 @@ const postgresMigrations: MigrationDefinition[] = [
checksum: checksumSql(sessionStateStorageMigrationSql),
sql: sessionStateStorageMigrationSql,
},
{
id: "008_v01_dsflash_go_backend_profile",
checksum: checksumSql(dsflashGoBackendProfileMigrationSql),
sql: dsflashGoBackendProfileMigrationSql,
},
];
export function postgresMigrationContract(): JsonRecord {
+19 -11
View File
@@ -122,7 +122,7 @@ export async function setProviderProfileConfig(profileValue: string, body: unkno
configHashSuffix: shortHash(configToml),
updatedAt: objectPath(applied, ["metadata", "annotations", `${credentialAnnotationPrefix}-updated-at`]) ?? new Date().toISOString(),
delegatedBy,
requiresExternalBridgeUpdate: profile === "deepseek",
requiresExternalBridgeUpdate: profile === "deepseek" || profile === "dsflash-go",
configTomlPrinted: false,
credentialValuesPrinted: false,
valuesPrinted: false,
@@ -180,7 +180,7 @@ export async function setProviderProfileCredential(profileValue: string, body: u
updatedAt: objectPath(applied, ["metadata", "annotations", `${credentialAnnotationPrefix}-updated-at`]) ?? new Date().toISOString(),
configSummary: rendered.config.configSummary,
delegatedBy,
requiresExternalBridgeUpdate: profile === "deepseek",
requiresExternalBridgeUpdate: profile === "deepseek" || profile === "dsflash-go",
valuesPrinted: false,
pollCommands: {
show: `./scripts/agentrun provider-profiles show ${profile}`,
@@ -297,14 +297,15 @@ function authPayload(apiKey: string): JsonRecord {
}
function renderConfigToml(config: ProfileConfig): string {
const { contextWindow, autoCompactTokenLimit } = contextWindowSettings(config.model);
return [
`model_provider = ${tomlString(config.providerName)}`,
`model = ${tomlString(config.model)}`,
`review_model = ${tomlString(config.model)}`,
"disable_response_storage = true",
"network_access = \"enabled\"",
"model_context_window = 128000",
"model_auto_compact_token_limit = 110000",
`model_context_window = ${contextWindow}`,
`model_auto_compact_token_limit = ${autoCompactTokenLimit}`,
"approvals_reviewer = \"user\"",
"",
`[model_providers.${config.providerName}]`,
@@ -368,8 +369,8 @@ async function existingConfigToml(profile: BackendProfile, options: ProviderProf
function existingConfigAllowed(profile: BackendProfile, configToml: string): boolean {
if (configToml.trim().length === 0) return false;
if (profile === "deepseek" && configToml.includes("hyueapi.com")) return false;
if (profile === "deepseek" && !configToml.includes("hwlab-deepseek-proxy.hwlab-v02.svc.cluster.local")) return false;
if ((profile === "deepseek" || profile === "dsflash-go") && configToml.includes("hyueapi.com")) return false;
if ((profile === "deepseek" || profile === "dsflash-go") && !configToml.includes("hwlab-deepseek-proxy.hwlab-v02.svc.cluster.local")) return false;
return true;
}
@@ -414,7 +415,7 @@ function defaultConfig(profile: BackendProfile): ProfileConfig {
displayName: "OpenAI",
};
}
if (profile === "ofcx-go") {
if (profile === "dsflash-go") {
return {
model: "deepseek-v4-flash",
providerName: "opencode",
@@ -451,14 +452,21 @@ function validateBaseUrl(profile: BackendProfile, value: string): void {
} catch {
throw new AgentRunError("schema-invalid", "config.baseUrl must be a valid URL", { httpStatus: 400 });
}
if (profile === "deepseek" && url.hostname === "hyueapi.com") {
throw new AgentRunError("tenant-policy-denied", "deepseek profile must use HWLAB Moon Bridge, not hyueapi.com", { httpStatus: 403 });
if ((profile === "deepseek" || profile === "dsflash-go") && url.hostname === "hyueapi.com") {
throw new AgentRunError("tenant-policy-denied", `${profile} profile must use HWLAB Moon Bridge, not hyueapi.com`, { httpStatus: 403 });
}
if (profile === "deepseek" && url.hostname !== "hwlab-deepseek-proxy.hwlab-v02.svc.cluster.local") {
throw new AgentRunError("tenant-policy-denied", "deepseek profile baseUrl must point to HWLAB v0.2 Moon Bridge", { httpStatus: 403 });
if ((profile === "deepseek" || profile === "dsflash-go") && url.hostname !== "hwlab-deepseek-proxy.hwlab-v02.svc.cluster.local") {
throw new AgentRunError("tenant-policy-denied", `${profile} profile baseUrl must point to HWLAB v0.2 Moon Bridge`, { httpStatus: 403 });
}
}
function contextWindowSettings(model: string): { contextWindow: number; autoCompactTokenLimit: number } {
if (model === "deepseek-v4-pro" || model === "deepseek-v4-flash") {
return { contextWindow: 1_000_000, autoCompactTokenLimit: 900_000 };
}
return { contextWindow: 200_000, autoCompactTokenLimit: 180_000 };
}
function validationExecutionPolicy(profile: BackendProfile, namespace: string): ExecutionPolicy {
const spec = requiredSpec(profile);
return {
+3 -3
View File
@@ -13,9 +13,9 @@ const selfTest: SelfTestCase = async () => {
(error) => error instanceof AgentRunError && error.failureKind === "infra-failed" && error.message.includes("DATABASE_URL is required"),
);
const postgresContract = postgresMigrationContract();
assert.equal(postgresContract.latestMigrationId, "007_v01_session_state_storage");
assert.equal((postgresContract.migrationIds as string[]).includes("007_v01_session_state_storage"), true);
assert.ok(typeof (postgresContract.checksums as Record<string, string>)["007_v01_session_state_storage"] === "string" && (postgresContract.checksums as Record<string, string>)["007_v01_session_state_storage"].length > 0);
assert.equal(postgresContract.latestMigrationId, "008_v01_dsflash_go_backend_profile");
assert.equal((postgresContract.migrationIds as string[]).includes("008_v01_dsflash_go_backend_profile"), true);
assert.ok(typeof (postgresContract.checksums as Record<string, string>)["008_v01_dsflash_go_backend_profile"] === "string" && (postgresContract.checksums as Record<string, string>)["008_v01_dsflash_go_backend_profile"].length > 0);
assert.equal((postgresContract.checksums as Record<string, string>)["002_v01_backend_profiles"], "928b5c490cc4539cb64ecef34784557601b2724fa2870570f16a53576804e49c");
assert.ok(Array.isArray(postgresContract.requiredTables));
assert.ok(postgresContract.requiredTables.includes("agentrun_schema_migrations"));
+16 -1
View File
@@ -86,6 +86,21 @@ const selfTest: SelfTestCase = async (context) => {
assertRunnerJobDoesNotMountProfile(minimaxRendered.manifest as JsonRecord, "deepseek-0");
assertNoSecretLeak(minimaxRendered);
const dsflashGoItem = await createRunWithCommand(client, { ...context, backendProfile: "dsflash-go" }, "dsflash-go job smoke", "selftest-dsflash-go-job-render", 15_000);
const dsflashGoRendered = renderRunnerJobDryRun({
run: await client.get(`/api/v1/runs/${dsflashGoItem.runId}`) as RunRecord,
commandId: dsflashGoItem.commandId,
managerUrl: server.baseUrl,
image: "127.0.0.1:5000/agentrun/agentrun-mgr@sha256:1111111111111111111111111111111111111111111111111111111111111111",
attemptId: "attempt_selftest_dsflash_go",
sourceCommit: "self-test",
});
assertRunnerJobUsesWritableCodexHome(dsflashGoRendered.manifest as JsonRecord, context.deepseekHome, "dsflash-go-0", "/var/run/agentrun/secrets/dsflash-go-0");
assertRunnerJobDoesNotMountProfile(dsflashGoRendered.manifest as JsonRecord, "codex-0");
assertRunnerJobDoesNotMountProfile(dsflashGoRendered.manifest as JsonRecord, "deepseek-0");
assertRunnerJobDoesNotMountProfile(dsflashGoRendered.manifest as JsonRecord, "minimax-m3-0");
assertNoSecretLeak(dsflashGoRendered);
const fakeKubectl = path.join(context.tmp, "fake-kubectl.js");
const createdManifest = path.join(context.tmp, "created-runner-job.json");
await writeFile(fakeKubectl, `#!/usr/bin/env bun
@@ -193,7 +208,7 @@ console.log(JSON.stringify({ apiVersion: manifest.apiVersion, kind: manifest.kin
assert.equal(envMap.get("AGENTRUN_SESSION_PVC_NAMESPACE"), "agentrun-v01");
assert.equal(envMap.get("AGENTRUN_SESSION_PVC_MOUNT_PATH"), "/home/agentrun/.codex-codex/sessions");
assert.equal(envMap.get("AGENTRUN_CODEX_ROLLOUT_SUBDIR"), "sessions");
return { name: "runner-k8s-job", tests: ["runner-k8s-job-dry-run", "runner-k8s-job-deepseek-profile-dry-run", "runner-k8s-job-minimax-m3-profile-dry-run", "runner-k8s-job-create-api", "runner-k8s-job-retention-ttl", "runner-job-transient-env", "runner-job-tool-credential-env", "runner-job-unidesk-ssh-tool-credential-env", "runner-job-unidesk-ssh-transient-env-denied", "runner-k8s-job-session-pvc-volume-and-env"] };
return { name: "runner-k8s-job", tests: ["runner-k8s-job-dry-run", "runner-k8s-job-deepseek-profile-dry-run", "runner-k8s-job-minimax-m3-profile-dry-run", "runner-k8s-job-dsflash-go-profile-dry-run", "runner-k8s-job-create-api", "runner-k8s-job-retention-ttl", "runner-job-transient-env", "runner-job-tool-credential-env", "runner-job-unidesk-ssh-tool-credential-env", "runner-job-unidesk-ssh-transient-env-denied", "runner-k8s-job-session-pvc-volume-and-env"] };
} finally {
await new Promise<void>((resolve) => server.server.close(() => resolve()));
}
+11 -1
View File
@@ -56,6 +56,16 @@ const selfTest: SelfTestCase = async (context) => {
assert.ok(minimaxM3Events.items?.some((event) => event.type === "backend_status" && JSON.stringify(event.payload).includes("minimax-m3")), "minimax-m3 backend_status should include profile metadata");
assertNoSecretLeak(minimaxM3Events);
const dsflashGoHome = path.join(context.tmp, "runtime-dsflash-go-home");
const dsflashGo = await createRunWithCommand(client, { ...context, backendProfile: "dsflash-go" }, "hello dsflash-go", "selftest-dsflash-go-turn", 15_000);
const dsflashGoResult = await runOnce({ managerUrl: server.baseUrl, runId: dsflashGo.runId, backendProfile: "dsflash-go", codexCommand: context.fakeCodexCommand, codexArgs: context.fakeCodexArgs, codexHome: dsflashGoHome, env: { CODEX_HOME: dsflashGoHome, AGENTRUN_CODEX_SECRET_HOME: context.deepseekHome }, oneShot: true });
assert.equal(dsflashGoResult.terminalStatus, "completed");
await access(path.join(dsflashGoHome, "auth.json"));
await access(path.join(dsflashGoHome, "config.toml"));
const dsflashGoEvents = await client.get(`/api/v1/runs/${dsflashGo.runId}/events?afterSeq=0&limit=100`) as { items?: Array<{ type: string; payload: unknown }> };
assert.ok(dsflashGoEvents.items?.some((event) => event.type === "backend_status" && JSON.stringify(event.payload).includes("dsflash-go")), "dsflash-go backend_status should include profile metadata");
assertNoSecretLeak(dsflashGoEvents);
await assert.rejects(
() => createRunWithCommand(client, { ...context, backendProfile: "deepseek", includeOnlyProfile: "codex" }, "missing deepseek", "selftest-deepseek-missing-secret", 15_000),
(error) => error instanceof Error && error.message.includes("requires a matching provider credential"),
@@ -217,7 +227,7 @@ const selfTest: SelfTestCase = async (context) => {
await runSessionStorageSubdirCase({ client, managerUrl: server.baseUrl, context });
await runSessionStorageNoSecretLeakCase({ client, managerUrl: server.baseUrl, context });
return { name: "codex-stdio", tests: ["runner-lease-heartbeat", "runner-lease-conflict-recovery", "codex-stdio-fake-turn", "codex-stdio-projected-writable-home", "codex-stdio-deepseek-profile-fake-turn", "codex-stdio-minimax-m3-profile-fake-turn", "codex-stdio-deepseek-missing-secret-no-fallback", "codex-stdio-minimax-m3-missing-secret-no-fallback", "codex-stdio-config-model-authoritative", "codex-stdio-explicit-model-forwarded", "codex-stdio-final-agent-message-only", "codex-stdio-web-search-progress", "codex-stdio-stale-thread-resume-failed", "codex-stdio-live-tool-events", "codex-stdio-noisy-reasoning-suppression", "codex-stdio-missing-turn-result", "codex-stdio-provider-auth-failed", "codex-stdio-provider-rate-limited", "codex-stdio-provider-invalid-tool-call", "codex-stdio-provider-503-rpc-error", "codex-stdio-provider-503-terminal", "codex-stdio-provider-503-retry-event", "codex-stdio-invalid-json", "codex-stdio-timeout", "codex-stdio-idle-timeout-progress-refresh", "codex-stdio-command-failure-keeps-run-open", "codex-stdio-secret-unavailable", "codex-stdio-spawn-failure"] };
return { name: "codex-stdio", tests: ["runner-lease-heartbeat", "runner-lease-conflict-recovery", "codex-stdio-fake-turn", "codex-stdio-projected-writable-home", "codex-stdio-deepseek-profile-fake-turn", "codex-stdio-dsflash-go-profile-fake-turn", "codex-stdio-minimax-m3-profile-fake-turn", "codex-stdio-deepseek-missing-secret-no-fallback", "codex-stdio-minimax-m3-missing-secret-no-fallback", "codex-stdio-config-model-authoritative", "codex-stdio-explicit-model-forwarded", "codex-stdio-final-agent-message-only", "codex-stdio-web-search-progress", "codex-stdio-stale-thread-resume-failed", "codex-stdio-live-tool-events", "codex-stdio-noisy-reasoning-suppression", "codex-stdio-missing-turn-result", "codex-stdio-provider-auth-failed", "codex-stdio-provider-rate-limited", "codex-stdio-provider-invalid-tool-call", "codex-stdio-provider-503-rpc-error", "codex-stdio-provider-503-terminal", "codex-stdio-provider-503-retry-event", "codex-stdio-invalid-json", "codex-stdio-timeout", "codex-stdio-idle-timeout-progress-refresh", "codex-stdio-command-failure-keeps-run-open", "codex-stdio-secret-unavailable", "codex-stdio-spawn-failure"] };
} finally {
await new Promise<void>((resolve) => server.server.close(() => resolve()));
}
+7 -1
View File
@@ -32,6 +32,12 @@ const selfTest: SelfTestCase = async (context) => {
assert.equal(JSON.stringify(minimaxM3SecretPlan).includes("MiniMax-M3"), false);
assert.equal(JSON.stringify(minimaxM3SecretPlan).includes("api.minimaxi.com"), false);
const dsflashGoSecretPlan = await renderCodexProviderSecretPlan({ profile: "dsflash-go", codexHome: context.deepseekHome, dryRun: true });
assert.equal(dsflashGoSecretPlan.secretName, "agentrun-v01-provider-dsflash-go");
assert.equal(dsflashGoSecretPlan.profile, "dsflash-go");
assert.equal(JSON.stringify(dsflashGoSecretPlan).includes("test-token-material-deepseek"), false);
assert.equal(JSON.stringify(dsflashGoSecretPlan).includes("deepseek-test"), false);
await assert.rejects(
() => renderCodexProviderSecretPlan({ codexHome: path.join(context.tmp, "missing-codex-home"), dryRun: true }),
(error) => error instanceof AgentRunError && error.failureKind === "secret-unavailable",
@@ -56,7 +62,7 @@ const selfTest: SelfTestCase = async (context) => {
(error) => error instanceof AgentRunError && error.failureKind === "schema-invalid",
);
return { name: "secret-render", tests: ["codex-secret-dry-run", "deepseek-secret-dry-run", "minimax-m3-secret-dry-run"] };
return { name: "secret-render", tests: ["codex-secret-dry-run", "deepseek-secret-dry-run", "minimax-m3-secret-dry-run", "dsflash-go-secret-dry-run"] };
};
export default selfTest;
@@ -13,8 +13,8 @@ const selfTest: SelfTestCase = async (context) => {
const gitopsRenderer = await readFile(path.join(context.root, "scripts/src/gitops-render.ts"), "utf8");
assert.equal(gitopsRenderer.includes("agentrun-v01-mgr-provider-secret-manager"), true);
assert.equal(gitopsRenderer.includes('verbs: ["get", "patch", "update"]'), true);
assert.equal(gitopsRenderer.includes('resourceNames: ["agentrun-v01-provider-codex", "agentrun-v01-provider-deepseek", "agentrun-v01-provider-minimax-m3", "agentrun-v01-provider-ofcx-go"]'), true);
for (const profile of ["codex", "deepseek", "minimax-m3", "ofcx-go"]) {
assert.equal(gitopsRenderer.includes('resourceNames: ["agentrun-v01-provider-codex", "agentrun-v01-provider-deepseek", "agentrun-v01-provider-minimax-m3", "agentrun-v01-provider-dsflash-go"]'), true);
for (const profile of ["codex", "deepseek", "minimax-m3", "dsflash-go"]) {
assert.equal(gitopsRenderer.includes(`agentrun-v01-provider-${profile}`), true);
}
@@ -56,8 +56,9 @@ console.log(JSON.stringify({ apiVersion: manifest.apiVersion, kind: manifest.kin
async function assertBackendPreflight(client: ManagerClient): Promise<void> {
const response = await client.get("/api/v1/backends") as { items?: JsonRecord[] };
const items = response.items ?? [];
assert.ok(items.length >= 3, "codex/deepseek/minimax-m3 backend capabilities should be visible");
assert.ok(items.length >= 4, "codex/deepseek/minimax-m3/dsflash-go backend capabilities should be visible");
assert.ok(items.some((item) => item.profile === "minimax-m3"), "minimax-m3 backend capability should be visible");
assert.ok(items.some((item) => item.profile === "dsflash-go"), "dsflash-go backend capability should be visible");
for (const item of items) {
const preflight = item.preflight as JsonRecord;
const defaultSecretRef = item.defaultSecretRef as JsonRecord;
+1
View File
@@ -109,6 +109,7 @@ export function assertNoSecretLeak(value: unknown): void {
export function profileSecretHome(context: Pick<SelfTestContext, "codexHome"> & Partial<Pick<SelfTestContext, "deepseekHome" | "minimaxM3Home">>, profile: BackendProfile): string {
if (profile === "deepseek") return context.deepseekHome ?? context.codexHome;
if (profile === "dsflash-go") return context.deepseekHome ?? context.codexHome;
if (profile === "minimax-m3") return context.minimaxM3Home ?? context.codexHome;
return context.codexHome;
}