




















1+import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
2+3+const extractFileContentFromSourceMock = vi.fn();
4+5+vi.mock("../media/input-files.js", async () => {
6+const actual =
7+await vi.importActual<typeof import("../media/input-files.js")>("../media/input-files.js");
8+return {
9+ ...actual,
10+extractFileContentFromSource: (...args: unknown[]) => extractFileContentFromSourceMock(...args),
11+};
12+});
13+14+import {
15+agentCommand,
16+getFreePort,
17+installGatewayTestHooks,
18+startGatewayServerWithRetries,
19+} from "./test-helpers.js";
20+21+installGatewayTestHooks({ scope: "suite" });
22+23+let server: Awaited<ReturnType<typeof startGatewayServerWithRetries>>["server"];
24+let port: number;
25+26+beforeAll(async () => {
27+const started = await startGatewayServerWithRetries({
28+port: await getFreePort(),
29+opts: {
30+host: "127.0.0.1",
31+auth: { mode: "none" },
32+controlUiEnabled: false,
33+openResponsesEnabled: true,
34+},
35+});
36+port = started.port;
37+server = started.server;
38+});
39+40+afterAll(async () => {
41+await server?.close({ reason: "openresponses file-only suite done" });
42+});
43+44+beforeEach(() => {
45+vi.clearAllMocks();
46+});
47+48+async function postResponses(body: unknown) {
49+return await fetch(`http://127.0.0.1:${port}/v1/responses`, {
50+method: "POST",
51+headers: {
52+"content-type": "application/json",
53+"x-openclaw-scopes": "operator.write",
54+},
55+body: JSON.stringify(body),
56+});
57+}
58+59+describe("OpenResponses file-only input that renders to images", () => {
60+it("accepts a file-only turn whose file renders to images and forwards them", async () => {
61+extractFileContentFromSourceMock.mockResolvedValueOnce({
62+filename: "scan.pdf",
63+text: "",
64+images: [
65+{ type: "image", data: Buffer.alloc(8, 1).toString("base64"), mimeType: "image/png" },
66+],
67+});
68+agentCommand.mockResolvedValueOnce({ payloads: [{ text: "ok" }] } as never);
69+70+const res = await postResponses({
71+model: "openclaw",
72+instructions: "Describe the attached scan.",
73+input: [
74+{
75+type: "message",
76+role: "user",
77+content: [
78+{
79+type: "input_file",
80+source: {
81+type: "base64",
82+media_type: "application/pdf",
83+data: Buffer.from("%PDF-1.4 scanned").toString("base64"),
84+filename: "scan.pdf",
85+},
86+},
87+],
88+},
89+],
90+});
91+92+expect(res.status).toBe(200);
93+expect(agentCommand).toHaveBeenCalledTimes(1);
94+const opts = agentCommand.mock.calls[0]?.[0] as { message?: string; images?: unknown[] };
95+expect(opts.message ?? "").not.toBe("");
96+expect(opts.images?.length).toBe(1);
97+await res.text();
98+});
99+100+it("keeps an empty extracted file visible to the model", async () => {
101+extractFileContentFromSourceMock.mockResolvedValueOnce({
102+filename: "empty.txt",
103+text: "",
104+images: [],
105+});
106+agentCommand.mockResolvedValueOnce({ payloads: [{ text: "ok" }] } as never);
107+108+const res = await postResponses({
109+model: "openclaw",
110+input: [
111+{
112+type: "message",
113+role: "user",
114+content: [
115+{
116+type: "input_file",
117+source: {
118+type: "base64",
119+media_type: "text/plain",
120+data: Buffer.from("binary-only file").toString("base64"),
121+filename: "empty.txt",
122+},
123+},
124+],
125+},
126+],
127+});
128+129+const body = await res.text();
130+expect(res.status, body).toBe(200);
131+expect(agentCommand).toHaveBeenCalledTimes(1);
132+const opts = agentCommand.mock.calls[0]?.[0] as { extraSystemPrompt?: string };
133+expect(opts.extraSystemPrompt).toContain('<file name="empty.txt">');
134+expect(opts.extraSystemPrompt).toContain("[No extractable text]");
135+});
136+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。