1use anthropic::{ANTHROPIC_API_URL, AnthropicError};
2use anyhow::{Context as _, Result, anyhow};
3use client::telemetry::Telemetry;
4use gpui::BackgroundExecutor;
5use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
6use std::env;
7use std::sync::Arc;
8use telemetry_events::{AssistantEventData, AssistantKind, AssistantPhase};
9use util::ResultExt;
10
11pub const ANTHROPIC_PROVIDER_ID: &str = "anthropic";
12
13pub fn report_assistant_event(
14 event: AssistantEventData,
15 telemetry: Option<Arc<Telemetry>>,
16 client: Arc<dyn HttpClient>,
17 model_api_key: Option<String>,
18 executor: &BackgroundExecutor,
19) {
20 if let Some(telemetry) = telemetry.as_ref() {
21 telemetry.report_assistant_event(event.clone());
22 if telemetry.metrics_enabled() && event.model_provider == ANTHROPIC_PROVIDER_ID {
23 executor
24 .spawn(async move {
25 report_anthropic_event(event, client, model_api_key)
26 .await
27 .log_err();
28 })
29 .detach();
30 }
31 }
32}
33
34async fn report_anthropic_event(
35 event: AssistantEventData,
36 client: Arc<dyn HttpClient>,
37 model_api_key: Option<String>,
38) -> Result<(), AnthropicError> {
39 let api_key = match model_api_key {
40 Some(key) => key,
41 None => {
42 return Err(AnthropicError::Other(anyhow!(
43 "Anthropic API key is not set"
44 )));
45 }
46 };
47
48 let uri = format!("{ANTHROPIC_API_URL}/v1/log/zed");
49 let request_builder = HttpRequest::builder()
50 .method(Method::POST)
51 .uri(uri)
52 .header("X-Api-Key", api_key)
53 .header("Content-Type", "application/json");
54 let serialized_event: serde_json::Value = serde_json::json!({
55 "completion_type": match event.kind {
56 AssistantKind::Inline => "natural_language_completion_in_editor",
57 AssistantKind::InlineTerminal => "natural_language_completion_in_terminal",
58 AssistantKind::Panel => "conversation_message",
59 },
60 "event": match event.phase {
61 AssistantPhase::Response => "response",
62 AssistantPhase::Invoked => "invoke",
63 AssistantPhase::Accepted => "accept",
64 AssistantPhase::Rejected => "reject",
65 },
66 "metadata": {
67 "language_name": event.language_name,
68 "message_id": event.message_id,
69 "platform": env::consts::OS,
70 }
71 });
72
73 let request = request_builder
74 .body(AsyncBody::from(serialized_event.to_string()))
75 .context("failed to construct request body")?;
76
77 let response = client
78 .send(request)
79 .await
80 .context("failed to send request to Anthropic")?;
81
82 if response.status().is_success() {
83 return Ok(());
84 }
85
86 return Err(AnthropicError::Other(anyhow!(
87 "Failed to log: {}",
88 response.status(),
89 )));
90}