Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion crates/braintrust-llm-router/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,11 @@ let stream = router.complete_stream(body, "gpt-4", ProviderFormat::ChatCompletio
### Authentication

```rust
use braintrust_llm_router::AuthConfig;
use braintrust_llm_router::{api_key_auth, AuthConfig};

// OpenAI-style (Authorization: Bearer <key>)
api_key_auth("sk-...")
// Or equivalently:
AuthConfig::ApiKey {
key: "sk-...".into(),
header: Some("authorization".into()),
Expand Down
14 changes: 9 additions & 5 deletions crates/braintrust-llm-router/examples/custom_auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@

use anyhow::Result;
use braintrust_llm_router::{
serde_json::json, AuthConfig, ClientHeaders, OpenAIConfig, OpenAIProvider, ProviderFormat,
Router,
api_key_auth, serde_json::json, AuthConfig, ClientHeaders, OpenAIConfig, OpenAIProvider,
ProviderFormat, Router,
};
use bytes::Bytes;
use serde_json::Value;
Expand Down Expand Up @@ -119,8 +119,12 @@ async fn main() -> Result<()> {

let router = Router::builder()
.with_catalog(catalog)
.add_provider("openai", openai_provider)
.add_api_key("openai", api_key) // Convenience method
.add_provider(
"openai",
openai_provider,
api_key_auth(&api_key),
vec![ProviderFormat::ChatCompletions],
)
.build()?;

let model = "gpt-4";
Expand All @@ -132,7 +136,7 @@ async fn main() -> Result<()> {

println!(" Sending authenticated request to GPT-4...");
let body = Bytes::from(serde_json::to_vec(&payload)?);
let bytes = router
let bytes: Bytes = router
.complete(
body,
model,
Expand Down
33 changes: 18 additions & 15 deletions crates/braintrust-llm-router/examples/multi_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

use anyhow::Result;
use braintrust_llm_router::{
serde_json::json, AnthropicConfig, AnthropicProvider, AuthConfig, ClientHeaders, ModelCatalog,
OpenAIConfig, OpenAIProvider, ProviderFormat, Router,
api_key_auth, serde_json::json, AnthropicConfig, AnthropicProvider, AuthConfig, ClientHeaders,
ModelCatalog, OpenAIConfig, OpenAIProvider, ProviderFormat, Router,
};
use bytes::Bytes;
use serde_json::Value;
Expand Down Expand Up @@ -40,25 +40,28 @@ async fn main() -> Result<()> {
// Add OpenAI provider if key is available
if let Some(key) = &openai_key {
let openai_provider = OpenAIProvider::new(OpenAIConfig::default())?;
builder = builder
.add_provider("openai", openai_provider)
.add_api_key("openai", key.clone());
builder = builder.add_provider(
"openai",
openai_provider,
api_key_auth(key),
vec![ProviderFormat::ChatCompletions],
);
println!("✅ OpenAI provider configured");
}

// Add Anthropic provider if key is available
if let Some(key) = &anthropic_key {
let anthropic_provider = AnthropicProvider::new(AnthropicConfig::default())?;
builder = builder
.add_provider("anthropic", anthropic_provider)
.add_auth(
"anthropic",
AuthConfig::ApiKey {
key: key.clone(),
header: Some("x-api-key".into()),
prefix: None,
},
);
builder = builder.add_provider(
"anthropic",
anthropic_provider,
AuthConfig::ApiKey {
key: key.clone(),
header: Some("x-api-key".into()),
prefix: None,
},
vec![ProviderFormat::ChatCompletions],
);
println!("✅ Anthropic provider configured");
}

Expand Down
16 changes: 10 additions & 6 deletions crates/braintrust-llm-router/examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

use anyhow::Result;
use braintrust_llm_router::{
serde_json::json, ClientHeaders, ModelCatalog, OpenAIConfig, OpenAIProvider, ProviderFormat,
Router,
api_key_auth, serde_json::json, ClientHeaders, ModelCatalog, OpenAIConfig, OpenAIProvider,
ProviderFormat, Router,
};
use bytes::Bytes;
use serde_json::Value;
Expand Down Expand Up @@ -37,8 +37,12 @@ async fn main() -> Result<()> {

let router = Router::builder()
.with_catalog(catalog)
.add_provider("openai", openai_provider)
.add_api_key("openai", api_key)
.add_provider(
"openai",
openai_provider,
api_key_auth(&api_key),
vec![ProviderFormat::ChatCompletions],
)
.build()?;

// Simple chat completion
Expand All @@ -57,7 +61,7 @@ async fn main() -> Result<()> {

// Convert payload to bytes and send request
let body = Bytes::from(serde_json::to_vec(&payload)?);
let bytes = router
let bytes: Bytes = router
.complete(
body,
model,
Expand Down Expand Up @@ -91,7 +95,7 @@ async fn main() -> Result<()> {
// Print model information
println!("\n🔍 Model Information:");
if let Some(spec) = router.catalog().get("gpt-4") {
let spec = spec.as_ref();
let spec: &braintrust_llm_router::ModelSpec = spec.as_ref();
println!(
" Display Name: {}",
spec.display_name.as_deref().unwrap_or("GPT-4")
Expand Down
7 changes: 4 additions & 3 deletions crates/braintrust-llm-router/examples/streaming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ async fn main() -> Result<()> {

let router = Router::builder()
.with_catalog(catalog)
.add_provider("anthropic", anthropic_provider)
.add_auth(
.add_provider(
"anthropic",
anthropic_provider,
AuthConfig::ApiKey {
key: anthropic_api_key,
header: Some("x-api-key".into()),
prefix: None,
},
vec![ProviderFormat::ChatCompletions],
)
.build()?;

Expand All @@ -59,7 +60,7 @@ async fn main() -> Result<()> {
});

let body = Bytes::from(serde_json::to_vec(&payload)?);
let mut stream = router
let mut stream: ResponseStream = router
.complete_stream(
body,
model,
Expand Down
9 changes: 9 additions & 0 deletions crates/braintrust-llm-router/src/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ pub enum AuthConfig {
},
}

/// Convenience function to create an API key authentication configuration.
pub fn api_key_auth(api_key: &str) -> AuthConfig {
AuthConfig::ApiKey {
key: api_key.to_string(),
header: Some("authorization".into()),
prefix: Some("Bearer".into()),
}
}

impl AuthConfig {
pub fn auth_type(&self) -> AuthType {
match self {
Expand Down
12 changes: 3 additions & 9 deletions crates/braintrust-llm-router/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,16 @@ pub fn build_client(settings: &ClientSettings) -> Result<Client> {
}

pub fn build_middleware_client(settings: &ClientSettings) -> Result<ClientWithMiddleware> {
if let Some(existing) = OVERRIDE_CLIENT.read().clone() {
return Ok(existing);
}
let client = build_client(settings)?;
Ok(reqwest_middleware::ClientBuilder::new(client).build())
}

static OVERRIDE_CLIENT: Lazy<RwLock<Option<ClientWithMiddleware>>> =
Lazy::new(|| RwLock::new(None));

pub fn override_client() -> Result<ClientWithMiddleware> {
if let Some(existing) = OVERRIDE_CLIENT.read().clone() {
return Ok(existing);
}
let client = build_middleware_client(&ClientSettings::default())?;
*OVERRIDE_CLIENT.write() = Some(client.clone());
Ok(client)
}

pub fn set_override_client(client: ClientWithMiddleware) {
*OVERRIDE_CLIENT.write() = Some(client);
}
Expand Down
1 change: 1 addition & 0 deletions crates/braintrust-llm-router/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod streaming;
pub use lingua::serde_json;

pub use auth::{
api_key_auth,
azure::{AzureEntraCredentials, AzureEntraTokenManager},
databricks::{DatabricksCredentials, DatabricksTokenManager},
google::{GoogleServiceAccountConfig, GoogleTokenManager, ServiceAccountKey},
Expand Down
4 changes: 2 additions & 2 deletions crates/braintrust-llm-router/src/providers/anthropic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use reqwest_middleware::ClientWithMiddleware;

use crate::auth::AuthConfig;
use crate::catalog::ModelSpec;
use crate::client::{build_middleware_client, override_client, ClientSettings};
use crate::client::{build_middleware_client, ClientSettings};
use crate::error::{Error, Result, UpstreamHttpError};
use crate::providers::ClientHeaders;
use crate::streaming::{single_bytes_stream, sse_stream, RawResponseStream};
Expand Down Expand Up @@ -48,7 +48,7 @@ impl AnthropicProvider {
if let Some(timeout) = config.timeout {
settings.request_timeout = timeout;
}
let client = override_client().or_else(|_| build_middleware_client(&settings))?;
let client = build_middleware_client(&settings)?;
Ok(Self { client, config })
}

Expand Down
4 changes: 2 additions & 2 deletions crates/braintrust-llm-router/src/providers/azure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use reqwest_middleware::ClientWithMiddleware;

use crate::auth::AuthConfig;
use crate::catalog::ModelSpec;
use crate::client::{build_middleware_client, override_client, ClientSettings};
use crate::client::{build_middleware_client, ClientSettings};
use crate::error::{Error, Result, UpstreamHttpError};
use crate::providers::ClientHeaders;
use crate::streaming::{single_bytes_stream, sse_stream, RawResponseStream};
Expand Down Expand Up @@ -49,7 +49,7 @@ impl AzureProvider {
if let Some(timeout) = config.timeout {
settings.request_timeout = timeout;
}
let client = override_client().or_else(|_| build_middleware_client(&settings))?;
let client = build_middleware_client(&settings)?;
Ok(Self { client, config })
}

Expand Down
4 changes: 2 additions & 2 deletions crates/braintrust-llm-router/src/providers/bedrock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use reqwest_middleware::ClientWithMiddleware;

use crate::auth::AuthConfig;
use crate::catalog::ModelSpec;
use crate::client::{build_middleware_client, override_client, ClientSettings};
use crate::client::{build_middleware_client, ClientSettings};
use crate::error::{Error, Result, UpstreamHttpError};
use crate::providers::ClientHeaders;
use crate::streaming::{bedrock_event_stream, single_bytes_stream, RawResponseStream};
Expand Down Expand Up @@ -52,7 +52,7 @@ impl BedrockProvider {
if let Some(timeout) = config.timeout {
settings.request_timeout = timeout;
}
let client = override_client().or_else(|_| build_middleware_client(&settings))?;
let client = build_middleware_client(&settings)?;
Ok(Self { client, config })
}

Expand Down
5 changes: 2 additions & 3 deletions crates/braintrust-llm-router/src/providers/databricks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use reqwest_middleware::ClientWithMiddleware;

use crate::auth::AuthConfig;
use crate::catalog::ModelSpec;
use crate::client::{override_client, ClientSettings};
use crate::client::{build_middleware_client, ClientSettings};
use crate::error::{Error, Result, UpstreamHttpError};
use crate::providers::ClientHeaders;
use crate::streaming::{single_bytes_stream, sse_stream, RawResponseStream};
Expand All @@ -32,8 +32,7 @@ impl DatabricksProvider {
if let Some(timeout) = config.timeout {
settings.request_timeout = timeout;
}
let client =
override_client().or_else(|_| crate::client::build_middleware_client(&settings))?;
let client = build_middleware_client(&settings)?;
Ok(Self { client, config })
}

Expand Down
4 changes: 2 additions & 2 deletions crates/braintrust-llm-router/src/providers/google.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use reqwest_middleware::ClientWithMiddleware;

use crate::auth::AuthConfig;
use crate::catalog::ModelSpec;
use crate::client::{build_middleware_client, override_client, ClientSettings};
use crate::client::{build_middleware_client, ClientSettings};
use crate::error::{Error, Result, UpstreamHttpError};
use crate::providers::ClientHeaders;
use crate::streaming::{single_bytes_stream, sse_stream, RawResponseStream};
Expand Down Expand Up @@ -42,7 +42,7 @@ impl GoogleProvider {
if let Some(timeout) = config.timeout {
settings.request_timeout = timeout;
}
let client = override_client().or_else(|_| build_middleware_client(&settings))?;
let client = build_middleware_client(&settings)?;
Ok(Self { client, config })
}

Expand Down
5 changes: 2 additions & 3 deletions crates/braintrust-llm-router/src/providers/mistral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use reqwest_middleware::ClientWithMiddleware;

use crate::auth::AuthConfig;
use crate::catalog::ModelSpec;
use crate::client::{build_middleware_client, override_client, ClientSettings};
use crate::client::{build_middleware_client, ClientSettings};
use crate::error::{Error, Result, UpstreamHttpError};
use crate::providers::ClientHeaders;
use crate::streaming::{single_bytes_stream, sse_stream, RawResponseStream};
Expand Down Expand Up @@ -41,8 +41,7 @@ impl MistralProvider {
if let Some(timeout) = config.timeout {
settings.request_timeout = timeout;
}
let client = override_client().or_else(|_| build_middleware_client(&settings))?;

let client = build_middleware_client(&settings)?;
Ok(Self { client, config })
}

Expand Down
4 changes: 2 additions & 2 deletions crates/braintrust-llm-router/src/providers/openai.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use reqwest_middleware::ClientWithMiddleware;

use crate::auth::AuthConfig;
use crate::catalog::ModelSpec;
use crate::client::{build_middleware_client, override_client, ClientSettings};
use crate::client::{build_middleware_client, ClientSettings};
use crate::error::{Error, Result, UpstreamHttpError};
use crate::providers::ClientHeaders;
use crate::streaming::{single_bytes_stream, sse_stream, RawResponseStream};
Expand Down Expand Up @@ -51,7 +51,7 @@ impl OpenAIProvider {
if let Some(timeout) = config.timeout {
settings.request_timeout = timeout;
}
let client = override_client().or_else(|_| build_middleware_client(&settings))?;
let client = build_middleware_client(&settings)?;
let endpoint_template = config.endpoint_template.clone();
Ok(Self {
client,
Expand Down
4 changes: 2 additions & 2 deletions crates/braintrust-llm-router/src/providers/vertex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use reqwest_middleware::ClientWithMiddleware;

use crate::auth::AuthConfig;
use crate::catalog::ModelSpec;
use crate::client::{build_middleware_client, override_client, ClientSettings};
use crate::client::{build_middleware_client, ClientSettings};
use crate::error::{Error, Result, UpstreamHttpError};
use crate::providers::ClientHeaders;
use crate::streaming::{single_bytes_stream, sse_stream, RawResponseStream};
Expand Down Expand Up @@ -49,7 +49,7 @@ impl VertexProvider {
if let Some(timeout) = config.timeout {
settings.request_timeout = timeout;
}
let client = override_client().or_else(|_| build_middleware_client(&settings))?;
let client = build_middleware_client(&settings)?;
Ok(Self { client, config })
}

Expand Down
Loading
Loading