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