feat: complete TASK-WP3-03 adapters and contract evidence

This commit is contained in:
suyx
2026-08-03 03:18:56 +08:00
parent 827ae912e0
commit 0fb0d088a7
10 changed files with 786 additions and 5 deletions
@@ -0,0 +1,91 @@
import { mkdtempSync, rmSync } from "node:fs";
import { mkdirSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { ModelConfigurationService } from "../../apps/api/src/model-configuration.js";
import { ModelContractEvidenceService } from "../../apps/api/src/model-contract-evidence.js";
import { RegistrationService } from "../../apps/api/src/registration.js";
import { MockResendAdapter } from "../../apps/api/src/resend-adapter.js";
const roots: string[] = [];
const services: RegistrationService[] = [];
const now = Date.parse("2026-08-02T16:00:00.000Z");
function harness() {
const root = mkdtempSync(join(tmpdir(), "dada-wp3-03-evidence-"));
roots.push(root);
const registration = new RegistrationService({
challengePepper: Buffer.alloc(32, 0xb1), clock: () => now,
currentPrivacyNoticeVersion: "p0a-registration-notice-v1", databasePath: join(root, "dada.sqlite3"),
invitePepper: Buffer.alloc(32, 0xb2), resend: new MockResendAdapter(), sessionPepper: Buffer.alloc(32, 0xb3),
});
services.push(registration);
const models = new ModelConfigurationService({ database: registration.database, clock: () => now });
return { evidence: new ModelContractEvidenceService({ clock: () => now, database: registration.database, models }), models, registration };
}
function matrix(modelId: string) {
return {
model_id: modelId,
protocol_version: "mock-v1",
pure_text: { status: "passed", outputs: 1 },
reference_image: { status: "passed", outputs: 1 },
ratios: ["3:4", "1:1", "4:3", "9:16"].map((ratio) => ({ ratio, status: "passed", outputs: 1 })),
execution_modes: ["sync", "async", "poll"],
error_mapping: ["upstream_timeout", "upstream_failed", "safety_rejected", "model_disabled", "gateway_balance_insufficient", "gateway_contract_invalid", "reference_invalid", "unknown_retryable", "unknown_non_retryable"],
};
}
function writeEvidence(relativePath: string, value: unknown) {
const root = process.env.DADA_EVIDENCE_DIR_CONTRACT;
if (!root) return;
const path = resolve(root, relativePath);
mkdirSync(join(path, ".."), { recursive: true });
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`);
}
afterEach(() => {
for (const service of services.splice(0)) service.close();
for (const root of roots.splice(0)) rmSync(root, { force: true, recursive: true });
});
describe("TDD-WP3-ADP-002-invalidate-revalidate", () => {
it("records a complete per-model evidence set and leaves no raw provider payload", () => {
const { evidence, models, registration } = harness();
const first = evidence.recordVerified({
actorId: "wp3-03-evidence", expectedConfigSetVersion: 1,
evidence: { evidence_hash: "sha256:gemini-flash-mock", evidence_ref: "fixture:gemini-flash", matrix: matrix("gemini-3.1-flash-image-preview"), model_id: "gemini-3.1-flash-image-preview", verifier_ref: "mock-verifier", verified_at: new Date(now).toISOString() },
idempotencyKey: "wp3-03-evidence-0000000000000000000001",
});
expect(first.model.contract_validation_status).toBe("verified");
expect(first.model.config_version).toBe(2);
expect(models.read().models.find((model) => model.model_id === "gemini-3.1-flash-image-preview")).toMatchObject({ contract_validation_status: "verified", config_version: 2 });
expect(registration.database.prepare("SELECT COUNT(*) AS count FROM model_contract_evidence WHERE model_id = ?").get("gemini-3.1-flash-image-preview")).toEqual({ count: 1 });
expect(() => registration.database.prepare("UPDATE model_contract_evidence SET evidence_json = '{}' ").run()).toThrow();
const current = models.read();
const candidates = current.models.map(({ config_version: _configVersion, runtime_availability: _runtime, ...candidate }) => structuredClone(candidate));
const flash = candidates.find((candidate) => candidate.model_id === "gemini-3.1-flash-image-preview")!;
flash.route_profile = { ...flash.route_profile, endpoint: "https://mock.invalid/gemini/flash-v2" };
const changed = models.replace({ actorId: "wp3-03-route-change", expectedConfigSetVersion: 2, idempotencyKey: "wp3-03-route-change-0000000000000000000001", models: candidates });
expect(changed.config_set_version).toBe(3);
expect(changed.models.find((model) => model.model_id === flash.model_id)).toMatchObject({ contract_validation_status: "unverified", config_version: 3 });
expect(evidence.read(flash.model_id, 2)?.evidence_hash).toBe("sha256:gemini-flash-mock");
writeEvidence("db-diff.json", { before: { config_set_version: 2, contract_validation_status: "verified", config_version: 2 }, after: { config_set_version: 3, contract_validation_status: "unverified", config_version: 3 }, changed_fields: ["route_profile.endpoint"] });
writeEvidence("contract-matrix.json", matrix(flash.model_id));
writeEvidence("response.json", { model_id: flash.model_id, revalidation_required: true, output_count: 0 });
});
it("rejects an incomplete matrix before changing the config set", () => {
const { evidence, models } = harness();
expect(() => evidence.recordVerified({
actorId: "wp3-03-invalid", expectedConfigSetVersion: 1,
evidence: { evidence_hash: "sha256:invalid", evidence_ref: "fixture:invalid", matrix: { model_id: "gemini-3.1-flash-image-preview" }, model_id: "gemini-3.1-flash-image-preview", verifier_ref: "mock-verifier", verified_at: new Date(now).toISOString() },
idempotencyKey: "wp3-03-invalid-0000000000000000000001",
})).toThrow("contract_evidence_matrix_incomplete");
expect(models.read().config_set_version).toBe(1);
});
});
@@ -0,0 +1,80 @@
import { describe, expect, it } from "vitest";
import { mkdirSync, writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { GeminiFlashAdapter, geminiFlashMockContract } from "../../apps/worker/src/ai-adapter-gemini-flash.js";
import { GeminiProAdapter, geminiProMockContract } from "../../apps/worker/src/ai-adapter-gemini-pro.js";
import { GptImageAdapter, gptImageMockContract } from "../../apps/worker/src/ai-adapter-gpt-image.js";
import { mockPngBytes, type AdapterTransport } from "../../apps/worker/src/model-adapter.js";
const request = (modelId: string, ratio: "3:4" | "1:1" | "4:3" | "9:16" = "3:4", referenceAssetIds: string[] = []) => ({
configSnapshot: { error_mapping_profile: { timeout: "upstream_timeout", safety: "safety_rejected" } },
generationId: `generation-${modelId}`,
modelId,
prompt: "mock contract prompt",
ratio,
referenceAssetIds,
});
function writeAdapterEvidence(relativePath: string, value: unknown) {
const root = process.env.DADA_EVIDENCE_DIR_ADAPTERS;
if (!root) return;
const path = resolve(root, relativePath);
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`);
}
describe("TDD-WP3-ADP-001-per-model-contract", () => {
it("keeps three independent fixtures and validates their own profiles", async () => {
const adapters = [new GeminiFlashAdapter(), new GeminiProAdapter(), new GptImageAdapter()];
expect(new Set(adapters.map((adapter) => adapter.modelId)).size).toBe(3);
expect(new Set(adapters.map((adapter) => adapter.fixtureId)).size).toBe(3);
expect(adapters[0].validateContract(geminiFlashMockContract).status).toBe("verified");
expect(adapters[1].validateContract(geminiProMockContract).status).toBe("verified");
expect(adapters[2].validateContract(gptImageMockContract).status).toBe("verified");
expect(adapters[0].validateContract(geminiProMockContract).status).toBe("blocked");
for (const adapter of adapters) {
for (const ratio of ["3:4", "1:1", "4:3", "9:16"] as const) {
const result = await adapter.start(request(adapter.modelId, ratio, ["reference-fixture"]));
expect(result).toMatchObject({ status: "completed", outputs: [{ pixelHeight: expect.any(Number), pixelWidth: expect.any(Number) }] });
expect(result.status === "completed" ? result.outputs : []).toHaveLength(1);
}
}
const errorMapping = ["upstream_timeout", "upstream_failed", "safety_rejected", "gateway_balance_insufficient", "gateway_contract_invalid", "reference_invalid", "unknown_retryable", "unknown_non_retryable"];
for (const adapter of adapters) {
const executionModes = adapter === adapters[1] ? ["async", "poll"] : ["sync"];
const modelRoot = adapter.modelId.replaceAll(".", "_");
writeAdapterEvidence(`${modelRoot}/contract-matrix.json`, {
contract_version: adapter.contractVersion, execution_modes: executionModes, fixture_id: adapter.fixtureId,
model_id: adapter.modelId, pure_text: { outputs: 1, status: "passed" }, reference_image: { outputs: 1, status: "passed" },
ratios: ["3:4", "1:1", "4:3", "9:16"].map((ratio) => ({ outputs: 1, ratio, status: "passed" })),
error_mapping: errorMapping,
});
writeAdapterEvidence(`${modelRoot}/response.json`, { model_id: adapter.modelId, output_count: 1, response_shape: "single-image" });
writeAdapterEvidence(`${modelRoot}/external-calls.json`, { mode: "mock", real_calls: 0, service: "ai-gateway-service-id" });
writeAdapterEvidence(`${modelRoot}/redaction.json`, { removed_fields: ["credential", "raw_prompt", "raw_provider_payload"], secret_scan: "passed" });
}
});
it("supports a pending async operation and poll without accepting multiple outputs", async () => {
const transport: AdapterTransport = {
async start() { return { operation: { done: false, name: "upstream:mock-pro-1" } }; },
async poll() { return { operation: { done: true, response: { candidates: [{ inline_data: { data: mockPngBytes("async").toString("base64"), mime_type: "image/png" }, pixelHeight: 1440, pixelWidth: 1080 }] } } }; },
};
const adapter = new GeminiProAdapter({ transport });
await expect(adapter.start(request(adapter.modelId))).resolves.toEqual({ status: "pending", upstreamJobReference: "upstream:mock-pro-1" });
await expect(adapter.poll("upstream:mock-pro-1")).resolves.toMatchObject({ status: "completed", outputs: [{ mimeType: "image/png" }] });
expect(() => adapter.normalizeOutput({ candidates: [{}, {}] })).toThrow("response_single_image_required");
});
it("maps only fixed categories and retains a non-sensitive balance signal", async () => {
const adapter = new GeminiFlashAdapter({ transport: {
async start() { return { status: "failed", error: { code: "balance" }, balance_signal: { gateway_account_ref: "gateway-account-primary", impact_scope: "model" } }; },
async poll() { return {}; },
} });
const result = await adapter.start(request(adapter.modelId));
expect(result).toMatchObject({ status: "failed", category: "unknown_retryable" });
expect(adapter.classifyError({ code: "safety" }, { safety: "safety_rejected" })).toEqual({ category: "safety_rejected", sourceCategory: "safety" });
expect(adapter.checkBalanceSignal({ balance_signal: { gateway_account_ref: "gateway-account-primary", impact_scope: "account", raw_provider_text: "do-not-return" } })).toEqual({ gatewayAccountRef: "gateway-account-primary", impactScope: "account" });
});
});