Files
oak-gpui/crates/language_model/src/telemetry.rs
T
Richard Feldman cfbc2d0972 Don't spawn Anthropic telemetry event when API key is missing (#32813)
Minor refactor that I'm extracting from a branch because it can stand
alone.

- Now we no longer spawn an executor for `report_anthropic_event` if
it's just going to immediately fail due to API key being missing
- `report_anthropic_event` now takes a `String` API key instead of
`Option<String>` and the error reporting if the key is missing has been
moved to the caller.
- `report_anthropic_event` is longer coupled to `AnthropicError`,
because all it ever did was generate an `AnthropicEvent::Other`, which
in turn was then only used for `log_err` - so, can just be an
`anyhow::Result`.

Release Notes:

- N/A
2025-06-16 14:58:37 -04:00

86 lines
2.9 KiB
Rust

use anthropic::ANTHROPIC_API_URL;
use anyhow::{Context as _, anyhow};
use client::telemetry::Telemetry;
use gpui::BackgroundExecutor;
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
use std::env;
use std::sync::Arc;
use telemetry_events::{AssistantEventData, AssistantKind, AssistantPhase};
use util::ResultExt;
pub const ANTHROPIC_PROVIDER_ID: &str = "anthropic";
pub fn report_assistant_event(
event: AssistantEventData,
telemetry: Option<Arc<Telemetry>>,
client: Arc<dyn HttpClient>,
model_api_key: Option<String>,
executor: &BackgroundExecutor,
) {
if let Some(telemetry) = telemetry.as_ref() {
telemetry.report_assistant_event(event.clone());
if telemetry.metrics_enabled() && event.model_provider == ANTHROPIC_PROVIDER_ID {
if let Some(api_key) = model_api_key {
executor
.spawn(async move {
report_anthropic_event(event, client, api_key)
.await
.log_err();
})
.detach();
} else {
log::error!("Cannot send Anthropic telemetry because API key is missing");
}
}
}
}
async fn report_anthropic_event(
event: AssistantEventData,
client: Arc<dyn HttpClient>,
api_key: String,
) -> anyhow::Result<()> {
let uri = format!("{ANTHROPIC_API_URL}/v1/log/zed");
let request_builder = HttpRequest::builder()
.method(Method::POST)
.uri(uri)
.header("X-Api-Key", api_key)
.header("Content-Type", "application/json");
let serialized_event: serde_json::Value = serde_json::json!({
"completion_type": match event.kind {
AssistantKind::Inline => "natural_language_completion_in_editor",
AssistantKind::InlineTerminal => "natural_language_completion_in_terminal",
AssistantKind::Panel => "conversation_message",
},
"event": match event.phase {
AssistantPhase::Response => "response",
AssistantPhase::Invoked => "invoke",
AssistantPhase::Accepted => "accept",
AssistantPhase::Rejected => "reject",
},
"metadata": {
"language_name": event.language_name,
"message_id": event.message_id,
"platform": env::consts::OS,
}
});
let request = request_builder
.body(AsyncBody::from(serialized_event.to_string()))
.context("Failed to construct Anthropic telemetry HTTP request body")?;
let response = client
.send(request)
.await
.context("Failed to send telemetry HTTP request to Anthropic")?;
if response.status().is_success() {
Ok(())
} else {
Err(anyhow!(
"Anthropic telemetry logging failed with HTTP status: {}",
response.status()
))
}
}