telemetry.rs

 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}