Execute a fully-built (non-streaming) request body.
(&self, request_body: serde_json::Value)
| 169 | |
| 170 | /// Execute a fully-built (non-streaming) request body. |
| 171 | async fn send_request(&self, request_body: serde_json::Value) -> Result<LlmResponse> { |
| 172 | { |
| 173 | let request_started_at = Instant::now(); |
| 174 | let url = format!("{}/v1/messages", self.base_url); |
| 175 | |
| 176 | let headers = vec![ |
| 177 | ("x-api-key", self.api_key.expose()), |
| 178 | ("anthropic-version", "2023-06-01"), |
| 179 | ("anthropic-beta", "prompt-caching-2024-07-31"), |
| 180 | ]; |
| 181 | |
| 182 | let response = crate::retry::with_retry(&self.retry_config, |_attempt| { |
| 183 | let http = &self.http; |
| 184 | let url = &url; |
| 185 | let headers = headers.clone(); |
| 186 | let request_body = &request_body; |
| 187 | async move { |
| 188 | match http |
| 189 | .post(url, headers, request_body, CancellationToken::new()) |
| 190 | .await |
| 191 | { |
| 192 | Ok(resp) => { |
| 193 | let status = reqwest::StatusCode::from_u16(resp.status) |
| 194 | .unwrap_or(reqwest::StatusCode::INTERNAL_SERVER_ERROR); |
| 195 | if status.is_success() { |
| 196 | AttemptOutcome::Success(resp.body) |
| 197 | } else if self.retry_config.is_retryable_status(status) { |
| 198 | AttemptOutcome::Retryable { |
| 199 | status, |
| 200 | body: resp.body, |
| 201 | retry_after: None, |
| 202 | } |
| 203 | } else { |
| 204 | AttemptOutcome::Fatal(anyhow::anyhow!( |
| 205 | "Anthropic API error at {} ({}): {}", |
| 206 | url, |
| 207 | status, |
| 208 | resp.body |
| 209 | )) |
| 210 | } |
| 211 | } |
| 212 | Err(e) => AttemptOutcome::Fatal(e), |
| 213 | } |
| 214 | } |
| 215 | }) |
| 216 | .await?; |
| 217 | |
| 218 | let parsed: AnthropicResponse = |
| 219 | serde_json::from_str(&response).context("Failed to parse Anthropic response")?; |
| 220 | |
| 221 | tracing::debug!("Anthropic response: {:?}", parsed); |
| 222 | |
| 223 | let content: Vec<ContentBlock> = parsed |
| 224 | .content |
| 225 | .into_iter() |
| 226 | .map(|block| match block { |
| 227 | AnthropicContentBlock::Text { text } => ContentBlock::Text { text }, |
| 228 | AnthropicContentBlock::ToolUse { id, name, input } => { |
no test coverage detected