Files
dev/third_party/pi/packages/ai/test/transform-messages-cross-model-thinking.test.ts
alexion 6081e3e325 chore(vendor): import pi 1.3.0
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
2026-10-15 17:58:48 -04:00

100 lines
3.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { transformMessages } from "../src/api/transform-messages.ts";
import type { AssistantMessage, Message, Model } from "../src/types.ts";
const kimi: Model<"openai-completions"> = {
id: "moonshotai/Kimi-K3",
name: "Kimi K3",
api: "openai-completions",
provider: "sference",
baseUrl: "https://api.sference.com/v1",
reasoning: true,
input: ["text", "image"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 1048576,
maxTokens: 32768,
};
function assistant(model: string, content: AssistantMessage["content"]): AssistantMessage {
return {
role: "assistant",
content,
api: "openai-completions",
provider: "sference",
model,
usage: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
totalTokens: 0,
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
},
stopReason: "toolUse",
timestamp: Date.now(),
};
}
describe("thinking from other models", () => {
// https://github.com/earendil-works/pi/issues/10995
it("does not send another model's readable thinking as text", () => {
const messages: Message[] = [
{ role: "user", content: "Why does curl work but node fails?", timestamp: Date.now() },
assistant("zai-org/GLM-5.3", [
{
type: "thinking",
thinking: "The user asks about ECONNRESET. Candidates: proxy, IPv6, TLS.",
thinkingSignature: "reasoning_content",
},
{ type: "text", text: "Let me check the TLS versions." },
{ type: "toolCall", id: "call_1", name: "bash", arguments: { command: "curl -v" } },
]),
{
role: "toolResult",
toolCallId: "call_1",
toolName: "bash",
content: [{ type: "text", text: "TLSv1.2" }],
isError: false,
timestamp: Date.now(),
},
];
const result = transformMessages(messages, kimi);
const assistantMsg = result.find((m) => m.role === "assistant") as AssistantMessage;
expect(assistantMsg.content).toEqual([
{ type: "text", text: "Let me check the TLS versions." },
{ type: "toolCall", id: "call_1", name: "bash", arguments: { command: "curl -v" } },
]);
});
it("keeps thinking only from the target model when models alternate", () => {
const thinking = (text: string) => ({
type: "thinking" as const,
thinking: text,
thinkingSignature: "reasoning_content",
});
const messages: Message[] = [
{ role: "user", content: "1", timestamp: Date.now() },
{ ...assistant("moonshotai/Kimi-K3", [thinking("kimi 1"), { type: "text", text: "a" }]), stopReason: "stop" },
{ role: "user", content: "2", timestamp: Date.now() },
{ ...assistant("zai-org/GLM-5.3", [thinking("glm 1"), { type: "text", text: "b" }]), stopReason: "stop" },
{ role: "user", content: "3", timestamp: Date.now() },
{ ...assistant("moonshotai/Kimi-K3", [thinking("kimi 2"), { type: "text", text: "c" }]), stopReason: "stop" },
{ role: "user", content: "4", timestamp: Date.now() },
{ ...assistant("zai-org/GLM-5.3", [thinking("glm 2"), { type: "text", text: "d" }]), stopReason: "stop" },
{ role: "user", content: "hi", timestamp: Date.now() },
];
const result = transformMessages(messages, kimi);
const contents = result.filter((m) => m.role === "assistant").map((m) => (m as AssistantMessage).content);
expect(contents).toEqual([
[thinking("kimi 1"), { type: "text", text: "a" }],
[{ type: "text", text: "b" }],
[thinking("kimi 2"), { type: "text", text: "c" }],
[{ type: "text", text: "d" }],
]);
});
});