fix(ai,coding-agent): support anthropic-style cache control for openai compatibles closes #3392

This commit is contained in:
Mario Zechner
2026-04-20 17:12:05 +02:00
parent 4b2caf43a8
commit 3054fd7a3b
13 changed files with 326 additions and 71 deletions

View File

@@ -19,6 +19,7 @@
- Fixed shared/exported plain-text tool output to preserve indentation instead of collapsing leading whitespace in the web share page ([#3440](https://github.com/badlogic/pi-mono/issues/3440))
- Fixed skill resolution to dedupe symlinked aliases by canonical path, so `pi config` no longer shows duplicate skill entries when `~/.pi/agent/skills` points to `~/.agents/skills` ([#3405](https://github.com/badlogic/pi-mono/issues/3405))
- Fixed OpenRouter request attribution to include Pi app headers (`HTTP-Referer: https://pi.dev`, `X-OpenRouter-Title: pi`, `X-OpenRouter-Categories: cli-agent`) when sessions are created through the coding-agent SDK and install telemetry is enabled ([#3414](https://github.com/badlogic/pi-mono/issues/3414))
- Fixed custom-model `compat` schema/docs to support `cacheControlFormat: "anthropic"` for OpenAI-compatible providers that expose Anthropic-style prompt caching via `cache_control` markers ([#3392](https://github.com/badlogic/pi-mono/issues/3392))
## [0.67.68] - 2026-04-17

View File

@@ -183,12 +183,14 @@ models: [{
},
maxTokensField: "max_tokens", // instead of "max_completion_tokens"
requiresToolResultName: true, // tool results need name field
thinkingFormat: "qwen" // top-level enable_thinking: true
thinkingFormat: "qwen", // top-level enable_thinking: true
cacheControlFormat: "anthropic" // Anthropic-style cache_control markers
}
}]
```
Use `qwen-chat-template` instead for local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking`.
Use `cacheControlFormat: "anthropic"` for OpenAI-compatible providers that expose Anthropic-style prompt caching via `cache_control` on the system prompt, last tool definition, and last user/assistant text content.
> Migration note: Mistral moved from `openai-completions` to `mistral-conversations`.
> Use `mistral-conversations` for native Mistral models.
@@ -589,8 +591,10 @@ interface ProviderModelConfig {
requiresAssistantAfterToolResult?: boolean;
requiresThinkingAsText?: boolean;
thinkingFormat?: "openai" | "zai" | "qwen" | "qwen-chat-template";
cacheControlFormat?: "anthropic";
};
}
```
`qwen` is for DashScope-style top-level `enable_thinking`. Use `qwen-chat-template` for local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking`.
`cacheControlFormat: "anthropic"` applies Anthropic-style `cache_control` markers to the system prompt, last tool definition, and last user/assistant text content.

View File

@@ -304,12 +304,15 @@ For providers with partial OpenAI compatibility, use the `compat` field.
| `requiresAssistantAfterToolResult` | Insert an assistant message before a user message after tool results |
| `requiresThinkingAsText` | Convert thinking blocks to plain text |
| `thinkingFormat` | Use `reasoning_effort`, `zai`, `qwen`, or `qwen-chat-template` thinking parameters |
| `cacheControlFormat` | Use Anthropic-style `cache_control` markers on the system prompt, last tool definition, and last user/assistant text content. Currently only `anthropic` is supported. |
| `supportsStrictMode` | Include the `strict` field in tool definitions |
| `openRouterRouting` | OpenRouter provider routing preferences. This object is sent as-is in the `provider` field of the [OpenRouter API request](https://openrouter.ai/docs/guides/routing/provider-selection). |
| `vercelGatewayRouting` | Vercel AI Gateway routing config for provider selection (`only`, `order`) |
`qwen` uses top-level `enable_thinking`. Use `qwen-chat-template` for local Qwen-compatible servers that require `chat_template_kwargs.enable_thinking`.
`cacheControlFormat: "anthropic"` is for OpenAI-compatible providers that expose Anthropic-style prompt caching through `cache_control` markers on text content and tool definitions.
Example:
```json

View File

@@ -108,6 +108,7 @@ const OpenAICompletionsCompatSchema = Type.Object({
Type.Literal("qwen-chat-template"),
]),
),
cacheControlFormat: Type.Optional(Type.Literal("anthropic")),
openRouterRouting: Type.Optional(OpenRouterRoutingSchema),
vercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),
supportsStrictMode: Type.Optional(Type.Boolean()),

View File

@@ -374,7 +374,7 @@ describe("ModelRegistry", () => {
}
});
test("compat schema accepts reasoningEffortMap and supportsStrictMode", () => {
test("compat schema accepts reasoningEffortMap, supportsStrictMode, and cacheControlFormat", () => {
writeRawModelsJson({
demo: {
baseUrl: "https://example.com/v1",
@@ -394,6 +394,7 @@ describe("ModelRegistry", () => {
high: "max",
},
supportsStrictMode: false,
cacheControlFormat: "anthropic",
},
},
],
@@ -406,6 +407,7 @@ describe("ModelRegistry", () => {
expect(registry.getError()).toBeUndefined();
expect(compat?.reasoningEffortMap).toEqual({ minimal: "default", high: "max" });
expect(compat?.supportsStrictMode).toBe(false);
expect(compat?.cacheControlFormat).toBe("anthropic");
});
test("model-level baseUrl overrides provider-level baseUrl for custom models", () => {