fix(ai): cache Anthropic tools separately from transcript closes #3260
This commit is contained in:
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Changed Anthropic prompt caching to add a `cache_control` breakpoint on the last tool definition, so tool schemas can be cached independently from transcript updates while preserving existing cache retention behavior ([#3260](https://github.com/badlogic/pi-mono/issues/3260))
|
||||||
|
|
||||||
## [0.67.3] - 2026-04-15
|
## [0.67.3] - 2026-04-15
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import Anthropic from "@anthropic-ai/sdk";
|
import Anthropic from "@anthropic-ai/sdk";
|
||||||
import type {
|
import type {
|
||||||
|
CacheControlEphemeral,
|
||||||
ContentBlockParam,
|
ContentBlockParam,
|
||||||
MessageCreateParamsStreaming,
|
MessageCreateParamsStreaming,
|
||||||
MessageParam,
|
MessageParam,
|
||||||
@@ -49,7 +50,7 @@ function resolveCacheRetention(cacheRetention?: CacheRetention): CacheRetention
|
|||||||
function getCacheControl(
|
function getCacheControl(
|
||||||
baseUrl: string,
|
baseUrl: string,
|
||||||
cacheRetention?: CacheRetention,
|
cacheRetention?: CacheRetention,
|
||||||
): { retention: CacheRetention; cacheControl?: { type: "ephemeral"; ttl?: "1h" } } {
|
): { retention: CacheRetention; cacheControl?: CacheControlEphemeral } {
|
||||||
const retention = resolveCacheRetention(cacheRetention);
|
const retention = resolveCacheRetention(cacheRetention);
|
||||||
if (retention === "none") {
|
if (retention === "none") {
|
||||||
return { retention };
|
return { retention };
|
||||||
@@ -657,7 +658,7 @@ function buildParams(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (context.tools) {
|
if (context.tools) {
|
||||||
params.tools = convertTools(context.tools, isOAuthToken);
|
params.tools = convertTools(context.tools, isOAuthToken, cacheControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure thinking mode: adaptive (Opus 4.6 and Sonnet 4.6),
|
// Configure thinking mode: adaptive (Opus 4.6 and Sonnet 4.6),
|
||||||
@@ -709,7 +710,7 @@ function convertMessages(
|
|||||||
messages: Message[],
|
messages: Message[],
|
||||||
model: Model<"anthropic-messages">,
|
model: Model<"anthropic-messages">,
|
||||||
isOAuthToken: boolean,
|
isOAuthToken: boolean,
|
||||||
cacheControl?: { type: "ephemeral"; ttl?: "1h" },
|
cacheControl?: CacheControlEphemeral,
|
||||||
): MessageParam[] {
|
): MessageParam[] {
|
||||||
const params: MessageParam[] = [];
|
const params: MessageParam[] = [];
|
||||||
|
|
||||||
@@ -870,20 +871,25 @@ function convertMessages(
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertTools(tools: Tool[], isOAuthToken: boolean): Anthropic.Messages.Tool[] {
|
function convertTools(
|
||||||
|
tools: Tool[],
|
||||||
|
isOAuthToken: boolean,
|
||||||
|
cacheControl?: CacheControlEphemeral,
|
||||||
|
): Anthropic.Messages.Tool[] {
|
||||||
if (!tools) return [];
|
if (!tools) return [];
|
||||||
|
|
||||||
return tools.map((tool) => {
|
return tools.map((tool, index) => {
|
||||||
const jsonSchema = tool.parameters as any; // TypeBox already generates JSON Schema
|
const schema = tool.parameters as { properties?: unknown; required?: string[] };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: isOAuthToken ? toClaudeCodeName(tool.name) : tool.name,
|
name: isOAuthToken ? toClaudeCodeName(tool.name) : tool.name,
|
||||||
description: tool.description,
|
description: tool.description,
|
||||||
input_schema: {
|
input_schema: {
|
||||||
type: "object" as const,
|
type: "object",
|
||||||
properties: jsonSchema.properties || {},
|
properties: schema.properties ?? {},
|
||||||
required: jsonSchema.required || [],
|
required: schema.required ?? [],
|
||||||
},
|
},
|
||||||
|
...(cacheControl && index === tools.length - 1 ? { cache_control: cacheControl } : {}),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user