mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-06 23:44:36 +00:00
When switching models, thinking blocks from earlier assistant messages produced by a different model were sent as plain text. The new model saw the old model's reasoning as part of its replies and could start writing its own reasoning into the visible answer. These blocks are now dropped; text and tool calls are kept. closes #10995
99 lines
3.4 KiB
TypeScript
99 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" }],
|
|
]);
|
|
});
|
|
});
|