Q22: cap every tool result and bound connector responses
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4d6c74a21e
commit
8e8b66fed4
5 files changed
+79
-5
No files matched your search
@@ -0,0 +1,43 @@
|
||||
import { EventEmitter } from "node:events";
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
|
||||
const chunksToSend: Buffer[] = [];
|
||||
let aborted = false;
|
||||
|
||||
vi.mock("electron", () => ({
|
||||
net: {
|
||||
request: () => {
|
||||
const request = new EventEmitter() as EventEmitter & { setHeader: () => void; write: () => void; end: () => void; abort: () => void };
|
||||
request.setHeader = () => undefined;
|
||||
request.write = () => undefined;
|
||||
request.abort = () => { aborted = true; };
|
||||
request.end = () => {
|
||||
const response = new EventEmitter() as EventEmitter & { statusCode: number };
|
||||
response.statusCode = 200;
|
||||
request.emit("response", response);
|
||||
for (const chunk of chunksToSend) response.emit("data", chunk);
|
||||
response.emit("end");
|
||||
};
|
||||
return request;
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const { ComposioClient } = await import("../../src/main/services/composio/composio-client");
|
||||
|
||||
describe("composio client response limits", () => {
|
||||
test("a multi-byte character split across chunks decodes intact", async () => {
|
||||
const json = Buffer.from(JSON.stringify({ items: [{ slug: "GMAIL_SEND", description: "发送邮件" }] }), "utf8");
|
||||
const split = json.indexOf(Buffer.from("邮", "utf8")) + 1;
|
||||
chunksToSend.splice(0, chunksToSend.length, json.subarray(0, split), json.subarray(split));
|
||||
const tools = await new ComposioClient("ak_test").getTools(["gmail"]);
|
||||
expect(tools.map((tool) => [tool.name, tool.description])).toEqual([["GMAIL_SEND", "发送邮件"]]);
|
||||
});
|
||||
|
||||
test("a response over the byte cap aborts and fails loudly", async () => {
|
||||
aborted = false;
|
||||
chunksToSend.splice(0, chunksToSend.length, Buffer.alloc(1_500_000, 32), Buffer.alloc(1_500_000, 32));
|
||||
await expect(new ComposioClient("ak_test").getTools(["gmail"])).rejects.toThrow("returned more than 2000000 bytes");
|
||||
expect(aborted).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { compactToolHistory } from "../../src/main/services/run-context-compaction";
|
||||
import { capToolOutput, compactToolHistory } from "../../src/main/services/run-context-compaction";
|
||||
import { runAgentLoop } from "../../src/main/services/agent-run-loop";
|
||||
import { DatabaseConnection } from "../../src/main/database/connection";
|
||||
import { OrganizationService } from "../../src/main/services/organization-service";
|
||||
@@ -83,4 +83,11 @@ describe("tool history compaction", () => {
|
||||
expect(outputs.slice(2).every((output) => output.includes("y".repeat(3000)))).toBe(true);
|
||||
connection.close();
|
||||
});
|
||||
|
||||
test("every tool result is capped with a note that names the tool and the full size", () => {
|
||||
expect(capToolOutput("composio_gmail_list", "short")).toBe("short");
|
||||
const capped = capToolOutput("composio_gmail_list", "r".repeat(40_000));
|
||||
expect(capped.length).toBeLessThan(26_200);
|
||||
expect(capped.endsWith("[result of composio_gmail_list cut at 26000 of 40000 characters; ask for less (filters, pagination, fewer fields)]")).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user