Files
writing-agent/tests/driver.test.ts
T
chenmoranzhiandCursor 752eaccefa 增强各厂商思考与采样参数映射,并统一流式推理字段解析。
避免不同接口字段差异导致思考链丢失或采样参数被忽略。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-14 01:14:09 +08:00

134 lines
3.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
createMockToolCall,
MockLlmProvider,
} from "../src/llm/client.js";
import { createLocalLlmDriver } from "../src/runtime/driver.js";
describe("local LLM driver", () => {
it("continues while tools return results, then stops on text", async () => {
const llm = new MockLlmProvider([
createMockToolCall("lookup", { q: "a" }, "c1"),
"done",
]);
const driver = createLocalLlmDriver(llm);
const seen: string[] = [];
const run = await driver.run({
system: "sys",
messages: [{ role: "user", content: "go" }],
tools: [
{
type: "function",
function: {
name: "lookup",
description: "lookup",
parameters: { type: "object", properties: {} },
},
},
],
handleStep: (calls) => {
seen.push(...calls.map((c) => c.name));
return {
kind: "continue",
results: calls.map((c) => ({ callId: c.id, content: "ok" })),
};
},
});
expect(seen).toEqual(["lookup"]);
expect(run.iterations).toBe(2);
expect(run.stop).toEqual({ kind: "text", content: "done" });
});
it("uses reasoning as the story when tools return no content", async () => {
const llm = {
complete: async () => ({ content: "" }),
completeWithTools: async () => ({
content: null,
toolCalls: [],
reasoning: "门开了。她把耳机摘下,问:「你在干什么?」",
}),
};
const driver = createLocalLlmDriver(llm as never);
const run = await driver.run({
system: "sys",
messages: [{ role: "user", content: "go" }],
tools: [
{
type: "function",
function: {
name: "chance",
description: "roll",
parameters: { type: "object", properties: {} },
},
},
],
handleStep: () => ({ kind: "continue", results: [] }),
});
expect(run.stop).toEqual({
kind: "text",
content: "门开了。她把耳机摘下,问:「你在干什么?」",
});
});
it("stops when handleStep concludes, without another model call", async () => {
const llm = new MockLlmProvider([
createMockToolCall("run_worker", { workerId: "x" }, "t1"),
"should-not-be-used",
]);
const driver = createLocalLlmDriver(llm);
const run = await driver.run({
system: "sys",
messages: [{ role: "user", content: "go" }],
tools: [
{
type: "function",
function: {
name: "run_worker",
description: "run",
parameters: { type: "object", properties: {} },
},
},
],
handleStep: (calls) => ({ kind: "conclude", call: calls[0]! }),
});
expect(run.iterations).toBe(1);
expect(run.stop.kind).toBe("conclude");
if (run.stop.kind === "conclude") {
expect(run.stop.call.name).toBe("run_worker");
}
});
it("throws when the iteration cap is hit", async () => {
const llm = new MockLlmProvider([
createMockToolCall("ping", {}, "p1"),
]);
const driver = createLocalLlmDriver(llm);
await expect(
driver.run({
system: "sys",
messages: [{ role: "user", content: "go" }],
tools: [
{
type: "function",
function: {
name: "ping",
description: "ping",
parameters: { type: "object", properties: {} },
},
},
],
maxIterations: 2,
handleStep: (calls) => ({
kind: "continue",
results: calls.map((c) => ({ callId: c.id, content: "pong" })),
}),
}),
).rejects.toThrow(/exceeded 2 iterations/);
});
});