Send up Zed version with edit prediction and completion requests (#30136)

This PR makes it so we send up an `x-zed-version` header with the
client's version when making a request to llm.zed.dev for edit
predictions and completions.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2025-05-07 11:44:30 -04:00 committed by GitHub
parent 5ca114be24
commit a34fb6f6b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 26 additions and 9 deletions

5
Cargo.lock generated
View file

@ -7904,6 +7904,7 @@ dependencies = [
"partial-json-fixer", "partial-json-fixer",
"project", "project",
"proto", "proto",
"release_channel",
"schemars", "schemars",
"serde", "serde",
"serde_json", "serde_json",
@ -18883,9 +18884,9 @@ dependencies = [
[[package]] [[package]]
name = "zed_llm_client" name = "zed_llm_client"
version = "0.7.5" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fe0d60001c02d0d21a4114a13bee3a905fbb9e146ada80a90435c05fda18852" checksum = "a23b2fd00776b0c55072f389654910ceb501eb0083d7f78905ab0e5cc86949ec"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"serde", "serde",

View file

@ -610,7 +610,7 @@ wasmtime-wasi = "29"
which = "6.0.0" which = "6.0.0"
wit-component = "0.221" wit-component = "0.221"
workspace-hack = "0.1.0" workspace-hack = "0.1.0"
zed_llm_client = "0.7.5" zed_llm_client = "0.8.0"
zstd = "0.11" zstd = "0.11"
[workspace.dependencies.async-stripe] [workspace.dependencies.async-stripe]

View file

@ -1546,9 +1546,9 @@ impl Thread {
completion.queue_state = QueueState::Started; completion.queue_state = QueueState::Started;
} }
CompletionRequestStatus::Failed { CompletionRequestStatus::Failed {
code, message code, message, request_id
} => { } => {
return Err(anyhow!("completion request failed. code: {code}, message: {message}")); return Err(anyhow!("completion request failed. request_id: {request_id}, code: {code}, message: {message}"));
} }
CompletionRequestStatus::UsageUpdated { CompletionRequestStatus::UsageUpdated {
amount, limit amount, limit

View file

@ -41,6 +41,7 @@ open_ai = { workspace = true, features = ["schemars"] }
partial-json-fixer.workspace = true partial-json-fixer.workspace = true
project.workspace = true project.workspace = true
proto.workspace = true proto.workspace = true
release_channel.workspace = true
schemars.workspace = true schemars.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true

View file

@ -6,7 +6,9 @@ use feature_flags::{FeatureFlagAppExt, LlmClosedBetaFeatureFlag, ZedProFeatureFl
use futures::{ use futures::{
AsyncBufReadExt, FutureExt, Stream, StreamExt, future::BoxFuture, stream::BoxStream, AsyncBufReadExt, FutureExt, Stream, StreamExt, future::BoxFuture, stream::BoxStream,
}; };
use gpui::{AnyElement, AnyView, App, AsyncApp, Context, Entity, Subscription, Task}; use gpui::{
AnyElement, AnyView, App, AsyncApp, Context, Entity, SemanticVersion, Subscription, Task,
};
use http_client::{AsyncBody, HttpClient, Method, Response, StatusCode}; use http_client::{AsyncBody, HttpClient, Method, Response, StatusCode};
use language_model::{ use language_model::{
AuthenticateError, CloudModel, LanguageModel, LanguageModelCacheConfiguration, AuthenticateError, CloudModel, LanguageModel, LanguageModelCacheConfiguration,
@ -20,6 +22,7 @@ use language_model::{
MaxMonthlySpendReachedError, PaymentRequiredError, RefreshLlmTokenListener, MaxMonthlySpendReachedError, PaymentRequiredError, RefreshLlmTokenListener,
}; };
use proto::Plan; use proto::Plan;
use release_channel::AppVersion;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize, de::DeserializeOwned}; use serde::{Deserialize, Serialize, de::DeserializeOwned};
use settings::{Settings, SettingsStore}; use settings::{Settings, SettingsStore};
@ -39,7 +42,7 @@ use zed_llm_client::{
CompletionRequestStatus, CountTokensBody, CountTokensResponse, EXPIRED_LLM_TOKEN_HEADER_NAME, CompletionRequestStatus, CountTokensBody, CountTokensResponse, EXPIRED_LLM_TOKEN_HEADER_NAME,
MAX_LLM_MONTHLY_SPEND_REACHED_HEADER_NAME, MODEL_REQUESTS_RESOURCE_HEADER_VALUE, MAX_LLM_MONTHLY_SPEND_REACHED_HEADER_NAME, MODEL_REQUESTS_RESOURCE_HEADER_VALUE,
SERVER_SUPPORTS_STATUS_MESSAGES_HEADER_NAME, SUBSCRIPTION_LIMIT_RESOURCE_HEADER_NAME, SERVER_SUPPORTS_STATUS_MESSAGES_HEADER_NAME, SUBSCRIPTION_LIMIT_RESOURCE_HEADER_NAME,
TOOL_USE_LIMIT_REACHED_HEADER_NAME, TOOL_USE_LIMIT_REACHED_HEADER_NAME, ZED_VERSION_HEADER_NAME,
}; };
use crate::AllLanguageModelSettings; use crate::AllLanguageModelSettings;
@ -526,6 +529,7 @@ impl CloudLanguageModel {
async fn perform_llm_completion( async fn perform_llm_completion(
client: Arc<Client>, client: Arc<Client>,
llm_api_token: LlmApiToken, llm_api_token: LlmApiToken,
app_version: Option<SemanticVersion>,
body: CompletionBody, body: CompletionBody,
) -> Result<PerformLlmCompletionResponse> { ) -> Result<PerformLlmCompletionResponse> {
let http_client = &client.http_client(); let http_client = &client.http_client();
@ -542,6 +546,12 @@ impl CloudLanguageModel {
} else { } else {
request_builder.uri(http_client.build_zed_llm_url("/completions", &[])?.as_ref()) request_builder.uri(http_client.build_zed_llm_url("/completions", &[])?.as_ref())
}; };
let request_builder = if let Some(app_version) = app_version {
request_builder.header(ZED_VERSION_HEADER_NAME, app_version.to_string())
} else {
request_builder
};
let request = request_builder let request = request_builder
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {token}")) .header("Authorization", format!("Bearer {token}"))
@ -774,7 +784,7 @@ impl LanguageModel for CloudLanguageModel {
fn stream_completion( fn stream_completion(
&self, &self,
request: LanguageModelRequest, request: LanguageModelRequest,
_cx: &AsyncApp, cx: &AsyncApp,
) -> BoxFuture< ) -> BoxFuture<
'static, 'static,
Result< Result<
@ -784,6 +794,7 @@ impl LanguageModel for CloudLanguageModel {
let thread_id = request.thread_id.clone(); let thread_id = request.thread_id.clone();
let prompt_id = request.prompt_id.clone(); let prompt_id = request.prompt_id.clone();
let mode = request.mode; let mode = request.mode;
let app_version = cx.update(|cx| AppVersion::global(cx)).ok();
match &self.model { match &self.model {
CloudModel::Anthropic(model) => { CloudModel::Anthropic(model) => {
let request = into_anthropic( let request = into_anthropic(
@ -804,6 +815,7 @@ impl LanguageModel for CloudLanguageModel {
} = Self::perform_llm_completion( } = Self::perform_llm_completion(
client.clone(), client.clone(),
llm_api_token, llm_api_token,
app_version,
CompletionBody { CompletionBody {
thread_id, thread_id,
prompt_id, prompt_id,
@ -855,6 +867,7 @@ impl LanguageModel for CloudLanguageModel {
} = Self::perform_llm_completion( } = Self::perform_llm_completion(
client.clone(), client.clone(),
llm_api_token, llm_api_token,
app_version,
CompletionBody { CompletionBody {
thread_id, thread_id,
prompt_id, prompt_id,
@ -891,6 +904,7 @@ impl LanguageModel for CloudLanguageModel {
} = Self::perform_llm_completion( } = Self::perform_llm_completion(
client.clone(), client.clone(),
llm_api_token, llm_api_token,
app_version,
CompletionBody { CompletionBody {
thread_id, thread_id,
prompt_id, prompt_id,

View file

@ -55,7 +55,7 @@ use workspace::notifications::{ErrorMessagePrompt, NotificationId};
use worktree::Worktree; use worktree::Worktree;
use zed_llm_client::{ use zed_llm_client::{
EXPIRED_LLM_TOKEN_HEADER_NAME, MINIMUM_REQUIRED_VERSION_HEADER_NAME, PredictEditsBody, EXPIRED_LLM_TOKEN_HEADER_NAME, MINIMUM_REQUIRED_VERSION_HEADER_NAME, PredictEditsBody,
PredictEditsResponse, PredictEditsResponse, ZED_VERSION_HEADER_NAME,
}; };
const CURSOR_MARKER: &'static str = "<|user_cursor_is_here|>"; const CURSOR_MARKER: &'static str = "<|user_cursor_is_here|>";
@ -754,6 +754,7 @@ and then another
let request = request_builder let request = request_builder
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {}", token)) .header("Authorization", format!("Bearer {}", token))
.header(ZED_VERSION_HEADER_NAME, app_version.to_string())
.body(serde_json::to_string(&body)?.into())?; .body(serde_json::to_string(&body)?.into())?;
let mut response = http_client.send(request).await?; let mut response = http_client.send(request).await?;