@@ -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 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 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.
|
- Fixed GitHub Copilot and OpenRouter test model references that became stale after model regeneration.
|
||||||
|
|
||||||
## [0.78.0] - 2026-05-29
|
## [0.78.0] - 2026-05-29
|
||||||
|
|||||||
@@ -1343,7 +1343,11 @@ async function generateModels() {
|
|||||||
candidate.maxTokens = 4096;
|
candidate.maxTokens = 4096;
|
||||||
}
|
}
|
||||||
if (candidate.provider === "openrouter" && candidate.id.startsWith("moonshotai/kimi-k2.6")) {
|
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") {
|
if (candidate.provider === "openrouter" && candidate.id === "z-ai/glm-5") {
|
||||||
candidate.cost.input = 0.6;
|
candidate.cost.input = 0.6;
|
||||||
|
|||||||
@@ -10281,7 +10281,7 @@ export const MODELS = {
|
|||||||
api: "openai-completions",
|
api: "openai-completions",
|
||||||
provider: "openrouter",
|
provider: "openrouter",
|
||||||
baseUrl: "https://openrouter.ai/api/v1",
|
baseUrl: "https://openrouter.ai/api/v1",
|
||||||
compat: {"supportsDeveloperRole":false},
|
compat: {"supportsDeveloperRole":false,"requiresReasoningContentOnAssistantMessages":true},
|
||||||
reasoning: true,
|
reasoning: true,
|
||||||
input: ["text", "image"],
|
input: ["text", "image"],
|
||||||
cost: {
|
cost: {
|
||||||
@@ -10299,7 +10299,7 @@ export const MODELS = {
|
|||||||
api: "openai-completions",
|
api: "openai-completions",
|
||||||
provider: "openrouter",
|
provider: "openrouter",
|
||||||
baseUrl: "https://openrouter.ai/api/v1",
|
baseUrl: "https://openrouter.ai/api/v1",
|
||||||
compat: {"supportsDeveloperRole":false},
|
compat: {"supportsDeveloperRole":false,"requiresReasoningContentOnAssistantMessages":true},
|
||||||
reasoning: true,
|
reasoning: true,
|
||||||
input: ["text", "image"],
|
input: ["text", "image"],
|
||||||
cost: {
|
cost: {
|
||||||
|
|||||||
@@ -1100,11 +1100,13 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet
|
|||||||
|
|
||||||
const isGrok = provider === "xai" || baseUrl.includes("api.x.ai");
|
const isGrok = provider === "xai" || baseUrl.includes("api.x.ai");
|
||||||
const isDeepSeek = provider === "deepseek" || baseUrl.includes("deepseek.com");
|
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;
|
const cacheControlFormat = provider === "openrouter" && model.id.startsWith("anthropic/") ? "anthropic" : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
supportsStore: !isNonStandard,
|
supportsStore: !isNonStandard,
|
||||||
supportsDeveloperRole: !isNonStandard && !isOpenRouter,
|
supportsDeveloperRole: isOpenRouterDeveloperRoleModel || (!isNonStandard && !isOpenRouter),
|
||||||
supportsReasoningEffort: !isGrok && !isZai && !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia,
|
supportsReasoningEffort: !isGrok && !isZai && !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia,
|
||||||
supportsUsageInStreaming: true,
|
supportsUsageInStreaming: true,
|
||||||
maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens",
|
maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens",
|
||||||
|
|||||||
@@ -817,7 +817,7 @@ describe("openai-completions tool_choice", () => {
|
|||||||
expect(writeCall).not.toHaveProperty("partialArgs");
|
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")!;
|
const model = getModel("openrouter", "deepseek/deepseek-v4-pro")!;
|
||||||
let payload: unknown;
|
let payload: unknown;
|
||||||
|
|
||||||
@@ -839,6 +839,33 @@ describe("openai-completions tool_choice", () => {
|
|||||||
expect(params.messages?.[0]?.role).toBe("system");
|
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 () => {
|
it("keeps developer messages for OpenAI reasoning model instructions", async () => {
|
||||||
const { compat: _compat, ...baseModel } = getModel("openai", "gpt-5.5")!;
|
const { compat: _compat, ...baseModel } = getModel("openai", "gpt-5.5")!;
|
||||||
const model = { ...baseModel, api: "openai-completions" } as const;
|
const model = { ...baseModel, api: "openai-completions" } as const;
|
||||||
@@ -862,6 +889,14 @@ describe("openai-completions tool_choice", () => {
|
|||||||
expect(params.messages?.[0]?.role).toBe("developer");
|
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", () => {
|
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;
|
const providers = ["xiaomi", "xiaomi-token-plan-cn", "xiaomi-token-plan-ams", "xiaomi-token-plan-sgp"] as const;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user