telemetry.rs

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