@@ -10,6 +10,7 @@
|
||||
|
||||
- Fixed Amazon Bedrock requests to replace blank required user/tool-result text with a placeholder and skip blank replay text blocks ([#4975](https://github.com/earendil-works/pi/issues/4975)).
|
||||
- Fixed OpenAI GPT-5.5 generated metadata to omit unsupported minimal thinking ([#5243](https://github.com/earendil-works/pi/issues/5243)).
|
||||
- Fixed OpenRouter Kimi K2.6 thinking replay and preserved developer-role instructions for OpenRouter OpenAI and Anthropic models ([#5309](https://github.com/earendil-works/pi/issues/5309)).
|
||||
- Fixed GitHub Copilot and OpenRouter test model references that became stale after model regeneration.
|
||||
|
||||
## [0.78.0] - 2026-05-29
|
||||
|
||||
@@ -1343,7 +1343,11 @@ async function generateModels() {
|
||||
candidate.maxTokens = 4096;
|
||||
}
|
||||
if (candidate.provider === "openrouter" && candidate.id.startsWith("moonshotai/kimi-k2.6")) {
|
||||
candidate.compat = { ...candidate.compat, supportsDeveloperRole: false };
|
||||
candidate.compat = {
|
||||
...candidate.compat,
|
||||
supportsDeveloperRole: false,
|
||||
requiresReasoningContentOnAssistantMessages: true,
|
||||
};
|
||||
}
|
||||
if (candidate.provider === "openrouter" && candidate.id === "z-ai/glm-5") {
|
||||
candidate.cost.input = 0.6;
|
||||
|
||||
@@ -10281,7 +10281,7 @@ export const MODELS = {
|
||||
api: "openai-completions",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
compat: {"supportsDeveloperRole":false},
|
||||
compat: {"supportsDeveloperRole":false,"requiresReasoningContentOnAssistantMessages":true},
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
@@ -10299,7 +10299,7 @@ export const MODELS = {
|
||||
api: "openai-completions",
|
||||
provider: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
compat: {"supportsDeveloperRole":false},
|
||||
compat: {"supportsDeveloperRole":false,"requiresReasoningContentOnAssistantMessages":true},
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
|
||||
@@ -1100,11 +1100,13 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet
|
||||
|
||||
const isGrok = provider === "xai" || baseUrl.includes("api.x.ai");
|
||||
const isDeepSeek = provider === "deepseek" || baseUrl.includes("deepseek.com");
|
||||
const isOpenRouterDeveloperRoleModel =
|
||||
isOpenRouter && (model.id.startsWith("anthropic/") || model.id.startsWith("openai/"));
|
||||
const cacheControlFormat = provider === "openrouter" && model.id.startsWith("anthropic/") ? "anthropic" : undefined;
|
||||
|
||||
return {
|
||||
supportsStore: !isNonStandard,
|
||||
supportsDeveloperRole: !isNonStandard && !isOpenRouter,
|
||||
supportsDeveloperRole: isOpenRouterDeveloperRoleModel || (!isNonStandard && !isOpenRouter),
|
||||
supportsReasoningEffort: !isGrok && !isZai && !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia,
|
||||
supportsUsageInStreaming: true,
|
||||
maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens",
|
||||
|
||||
@@ -817,7 +817,7 @@ describe("openai-completions tool_choice", () => {
|
||||
expect(writeCall).not.toHaveProperty("partialArgs");
|
||||
});
|
||||
|
||||
it("uses system messages for OpenRouter reasoning model instructions", async () => {
|
||||
it("uses system messages for non-OpenAI/Anthropic OpenRouter reasoning model instructions", async () => {
|
||||
const model = getModel("openrouter", "deepseek/deepseek-v4-pro")!;
|
||||
let payload: unknown;
|
||||
|
||||
@@ -839,6 +839,33 @@ describe("openai-completions tool_choice", () => {
|
||||
expect(params.messages?.[0]?.role).toBe("system");
|
||||
});
|
||||
|
||||
it("keeps developer messages for OpenAI and Anthropic OpenRouter reasoning model instructions", async () => {
|
||||
for (const model of [
|
||||
getModel("openrouter", "openai/gpt-5.2-codex"),
|
||||
getModel("openrouter", "anthropic/claude-sonnet-4.5"),
|
||||
]) {
|
||||
expect(model).toBeDefined();
|
||||
let payload: unknown;
|
||||
|
||||
await streamSimple(
|
||||
model!,
|
||||
{
|
||||
systemPrompt: "Follow instructions.",
|
||||
messages: [{ role: "user", content: "Hi", timestamp: Date.now() }],
|
||||
},
|
||||
{
|
||||
apiKey: "test",
|
||||
onPayload: (params: unknown) => {
|
||||
payload = params;
|
||||
},
|
||||
},
|
||||
).result();
|
||||
|
||||
const params = payload as { messages?: Array<{ role?: string }> };
|
||||
expect(params.messages?.[0]?.role).toBe("developer");
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps developer messages for OpenAI reasoning model instructions", async () => {
|
||||
const { compat: _compat, ...baseModel } = getModel("openai", "gpt-5.5")!;
|
||||
const model = { ...baseModel, api: "openai-completions" } as const;
|
||||
@@ -862,6 +889,14 @@ describe("openai-completions tool_choice", () => {
|
||||
expect(params.messages?.[0]?.role).toBe("developer");
|
||||
});
|
||||
|
||||
it("stores OpenRouter Kimi K2.6 reasoning replay compat in built-in metadata", () => {
|
||||
for (const modelId of ["moonshotai/kimi-k2.6", "moonshotai/kimi-k2.6:free"] as const) {
|
||||
const model = getModel("openrouter", modelId)!;
|
||||
expect(model.compat?.supportsDeveloperRole).toBe(false);
|
||||
expect(model.compat?.requiresReasoningContentOnAssistantMessages).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("stores Xiaomi MiMo reasoning replay compat in built-in metadata", () => {
|
||||
const providers = ["xiaomi", "xiaomi-token-plan-cn", "xiaomi-token-plan-ams", "xiaomi-token-plan-sgp"] as const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user