Skip to main content

nemo_flow_adaptive/acg/
anthropic_plugin.rs

1// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Anthropic cache plugin for the Adaptive Cache Governor (ACG) system.
5//!
6//! Translates ACG stability classifications into Anthropic-specific
7//! `cache_control` breakpoints on content blocks. Implements the
8//! [`ProviderPlugin`] trait with:
9//!
10//! - **Breakpoint budget allocation**: Up to 4 breakpoints per request.
11//! - **Token minimum enforcement**: Per-model from [`CapabilityRegistry`].
12//! - **TTL mapping**: `RetentionTier` to Anthropic TTL (5m default or 1h extended).
13//! - **Tool schema canonicalization**: RFC 8785 via [`canonicalize_value`].
14//! - **System string-to-array conversion**: Handles both string and array-of-blocks format.
15//!
16//! # Threat mitigations
17//!
18//! - T-08-01: Only injects `cache_control` annotations with fixed structure `{"type": "ephemeral"}`.
19//! - T-08-02: `scope_label` is logged in detail but does NOT change cache visibility.
20//! - T-08-03: All JSON access uses Option-returning methods; errors propagate via `Result`.
21//! - T-08-05: TTL is hardcoded to exactly 2 behaviors (omit or `"1h"`).
22
23use crate::acg::capability::{BackendCapabilities, CapabilityRegistry};
24use crate::acg::plugin::{
25    HintPlanApplier, PluginInput, PluginOutput, ProviderPlugin, translate_with_hint_plan,
26};
27use crate::acg::prompt_ir::PromptIR;
28use crate::acg::translation::anthropic::AnthropicHintTranslator;
29use crate::acg::translation::{HintPlan, HintTranslation, HintTranslator};
30
31// ===================================================================
32// AnthropicCachePlugin
33// ===================================================================
34
35/// Anthropic-specific provider plugin for cache_control breakpoint injection.
36///
37/// Translates `CacheStability` and `Retention` intents into Anthropic's
38/// explicit `cache_control` annotations. Other intent types are marked
39/// `Ignored` / `NotRelevant`.
40///
41/// # Construction
42///
43/// ```rust,ignore
44/// let registry = CapabilityRegistry::with_defaults();
45/// let plugin = AnthropicCachePlugin::new(&registry);
46/// ```
47pub struct AnthropicCachePlugin {
48    translator: AnthropicHintTranslator,
49    capabilities: BackendCapabilities,
50}
51
52impl AnthropicCachePlugin {
53    /// Create a new Anthropic cache plugin backed by the given capability registry.
54    ///
55    /// The registry is cloned into an `Arc` for shared ownership.
56    pub fn new(registry: &CapabilityRegistry) -> Self {
57        Self {
58            translator: AnthropicHintTranslator::new(registry),
59            capabilities: registry
60                .get_backend("anthropic")
61                .cloned()
62                .unwrap_or_else(|| BackendCapabilities::none("anthropic")),
63        }
64    }
65
66    #[cfg_attr(not(test), allow(dead_code))]
67    pub(crate) fn build_hint_translation(
68        &self,
69        input: &PluginInput<'_>,
70    ) -> crate::acg::error::Result<HintTranslation> {
71        self.translator.translate(input)
72    }
73}
74
75impl ProviderPlugin for AnthropicCachePlugin {
76    fn plugin_id(&self) -> &str {
77        "anthropic"
78    }
79
80    fn plugin_name(&self) -> &str {
81        "Anthropic Cache Plugin"
82    }
83
84    fn translate(&self, input: &PluginInput<'_>) -> crate::acg::error::Result<PluginOutput> {
85        translate_with_hint_plan(&self.translator, self, input)
86    }
87
88    fn capabilities(&self) -> BackendCapabilities {
89        self.capabilities.clone()
90    }
91}
92
93impl HintPlanApplier for AnthropicCachePlugin {
94    fn apply_hint_plan(
95        &self,
96        request: &nemo_flow::api::llm::LlmRequest,
97        prompt_ir: &PromptIR,
98        hint_plan: &HintPlan,
99    ) -> crate::acg::error::Result<nemo_flow::api::llm::LlmRequest> {
100        crate::acg::request_surfaces::apply_request_surface(
101            self.plugin_id(),
102            request,
103            prompt_ir,
104            hint_plan,
105        )
106    }
107}
108
109#[cfg(test)]
110#[path = "../../tests/unit/acg/anthropic_plugin_tests.rs"]
111mod tests;