Merge remote-tracking branch 'origin/main' into litellm_openai_ws_frame_model_gate

# Conflicts:
#	litellm/proxy/pass_through_endpoints/pass_through_endpoints.py
#	tests/unit/proxy/pass_through_endpoints/test_pass_through_endpoints.py
This commit is contained in:
mateo-berri 2026-10-30 15:39:15 -07:00
commit 8f1c05cc34
598 changed files with 36305 additions and 4866 deletions

View file

@ -42,6 +42,11 @@ concurrency:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: sccache
SCCACHE_GHA_ENABLED: "true"
SCCACHE_GHA_VERSION: litellm-ci-v1
SCCACHE_GHA_RW_MODE: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && 'READ_WRITE' || 'READ_ONLY' }}
CARGO_INCREMENTAL: "0"
jobs:
db-queries:
@ -87,6 +92,11 @@ jobs:
workspaces: litellm-rust
cache-on-failure: true
- name: Set up sccache
uses: mozilla-actions/sccache-action@d651010b8da762cde178750d8eda7b5febfe147a
with:
version: "v0.18.0"
- name: Install Python dependencies
working-directory: .
run: uv sync --frozen --extra extra-proxy

View file

@ -78,6 +78,11 @@ concurrency:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: sccache
SCCACHE_GHA_ENABLED: "true"
SCCACHE_GHA_VERSION: litellm-ci-v1
SCCACHE_GHA_RW_MODE: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && 'READ_WRITE' || 'READ_ONLY' }}
CARGO_INCREMENTAL: "0"
jobs:
rust-lint:
@ -101,6 +106,11 @@ jobs:
cache-on-failure: true
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Set up sccache
uses: mozilla-actions/sccache-action@d651010b8da762cde178750d8eda7b5febfe147a
with:
version: "v0.18.0"
- run: cargo clippy --workspace --all-targets --locked -- -D warnings
rust-test:
@ -143,6 +153,11 @@ jobs:
cache-on-failure: true
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Set up sccache
uses: mozilla-actions/sccache-action@d651010b8da762cde178750d8eda7b5febfe147a
with:
version: "v0.18.0"
- name: Check generated trace contracts
working-directory: .
run: uv run scripts/generate_trace_types.py --check
@ -189,6 +204,11 @@ jobs:
cache-on-failure: true
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Set up sccache
uses: mozilla-actions/sccache-action@d651010b8da762cde178750d8eda7b5febfe147a
with:
version: "v0.18.0"
- run: uv build --wheel --out-dir dist
- run: python .github/scripts/verify_linux_native_wheel.py dist/*.whl

View file

@ -73,9 +73,21 @@ jobs:
timeout-minutes: 5
uses: ./.github/actions/cache-cargo-build
- name: Set up sccache
if: steps.changes.outputs.decision != 'skip' && steps.rust-bridge-cache.outputs.cache-hit != 'true'
uses: mozilla-actions/sccache-action@d651010b8da762cde178750d8eda7b5febfe147a
with:
version: "v0.18.0"
- name: Build editable Rust bridge
if: steps.changes.outputs.decision != 'skip' && steps.rust-bridge-cache.outputs.cache-hit != 'true'
timeout-minutes: 15
env:
RUSTC_WRAPPER: sccache
SCCACHE_GHA_ENABLED: "true"
SCCACHE_GHA_VERSION: litellm-ci-v1
SCCACHE_GHA_RW_MODE: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && 'READ_WRITE' || 'READ_ONLY' }}
CARGO_INCREMENTAL: "0"
run: |
mkdir -p rust-bridge-dist
uv run --no-project --with maturin==1.15.0 python -c 'import maturin; maturin.build_editable("rust-bridge-dist")'
@ -227,6 +239,7 @@ jobs:
tests/unit/llms/test_oss_decision.py
tests/unit/llms/test_polling_url_origin_match.py
tests/unit/llms/test_predibase_transformation.py
tests/unit/llms/test_subscription_accounts.py
tests/unit/llms/a2a
tests/unit/llms/aiml
tests/unit/llms/aiohttp_openai

View file

@ -55,6 +55,7 @@ BACKEND_PATH_PREFIXES: tuple[str, ...] = (
"/cache_settings",
"/coordination_redis/",
"/cost_tracking",
"/subscription_accounts/",
"/cost_optimization/",
"/cost/",
"/credentials",

View file

@ -0,0 +1,21 @@
SET LOCAL lock_timeout = '10s';
-- CreateTable
CREATE TABLE IF NOT EXISTS "LiteLLM_SubscriptionAccountTable" (
"subscription_account_id" TEXT NOT NULL,
"custom_llm_provider" TEXT NOT NULL,
"account_id" TEXT NOT NULL,
"label" TEXT,
"monthly_fee" DOUBLE PRECISION NOT NULL,
"currency" TEXT NOT NULL,
"billing_period_start" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"created_by" TEXT NOT NULL,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_by" TEXT NOT NULL,
CONSTRAINT "LiteLLM_SubscriptionAccountTable_pkey" PRIMARY KEY ("subscription_account_id")
);
-- CreateIndex
CREATE UNIQUE INDEX IF NOT EXISTS "LiteLLM_SubscriptionAccountTable_provider_account_key" ON "LiteLLM_SubscriptionAccountTable"("custom_llm_provider", "account_id");

View file

@ -2131,3 +2131,19 @@ model LiteLLM_AccountingDelivery {
@@id([event_id, target, version])
@@index([target, state, next_attempt_at])
}
model LiteLLM_SubscriptionAccountTable {
subscription_account_id String @id @default(uuid())
custom_llm_provider String
account_id String
label String?
monthly_fee Float
currency String
billing_period_start String
created_at DateTime @default(now()) @map("created_at")
created_by String
updated_at DateTime @default(now()) @updatedAt @map("updated_at")
updated_by String
@@unique([custom_llm_provider, account_id], map: "LiteLLM_SubscriptionAccountTable_provider_account_key")
}

View file

@ -0,0 +1,43 @@
---
name: rust-redaction
description: Keep secrets out of Debug output in litellm-rust with SecretValue and veil. Use when a struct or enum holds a password, token, key, credential URL or credential provider, when writing or reviewing a Debug impl, or when a type with secrets gets logged or traced
metadata:
internal: true
---
# Rust redaction
Every secret in a Debug output is hidden the same way, so a reviewer can tell at a glance that a field is covered and a new field cannot slip through a hand-written impl
## Where SecretValue lives
`SecretValue` lives in `crates/secret-value` and is imported as `litellm_secret_value::SecretValue` everywhere. No crate re-exports it, so there is one import path. Depend on `litellm-secret-value` directly. Do not pull in `litellm-auth-types` or `litellm-auth` just to get it
`crates/secret-value` holds only the type. Credential acquisition (`TokenProvider`, `CredentialPlan`, provider param tables) stays in `litellm-auth-types`, and generic clients such as `litellm-redis-client` must not depend on it
## Rules
- Never hand-write `impl Debug` to hide a value, and never invent markers like `"<redacted>"`, `"<configured>"` or `.map(|_| "...")`. A hand-written impl silently prints any field added later
- A secret that crosses a crate boundary or lives in config is typed as `SecretValue`. It redacts itself, compares in constant time and only gives up the plaintext through `expose()`. A type whose only secrets are `SecretValue` fields derives plain `Debug`
```rust
#[derive(Clone, Debug, PartialEq)]
pub struct HostEndpoint {
pub host: String,
pub port: u16,
pub username: Option<SecretValue>,
pub password: Option<SecretValue>,
pub tls: bool,
pub db: i64,
}
```
- Keep a secret as `SecretValue` until the last conversion into the third-party type (redis-rs, reqwest, an SDK) and call `expose()` there. Calling `expose()` early spreads plaintext `String`s through our own types
- A secret that has to stay a raw type, because a serde wire shape or a third-party struct owns the field, is covered by deriving `veil::Redact` on the holder and marking the field `#[redact(with = "[REDACTED]")]`. `Redact` replaces `Debug`, so do not derive both
- A URL that can carry userinfo is a secret as a whole. Do not parse it to show the host. Keep it as a `SecretValue`
- A bag of unparsed settings that may hold credentials (`additional_fields`, raw kwargs) is redacted as a whole with `#[redact(with = "[REDACTED]")]`
- An opaque handle over `Arc<dyn Trait>` whose trait has no `Debug` gets a newtype whose `Debug` prints only the type name with `finish_non_exhaustive()`. That impl shows nothing, so it is not a redaction impl and the first rule does not apply. A type holding such a handle can derive `Debug`
- Never put a secret in a tracing field or an error message. Log the type with `?value`, or log only non-secret fields. See the rust-tracing and rust-error skills
## Tests
- One test per type that holds a secret, built the way production builds it, asserting the plaintext is absent from `format!("{value:?}")` and `[REDACTED]` is present
- Use a distinctive plaintext (`"credential-value"`) so the absence check cannot pass by accident

View file

@ -1,5 +1,7 @@
# Rust workspace rules
For keeping secrets out of Debug output, follow [.agents/skills/rust-redaction/SKILL.md](.agents/skills/rust-redaction/SKILL.md)
For diagnostic tracing changes, follow [.agents/skills/rust-tracing/SKILL.md](.agents/skills/rust-tracing/SKILL.md)
For string-valued enums and their Serde conversions, follow [.agents/skills/rust-string-enums/SKILL.md](.agents/skills/rust-string-enums/SKILL.md)

View file

@ -3380,6 +3380,7 @@ dependencies = [
"litellm-auth-azure",
"litellm-auth-gcp",
"litellm-auth-types",
"litellm-secret-value",
]
[[package]]
@ -3416,6 +3417,7 @@ dependencies = [
"azure_identity",
"litellm-auth-types",
"litellm-error",
"litellm-secret-value",
"moka",
"rstest",
"serde_json",
@ -3435,6 +3437,7 @@ dependencies = [
"http 1.4.2",
"litellm-auth-types",
"litellm-error",
"litellm-secret-value",
"moka",
"rstest",
"serde",
@ -3450,10 +3453,10 @@ name = "litellm-auth-types"
version = "0.1.0"
dependencies = [
"litellm-error",
"litellm-secret-value",
"rstest",
"serde",
"serde_json",
"subtle",
"thiserror 2.0.19",
"tokio",
"veil",
@ -3523,6 +3526,7 @@ dependencies = [
"litellm-cache-testing",
"litellm-error",
"litellm-http",
"litellm-secret-value",
"percent-encoding",
"reqwest",
"rstest",
@ -3668,10 +3672,10 @@ dependencies = [
name = "litellm-callbacks-legacy-python"
version = "0.1.0"
dependencies = [
"litellm-auth",
"litellm-error",
"litellm-host",
"litellm-host-python",
"litellm-secret-value",
"proptest",
"pyo3",
"rstest",
@ -3692,9 +3696,9 @@ dependencies = [
name = "litellm-config"
version = "0.1.0"
dependencies = [
"litellm-auth-types",
"litellm-error",
"litellm-router-types",
"litellm-secret-value",
"rstest",
"serde",
"serde_json",
@ -3804,7 +3808,6 @@ dependencies = [
"envy",
"futures-util",
"http-body-util",
"litellm-auth-types",
"litellm-circuit-breaker",
"litellm-config",
"litellm-error",
@ -3817,6 +3820,7 @@ dependencies = [
"litellm-llms",
"litellm-redis-client",
"litellm-redis-testing",
"litellm-secret-value",
"litellm-secrets",
"litellm-tracing",
"rstest",
@ -3839,9 +3843,9 @@ dependencies = [
"axum",
"axum-login",
"futures-util",
"litellm-auth-types",
"litellm-config",
"litellm-error",
"litellm-secret-value",
"litellm-secrets",
"rstest",
"serde",
@ -3879,6 +3883,7 @@ dependencies = [
"litellm-llms",
"litellm-llms-types",
"litellm-router",
"litellm-secret-value",
"litellm-secrets",
"rstest",
"serde",
@ -3895,9 +3900,9 @@ name = "litellm-gateway-management"
version = "0.1.0"
dependencies = [
"base64 0.22.1",
"litellm-auth-types",
"litellm-error",
"litellm-gateway-auth",
"litellm-secret-value",
"rand 0.8.7",
"rstest",
"thiserror 2.0.19",
@ -3912,9 +3917,9 @@ dependencies = [
"base64 0.22.1",
"futures-util",
"http 1.4.2",
"litellm-auth-types",
"litellm-config",
"litellm-error",
"litellm-secret-value",
"litellm-secrets",
"moka",
"rmcp",
@ -3942,9 +3947,9 @@ dependencies = [
"futures-util",
"governor",
"jsonwebtoken",
"litellm-auth-types",
"litellm-error",
"litellm-gateway-auth",
"litellm-secret-value",
"rand 0.8.7",
"rstest",
"serde",
@ -3964,8 +3969,8 @@ name = "litellm-host"
version = "0.1.0"
dependencies = [
"futures-util",
"litellm-auth",
"litellm-coroutine",
"litellm-secret-value",
"rstest",
"serde_json",
"thiserror 2.0.19",
@ -4094,6 +4099,7 @@ dependencies = [
"litellm-inference-testing",
"litellm-llms",
"litellm-llms-types",
"litellm-secret-value",
"litellm-secrets",
"litellm-tracing",
"rstest",
@ -4124,6 +4130,7 @@ dependencies = [
"litellm-llms",
"litellm-llms-types",
"litellm-router-types",
"litellm-secret-value",
"litellm-secrets",
"litellm-tracing",
"reqwest",
@ -4154,6 +4161,7 @@ dependencies = [
"litellm-inference-testing",
"litellm-llms",
"litellm-llms-types",
"litellm-secret-value",
"litellm-secrets",
"litellm-tracing",
"mime_guess",
@ -4188,6 +4196,7 @@ dependencies = [
"litellm-inference-testing",
"litellm-llms",
"litellm-llms-types",
"litellm-secret-value",
"litellm-secrets",
"litellm-tracing",
"reqwest",
@ -4207,6 +4216,7 @@ dependencies = [
"litellm-auth",
"litellm-http",
"litellm-inference",
"litellm-secret-value",
"litellm-secrets",
"tokio",
]
@ -4254,6 +4264,7 @@ dependencies = [
"litellm-llms-types",
"litellm-python-compat",
"litellm-router-types",
"litellm-secret-value",
"litellm-secrets",
"reqwest",
"rstest",
@ -4347,6 +4358,7 @@ dependencies = [
"litellm-llms-types",
"litellm-model-catalog",
"litellm-router-types",
"litellm-secret-value",
"litellm-secrets",
"litellm-secrets-aws",
"litellm-secrets-types",
@ -4396,6 +4408,7 @@ dependencies = [
"futures-util",
"litellm-circuit-breaker",
"litellm-redis-testing",
"litellm-secret-value",
"redis",
"rstest",
"thiserror 2.0.19",
@ -4432,12 +4445,23 @@ name = "litellm-router-types"
version = "0.1.0"
dependencies = [
"litellm-auth-types",
"litellm-secret-value",
"rstest",
"serde",
"serde_json",
"serde_with",
]
[[package]]
name = "litellm-secret-value"
version = "0.1.0"
dependencies = [
"rstest",
"serde",
"subtle",
"veil",
]
[[package]]
name = "litellm-secrets"
version = "0.1.0"
@ -4451,6 +4475,7 @@ dependencies = [
"litellm-error",
"litellm-http",
"litellm-python-compat",
"litellm-secret-value",
"litellm-secrets-aws",
"litellm-secrets-azure",
"litellm-secrets-cyberark",
@ -4479,6 +4504,7 @@ dependencies = [
"base64 0.22.1",
"litellm-auth-aws",
"litellm-core-utils",
"litellm-secret-value",
"litellm-secrets-types",
"litellm-tracing",
"rstest",
@ -4498,6 +4524,7 @@ dependencies = [
"litellm-auth-types",
"litellm-core-utils",
"litellm-http",
"litellm-secret-value",
"litellm-secrets-types",
"percent-encoding",
"reqwest",
@ -4519,6 +4546,7 @@ dependencies = [
"base64 0.22.1",
"litellm-core-utils",
"litellm-http",
"litellm-secret-value",
"litellm-secrets-types",
"litellm-tracing",
"moka",
@ -4549,6 +4577,7 @@ dependencies = [
"litellm-auth-types",
"litellm-core-utils",
"litellm-http",
"litellm-secret-value",
"litellm-secrets-types",
"moka",
"percent-encoding",
@ -4570,6 +4599,7 @@ dependencies = [
"litellm-core-utils",
"litellm-error",
"litellm-http",
"litellm-secret-value",
"litellm-secrets-types",
"reqwest",
"rstest",
@ -4589,7 +4619,7 @@ dependencies = [
name = "litellm-secrets-types"
version = "0.1.0"
dependencies = [
"litellm-auth-types",
"litellm-secret-value",
"moka",
"rstest",
"serde",

View file

@ -37,6 +37,7 @@ litellm-callbacks-legacy-python = { path = "crates/callbacks-legacy-python" }
litellm-framer = { path = "crates/framer" }
litellm-auth = { path = "crates/auth" }
litellm-auth-types = { path = "crates/auth-types" }
litellm-secret-value = { path = "crates/secret-value" }
litellm-auth-aws = { path = "crates/auth-aws" }
litellm-auth-azure = { path = "crates/auth-azure" }
litellm-auth-gcp = { path = "crates/auth-gcp" }

View file

@ -6,6 +6,7 @@ license.workspace = true
repository.workspace = true
[dependencies]
litellm-secret-value.workspace = true
litellm-auth-types.workspace = true
moka.workspace = true

View file

@ -13,7 +13,8 @@ use azure_identity::{
use sha2::{Digest, Sha256};
use litellm_auth_types::{Error, ErrorDetail};
use litellm_auth_types::{InputSource, ResolvedCredential, SecretValue, Sourced};
use litellm_auth_types::{InputSource, ResolvedCredential, Sourced};
use litellm_secret_value::SecretValue;
use super::credential_provider_cache::{
AzureCredentialProviderCache, AzureCredentialProviderCacheKey,
@ -498,7 +499,8 @@ mod tests {
use azure_core::{Bytes, Result};
use super::{NativeAzureRequest, NativeAzureTokenAcquirer, ValidatedAzureRequest};
use litellm_auth_types::{InputSource, SecretValue, Sourced};
use litellm_auth_types::{InputSource, Sourced};
use litellm_secret_value::SecretValue;
fn deployment<T>(value: T) -> Sourced<T> {
Sourced::new(value, InputSource::Deployment)

View file

@ -1,7 +1,9 @@
use litellm_auth_types::{
CredentialFileRef, CredentialLookup, CredentialRef, InputSource, ResolvedCredential,
SecretValue, Sourced, TokenProviderHandle,
CredentialFileRef, CredentialLookup, CredentialRef, InputSource, ResolvedCredential, Sourced,
TokenProviderHandle,
};
use litellm_secret_value::SecretValue;
use litellm_auth_types::{Error, ErrorDetail};
use std::future::Future;
use std::pin::Pin;
@ -500,8 +502,9 @@ mod tests {
use litellm_auth_types::ResolvedCredential;
use litellm_auth_types::{
CredentialFileRef, CredentialLookup, CredentialLookupFuture, CredentialRef,
CredentialResolver, CredentialResolverHandle, InputSource, SecretValue, Sourced,
CredentialResolver, CredentialResolverHandle, InputSource, Sourced,
};
use litellm_secret_value::SecretValue;
#[derive(Debug)]
struct FileResolver;

View file

@ -1,8 +1,10 @@
use std::collections::BTreeMap;
use litellm_auth_types::{
CredentialResolverHandle, Error, InputSource, SecretValue, Sourced, TokenProviderHandle,
CredentialResolverHandle, Error, InputSource, Sourced, TokenProviderHandle,
};
use litellm_secret_value::SecretValue;
use serde_json::{Map, Value};
use strum::EnumString;

View file

@ -9,6 +9,7 @@ repository.workspace = true
google-sdk = ["dep:google-cloud-auth", "dep:http"]
[dependencies]
litellm-secret-value.workspace = true
litellm-auth-types.workspace = true
base64.workspace = true

View file

@ -258,7 +258,7 @@ fn auth_acquisition_error(error: gcp_auth::Error) -> Error {
#[cfg(test)]
mod tests {
use litellm_auth_types::SecretValue;
use litellm_secret_value::SecretValue;
use super::*;

View file

@ -4,7 +4,9 @@ use base64::{
Engine, alphabet,
engine::{DecodePaddingMode, GeneralPurpose, GeneralPurposeConfig},
};
use litellm_auth_types::{Error, ErrorDetail, InputSource, SecretValue, Sourced};
use litellm_auth_types::{Error, ErrorDetail, InputSource, Sourced};
use litellm_secret_value::SecretValue;
use serde_json::{Map, Value};
use litellm_auth_types::VertexParams;

View file

@ -7,9 +7,9 @@ repository.workspace = true
[dependencies]
litellm-error.workspace = true
litellm-secret-value.workspace = true
serde.workspace = true
serde_json.workspace = true
subtle.workspace = true
thiserror.workspace = true
veil.workspace = true

View file

@ -5,7 +5,8 @@ use std::sync::Arc;
use veil::Redact;
use crate::{Error, ResolvedCredential, SecretValue, TokenProviderHandle};
use crate::{Error, ResolvedCredential, TokenProviderHandle};
use litellm_secret_value::SecretValue;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CredentialFileRef {
@ -104,8 +105,8 @@ mod tests {
CredentialLookup, CredentialLookupFuture, CredentialPlan, CredentialPlanResolution,
CredentialRef, CredentialResolver, CredentialResolverHandle,
};
use crate::SecretValue;
use crate::{Error, ErrorDetail};
use litellm_secret_value::SecretValue;
#[derive(Debug)]
struct HostResolver;

View file

@ -5,7 +5,6 @@ mod error;
pub mod http;
mod params;
mod policy;
mod secret;
mod token;
use serde::{Deserialize, Serialize};
@ -55,5 +54,4 @@ pub use error::{Error, ErrorDetail};
pub use http::CredentialPlacement;
pub use params::{AwsParams, ParamKind, ParamSpec, VertexParams};
pub use policy::{CredentialPlanKind, CredentialRule, ExistingHeaderBehavior, ProviderAuthPolicy};
pub use secret::SecretValue;
pub use token::{ResolvedCredential, TokenFuture, TokenProvider, TokenProviderHandle};

View file

@ -71,7 +71,8 @@ impl ProviderAuthPolicy {
#[cfg(test)]
mod tests {
use super::{CredentialPlanKind, CredentialRule, ExistingHeaderBehavior, ProviderAuthPolicy};
use crate::{CredentialPlacement, ResolvedCredential, SecretValue};
use crate::{CredentialPlacement, ResolvedCredential};
use litellm_secret_value::SecretValue;
const RULES: &[CredentialRule] = &[CredentialRule {
kind: CredentialPlanKind::Static,

View file

@ -5,7 +5,8 @@ use std::time::SystemTime;
use veil::Redact;
use crate::{Error, SecretValue};
use crate::Error;
use litellm_secret_value::SecretValue;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ResolvedCredential {

View file

@ -8,9 +8,9 @@ use std::{
time::{Duration, SystemTime},
};
use litellm_auth_types::{
Error, ErrorDetail, ResolvedCredential, SecretValue, TokenProviderHandle,
};
use litellm_auth_types::{Error, ErrorDetail, ResolvedCredential, TokenProviderHandle};
use litellm_secret_value::SecretValue;
use rstest::rstest;
fn credential(index: usize, access_token: bool) -> ResolvedCredential {

View file

@ -16,3 +16,6 @@ litellm-auth-types.workspace = true
litellm-auth-aws = { workspace = true, optional = true }
litellm-auth-azure = { workspace = true, optional = true }
litellm-auth-gcp = { workspace = true, optional = true }
[dev-dependencies]
litellm-secret-value.workspace = true

View file

@ -1,7 +1,8 @@
use litellm_auth::{
CredentialPlacement, CredentialPlanKind, CredentialRule, ExistingHeaderBehavior,
ProviderAuthPolicy, ResolvedCredential, SecretValue,
ProviderAuthPolicy, ResolvedCredential,
};
use litellm_secret_value::SecretValue;
const RULES: &[CredentialRule] = &[CredentialRule {
kind: CredentialPlanKind::Static,

View file

@ -6,6 +6,7 @@ license.workspace = true
repository.workspace = true
[dependencies]
litellm-secret-value.workspace = true
litellm-http.workspace = true
futures-util.workspace = true
litellm-auth-gcp.workspace = true

View file

@ -1,7 +1,9 @@
use std::{future::Future, pin::Pin};
use litellm_auth_gcp::{VertexAuth, VertexConfig};
use litellm_auth_types::{InputSource, SecretValue, Sourced};
use litellm_auth_types::{InputSource, Sourced};
use litellm_secret_value::SecretValue;
use litellm_cache::Error;
pub trait TokenSource: Send + Sync + 'static {

View file

@ -16,7 +16,7 @@ serde_json.workspace = true
thiserror.workspace = true
[dev-dependencies]
litellm-auth.workspace = true
litellm-secret-value.workspace = true
proptest.workspace = true
rstest.workspace = true
serde_json.workspace = true

View file

@ -925,11 +925,11 @@ assert all(value is failure for name, value in logger.calls if name.endswith('_h
mod payload_tests {
use std::ffi::CStr;
use litellm_auth::SecretValue;
use litellm_host::hooks::CallHooks;
use litellm_host::interceptors::{RawResponse, RequestContext, WireRequest};
use litellm_host::lifecycle::ExecutionEvent;
use litellm_host_python::{HookStep, PythonCallEvent, PythonOwned, to_py};
use litellm_secret_value::SecretValue;
use proptest::prelude::*;
use pyo3::gc::{PyTraverseError, PyVisit};
use pyo3::prelude::*;

View file

@ -6,7 +6,7 @@ license.workspace = true
repository.workspace = true
[dependencies]
litellm-auth-types.workspace = true
litellm-secret-value.workspace = true
litellm-router-types.workspace = true
serde.workspace = true
serde_json.workspace = true

View file

@ -1,6 +1,6 @@
use std::{collections::BTreeMap, fmt};
use litellm_auth_types::SecretValue;
use litellm_secret_value::SecretValue;
use serde::Deserialize;
use crate::Object;

View file

@ -1,7 +1,7 @@
use std::{collections::BTreeMap, fmt, time::Duration};
use litellm_auth_types::SecretValue;
use litellm_router_types::Spelled;
use litellm_secret_value::SecretValue;
use serde::{Deserialize, Deserializer, de::Error as _};
use crate::{AdditionalFields, Error, Object, Value};

View file

@ -1,6 +1,6 @@
use std::fmt;
use litellm_auth_types::SecretValue;
use litellm_secret_value::SecretValue;
use serde::Deserialize;
use crate::{

View file

@ -6,9 +6,9 @@ license.workspace = true
repository.workspace = true
[dependencies]
litellm-secret-value.workspace = true
axum = { workspace = true, features = ["matched-path"] }
axum-login.workspace = true
litellm-auth-types.workspace = true
litellm-config.workspace = true
litellm-error.workspace = true
litellm-secrets.workspace = true

View file

@ -1,8 +1,8 @@
use std::{future::Future, pin::Pin, sync::Arc, time::SystemTime};
use axum::http::{Request, request::Parts};
use litellm_auth_types::SecretValue;
use litellm_config::Config;
use litellm_secret_value::SecretValue;
use litellm_secrets::source::SecretSource;
use sha2::{Digest, Sha256};
use subtle::ConstantTimeEq;

View file

@ -4,7 +4,7 @@ use axum::{
middleware::Next,
response::Response,
};
use litellm_auth_types::SecretValue;
use litellm_secret_value::SecretValue;
use crate::{AccessRequest, Auth, AuthenticatedRequest, Credential, Error};

View file

@ -1,6 +1,6 @@
use std::{future::Future, pin::Pin, time::SystemTime};
use litellm_auth_types::SecretValue;
use litellm_secret_value::SecretValue;
use crate::{Error, hash_token};

View file

@ -3,7 +3,7 @@ use std::{convert::Infallible, sync::Arc};
use axum::extract::FromRequestParts;
use axum::http::{header::AUTHORIZATION, request::Parts};
use axum_login::{AuthUser, AuthnBackend, UserId};
use litellm_auth_types::SecretValue;
use litellm_secret_value::SecretValue;
use serde::Deserialize;
use sha2::{Digest, Sha256};
use subtle::ConstantTimeEq;

View file

@ -8,9 +8,9 @@ use axum::{
routing::get,
};
use futures_util::future::BoxFuture;
use litellm_auth_types::SecretValue;
use litellm_config::Config;
use litellm_gateway_auth::{Auth, RequireMasterKey, hash_token};
use litellm_secret_value::SecretValue;
use litellm_secrets::source::SecretSource;
use rstest::{fixture, rstest};
use tower::ServiceExt;

View file

@ -1,10 +1,10 @@
use std::time::{Duration, SystemTime};
use litellm_auth_types::SecretValue;
use litellm_gateway_auth::{
Error,
keys::{KeyHash, KeyLookup, KeyStatus, LookupFuture, verify_key},
};
use litellm_secret_value::SecretValue;
use rstest::{fixture, rstest};
struct Lookup {

View file

@ -1,6 +1,6 @@
use axum_login::{AuthUser, AuthnBackend};
use litellm_auth_types::SecretValue;
use litellm_gateway_auth::{UiBackend, UiCredentials};
use litellm_secret_value::SecretValue;
use rstest::{fixture, rstest};
#[fixture]

View file

@ -6,6 +6,7 @@ license.workspace = true
repository.workspace = true
[dependencies]
litellm-secret-value.workspace = true
litellm-cache-response.workspace = true
axum = { workspace = true, features = ["json", "multipart", "original-uri"] }
base64.workspace = true

View file

@ -2,10 +2,10 @@ use litellm_gateway_auth::AuthenticatedRequest;
use std::sync::Arc;
use axum::{Json, extract::State, http::HeaderMap, response::IntoResponse};
use litellm_auth::SecretValue;
use litellm_inference_ocr::types::{LiteLLMOcrRequest, OcrConnectionInputs, OcrDocumentInput};
use litellm_llms::base_llm::ocr::transformation::decode_request_value;
use litellm_llms_types::formats::ocr::OcrDocument;
use litellm_secret_value::SecretValue;
use serde_json::Value;
use crate::{

View file

@ -13,7 +13,9 @@ use futures_util::future::BoxFuture;
use litellm_gateway_inference::{Deployment, Gateway, router};
use litellm_http::{HttpClientPool, HttpSettings, Resolution, media::PublicDnsResolver};
use litellm_inference::resources::CoreResources;
use litellm_secrets::{SecretValue, source::SecretSource};
use litellm_secret_value::SecretValue;
use litellm_secrets::source::SecretSource;
use serde_json::Value;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tower::ServiceExt;

View file

@ -6,8 +6,8 @@ license.workspace = true
repository.workspace = true
[dependencies]
litellm-secret-value.workspace = true
base64.workspace = true
litellm-auth-types.workspace = true
litellm-error.workspace = true
litellm-gateway-auth.workspace = true
rand.workspace = true

View file

@ -1,8 +1,8 @@
use std::{future::Future, pin::Pin, sync::Arc, time::SystemTime};
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
use litellm_auth_types::SecretValue;
use litellm_gateway_auth::keys::{KeyHash, KeyStatus};
use litellm_secret_value::SecretValue;
use rand::{RngCore, rngs::OsRng};
use crate::Error;

View file

@ -7,7 +7,6 @@ use std::{
};
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
use litellm_auth_types::SecretValue;
use litellm_gateway_auth::{
hash_token,
keys::{KeyHash, KeyLookup, KeyStatus, verify_key},
@ -16,6 +15,7 @@ use litellm_gateway_management::{
Error,
keys::{Keys, Revocation},
};
use litellm_secret_value::SecretValue;
use rstest::{fixture, rstest};
use support::{MemoryStore, Unavailable};

View file

@ -7,11 +7,11 @@ license.workspace = true
repository.workspace = true
[dependencies]
litellm-secret-value.workspace = true
axum = { workspace = true, features = ["json", "query"] }
futures-util.workspace = true
litellm-config.workspace = true
litellm-secrets.workspace = true
litellm-auth-types.workspace = true
litellm-error.workspace = true
base64.workspace = true
http.workspace = true

View file

@ -8,8 +8,8 @@ use std::{
use base64::{Engine, engine::general_purpose::STANDARD};
use futures_util::future::try_join_all;
use http::{HeaderName, HeaderValue};
use litellm_auth_types::SecretValue;
use litellm_config::{McpAuth, McpServer, McpTransport};
use litellm_secret_value::SecretValue;
use litellm_secrets::source::SecretSource;
use rmcp::{
RoleClient, ServiceExt,

View file

@ -23,10 +23,10 @@ tower-http = { version = "0.6.11", features = ["fs", "set-header"] }
tower-sessions.workspace = true
[dev-dependencies]
litellm-secret-value.workspace = true
futures-util.workspace = true
serde_json.workspace = true
tower = { version = "0.5", features = ["util"] }
litellm-auth-types.workspace = true
rstest.workspace = true
tempfile.workspace = true
tokio.workspace = true

View file

@ -16,8 +16,8 @@ use tower_sessions::{
session::{Id, Record},
};
use litellm_auth_types::SecretValue;
use litellm_gateway_auth::UiBackend;
use litellm_secret_value::SecretValue;
struct App {
router: Router,

View file

@ -6,6 +6,7 @@ license.workspace = true
repository.workspace = true
[dependencies]
litellm-secret-value.workspace = true
axum.workspace = true
envy = "0.4.2"
http-body-util = "0.1"
@ -17,7 +18,6 @@ thiserror.workspace = true
futures-util.workspace = true
litellm-gateway-auth.workspace = true
litellm-gateway-ui.workspace = true
litellm-auth-types.workspace = true
litellm-config.workspace = true
litellm-error.workspace = true
litellm-circuit-breaker.workspace = true

View file

@ -152,12 +152,12 @@ async fn reachable(client: RedisClient) -> Option<RedisClient> {
fn client(params: Params, breaker: Option<BreakerConfig>) -> Result<RedisClient, Error> {
tracing::info!(endpoint = ?params.endpoint, namespace = ?params.namespace, "redis client configured");
let endpoint = match params.endpoint {
RedisEndpoint::Url(url) => Endpoint::Url(url.expose().to_owned()),
RedisEndpoint::Url(url) => Endpoint::Url(url),
RedisEndpoint::Host(host) => Endpoint::Host(HostEndpoint {
host: host.host,
port: host.port,
username: host.username.map(|value| value.expose().to_owned()),
password: host.password.map(|value| value.expose().to_owned()),
username: host.username,
password: host.password,
tls: host.tls,
db: host.db,
}),
@ -170,8 +170,8 @@ fn client(params: Params, breaker: Option<BreakerConfig>) -> Result<RedisClient,
port: node.port,
})
.collect(),
username: cluster.username.map(|value| value.expose().to_owned()),
password: cluster.password.map(|value| value.expose().to_owned()),
username: cluster.username,
password: cluster.password,
tls: cluster.tls,
}),
};

View file

@ -1,8 +1,8 @@
use std::{collections::BTreeMap, sync::Arc};
use futures_util::future::BoxFuture;
use litellm_auth_types::SecretValue;
use litellm_config::{Object, Value};
use litellm_secret_value::SecretValue;
use litellm_secrets::source::SecretSource;
use crate::Error;

View file

@ -1,6 +1,6 @@
use std::ffi::OsString;
use litellm_auth_types::SecretValue;
use litellm_secret_value::SecretValue;
use litellm_tracing::Level;
use serde::{Deserialize, de::DeserializeOwned};

View file

@ -424,7 +424,7 @@ impl litellm_secrets::source::SecretSource for NoSecrets {
_: &'a str,
) -> futures_util::future::BoxFuture<
'a,
Result<Option<litellm_auth_types::SecretValue>, litellm_secrets::Error>,
Result<Option<litellm_secret_value::SecretValue>, litellm_secrets::Error>,
> {
Box::pin(async { Ok(None) })
}

View file

@ -159,7 +159,7 @@ async fn mounts_ui_without_exposing_credentials_or_authorizing_inference(inferen
std::fs::write(assets.path().join("index.html"), "dashboard").unwrap();
let backend = litellm_gateway_auth::UiBackend::new(
"admin".into(),
litellm_auth_types::SecretValue::new("ui-password"),
litellm_secret_value::SecretValue::new("ui-password"),
)
.unwrap();
let ui = litellm_gateway_ui::router(

View file

@ -6,8 +6,8 @@ license.workspace = true
repository.workspace = true
[dependencies]
litellm-secret-value.workspace = true
futures-util.workspace = true
litellm-auth.workspace = true
litellm-coroutine.workspace = true
serde_json.workspace = true
thiserror.workspace = true

View file

@ -22,7 +22,7 @@ pub struct RequestContext {
/// Optional-param names that carry credentials and must be redacted when logged.
pub secret_fields: Vec<String>,
/// The credential the route resolved for the provider call.
pub api_key: Option<litellm_auth::SecretValue>,
pub api_key: Option<litellm_secret_value::SecretValue>,
}
#[derive(Clone, Debug, PartialEq, Eq)]

View file

@ -6,6 +6,7 @@ license.workspace = true
repository.workspace = true
[dependencies]
litellm-secret-value.workspace = true
litellm-auth = { workspace = true, features = ["aws", "azure", "gcp"] }
litellm-cache-response.workspace = true
litellm-core-utils.workspace = true

View file

@ -58,7 +58,7 @@ pub(super) async fn execute(
custom_llm_provider: provider_name.to_owned(),
optional_params: Value::Object(call.optional_params),
secret_fields: Vec::new(),
api_key: call.api_key.map(litellm_auth::SecretValue::new),
api_key: call.api_key.map(litellm_secret_value::SecretValue::new),
};
let authenticated = resolve_auth(auth, environment, &|key| secrets.get(key)).await?;
let identity = litellm_host::interceptors::ProviderIdentity {

View file

@ -140,7 +140,7 @@ impl litellm_secrets::source::SecretSource for ChangingSecrets {
name: &'a str,
) -> futures_util::future::BoxFuture<
'a,
Result<Option<litellm_secrets::SecretValue>, litellm_secrets::Error>,
Result<Option<litellm_secret_value::SecretValue>, litellm_secrets::Error>,
> {
Box::pin(async move {
let revision = self.revision.load(Ordering::SeqCst);
@ -154,7 +154,7 @@ impl litellm_secrets::source::SecretSource for ChangingSecrets {
} else {
None
};
Ok(value.map(litellm_secrets::SecretValue::new))
Ok(value.map(litellm_secret_value::SecretValue::new))
})
}
}

View file

@ -6,6 +6,7 @@ license.workspace = true
repository.workspace = true
[dependencies]
litellm-secret-value.workspace = true
bytes.workspace = true
futures-util.workspace = true
litellm-auth = { workspace = true, features = ["aws", "azure", "gcp"] }

View file

@ -2,7 +2,9 @@ use std::time::Duration;
use bytes::Bytes;
use futures_util::{FutureExt, StreamExt, TryStreamExt, stream::BoxStream};
use litellm_auth::{AuthServices, SecretValue};
use litellm_auth::AuthServices;
use litellm_secret_value::SecretValue;
use litellm_core_utils::{
dot_notation_indexing::delete_nested_value,
get_provider_specific_headers::get_provider_specific_headers, settings::Lookup,

View file

@ -107,7 +107,7 @@ impl litellm_secrets::source::SecretSource for ChangingSecrets {
name: &'a str,
) -> futures_util::future::BoxFuture<
'a,
Result<Option<litellm_secrets::SecretValue>, litellm_secrets::Error>,
Result<Option<litellm_secret_value::SecretValue>, litellm_secrets::Error>,
> {
Box::pin(async move {
let revision = self.revision.load(Ordering::SeqCst);
@ -121,7 +121,7 @@ impl litellm_secrets::source::SecretSource for ChangingSecrets {
} else {
None
};
Ok(value.map(litellm_secrets::SecretValue::new))
Ok(value.map(litellm_secret_value::SecretValue::new))
})
}
}

View file

@ -6,6 +6,7 @@ license.workspace = true
repository.workspace = true
[dependencies]
litellm-secret-value.workspace = true
base64.workspace = true
bytes.workspace = true
futures-util.workspace = true

View file

@ -128,7 +128,7 @@ mod tests {
decode_request(OcrWireRequest {
model: model.into(),
document,
api_key: Some(litellm_auth::SecretValue::new("test-key")),
api_key: Some(litellm_secret_value::SecretValue::new("test-key")),
api_base: Some(base.into()),
custom_llm_provider: None,
extra_headers: None,

View file

@ -1,4 +1,6 @@
use litellm_auth::{InputSource, SecretValue, Sourced};
use litellm_auth::{InputSource, Sourced};
use litellm_secret_value::SecretValue;
use litellm_core_utils::get_llm_provider_logic::{
CustomLlmProvider, LlmProviders, get_custom_llm_provider,
};
@ -407,7 +409,7 @@ mod tests {
fn connection_resolution_preserves_dynamic_precedence_and_input_sources() {
let connection = OcrConfigKind::Mistral.resolve_connection_params(OcrCredentialInputs {
api_key: Some(Sourced::new(
litellm_auth::SecretValue::new("explicit-key"),
litellm_secret_value::SecretValue::new("explicit-key"),
InputSource::Deployment,
)),
api_base: Some(Sourced::new(
@ -415,7 +417,7 @@ mod tests {
InputSource::Deployment,
)),
dynamic_api_key: Some(Sourced::new(
litellm_auth::SecretValue::new("dynamic-key"),
litellm_secret_value::SecretValue::new("dynamic-key"),
InputSource::Environment,
)),
dynamic_api_base: Some(Sourced::new(
@ -455,7 +457,7 @@ mod tests {
) {
let dynamic_key = dynamic_value.map(|value| {
Sourced::new(
litellm_auth::SecretValue::new(value),
litellm_secret_value::SecretValue::new(value),
InputSource::Environment,
)
});
@ -463,7 +465,7 @@ mod tests {
dynamic_value.map(|value| Sourced::new(value.into(), InputSource::Environment));
let connection = OcrConfigKind::Mistral.resolve_connection_params(OcrCredentialInputs {
api_key: Some(Sourced::new(
litellm_auth::SecretValue::new("explicit-key"),
litellm_secret_value::SecretValue::new("explicit-key"),
InputSource::Deployment,
)),
api_base: Some(Sourced::new(
@ -502,14 +504,14 @@ mod tests {
OcrCredentialInputs {
api_key: explicit_key.map(|value| {
Sourced::new(
litellm_auth::SecretValue::new(value),
litellm_secret_value::SecretValue::new(value),
InputSource::Deployment,
)
}),
api_base: explicit_base
.map(|value| Sourced::new(value.into(), InputSource::Deployment)),
dynamic_api_key: Some(Sourced::new(
litellm_auth::SecretValue::new("dynamic-key"),
litellm_secret_value::SecretValue::new("dynamic-key"),
InputSource::Environment,
)),
dynamic_api_base: Some(Sourced::new(

View file

@ -1,7 +1,9 @@
use std::{collections::BTreeMap, path::PathBuf, time::Duration};
use bytes::Bytes;
use litellm_auth::{InputSource, SecretValue, TokenProviderHandle};
use litellm_auth::{InputSource, TokenProviderHandle};
use litellm_secret_value::SecretValue;
use litellm_core_utils::call_arguments::CallArguments;
use litellm_llms::base_llm::ocr::{
error::Error,

View file

@ -1,6 +1,8 @@
use std::{collections::BTreeMap, time::Duration};
use litellm_auth::{InputSource, SecretValue};
use litellm_auth::InputSource;
use litellm_secret_value::SecretValue;
use litellm_llms::base_llm::ocr::{error::Error, transformation::decode_request_value};
use litellm_llms_types::formats::ocr::OcrDocument;
use serde::Deserialize;

View file

@ -3,9 +3,9 @@ use std::sync::{
atomic::{AtomicUsize, Ordering},
};
use litellm_auth::{
ResolvedCredential, SecretValue, TokenFuture, TokenProvider, TokenProviderHandle,
};
use litellm_auth::{ResolvedCredential, TokenFuture, TokenProvider, TokenProviderHandle};
use litellm_secret_value::SecretValue;
use rstest::rstest;
use super::*;

View file

@ -239,7 +239,7 @@ impl litellm_host_native::services::HostCallHandler<Ocr> for CallerTokenHost {
OcrOp::AcquireAzureAdToken(reply) => {
self.trace.lock().unwrap().push("token".into());
reply.send(litellm_auth::ResolvedCredential::Static(
litellm_auth::SecretValue::new("caller-token"),
litellm_secret_value::SecretValue::new("caller-token"),
));
Ok(())
}

View file

@ -80,7 +80,7 @@ fn wire(model: &str, base: &str, document: Value, options: Value) -> OcrWireRequ
OcrWireRequest {
model: model.into(),
document,
api_key: Some(litellm_auth::SecretValue::new("test-key")),
api_key: Some(litellm_secret_value::SecretValue::new("test-key")),
api_base: Some(base.into()),
custom_llm_provider: None,
extra_headers: None,

View file

@ -25,6 +25,7 @@ tokio-tungstenite.workspace = true
tracing.workspace = true
[dev-dependencies]
litellm-secret-value.workspace = true
litellm-http = { workspace = true, features = ["test-support"] }
litellm-cache.workspace = true
litellm-cache-memory.workspace = true

View file

@ -98,7 +98,7 @@ impl litellm_secrets::source::SecretSource for ChangingSecrets {
name: &'a str,
) -> futures_util::future::BoxFuture<
'a,
Result<Option<litellm_secrets::SecretValue>, litellm_secrets::Error>,
Result<Option<litellm_secret_value::SecretValue>, litellm_secrets::Error>,
> {
Box::pin(async move {
let revision = self.revision.load(Ordering::SeqCst);
@ -112,7 +112,7 @@ impl litellm_secrets::source::SecretSource for ChangingSecrets {
} else {
None
};
Ok(value.map(litellm_secrets::SecretValue::new))
Ok(value.map(litellm_secret_value::SecretValue::new))
})
}
}

View file

@ -7,6 +7,7 @@ repository.workspace = true
publish = false
[dependencies]
litellm-secret-value.workspace = true
litellm-auth.workspace = true
futures-util.workspace = true
litellm-http.workspace = true

View file

@ -5,7 +5,8 @@ use litellm_http::{
ClientVariant, HttpClientConfig, HttpClientPool, HttpSettings, Resolution,
media::PublicDnsResolver,
};
use litellm_secrets::{SecretValue, source::SecretSource};
use litellm_secret_value::SecretValue;
use litellm_secrets::source::SecretSource;
use litellm_inference::resources::CoreResources;

View file

@ -199,7 +199,7 @@ impl AnthropicBeta {
&[BetaProvider::BedrockConverse, BetaProvider::Databricks]
}
Self::ContextManagement20250627 => &[BetaProvider::BedrockConverse],
Self::AdvisorTool20260301 | Self::Compact20260904 => &[
Self::AdvisorTool20260301 => &[
BetaProvider::AzureAi,
BetaProvider::BedrockConverse,
BetaProvider::Bedrock,
@ -207,6 +207,12 @@ impl AnthropicBeta {
BetaProvider::VertexAi,
BetaProvider::Databricks,
],
Self::Compact20260904 => &[
BetaProvider::BedrockConverse,
BetaProvider::Bedrock,
BetaProvider::BedrockMantle,
BetaProvider::Databricks,
],
Self::Bash20241022
| Self::Bash20250124
| Self::McpServers20251204
@ -256,14 +262,18 @@ impl AnthropicBeta {
Self::InterleavedThinking20250514 | Self::WebSearch20250305 => {
&[BetaProvider::BedrockConverse, BetaProvider::Bedrock]
}
Self::MidConversationOutputConfig20260701
| Self::MidConversationToolChanges20260701 => &[
Self::MidConversationOutputConfig20260701 => &[
BetaProvider::AzureAi,
BetaProvider::BedrockConverse,
BetaProvider::BedrockMantle,
BetaProvider::VertexAi,
BetaProvider::Databricks,
],
Self::MidConversationToolChanges20260701 => &[
BetaProvider::BedrockConverse,
BetaProvider::BedrockMantle,
BetaProvider::Databricks,
],
Self::Output128k20250219 | Self::TokenEfficientTools20250219 => &[
BetaProvider::AzureAi,
BetaProvider::BedrockConverse,
@ -483,7 +493,7 @@ mod tests {
let config = beta_headers_config();
let config_columns: Vec<String> = config
.keys()
.filter(|column| column.as_str() != "description")
.filter(|column| column.as_str() != "description" && !column.ends_with("_header_only"))
.cloned()
.collect();
let beta_provider_columns: Vec<String> = BetaProvider::VARIANTS
@ -502,12 +512,16 @@ mod tests {
let config = beta_headers_config();
for provider in BetaProvider::VARIANTS {
for beta in &AnthropicBeta::KNOWN {
let expected = config
.get(provider.as_ref())
.and_then(serde_json::Value::as_object)
.and_then(|column| column.get(beta.as_str()))
.and_then(serde_json::Value::as_str)
.map(str::to_string);
let cell = |column: &str| {
config
.get(column)
.and_then(serde_json::Value::as_object)
.and_then(|values| values.get(beta.as_str()))
.and_then(serde_json::Value::as_str)
.map(str::to_string)
};
let expected =
cell(&format!("{provider}_header_only")).or_else(|| cell(provider.as_ref()));
let actual = beta.on(*provider).map(|name| name.to_string());
assert_eq!(actual, expected, "provider {provider}, beta {beta}");
if matches!(beta, AnthropicBeta::AdvancedToolUse20251120)

View file

@ -9,6 +9,7 @@ repository.workspace = true
test-support = ["litellm-http/test-support"]
[dependencies]
litellm-secret-value.workspace = true
litellm-llms-types.workspace = true
litellm-core-utils.workspace = true
litellm-error.workspace = true

View file

@ -1,4 +1,3 @@
use litellm_auth::SecretValue;
use litellm_core_utils::decline::Decline;
use litellm_core_utils::{
core_helpers::{finish_reason_for, unix_now, usage_from_parts},
@ -10,6 +9,7 @@ use litellm_llms_types::{
},
providers::anthropic::DEFAULT_HEADERS,
};
use litellm_secret_value::SecretValue;
use serde::Deserialize;
use serde_json::{Map, Value, json};

View file

@ -1,5 +1,7 @@
use crate::base_llm::messages::context::MessagesModelCapabilities;
use litellm_auth::{CredentialPlacement, SecretValue};
use litellm_auth::CredentialPlacement;
use litellm_secret_value::SecretValue;
use litellm_core_utils::settings::resolve_non_empty;
use litellm_http::request::{
has_header, header_value, header_values, with_header, without_headers,

View file

@ -1,7 +1,8 @@
use litellm_auth::CredentialPlacement;
use litellm_llms_types::{
formats::messages::{
ContextEdit, ContextManagement, Message, MessagesOptionalParams, MessagesRequest, Speed,
ContentBlock, ContentBlockType, ContextEdit, ContextManagement, Message,
MessagesOptionalParams, MessagesRequest, Speed, ThinkingConfig, ThinkingDisplay,
},
providers::anthropic::{AnthropicBeta, BetaProvider, BetaSet, DEFAULT_HEADERS},
recognized::Recognized,
@ -203,6 +204,10 @@ fn feature_betas(request: &MessagesRequest) -> BetaSet {
.then_some(AnthropicBeta::FastMode20260201),
messages_carry_output_config(&request.messages)
.then_some(AnthropicBeta::PerTurnControl20260701),
uses_thinking_display_updates(params)
.then_some(AnthropicBeta::ThinkingDisplayUpdates20260818),
messages_carry_tool_changes(&request.messages)
.then_some(AnthropicBeta::MidConversationToolChanges20260701),
has_advisor_tool(tools).then_some(AnthropicBeta::AdvisorTool20260301),
is_tool_search_used(tools).then_some(AnthropicBeta::AdvancedToolUse20251120),
]
@ -253,6 +258,36 @@ fn messages_carry_output_config(messages: &[Message]) -> bool {
.any(|message| message.extra.contains_key("output_config"))
}
fn uses_thinking_display_updates(params: &MessagesOptionalParams) -> bool {
let display = match params.thinking.as_ref().and_then(Recognized::known) {
Some(ThinkingConfig::Enabled(thinking)) => thinking.display.as_ref(),
Some(ThinkingConfig::Adaptive(thinking)) => thinking.display.as_ref(),
_ => None,
};
display.and_then(Recognized::known) == Some(&ThinkingDisplay::Updates)
}
fn is_tool_reference_change(block: &ContentBlock) -> bool {
matches!(
&block.block_type,
Some(ContentBlockType::Other(block_type))
if block_type == "tool_addition" || block_type == "tool_removal"
) && block
.extra
.get("tool")
.and_then(|tool| tool.get("type"))
.and_then(Value::as_str)
== Some("tool_reference")
}
fn messages_carry_tool_changes(messages: &[Message]) -> bool {
messages
.iter()
.filter(|message| message.role == "system")
.flat_map(Message::blocks)
.any(is_tool_reference_change)
}
fn unsupported_param(model: &str, param: &str, value: &str, hint: &str) -> Error {
Error::InvalidRequest(crate::ErrorDetail::UnsupportedParameter {
model: model.into(),
@ -1071,6 +1106,41 @@ mod tests {
json!({"safeguards": [{"type": "dangerous_tool_use", "classifier_context": {"v": 1, "permission_mode": "auto"}}]}),
&[]
)]
#[case::adaptive_display_updates(
json!({"thinking": {"type": "adaptive", "display": "updates"}}),
&["thinking-display-updates-2026-08-18"]
)]
#[case::enabled_display_updates(
json!({"thinking": {"type": "enabled", "budget_tokens": 2048, "display": "updates"}}),
&["thinking-display-updates-2026-08-18"]
)]
#[case::summarized_display(json!({"thinking": {"type": "adaptive", "display": "summarized"}}), &[])]
#[case::disabled_thinking(json!({"thinking": {"type": "disabled"}}), &[])]
#[case::system_tool_addition(
json!({"messages": [
{"role": "user", "content": "Use the ping tool"},
{"role": "system", "content": [{"type": "tool_addition", "tool": {"type": "tool_reference", "name": "ping"}}]},
]}),
&["mid-conversation-tool-changes-2026-07-01"]
)]
#[case::system_tool_removal(
json!({"messages": [
{"role": "system", "content": [{"type": "tool_removal", "tool": {"type": "tool_reference", "name": "ping"}}]},
]}),
&["mid-conversation-tool-changes-2026-07-01"]
)]
#[case::user_tool_addition(
json!({"messages": [
{"role": "user", "content": [{"type": "tool_addition", "tool": {"type": "tool_reference", "name": "ping"}}]},
]}),
&[]
)]
#[case::tool_addition_without_a_tool_reference(
json!({"messages": [
{"role": "system", "content": [{"type": "tool_addition", "tool": {"type": "tool_definition"}}]},
]}),
&[]
)]
fn feature_betas_follow_the_request(#[case] fields: Value, #[case] expected: &[&str]) {
assert_eq!(feature_betas(&request(fields)), betas(expected));
}
@ -1093,6 +1163,33 @@ mod tests {
);
}
#[rstest]
#[case::vertex_ai(
BetaProvider::VertexAi,
&["mid-conversation-tool-changes-2026-07-01", "thinking-display-updates-2026-08-18"]
)]
#[case::azure_ai(
BetaProvider::AzureAi,
&["mid-conversation-tool-changes-2026-07-01", "thinking-display-updates-2026-08-18"]
)]
#[case::bedrock_converse(BetaProvider::BedrockConverse, &["thinking-display-updates-2026-08-18"])]
#[case::databricks(BetaProvider::Databricks, &["thinking-display-updates-2026-08-18"])]
fn tool_change_and_display_betas_reach_the_providers_that_accept_them(
#[case] provider: BetaProvider,
#[case] expected: &[&str],
) {
let fields = json!({
"thinking": {"type": "adaptive", "display": "updates"},
"messages": [
{"role": "system", "content": [{"type": "tool_addition", "tool": {"type": "tool_reference", "name": "ping"}}]},
],
});
assert_eq!(
provider_feature_betas(&request(fields), provider),
betas(expected)
);
}
#[rstest]
fn hosts_without_a_beta_map_column_get_safeguards_without_a_derived_beta() {
let client = headers(&[("anthropic-beta", "claude-code-20250219")]);

View file

@ -1,10 +1,10 @@
use litellm_auth::SecretValue;
use litellm_http::request::{has_bearer_auth, has_header};
use litellm_llms_types::{
formats::messages::{MessagesOptionalParams, MessagesRequest},
providers::anthropic::{BetaProvider, DEFAULT_HEADERS, MESSAGES_PATH},
};
use litellm_router_types::LitellmParams;
use litellm_secret_value::SecretValue;
use crate::{
Error,
@ -545,6 +545,11 @@ mod tests {
&[("anthropic-beta", "web-search-2025-03-05")],
&[("x-api-key", "k"), ("anthropic-beta", "structured-outputs-2025-11-13,web-search-2025-03-05")]
)]
#[case::compaction_needs_the_compact_2026_09_04_beta(
json!({"compaction": {"type": "summarize"}}),
&[],
&[("x-api-key", "k"), ("anthropic-beta", "compact-2026-09-04")]
)]
#[case::no_feature_needs_a_beta(json!({}), &[], &[("x-api-key", "k")])]
#[case::safeguards_need_the_dangerous_tool_use_beta(
json!({"safeguards": [{"type": "dangerous_tool_use"}]}),

View file

@ -1250,7 +1250,7 @@ mod tests {
#[tokio::test]
async fn request_endpoint_accepts_request_owned_key() {
let connection = OcrConnection {
api_key: Some(litellm_auth::SecretValue::new("request-key")),
api_key: Some(litellm_secret_value::SecretValue::new("request-key")),
api_key_source: InputSource::Request,
api_base: Some("https://request.example".into()),
api_base_source: InputSource::Request,

View file

@ -211,7 +211,7 @@ mod tests {
#[fixture]
fn connection() -> OcrConnection {
OcrConnection {
api_key: Some(litellm_auth::SecretValue::new("request-key")),
api_key: Some(litellm_secret_value::SecretValue::new("request-key")),
api_base: Some("https://example.com".into()),
..Default::default()
}
@ -312,7 +312,7 @@ mod tests {
#[tokio::test]
async fn request_endpoint_accepts_request_owned_key() {
let connection = OcrConnection {
api_key: Some(litellm_auth::SecretValue::new("request-key")),
api_key: Some(litellm_secret_value::SecretValue::new("request-key")),
api_key_source: InputSource::Request,
api_base: Some("https://request.example".into()),
api_base_source: InputSource::Request,

View file

@ -5,7 +5,9 @@
//! that split: `validate_environment` shapes the forwarded headers and names the credential
//! as an [`AuthScheme`], and [`resolve_auth`] turns the scheme into headers and a signer.
use litellm_auth::{AuthServices, CredentialPlacement, SecretValue, TokenProviderHandle};
use litellm_auth::{AuthServices, CredentialPlacement, TokenProviderHandle};
use litellm_secret_value::SecretValue;
use litellm_auth_aws::{AwsCredentialSource, SigV4Signer};
use litellm_auth_gcp::VertexConfig;
use litellm_http::request::with_header;

View file

@ -5,7 +5,9 @@ use serde::{
use serde_json::{Map, Value};
use std::{collections::BTreeMap, future::Future, sync::Arc, time::Duration};
use litellm_auth::{InputSource, SecretValue, Sourced, TokenProviderHandle};
use litellm_auth::{InputSource, Sourced, TokenProviderHandle};
use litellm_secret_value::SecretValue;
use litellm_core_utils::{call_arguments::CallArguments, settings::ProcessEnvironment};
use litellm_http::outbound::RequestSigner;
use litellm_llms_types::formats::ocr::{LiteLLMOcrResponse, OcrDocument, OcrResponseFormat};

View file

@ -1,5 +1,7 @@
use litellm_auth::AwsParams;
use litellm_auth::{CredentialPlacement, SecretValue};
use litellm_auth::CredentialPlacement;
use litellm_secret_value::SecretValue;
use litellm_auth_aws::{
AwsCredentialSource, bedrock_model_id_and_region,
constants::{AWS_BEARER_TOKEN_BEDROCK, BEDROCK_RUNTIME_ENDPOINT_TEMPLATE, BEDROCK_SERVICE},

View file

@ -1,6 +1,8 @@
use std::sync::LazyLock;
use litellm_auth::{AwsParams, CredentialPlacement, ParamKind, ParamSpec, SecretValue};
use litellm_auth::{AwsParams, CredentialPlacement, ParamKind, ParamSpec};
use litellm_secret_value::SecretValue;
use litellm_auth_aws::{AwsCredentialSource, resolve_aws_region};
use litellm_core_utils::settings::resolve_non_empty;
use litellm_http::request::{header_value, with_header, without_headers};

View file

@ -7,7 +7,9 @@ use crate::{
};
use futures_util::StreamExt;
use litellm_auth::AwsParams;
use litellm_auth::{CredentialPlacement, SecretValue};
use litellm_auth::CredentialPlacement;
use litellm_secret_value::SecretValue;
use litellm_auth_aws::{
AwsCredentialSource, bedrock_model_id_and_region,
constants::{

View file

@ -1,6 +1,8 @@
use std::sync::LazyLock;
use litellm_auth::{AwsParams, CredentialPlacement, SecretValue};
use litellm_auth::{AwsParams, CredentialPlacement};
use litellm_secret_value::SecretValue;
use litellm_auth_aws::AwsCredentialSource;
use litellm_auth_aws::bedrock_model_id_and_region;
use litellm_core_utils::settings::resolve_non_empty;

View file

@ -735,7 +735,7 @@ mod tests {
assert!(matches!(
CohereParseConfig.resolve_headers(
&OcrConnection {
api_key: Some(litellm_auth::SecretValue::new(" ")),
api_key: Some(litellm_secret_value::SecretValue::new(" ")),
..Default::default()
},
&|_| None,

View file

@ -1,4 +1,6 @@
use litellm_auth::{CredentialPlacement, SecretValue};
use litellm_auth::CredentialPlacement;
use litellm_secret_value::SecretValue;
use litellm_core_utils::settings::resolve_non_empty;
use litellm_llms_types::{
formats::messages::{MessagesOptionalParams, MessagesRequest, MessagesTool},

View file

@ -1,4 +1,6 @@
use litellm_auth::{CredentialPlacement, SecretValue};
use litellm_auth::CredentialPlacement;
use litellm_secret_value::SecretValue;
use litellm_core_utils::settings::resolve_non_empty;
use litellm_llms_types::formats::messages::{
MessagesOptionalParams, MessagesRequest, MessagesResponse,

View file

@ -1,4 +1,6 @@
use litellm_auth::{CredentialPlacement, SecretValue};
use litellm_auth::CredentialPlacement;
use litellm_secret_value::SecretValue;
use litellm_core_utils::settings::resolve_non_empty;
use litellm_llms_types::{
formats::messages::{MessagesOptionalParams, MessagesRequest},

View file

@ -219,7 +219,7 @@ mod tests {
#[default(vec![])] extra_headers: Vec<(String, String)>,
) -> OcrConnection {
OcrConnection {
api_key: api_key.map(litellm_auth::SecretValue::new),
api_key: api_key.map(litellm_secret_value::SecretValue::new),
extra_headers,
..OcrConnection::default()
}

View file

@ -3,7 +3,8 @@ use litellm_llms_types::formats::responses::{
};
use serde_json::{Map, Value};
use litellm_auth::{CredentialPlacement, SecretValue};
use litellm_auth::CredentialPlacement;
use litellm_secret_value::SecretValue;
use crate::{
Error, ErrorDetail,

View file

@ -4,7 +4,9 @@
//! the `max_completion_tokens` -> `max_tokens` rename and the usage
//! `*_tokens` null-to-zero sanitize.
use litellm_auth::{CredentialPlacement, SecretValue};
use litellm_auth::CredentialPlacement;
use litellm_secret_value::SecretValue;
use litellm_core_utils::core_helpers::unix_now;
use litellm_llms_types::formats::chat_completions::{
ChatCompletionsChoice, ChatCompletionsChoiceMessage, ChatCompletionsResponse,

View file

@ -1,4 +1,6 @@
use litellm_auth::{CredentialPlacement, SecretValue};
use litellm_auth::CredentialPlacement;
use litellm_secret_value::SecretValue;
use litellm_core_utils::settings::resolve_non_empty;
use litellm_http::request::{has_bearer_auth, without_headers};
use litellm_llms_types::{

View file

@ -1,4 +1,6 @@
use litellm_auth::{CredentialPlacement, SecretValue};
use litellm_auth::CredentialPlacement;
use litellm_secret_value::SecretValue;
use litellm_core_utils::settings::resolve_non_empty;
use litellm_llms_types::formats::responses::ResponsesApiResponse;
use serde_json::{Map, Value};

Some files were not shown because too many files have changed in this diff Show more