Skip to content

fix: minimize anthropic prompt cache change#9197

Open
911218sky wants to merge 1 commit into
AstrBotDevs:masterfrom
911218sky:fix/anthropic-prompt-cache-breakpoints
Open

fix: minimize anthropic prompt cache change#9197
911218sky wants to merge 1 commit into
AstrBotDevs:masterfrom
911218sky:fix/anthropic-prompt-cache-breakpoints

Conversation

@911218sky

@911218sky 911218sky commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Related to #9196

Changes / 改动

  • add explicit cache_control to the last system block in the Anthropic provider request payload

Verification / 验证

  • python3 -m py_compile astrbot/core/provider/sources/anthropic_source.py
  • tested with the running astrbot Docker container after syncing the updated file
  • confirmed the container loaded the updated code

@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. labels Jul 9, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces prompt caching support for the Anthropic provider by adding cache control parameters and sorting tools to ensure consistent payloads. The review feedback suggests refactoring the duplicated payload preparation logic shared between _query and _query_stream into a single helper method to reduce code duplication and improve maintainability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +484 to +507
@classmethod
def _enable_prompt_caching(cls, payloads: dict) -> None:
payloads["cache_control"] = dict(cls._PROMPT_CACHE_CONTROL)

@staticmethod
def _sort_anthropic_tools(tool_list: list[dict]) -> list[dict]:
return sorted(tool_list, key=lambda tool: str(tool.get("name", "")))

@classmethod
def _apply_explicit_prompt_cache_breakpoints(cls, payloads: dict) -> None:
tools = payloads.get("tools")
if isinstance(tools, list) and tools:
last_tool = tools[-1]
if isinstance(last_tool, dict) and "cache_control" not in last_tool:
last_tool["cache_control"] = dict(cls._PROMPT_CACHE_CONTROL)

system_blocks = payloads.get("system")
if not isinstance(system_blocks, list) or not system_blocks:
return

last_block = system_blocks[-1]
if isinstance(last_block, dict) and "cache_control" not in last_block:
last_block["cache_control"] = dict(cls._PROMPT_CACHE_CONTROL)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To avoid code duplication and adhere to the repository's general rules, the identical payload preparation logic in _query and _query_stream should be refactored into a shared helper method (e.g., _prepare_query_payload). This helper will handle sorting tools, setting tool choices, configuring max tokens, enabling prompt caching, applying cache breakpoints, applying thinking config, and sanitizing assistant messages.

    @classmethod
    def _enable_prompt_caching(cls, payloads: dict) -> None:
        payloads["cache_control"] = dict(cls._PROMPT_CACHE_CONTROL)

    @staticmethod
    def _sort_anthropic_tools(tool_list: list[dict]) -> list[dict]:
        return sorted(tool_list, key=lambda tool: str(tool.get("name", "")))

    @classmethod
    def _apply_explicit_prompt_cache_breakpoints(cls, payloads: dict) -> None:
        tools = payloads.get("tools")
        if isinstance(tools, list) and tools:
            last_tool = tools[-1]
            if isinstance(last_tool, dict) and "cache_control" not in last_tool:
                last_tool["cache_control"] = dict(cls._PROMPT_CACHE_CONTROL)

        system_blocks = payloads.get("system")
        if not isinstance(system_blocks, list) or not system_blocks:
            return

        last_block = system_blocks[-1]
        if isinstance(last_block, dict) and "cache_control" not in last_block:
            last_block["cache_control"] = dict(cls._PROMPT_CACHE_CONTROL)

    def _prepare_query_payload(self, payloads: dict, tools: ToolSet | None) -> None:
        if tools:
            if tool_list := tools.get_func_desc_anthropic_style():
                payloads["tools"] = self._sort_anthropic_tools(tool_list)
                payloads["tool_choice"] = self._normalize_tool_choice(
                    payloads.get("tool_choice", "auto")
                )

        if "max_tokens" not in payloads:
            payloads["max_tokens"] = 65536
        self._enable_prompt_caching(payloads)
        self._apply_explicit_prompt_cache_breakpoints(payloads)
        self._apply_thinking_config(payloads)
        self._sanitize_assistant_messages(payloads)
References
  1. When implementing similar functionality for different cases, refactor the logic into a shared helper function to avoid code duplication.

Comment on lines 609 to 614
if tools:
if tool_list := tools.get_func_desc_anthropic_style():
payloads["tools"] = tool_list
payloads["tools"] = self._sort_anthropic_tools(tool_list)
payloads["tool_choice"] = self._normalize_tool_choice(
payloads.get("tool_choice", "auto")
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use the newly introduced _prepare_query_payload helper method here as well to simplify the payload preparation in _query_stream.

        self._prepare_query_payload(payloads, tools)
References
  1. When implementing similar functionality for different cases, refactor the logic into a shared helper function to avoid code duplication.

Comment on lines 627 to 632
if "max_tokens" not in payloads:
payloads["max_tokens"] = 65536
self._enable_prompt_caching(payloads)
self._apply_explicit_prompt_cache_breakpoints(payloads)
self._apply_thinking_config(payloads)
self._sanitize_assistant_messages(payloads)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Remove this duplicated payload preparation block as it is now fully handled by the _prepare_query_payload helper method.

References
  1. When implementing similar functionality for different cases, refactor the logic into a shared helper function to avoid code duplication.

@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels Jul 9, 2026
@911218sky 911218sky changed the title fix: stabilize anthropic prompt caching fix: minimize anthropic prompt cache change Jul 9, 2026
@911218sky 911218sky force-pushed the fix/anthropic-prompt-cache-breakpoints branch from 423b46c to 67339df Compare July 9, 2026 11:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant