language_models: Support custom HTTP headers for provider requests (#55246)

Add a `custom_headers` setting to each HTTP-based language model
provider
(Anthropic, Bedrock, DeepSeek, Google, LM Studio, Mistral, Ollama,
OpenAI,
OpenAI-compatible, OpenCode, OpenRouter, Vercel AI Gateway, and xAI) so
users
can attach extra headers to every outgoing request. Headers managed by
Zed
(authentication, content-type, etc.) cannot be overridden and are
skipped with
a warning.

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Added support for configuring custom HTTP headers on language model
  providers via `language_models.<provider>.custom_headers`.

Signed-off-by: Aurabindo Pillai <mail@aurabindo.in>
This commit is contained in:
Aurabindo Pillai authored and GitHub committed 2026-06-02 13:29:23 +00:00
1 parent 27db63f0e6
commit d524bc9a78
34 files changed
+678 -157

No files matched your search

+8 -5
View File
@@ -1,6 +1,8 @@
use anyhow::{Result, anyhow};
use futures::{AsyncBufReadExt, AsyncReadExt, StreamExt, io::BufReader, stream::BoxStream};
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
use http_client::{
AsyncBody, CustomHeaders, HttpClient, Method, Request as HttpRequest, RequestBuilderExt,
};
use language_model_core::ReasoningEffort;
use serde::{Deserialize, Serialize};
use strum::EnumIter;
@@ -642,6 +644,7 @@ pub async fn stream_generate_content(
api_url: &str,
api_key: &str,
request: google_ai::GenerateContentRequest,
extra_headers: &CustomHeaders,
) -> Result<BoxStream<'static, Result<google_ai::GenerateContentResponse>>> {
let api_key = api_key.trim();
@@ -649,13 +652,13 @@ pub async fn stream_generate_content(
let uri = format!("{api_url}/v1/models/{model_id}:streamGenerateContent?alt=sse");
let request_builder = HttpRequest::builder()
let request = HttpRequest::builder()
.method(Method::POST)
.uri(uri)
.header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {api_key}"));
let request = request_builder.body(AsyncBody::from(serde_json::to_string(&request)?))?;
.header("Authorization", format!("Bearer {api_key}"))
.extra_headers(extra_headers)
.body(AsyncBody::from(serde_json::to_string(&request)?))?;
let mut response = client.send(request).await?;
if response.status().is_success() {
let reader = BufReader::new(response.into_body());