| 89 | } |
| 90 | |
| 91 | pub(crate) fn build_request( |
| 92 | &self, |
| 93 | messages: &[Message], |
| 94 | system: Option<&str>, |
| 95 | tools: &[ToolDefinition], |
| 96 | ) -> serde_json::Value { |
| 97 | let mut request = serde_json::json!({ |
| 98 | "model": self.model, |
| 99 | "max_tokens": self.max_tokens, |
| 100 | "messages": messages, |
| 101 | }); |
| 102 | |
| 103 | // System prompt with cache_control for prompt caching. |
| 104 | // Anthropic caches system content blocks marked with |
| 105 | // `cache_control: { type: "ephemeral" }`. |
| 106 | if let Some(sys) = system { |
| 107 | request["system"] = serde_json::json!([ |
| 108 | { |
| 109 | "type": "text", |
| 110 | "text": sys, |
| 111 | "cache_control": { "type": "ephemeral" } |
| 112 | } |
| 113 | ]); |
| 114 | } |
| 115 | |
| 116 | if !tools.is_empty() { |
| 117 | let mut tool_defs: Vec<serde_json::Value> = tools |
| 118 | .iter() |
| 119 | .map(|t| { |
| 120 | serde_json::json!({ |
| 121 | "name": t.name, |
| 122 | "description": t.description, |
| 123 | "input_schema": t.parameters, |
| 124 | }) |
| 125 | }) |
| 126 | .collect(); |
| 127 | |
| 128 | // Mark the last tool definition with cache_control so the |
| 129 | // entire tool block is cached on subsequent requests. |
| 130 | if let Some(last) = tool_defs.last_mut() { |
| 131 | last["cache_control"] = serde_json::json!({ "type": "ephemeral" }); |
| 132 | } |
| 133 | |
| 134 | request["tools"] = serde_json::json!(tool_defs); |
| 135 | } |
| 136 | |
| 137 | // Apply optional sampling parameters |
| 138 | if let Some(temp) = self.temperature { |
| 139 | request["temperature"] = serde_json::json!(temp); |
| 140 | } |
| 141 | |
| 142 | // Extended thinking (Anthropic-specific) |
| 143 | if let Some(budget) = self.thinking_budget { |
| 144 | request["thinking"] = serde_json::json!({ |
| 145 | "type": "enabled", |
| 146 | "budget_tokens": budget |
| 147 | }); |
| 148 | // Thinking requires temperature=1 per Anthropic docs |