telemetry.rs

 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}