fix: 接入 dsflash-go model catalog

This commit is contained in:
Codex
2026-06-08 23:31:33 +08:00
parent 1bd65a4d1a
commit 6dd8c75528
27 changed files with 485 additions and 123 deletions
+15
View File
@@ -162,6 +162,16 @@ SET execution_policy = replace(replace(replace(execution_policy::text,
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"], { requiredSecretKeysByProfile: { "dsflash-go": ["auth.json", "config.toml"] } })}
ON CONFLICT (profile) DO UPDATE SET
capabilities = EXCLUDED.capabilities,
capacity = EXCLUDED.capacity,
health = EXCLUDED.health,
updated_at = EXCLUDED.updated_at;
`;
const dsflashGoModelCatalogBackendProfileMigrationSql = `
INSERT INTO agentrun_backends (profile, capabilities, capacity, health, updated_at)
VALUES ${backendCapabilitiesSqlValues(["dsflash-go"])}
ON CONFLICT (profile) DO UPDATE SET
capabilities = EXCLUDED.capabilities,
@@ -360,6 +370,11 @@ const postgresMigrations: MigrationDefinition[] = [
checksum: checksumSql(dsflashGoBackendProfileMigrationSql),
sql: dsflashGoBackendProfileMigrationSql,
},
{
id: "009_v01_dsflash_go_model_catalog",
checksum: checksumSql(dsflashGoModelCatalogBackendProfileMigrationSql),
sql: dsflashGoModelCatalogBackendProfileMigrationSql,
},
];
export function postgresMigrationContract(): JsonRecord {
+80 -17
View File
@@ -2,6 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
import { spawn } from "node:child_process";
import { AgentRunError } from "../common/errors.js";
import { backendProfileSpec, backendProfileSpecs, isBackendProfileSlug } from "../common/backend-profiles.js";
import { dsflashGoModelCatalogFile, dsflashGoModelCatalogJson, dsflashGoModelSlug, dsflashGoRuntimeModelCatalogPath } from "../common/model-catalogs.js";
import type { AgentRunStore } from "./store.js";
import type { BackendProfile, ExecutionPolicy, JsonRecord, JsonValue } from "../common/types.js";
import { asRecord, validateBackendProfile } from "../common/validation.js";
@@ -33,6 +34,7 @@ interface ProfileConfig {
envKey: string;
wireApi: "responses";
displayName: string;
modelCatalogPath?: string;
}
interface RenderedConfig {
@@ -110,13 +112,14 @@ export async function setProviderProfileConfig(profileValue: string, body: unkno
const profile = validateBackendProfile(profileValue);
const spec = requiredSpec(profile);
const record = asRecord(body ?? {}, "providerProfileConfig");
const configToml = configTomlField(record);
const configToml = configTomlField(record, profile);
const delegatedBy = delegatedBySummary(record.delegatedBy);
const namespace = profileNamespace(options);
const existingSecret = await kubectlGetSecret(spec.defaultSecretName, namespace, options.kubectlCommand ?? "kubectl");
const existingData = asOptionalRecord(existingSecret?.data);
const authJsonData = dataKey(existingData, "auth.json");
const credentialHashSuffix = hashDataKey(existingData, "auth.json");
const secretData = providerProfileSecretData({ profile, authJsonData, configToml, existingData });
const secretManifest: JsonRecord = {
apiVersion: "v1",
kind: "Secret",
@@ -136,14 +139,14 @@ export async function setProviderProfileConfig(profileValue: string, body: unkno
},
},
type: "Opaque",
data: providerProfileSecretData({ authJsonData, configToml }),
data: secretData,
};
const applied = await kubectlUpsertSecret(secretManifest, options.kubectlCommand ?? "kubectl");
return {
action: "provider-profile-config-updated",
mutation: true,
profile,
configured: Boolean(authJsonData),
configured: hasRequiredKeys(secretData, spec.requiredSecretKeys),
secretRef: secretRefSummary(profile, namespace),
resourceVersion: objectPath(applied, ["metadata", "resourceVersion"]),
credentialHashSuffix: credentialHashSuffix ?? null,
@@ -169,8 +172,11 @@ export async function setProviderProfileCredential(profileValue: string, body: u
const record = asRecord(body ?? {}, "providerProfileCredential");
const credential = credentialAuthJson(record);
const delegatedBy = delegatedBySummary(record.delegatedBy);
const renderedConfig = await renderedConfigForWrite(profile, record, options);
const namespace = profileNamespace(options);
const existingSecret = await kubectlGetSecret(spec.defaultSecretName, namespace, options.kubectlCommand ?? "kubectl");
const existingData = asOptionalRecord(existingSecret?.data);
const renderedConfig = await renderedConfigForWrite(profile, record, options);
const secretData = providerProfileSecretData({ profile, authJsonData: base64Data(credential.authJson), configToml: renderedConfig.configToml, existingData });
const secretManifest: JsonRecord = {
apiVersion: "v1",
kind: "Secret",
@@ -190,17 +196,14 @@ export async function setProviderProfileCredential(profileValue: string, body: u
},
},
type: "Opaque",
data: {
"auth.json": base64Data(credential.authJson),
"config.toml": base64Data(renderedConfig.configToml),
},
data: secretData,
};
const applied = await kubectlUpsertSecret(secretManifest, options.kubectlCommand ?? "kubectl");
return {
action: "provider-profile-credential-updated",
mutation: true,
profile,
configured: true,
configured: hasRequiredKeys(secretData, spec.requiredSecretKeys),
secretRef: secretRefSummary(profile, namespace),
resourceVersion: objectPath(applied, ["metadata", "resourceVersion"]),
credentialHashSuffix: shortHash(credential.authJson),
@@ -357,12 +360,18 @@ function profileFromSecretName(name: string | null): string | null {
return profile.length > 0 ? profile : null;
}
function providerProfileSecretData(input: { authJsonData?: string | null; configToml: string }): JsonRecord {
function providerProfileSecretData(input: { profile: BackendProfile; authJsonData?: string | null; configToml: string; existingData?: JsonRecord | null }): JsonRecord {
const data: JsonRecord = { "config.toml": base64Data(input.configToml) };
if (input.authJsonData) data["auth.json"] = input.authJsonData;
const modelCatalogData = input.profile === "dsflash-go" ? dataKey(input.existingData ?? null, dsflashGoModelCatalogFile) ?? defaultModelCatalogData(input.profile) : null;
if (modelCatalogData) data[dsflashGoModelCatalogFile] = modelCatalogData;
return data;
}
function defaultModelCatalogData(profile: BackendProfile): string | null {
return profile === "dsflash-go" ? base64Data(dsflashGoModelCatalogJson()) : null;
}
function credentialAuthJson(record: JsonRecord): { authJson: string; source: "auth-json" | "api-key" } {
const authJson = record.authJson;
if (typeof authJson === "string" && authJson.trim().length > 0) return { authJson: authJsonField(authJson), source: "auth-json" };
@@ -397,6 +406,7 @@ function renderConfigToml(config: ProfileConfig): string {
"network_access = \"enabled\"",
`model_context_window = ${contextWindow}`,
`model_auto_compact_token_limit = ${autoCompactTokenLimit}`,
...(config.modelCatalogPath ? [`model_catalog_json = ${tomlString(config.modelCatalogPath)}`] : []),
"approvals_reviewer = \"user\"",
"",
`[model_providers.${config.providerName}]`,
@@ -439,6 +449,7 @@ function renderedConfigFromProfileConfig(config: ProfileConfig, source: string):
baseUrl: config.baseUrl,
authField: config.envKey,
wireApi: config.wireApi,
modelCatalogJson: config.modelCatalogPath ?? null,
valuesPrinted: false,
},
};
@@ -461,7 +472,18 @@ async function existingConfigToml(profile: BackendProfile, options: ProviderProf
function existingConfigAllowed(profile: BackendProfile, configToml: string): boolean {
if (configToml.trim().length === 0) 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;
if (profile === "deepseek" && !configToml.includes("hwlab-deepseek-proxy.hwlab-v02.svc.cluster.local")) return false;
if (profile === "dsflash-go") {
if (!configToml.includes(dsflashGoModelSlug)) return false;
if (modelCatalogPathFromConfigToml(configToml) !== dsflashGoRuntimeModelCatalogPath) return false;
const baseUrl = baseUrlFromConfigToml(configToml);
if (!baseUrl) return false;
try {
if (!isAllowedDsflashBridgeUrl(new URL(baseUrl))) return false;
} catch {
return false;
}
}
return true;
}
@@ -475,9 +497,10 @@ function configTomlFromData(data: JsonRecord | null, profile: BackendProfile): s
}
}
function configTomlField(record: JsonRecord): string {
function configTomlField(record: JsonRecord, profile: BackendProfile): string {
const value = record.configToml;
if (typeof value !== "string" || value.trim().length === 0) throw new AgentRunError("schema-invalid", "configToml is required", { httpStatus: 400 });
validateConfigTomlForProfile(profile, value);
return value;
}
@@ -489,6 +512,7 @@ function configField(value: unknown, profile: BackendProfile): ProfileConfig {
const baseUrl = optionalString(record.baseUrl) ?? defaults.baseUrl;
const providerName = optionalString(record.providerName) ?? defaults.providerName;
const envKey = optionalString(record.envKey) ?? defaults.envKey;
if (profile === "dsflash-go" && model !== dsflashGoModelSlug) throw new AgentRunError("schema-invalid", `dsflash-go model must be ${dsflashGoModelSlug}`, { httpStatus: 400 });
validateBaseUrl(profile, baseUrl);
if (!/^[A-Z_][A-Z0-9_]{0,63}$/u.test(envKey)) throw new AgentRunError("schema-invalid", "config.envKey must be an uppercase env name", { httpStatus: 400 });
if (!/^[A-Za-z][A-Za-z0-9_-]{0,63}$/u.test(providerName)) throw new AgentRunError("schema-invalid", "config.providerName must be a provider identifier", { httpStatus: 400 });
@@ -513,7 +537,8 @@ function defaultConfig(profile: BackendProfile): ProfileConfig {
baseUrl: "http://hwlab-deepseek-proxy.hwlab-v02.svc.cluster.local:4000/v1",
envKey: "OPENAI_API_KEY",
wireApi: "responses",
displayName: "OpenCode",
displayName: "Moon Bridge OpenCode Zen Go Flash",
modelCatalogPath: dsflashGoRuntimeModelCatalogPath,
};
}
if (profile === "minimax-m3") {
@@ -537,7 +562,7 @@ function defaultConfig(profile: BackendProfile): ProfileConfig {
}
function profileUsesMoonBridge(profile: BackendProfile, configToml: string): boolean {
return profile === "deepseek" || profile === "dsflash-go" || configToml.includes("hwlab-deepseek-proxy.hwlab-v02.svc.cluster.local");
return profile === "deepseek" || profile === "dsflash-go" || /hwlab-deepseek-proxy\.hwlab-v02\.svc\.cluster\.local|moon|bridge/iu.test(configToml);
}
function validateBaseUrl(profile: BackendProfile, value: string): void {
@@ -547,12 +572,50 @@ 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" || profile === "dsflash-go") && url.hostname === "hyueapi.com") {
if ((profile === "deepseek" || profile === "dsflash-go") && isHyueHost(url.hostname)) {
throw new AgentRunError("tenant-policy-denied", `${profile} profile must use HWLAB Moon Bridge, not hyueapi.com`, { 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 });
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 DeepSeek bridge", { httpStatus: 403 });
}
if (profile === "dsflash-go" && !isAllowedDsflashBridgeUrl(url)) {
throw new AgentRunError("tenant-policy-denied", "dsflash-go profile baseUrl must point to a Moon Bridge service or wrapper-local bridge", { httpStatus: 403 });
}
}
function validateConfigTomlForProfile(profile: BackendProfile, configToml: string): void {
if ((profile === "deepseek" || profile === "dsflash-go") && /hyueapi\.com/iu.test(configToml)) {
throw new AgentRunError("tenant-policy-denied", `${profile} profile must use HWLAB Moon Bridge, not hyueapi.com`, { httpStatus: 403 });
}
if (profile === "deepseek" && !configToml.includes("hwlab-deepseek-proxy.hwlab-v02.svc.cluster.local")) {
throw new AgentRunError("tenant-policy-denied", "deepseek profile config.toml must point to HWLAB v0.2 DeepSeek bridge", { httpStatus: 403 });
}
if (profile !== "dsflash-go") return;
if (!configToml.includes(dsflashGoModelSlug)) throw new AgentRunError("schema-invalid", `dsflash-go config.toml must use model ${dsflashGoModelSlug}`, { httpStatus: 400 });
if (modelCatalogPathFromConfigToml(configToml) !== dsflashGoRuntimeModelCatalogPath) throw new AgentRunError("schema-invalid", `dsflash-go config.toml must set model_catalog_json to ${dsflashGoRuntimeModelCatalogPath}`, { httpStatus: 400 });
const baseUrl = baseUrlFromConfigToml(configToml);
if (!baseUrl) throw new AgentRunError("schema-invalid", "dsflash-go config.toml must include model_providers base_url", { httpStatus: 400 });
validateBaseUrl(profile, baseUrl);
}
function isHyueHost(hostname: string): boolean {
return hostname === "hyueapi.com" || hostname.endsWith(".hyueapi.com");
}
function isAllowedDsflashBridgeUrl(url: URL): boolean {
if (url.hostname === "127.0.0.1" || url.hostname === "localhost") return true;
if (url.hostname === "hwlab-deepseek-proxy.hwlab-v02.svc.cluster.local") return true;
return url.hostname.endsWith(".svc.cluster.local") && /moon|bridge|deepseek|opencode/iu.test(url.hostname);
}
function baseUrlFromConfigToml(configToml: string): string | null {
const match = configToml.match(/^\s*base_url\s*=\s*"([^"]+)"\s*$/mu);
return match?.[1] ?? null;
}
function modelCatalogPathFromConfigToml(configToml: string): string | null {
const match = configToml.match(/^\s*model_catalog_json\s*=\s*"([^"]+)"\s*$/mu);
return match?.[1] ?? null;
}
function contextWindowSettings(model: string): { contextWindow: number; autoCompactTokenLimit: number } {