mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
Whitespace-only tool results (e.g. a bare \r\n from bash) were sent verbatim to OpenAI Completions, OpenAI Responses, and Google providers. Some providers reject that with a 400, which then repeats on every request in the session. They now use the same placeholder as empty output. closes #8720
62 lines
2 KiB
TypeScript
62 lines
2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { convertResponsesMessages } from "../src/api/openai-responses-shared.ts";
|
|
import { getModel, normalizeContext } from "../src/compat.ts";
|
|
import type { AssistantMessage, ToolResultMessage, Usage } from "../src/types.ts";
|
|
|
|
const usage: Usage = {
|
|
input: 0,
|
|
output: 0,
|
|
cacheRead: 0,
|
|
cacheWrite: 0,
|
|
totalTokens: 0,
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
};
|
|
|
|
function buildEmptyToolResult(toolCallId: string, timestamp: number, text = ""): ToolResultMessage {
|
|
return {
|
|
role: "toolResult",
|
|
toolCallId,
|
|
toolName: "bash",
|
|
content: [{ type: "text", text }],
|
|
isError: false,
|
|
timestamp,
|
|
};
|
|
}
|
|
|
|
describe("OpenAI Responses convertResponsesMessages empty tool result", () => {
|
|
// Whitespace-only cases: https://github.com/earendil-works/pi/issues/8720
|
|
it.each(["", "\r\n", " \n\t"])(
|
|
"uses '(no tool output)' placeholder for tool result text %j without images",
|
|
(text) => {
|
|
const model = getModel("openai", "gpt-4o-mini");
|
|
const now = Date.now();
|
|
const assistant: AssistantMessage = {
|
|
role: "assistant",
|
|
content: [{ type: "toolCall", id: "tool-1", name: "bash", arguments: { command: "true" } }],
|
|
api: model.api,
|
|
provider: model.provider,
|
|
model: model.id,
|
|
usage,
|
|
stopReason: "toolUse",
|
|
timestamp: now,
|
|
};
|
|
|
|
const context = normalizeContext({
|
|
messages: [
|
|
{ role: "user", content: "Run the command", timestamp: now - 1 },
|
|
assistant,
|
|
buildEmptyToolResult("tool-1", now + 1, text),
|
|
],
|
|
});
|
|
|
|
const input = convertResponsesMessages(model, context, new Set(["openai", "openai-codex", "opencode"]));
|
|
const functionCallOutput = input.find((item) => item.type === "function_call_output") as
|
|
| { type: "function_call_output"; output: string }
|
|
| undefined;
|
|
|
|
expect(functionCallOutput).toBeTruthy();
|
|
expect(functionCallOutput?.output).toBe("(no tool output)");
|
|
expect(functionCallOutput?.output).not.toContain("see attached image");
|
|
},
|
|
);
|
|
});
|