fix(POSTV1-02): 接通真实AI生成链路
Dada P0-A isolated Windows CI / validate-and-package (push) Waiting to run

This commit is contained in:
suyx
2026-08-05 12:29:14 +08:00
parent 43d946bb5c
commit cbb7f658a3
10 changed files with 440 additions and 24 deletions
+44
View File
@@ -0,0 +1,44 @@
import { createRequire } from "node:module";
import { describe, expect, it } from "vitest";
import {
ModelConfigurationService,
portableRuntimeModelCandidates,
} from "../../apps/api/src/model-configuration.js";
const requireFromApi = createRequire(new URL("../../apps/api/package.json", import.meta.url));
const Database = requireFromApi("better-sqlite3") as new (path: string) => {
close(): void;
};
describe("POSTV1-02 portable runtime model seed", () => {
it("enables only models backed by the real OneAPI contract", () => {
const database = new Database(":memory:");
try {
const models = new ModelConfigurationService({ database, seedCandidates: portableRuntimeModelCandidates }).read();
const flash = models.models.find((model) => model.model_id === "gemini-3.1-flash-image-preview");
const pro = models.models.find((model) => model.model_id === "gemini-3-pro-image-preview");
const gpt = models.models.find((model) => model.model_id === "gpt-image-2");
expect(models.configured_default_model_id).toBe("gemini-3.1-flash-image-preview");
expect(flash).toMatchObject({
contract_validation_status: "verified",
enabled: true,
runtime_availability: { available_for_new_jobs: true, reason: "available" },
});
expect(flash?.route_profile).toMatchObject({ endpoint: "https://oneapi.intelligrow.cn/v1/chat/completions" });
expect(pro).toMatchObject({
contract_validation_status: "unverified",
enabled: false,
runtime_availability: { available_for_new_jobs: false, reason: "configured_disabled" },
});
expect(gpt).toMatchObject({
contract_validation_status: "verified",
enabled: true,
runtime_availability: { available_for_new_jobs: true, reason: "available" },
});
} finally {
database.close();
}
});
});
+4 -1
View File
@@ -46,7 +46,10 @@ async function stop(child) {
test("portable package serves the product and keeps SQLite data across API restart", async () => {
assert.ok(existsSync(join(packageRoot, "Dada.exe")));
assert.ok(existsSync(join(packageRoot, "web", "index.html")));
assert.match(await readFile(join(packageRoot, "server", "worker", "dist", "worker.js"), "utf8"), /GenerationProcessor/);
const packagedWorker = await readFile(join(packageRoot, "server", "worker", "dist", "worker.js"), "utf8");
assert.match(packagedWorker, /GenerationProcessor/);
assert.match(packagedWorker, /oneapi\.intelligrow\.cn/);
assert.doesNotMatch(packagedWorker, /portable-mock-worker/);
const root = await mkdtemp(join(tmpdir(), "dada-postv1-"));
const dataRoot = join(root, "data");
const configPath = join(root, "instance.json");
@@ -0,0 +1,66 @@
import { describe, expect, it, vi } from "vitest";
import type { GenerationAdapterRequest } from "../../apps/worker/src/ai-adapter-contract.js";
import { OneApiGenerationAdapter } from "../../apps/worker/src/oneapi-generation-adapter.js";
function request(overrides: Partial<GenerationAdapterRequest> = {}): GenerationAdapterRequest {
return {
configSnapshot: {},
generationId: "00000000-0000-4000-8000-000000000001",
modelId: "gemini-3.1-flash-image-preview",
prompt: "一张用于本机验收的抽象色彩图",
ratio: "1:1",
referenceAssetIds: [],
...overrides,
};
}
describe("POSTV1-02 OneAPI runtime adapter", () => {
it("uses the fixed Gemini gateway and normalizes one real-shaped response", async () => {
const source = Buffer.from(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=",
"base64",
);
const gateway = vi.fn(async (_url: string | URL | Request, init?: RequestInit) => {
const headers = new Headers(init?.headers);
expect(headers.get("authorization")).toBe("Bearer synthetic-runtime-token");
expect(init?.redirect).toBe("error");
return new Response(JSON.stringify({
choices: [{ message: { content: `![result](data:image/png;base64,${source.toString("base64")})` } }],
}), { headers: { "content-type": "application/json" }, status: 200 });
});
const credential = Buffer.from("synthetic-runtime-token");
const adapter = new OneApiGenerationAdapter({ credential, fetch: gateway as typeof fetch });
const result = await adapter.start(request());
expect(gateway).toHaveBeenCalledOnce();
expect(gateway.mock.calls[0]?.[0]).toBe("https://oneapi.intelligrow.cn/v1/chat/completions");
expect(result.status === "failed" ? result.sourceCategory : "completed").toBe("completed");
if (result.status === "completed") {
expect(result.outputs).toHaveLength(1);
expect(result.outputs[0]).toMatchObject({ mimeType: "image/png", pixelHeight: 1080, pixelWidth: 1080 });
expect(result.outputs[0]?.bytes.length).toBeGreaterThan(0);
}
expect(JSON.stringify(result)).not.toContain("synthetic-runtime-token");
adapter.dispose();
credential.fill(0);
});
it("fails closed instead of returning a mock image", async () => {
const adapter = new OneApiGenerationAdapter({
credential: Buffer.from("synthetic-runtime-token"),
fetch: vi.fn(async () => new Response(null, { status: 503 })) as typeof fetch,
});
await expect(adapter.start(request())).resolves.toEqual({
category: "upstream_failed",
sourceCategory: "upstream_http_503",
status: "failed",
});
adapter.dispose();
await expect(adapter.start(request())).resolves.toEqual({
category: "upstream_failed",
sourceCategory: "adapter_disposed",
status: "failed",
});
});
});