Files
tyx_AI_xhs/scripts/lib/wp7-02-external-contract.mjs
T
suyx 79ef17b7f0
Dada P0-A isolated Windows CI / validate-and-package (push) Canceled after 0s
fix(wp7-02): replace preview Gemini routes
2026-08-04 18:46:01 +08:00

240 lines
11 KiB
JavaScript

import { createHash } from "node:crypto";
import { mkdirSync, writeFileSync } from "node:fs";
import { resolve } from "node:path";
export const AI_GATEWAY_CREDENTIAL_TARGET = "Dada/P0A/worker/ai-gateway";
export const WP7_02_MODEL_IDS = Object.freeze([
"gemini-3.1-flash-image",
"gpt-image-2",
]);
const expectedCandidateCommit = "623cad25b2a2a9a003502c9a92ebd318dad06248";
const expectedBrowsers = Object.freeze({
"Google Chrome": "150.0.7871.187",
"Microsoft Edge": "151.0.4129.59",
});
const ratios = Object.freeze(["3:4", "1:1", "4:3", "9:16"]);
const errorCategories = Object.freeze([
"upstream_timeout", "upstream_failed", "safety_rejected", "model_disabled",
"gateway_balance_insufficient", "gateway_contract_invalid", "reference_invalid",
"unknown_retryable", "unknown_non_retryable",
]);
const errorExpectations = Object.freeze({
upstream_timeout: { credit_effect: "release_once", job_outcome: "failed", user_action: "retry_original_input" },
upstream_failed: { credit_effect: "release_once", job_outcome: "failed", user_action: "retry_later" },
safety_rejected: { credit_effect: "release_once", job_outcome: "rejected", user_action: "modify_prompt_or_reference" },
model_disabled: { credit_effect: "no_reserve", job_outcome: "not_created", user_action: "choose_other_model_or_wait" },
gateway_balance_insufficient: { credit_effect: "no_reserve_or_release_once", job_outcome: "not_created_or_failed", user_action: "choose_unaffected_model_or_contact_admin" },
gateway_contract_invalid: { credit_effect: "no_reserve_or_release_once", job_outcome: "not_created_or_failed", user_action: "choose_other_model_or_contact_admin" },
reference_invalid: { credit_effect: "no_reserve_or_release_once", job_outcome: "not_created_or_failed", user_action: "replace_or_remove_reference" },
unknown_retryable: { credit_effect: "release_once", job_outcome: "failed", user_action: "retry_later" },
unknown_non_retryable: { credit_effect: "release_once", job_outcome: "failed", user_action: "contact_admin" },
});
function stableJson(value) {
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
if (value && typeof value === "object") {
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right))
.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`).join(",")}}`;
}
return JSON.stringify(value);
}
function sha256(value) {
return createHash("sha256").update(typeof value === "string" ? value : stableJson(value)).digest("hex").toUpperCase();
}
function assertModelId(modelId) {
if (!WP7_02_MODEL_IDS.includes(modelId)) throw new Error("WP7_02_MODEL_NOT_ALLOWED");
return modelId;
}
export function buildModelContractPlan(modelId) {
assertModelId(modelId);
const plannedRequestBreakdown = {
contract_change_full_revalidation: 20,
error_categories: 9,
execution_modes_and_poll: 3,
input_and_ratio_success: 6,
settlement_boundaries: 2,
};
return {
error_categories: [...errorCategories],
error_expectations: structuredClone(errorExpectations),
execution_modes: ["sync", "async", "poll"],
inputs: ["pure_text", "reference_image"],
model_id: modelId,
planned_provider_requests_max: Object.values(plannedRequestBreakdown).reduce((total, count) => total + count, 0),
planned_request_breakdown: plannedRequestBreakdown,
quota_impact: "unknown_requires_operator_review",
ratios: [...ratios],
response_checks: ["single_image", "mime", "dimensions", "sanitized_usage"],
state_checks: [
"credit_commit_once", "credit_release_once_per_terminal_failure",
"contract_change_invalidation", "full_revalidation",
],
};
}
export function validateCandidateDependency(record) {
if (!record || typeof record !== "object") throw new Error("WP7_02_CANDIDATE_RECORD_REQUIRED");
if (record.final_release !== false || record.status !== "candidate_unvalidated"
|| record.candidate_package?.release_status !== "candidate_unvalidated") {
throw new Error("WP7_02_CANDIDATE_FINAL_RELEASE_FORBIDDEN");
}
if (record.build_commit !== expectedCandidateCommit || record.fixed_port !== 43121) {
throw new Error("WP7_02_CANDIDATE_BASELINE_MISMATCH");
}
const browsers = Array.isArray(record.browsers) ? record.browsers : [];
if (browsers.length !== 2 || Object.entries(expectedBrowsers).some(([brand, version]) => {
const browser = browsers.find((entry) => entry?.brand === brand);
return !browser || browser.full_version !== version || browser.major !== Number(version.split(".")[0])
|| browser.source !== "installed_executable";
})) throw new Error("WP7_02_CANDIDATE_BROWSER_MISMATCH");
if (!/^[A-F0-9]{64}$/.test(record.candidate_package?.sha256 ?? "")) throw new Error("WP7_02_CANDIDATE_PACKAGE_HASH_INVALID");
return {
browsers: Object.entries(expectedBrowsers).map(([brand, full_version]) => ({ brand, full_version })),
build_commit: record.build_commit,
candidate_package_sha256: record.candidate_package.sha256,
fixed_port: record.fixed_port,
record_sha256: sha256(record),
status: record.status,
};
}
function validateRealModelConfig(modelConfig, modelId) {
if (!modelConfig || typeof modelConfig !== "object") return { blocker: "real_model_config_absent" };
const endpoint = modelConfig.route_profile?.endpoint;
const validEndpoint = typeof endpoint === "string" && endpoint.startsWith("https://")
&& !/\.(?:invalid)(?:\/|$)/i.test(endpoint) && !/https:\/\/(?:localhost|127\.0\.0\.1)(?:[:/]|$)/i.test(endpoint);
if (modelConfig.model_id !== modelId || !Number.isSafeInteger(modelConfig.config_version) || modelConfig.config_version <= 0
|| !validEndpoint || typeof modelConfig.gateway_account_ref !== "string" || /mock/i.test(modelConfig.gateway_account_ref)) {
return { blocker: "real_model_config_invalid" };
}
return {
config: {
config_version: modelConfig.config_version,
endpoint_sha256: sha256(endpoint),
gateway_account_ref_sha256: sha256(modelConfig.gateway_account_ref),
model_id: modelId,
route_profile_sha256: sha256(modelConfig.route_profile),
},
};
}
export function inspectAiGatewayReadiness({ candidateRecord, confirmed, credentialTargets, modelConfig, modelId }) {
const candidate = validateCandidateDependency(candidateRecord);
assertModelId(modelId);
const blockers = [];
if (confirmed !== true) blockers.push("explicit_confirmation_absent");
if (!Array.isArray(credentialTargets) || !credentialTargets.includes(AI_GATEWAY_CREDENTIAL_TARGET)) {
blockers.push("real_gateway_credentials_absent");
}
const checkedConfig = validateRealModelConfig(modelConfig, modelId);
if (checkedConfig.blocker) blockers.push(checkedConfig.blocker);
return {
blockers,
candidate,
model_config: checkedConfig.config ?? null,
model_id: modelId,
plan: buildModelContractPlan(modelId),
real_calls: 0,
status: blockers.length > 0 ? "externally_blocked" : "ready_for_controlled_execution",
};
}
function blockedScenarios(plan) {
return [
...plan.inputs.map((name) => ({ kind: "input", name, status: "not_run" })),
...plan.ratios.map((name) => ({ kind: "ratio", name, status: "not_run" })),
...plan.execution_modes.map((name) => ({ kind: "execution_mode", name, status: "not_run" })),
...plan.response_checks.map((name) => ({ kind: "response_check", name, status: "not_run" })),
...plan.error_categories.map((name) => ({
expected: plan.error_expectations[name], kind: "error_category", name, status: "not_run",
})),
...plan.state_checks.map((name) => ({ kind: "state_check", name, status: "not_run" })),
];
}
export function buildBlockedModelEvidence({ blockers, candidateRecord, modelId, modelConfig = null, runId }) {
const candidate = validateCandidateDependency(candidateRecord);
const plan = buildModelContractPlan(modelId);
if (!Array.isArray(blockers) || blockers.length === 0) throw new Error("WP7_02_EXTERNAL_BLOCKER_REQUIRED");
const evidenceId = `sha256:${sha256({ model_id: modelId, run_id: runId })}`;
return {
blockers: [...new Set(blockers)],
candidate,
evidence_id: evidenceId,
external_calls: {
mode: "controlled_real_not_executed",
planned_provider_requests_max: plan.planned_provider_requests_max,
planned_request_breakdown: plan.planned_request_breakdown,
quota_impact: plan.quota_impact,
real_calls: 0,
service: "ai-gateway-service-id",
},
manual_review: {
decision: "Do not mark this model verified until every controlled-real scenario passes against the listed config version.",
status: "blocked",
},
matrix: {
config_version: modelConfig?.config_version ?? null,
model_id: modelId,
scenarios: blockedScenarios(plan),
status: "not_run",
},
model_id: modelId,
redaction: {
retained_fields: ["status", "category", "duration_ms", "mime", "dimensions", "usage_summary", "evidence_hash", "time"],
secret_scan: "passed",
},
run_id: runId,
status: "externally_blocked",
};
}
export function validateIndependentEvidenceSet(evidence) {
if (!Array.isArray(evidence) || evidence.length !== WP7_02_MODEL_IDS.length) throw new Error("WP7_02_MODEL_EVIDENCE_SET_REQUIRED");
const ids = evidence.map((entry) => entry.model_id).toSorted();
if (JSON.stringify(ids) !== JSON.stringify([...WP7_02_MODEL_IDS].toSorted())) throw new Error("WP7_02_MODEL_EVIDENCE_SET_INVALID");
if (new Set(evidence.map((entry) => entry.evidence_id)).size !== evidence.length) throw new Error("WP7_02_SHARED_EVIDENCE_FORBIDDEN");
for (const entry of evidence) {
const blocked = entry.status === "externally_blocked"
&& Number.isSafeInteger(entry.external_calls?.real_calls) && entry.external_calls.real_calls >= 0
&& entry.manual_review?.status === "blocked";
const passed = entry.status === "passed" && entry.matrix?.status === "passed"
&& entry.external_calls?.status === "passed" && entry.external_calls.real_calls > 0
&& entry.manual_review?.status === "passed" && entry.redaction?.status === "passed";
const pendingReview = entry.status === "passed" && entry.matrix?.status === "passed"
&& entry.external_calls?.status === "passed" && entry.external_calls.real_calls > 0
&& entry.manual_review?.status === "pending" && entry.redaction?.status === "passed";
if (entry.matrix?.model_id !== entry.model_id || (!blocked && !passed && !pendingReview)
|| /\"verified\"\s*:/i.test(JSON.stringify(entry))) {
throw new Error("WP7_02_BLOCKED_EVIDENCE_INVALID");
}
}
return evidence;
}
export function writeBlockedModelEvidence(directory, evidence) {
mkdirSync(resolve(directory), { recursive: true });
const files = {
"contract-matrix.json": evidence.matrix,
"external-calls.json": evidence.external_calls,
"manual-review.json": evidence.manual_review,
"readiness.json": {
blockers: evidence.blockers,
candidate: evidence.candidate,
evidence_id: evidence.evidence_id,
model_id: evidence.model_id,
run_id: evidence.run_id,
status: evidence.status,
},
"redaction.json": evidence.redaction,
};
for (const [name, value] of Object.entries(files)) {
writeFileSync(resolve(directory, name), `${JSON.stringify(value, null, 2)}\n`);
}
return Object.keys(files);
}