fix(security): remove the publicly known master key from the repo (#44718)

* fix(security): hash the publicly known master key

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* docs: replace weak master key examples and regenerate artifacts

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test: replace weak key fixtures with generated test keys

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* ci: generate master keys for proxy startup

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: preserve lens dev key entropy

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: restore proxy key compatibility in scrub examples

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: scrub merged SSO fixture and refresh dashboard bundle

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(ci): stabilize test keys and metadata collection

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore: drop the rebuilt dashboard bundle

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: mateo <mateo@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
devin-ai-integration[bot] 2026-10-06 10:55:24 -07:00 • committed by GitHub
parent ab61410a39
commit 837c6a7481
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
286 changed files with 1895 additions and 1591 deletions

View file

@ -1706,6 +1706,11 @@ jobs:
working_directory: ~/project
steps:
- checkout
- run:
name: Generate LiteLLM master key
command: |
key="$(openssl rand -hex 16)"
printf 'export LITELLM_MASTER_KEY=sk-%s\n' "$key" >> "$BASH_ENV"
- skip_if_unrelated_changes
- setup_google_dns
- install_uv
@ -1729,9 +1734,8 @@ jobs:
command: |
docker run -d \
-p 4001:4000 \
-e LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY=true \
-e DATABASE_URL="postgresql://postgres:postgres@host.docker.internal:5432/litellm_test" \
-e LITELLM_MASTER_KEY="sk-1234" \
-e LITELLM_MASTER_KEY="$LITELLM_MASTER_KEY" \
--name schema-seed \
--add-host=host.docker.internal:host-gateway \
-v $(pwd)/litellm/proxy/example_config_yaml/simple_config.yaml:/app/config.yaml \
@ -1802,6 +1806,11 @@ jobs:
working_directory: ~/project
steps:
- checkout
- run:
name: Generate LiteLLM master key
command: |
key="$(openssl rand -hex 16)"
printf 'export LITELLM_MASTER_KEY=sk-%s\n' "$key" >> "$BASH_ENV"
- skip_if_unrelated_changes
- attach_workspace:
at: ~/project
@ -1825,9 +1834,8 @@ jobs:
command: |
docker run -d \
-p 4000:4000 \
-e LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY=true \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e LITELLM_MASTER_KEY="sk-1234" \
-e LITELLM_MASTER_KEY="$LITELLM_MASTER_KEY" \
-e USE_PRISMA_MIGRATE=True \
-e FAKE_OPENAI_API_BASE=http://host.docker.internal:8190 \
-e AZURE_API_KEY=$AZURE_API_KEY \
@ -1872,7 +1880,7 @@ jobs:
name: Seed the routing strategy through /config/update
command: |
curl --noproxy '*' -sSf -X POST http://localhost:4000/config/update \
-H 'Authorization: Bearer sk-1234' -H 'Content-Type: application/json' \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' \
-d '{"router_settings": {"routing_strategy": "usage-based-routing-v2"}}'
- run:
name: Run tests
@ -1901,6 +1909,11 @@ jobs:
working_directory: ~/project
steps:
- checkout
- run:
name: Generate LiteLLM master key
command: |
key="$(openssl rand -hex 16)"
printf 'export LITELLM_MASTER_KEY=sk-%s\n' "$key" >> "$BASH_ENV"
- skip_if_unrelated_changes
- setup_google_dns
- install_uv
@ -1922,9 +1935,8 @@ jobs:
command: |
docker run -d \
-p 4000:4000 \
-e LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY=true \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e LITELLM_MASTER_KEY="sk-1234" \
-e LITELLM_MASTER_KEY="$LITELLM_MASTER_KEY" \
-e AZURE_API_KEY=$AZURE_API_KEY \
-e AZURE_API_BASE=$AZURE_API_BASE \
-e AZURE_API_VERSION="2024-05-01-preview" \
@ -1987,6 +1999,11 @@ jobs:
working_directory: ~/project
steps:
- checkout
- run:
name: Generate LiteLLM master key
command: |
key="$(openssl rand -hex 16)"
printf 'export LITELLM_MASTER_KEY=sk-%s\n' "$key" >> "$BASH_ENV"
- skip_if_unrelated_changes
- setup_google_dns
- install_uv
@ -2012,12 +2029,11 @@ jobs:
command: |
docker run -d \
-p 4000:4000 \
-e LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY=true \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e REDIS_HOST=$REDIS_HOST \
-e REDIS_PASSWORD=$REDIS_PASSWORD \
-e REDIS_PORT=$REDIS_PORT \
-e LITELLM_MASTER_KEY="sk-1234" \
-e LITELLM_MASTER_KEY="$LITELLM_MASTER_KEY" \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
-e FAKE_OPENAI_API_BASE=http://host.docker.internal:8190 \
-e LITELLM_LICENSE=$LITELLM_LICENSE \
@ -2073,12 +2089,11 @@ jobs:
command: |
docker run -d \
-p 4000:4000 \
-e LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY=true \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e REDIS_HOST=$REDIS_HOST \
-e REDIS_PASSWORD=$REDIS_PASSWORD \
-e REDIS_PORT=$REDIS_PORT \
-e LITELLM_MASTER_KEY="sk-1234" \
-e LITELLM_MASTER_KEY="$LITELLM_MASTER_KEY" \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
-e FAKE_OPENAI_API_BASE=http://host.docker.internal:8190 \
-e LITELLM_LICENSE="bad-license" \
@ -2122,6 +2137,11 @@ jobs:
working_directory: ~/project
steps:
- checkout
- run:
name: Generate LiteLLM master key
command: |
key="$(openssl rand -hex 16)"
printf 'export LITELLM_MASTER_KEY=sk-%s\n' "$key" >> "$BASH_ENV"
- skip_if_unrelated_changes
- setup_google_dns
- install_uv
@ -2151,11 +2171,10 @@ jobs:
command: |
docker run -d \
-p 4000:4000 \
-e LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY=true \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e REDIS_HOST=host.docker.internal \
-e REDIS_PORT=6379 \
-e LITELLM_MASTER_KEY="sk-1234" \
-e LITELLM_MASTER_KEY="$LITELLM_MASTER_KEY" \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
-e FAKE_OPENAI_API_BASE=http://host.docker.internal:8190 \
-e LITELLM_LICENSE=$LITELLM_LICENSE \
@ -2210,6 +2229,11 @@ jobs:
working_directory: ~/project
steps:
- checkout
- run:
name: Generate LiteLLM master key
command: |
key="$(openssl rand -hex 16)"
printf 'export LITELLM_MASTER_KEY=sk-%s\n' "$key" >> "$BASH_ENV"
- skip_if_unrelated_changes
- setup_google_dns
- install_uv
@ -2234,12 +2258,11 @@ jobs:
command: |
docker run -d \
-p 4000:4000 \
-e LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY=true \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e REDIS_HOST=$REDIS_HOST \
-e REDIS_PASSWORD=$REDIS_PASSWORD \
-e REDIS_PORT=$REDIS_PORT \
-e LITELLM_MASTER_KEY="sk-1234" \
-e LITELLM_MASTER_KEY="$LITELLM_MASTER_KEY" \
-e FAKE_OPENAI_API_BASE=http://host.docker.internal:8190 \
-e LITELLM_LICENSE=$LITELLM_LICENSE \
-e USE_DDTRACE=True \
@ -2257,12 +2280,11 @@ jobs:
command: |
docker run -d \
-p 4001:4001 \
-e LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY=true \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e REDIS_HOST=$REDIS_HOST \
-e REDIS_PASSWORD=$REDIS_PASSWORD \
-e REDIS_PORT=$REDIS_PORT \
-e LITELLM_MASTER_KEY="sk-1234" \
-e LITELLM_MASTER_KEY="$LITELLM_MASTER_KEY" \
-e FAKE_OPENAI_API_BASE=http://host.docker.internal:8190 \
-e LITELLM_LICENSE=$LITELLM_LICENSE \
-e USE_DDTRACE=True \
@ -2309,6 +2331,11 @@ jobs:
working_directory: ~/project
steps:
- checkout
- run:
name: Generate LiteLLM master key
command: |
key="$(openssl rand -hex 16)"
printf 'export LITELLM_MASTER_KEY=sk-%s\n' "$key" >> "$BASH_ENV"
- skip_if_unrelated_changes
- setup_google_dns
- install_uv
@ -2335,10 +2362,9 @@ jobs:
docker run -d \
--restart on-failure \
-p 4000:4000 \
-e LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY=true \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e STORE_MODEL_IN_DB="True" \
-e LITELLM_MASTER_KEY="sk-1234" \
-e LITELLM_MASTER_KEY="$LITELLM_MASTER_KEY" \
-e FAKE_OPENAI_API_BASE=http://host.docker.internal:8190 \
-e TEAM_METADATA_VALIDATION_SERVICE_URL=http://host.docker.internal:9414/validate \
-e LITELLM_LICENSE=$LITELLM_LICENSE \
@ -2388,6 +2414,11 @@ jobs:
working_directory: ~/project
steps:
- checkout
- run:
name: Generate LiteLLM master key
command: |
key="$(openssl rand -hex 16)"
printf 'export LITELLM_MASTER_KEY=sk-%s\n' "$key" >> "$BASH_ENV"
- skip_if_unrelated_changes
- setup_google_dns
# Remove Docker CLI installation since it's already available in machine executor
@ -2410,12 +2441,11 @@ jobs:
command: |
docker run -d \
-p 4000:4000 \
-e LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY=true \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e REDIS_HOST=$REDIS_HOST \
-e REDIS_PASSWORD=$REDIS_PASSWORD \
-e REDIS_PORT=$REDIS_PORT \
-e LITELLM_MASTER_KEY="sk-1234" \
-e LITELLM_MASTER_KEY="$LITELLM_MASTER_KEY" \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
-e FAKE_OPENAI_API_BASE=http://host.docker.internal:8190 \
-e LITELLM_LICENSE=$LITELLM_LICENSE \
@ -2472,6 +2502,11 @@ jobs:
working_directory: ~/project
steps:
- checkout
- run:
name: Generate LiteLLM master key
command: |
key="$(openssl rand -hex 16)"
printf 'export LITELLM_MASTER_KEY=sk-%s\n' "$key" >> "$BASH_ENV"
- skip_if_unrelated_changes
- setup_google_dns
- install_uv
@ -2493,9 +2528,8 @@ jobs:
command: |
docker run -d \
-p 4000:4000 \
-e LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY=true \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e LITELLM_MASTER_KEY="sk-1234" \
-e LITELLM_MASTER_KEY="$LITELLM_MASTER_KEY" \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
-e GEMINI_API_KEY=$GEMINI_API_KEY \
-e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
@ -2563,6 +2597,11 @@ jobs:
working_directory: ~/project
steps:
- checkout
- run:
name: Generate LiteLLM master key
command: |
key="$(openssl rand -hex 16)"
printf 'export LITELLM_MASTER_KEY=sk-%s\n' "$key" >> "$BASH_ENV"
- skip_if_unrelated_changes
- setup_google_dns
- install_uv
@ -2585,9 +2624,8 @@ jobs:
command: |
docker run -d \
-p 4000:4000 \
-e LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY=true \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e LITELLM_MASTER_KEY="sk-1234" \
-e LITELLM_MASTER_KEY="$LITELLM_MASTER_KEY" \
-e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
-e RECORDER_ANTHROPIC_BASE_URL=http://host.docker.internal:8090/__recorder_upstream/api.anthropic.com \
-e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \
@ -2613,7 +2651,7 @@ jobs:
command: |
mkdir -p test-results
export LITELLM_PROXY_URL="http://localhost:4000"
export LITELLM_API_KEY="sk-1234"
export LITELLM_API_KEY="$LITELLM_MASTER_KEY"
TEST_FILES=$(circleci tests glob "tests/proxy_e2e_anthropic_messages_tests/**/test_*.py")
echo "$TEST_FILES" | circleci tests run \
--verbose \
@ -2676,6 +2714,11 @@ jobs:
PROXY_LOGOUT_URL: "https://www.example.com"
steps:
- checkout
- run:
name: Generate LiteLLM master key
command: |
key="$(openssl rand -hex 16)"
printf 'export LITELLM_MASTER_KEY=sk-%s\n' "$key" >> "$BASH_ENV"
- skip_if_unrelated_changes:
category: client
- setup_google_dns
@ -2766,8 +2809,6 @@ jobs:
- run:
name: Start LiteLLM proxy
environment:
LITELLM_MASTER_KEY: "sk-1234"
LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY: "true"
MOCK_LLM_URL: "http://127.0.0.1:8090/v1"
DISABLE_SCHEMA_UPDATE: "true"
SERVER_ROOT_PATH: ""
@ -2787,7 +2828,7 @@ jobs:
name: Wait for proxy to be ready
command: |
for i in $(seq 1 60); do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:4000/health -H "Authorization: Bearer sk-1234" 2>/dev/null || true)
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:4000/health -H "Authorization: Bearer $LITELLM_MASTER_KEY" 2>/dev/null || true)
if [ "$HTTP_CODE" = "200" ]; then
echo "Proxy is ready"
exit 0
@ -2834,6 +2875,11 @@ jobs:
SERVER_ROOT_PATH: "/litellm"
steps:
- checkout
- run:
name: Generate LiteLLM master key
command: |
key="$(openssl rand -hex 16)"
printf 'export LITELLM_MASTER_KEY=sk-%s\n' "$key" >> "$BASH_ENV"
- skip_if_unrelated_changes:
category: client
- setup_google_dns
@ -2910,8 +2956,6 @@ jobs:
- run:
name: Start LiteLLM proxy under a server root path
environment:
LITELLM_MASTER_KEY: "sk-1234"
LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY: "true"
MOCK_LLM_URL: "http://127.0.0.1:8090/v1"
DISABLE_SCHEMA_UPDATE: "true"
# Output flows to this step's own log, so a boot crash is visible here
@ -2926,7 +2970,7 @@ jobs:
name: Wait for prefixed proxy to be ready
command: |
for i in $(seq 1 60); do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer sk-1234" http://127.0.0.1:4000/litellm/health 2>/dev/null || true)
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer $LITELLM_MASTER_KEY" http://127.0.0.1:4000/litellm/health 2>/dev/null || true)
if [ "$HTTP_CODE" = "200" ]; then
echo "Prefixed proxy is ready"
exit 0

View file

@ -72,7 +72,7 @@ export PATH="$PWD/.venv/bin:$PATH"
export PYTHONPATH="$PWD:$PWD/tests:$PWD/tests/e2e"
export DATABASE_URL="postgresql://postgres:postgres@127.0.0.1:5432/circle_test"
export REDIS_HOST=127.0.0.1 REDIS_PORT=6379
export LITELLM_MASTER_KEY=sk-integration-master LITELLM_SALT_KEY=sk-integration-salt
export LITELLM_MASTER_KEY="sk-$(openssl rand -hex 16)" LITELLM_SALT_KEY=sk-integration-salt
export LITELLM_MODE=PRODUCTION LITELLM_LOCAL_MODEL_COST_MAP=True
export STORE_MODEL_IN_DB=True AWS_EC2_METADATA_DISABLED=true DO_NOT_TRACK=1
export INTEGRATION_PROXY_URL=http://127.0.0.1:4000

View file

@ -84,10 +84,6 @@ secret:
- name: Langfuse test credentials in test_completion
match: c39310f68cc3d3e22f7b298bb6353c4f45759adcc37080d8b7f4e535d3cfd7f4
# Test password "sk-1234" in e2e test fixtures - test fixture, not a real secret
- name: Test password in e2e test fixtures
match: ce32b547202e209ec1dd50107b64be4cfcf2eb15c3b4f8e9dc611ef747af634f
# === Preventive patterns for test keys (pattern-based) ===
# Test API keys (124 instances across 45 files)
@ -108,4 +104,3 @@ secret:
- name: Short fake sk keys (1–9 digits only)
match: \bsk-\d{1,9}\b

View file

@ -150,7 +150,7 @@ For each e2e test you added or changed, list the manual steps a reviewer can fol
Example checklists:
- tests/e2e/quota_management/ratelimit/test_rate_limit_e2e.py::TestKeyRateLimits::test_rpm_limit_blocks_over_limit - a key allowed 2 requests a minute serves exactly 2 and refuses the 3rd
- [ ] Generate a limited key: curl -X POST http://localhost:4000/key/generate -H "Authorization: Bearer sk-1234" -d '{"rpm_limit": 2}'
- [ ] Generate a limited key: curl -X POST http://localhost:4000/key/generate -H "Authorization: Bearer $LITELLM_MASTER_KEY" -d '{"rpm_limit": 2}'
- [ ] Send three /v1/chat/completions requests with that key inside one minute
- [ ] Expect the first two to return 200 and the third to return 429 naming the rpm limit
- [ ] Sanity check: this test makes sense to add and is not hand-wavey (e.g., assert actual expected spend instead of just spend > 0) or potentially flaky
@ -164,4 +164,3 @@ Example checklists:
## Final Attestation
- [ ] The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

View file

@ -95,7 +95,9 @@ jobs:
- name: test_e2e_metadata
env:
PYTHONPATH: tests/e2e
run: uv run --no-sync pytest -q --noconftest -p no:cacheprovider -c /dev/null tests/code_coverage_tests/test_e2e_metadata.py tests/code_coverage_tests/test_e2e_junit_report.py
run: |
export LITELLM_MASTER_KEY="sk-$(openssl rand -hex 16)"
uv run --no-sync pytest -q --noconftest -p no:cacheprovider -c /dev/null tests/code_coverage_tests/test_e2e_metadata.py tests/code_coverage_tests/test_e2e_junit_report.py
- name: Check merge smoke harness
run: uv run --no-sync pytest -q --noconftest -p no:cacheprovider -c /dev/null tests/code_coverage_tests/test_merge_smoke.py
@ -163,6 +165,9 @@ jobs:
- name: check_migrations_no_data_rewrites
run: uv run --no-sync python ./tests/code_coverage_tests/check_migrations_no_data_rewrites.py
- name: check_no_publicly_known_master_key
run: uv run --no-sync python ./tests/code_coverage_tests/check_no_publicly_known_master_key.py
- name: check_unbounded_in_lists (fails on findings not in the baseline)
run: uv run --no-sync python ./tests/code_coverage_tests/check_unbounded_in_lists.py

View file

@ -5,7 +5,7 @@ Standard patterns for test/mock keys and credentials in the LiteLLM codebase to
## How GitGuardian Works
GitGuardian uses **machine learning and entropy analysis**, not just pattern matching:
- **Low entropy** values (like `sk-1234`, `postgres`) are automatically ignored
- **Low entropy** values (like `sk-test`, `postgres`) are automatically ignored
- **High entropy** values (realistic-looking secrets) trigger detection
- **Context-aware** detection understands code syntax like `os.environ["KEY"]`
@ -15,8 +15,8 @@ GitGuardian uses **machine learning and entropy analysis**, not just pattern mat
These won't trigger GitGuardian's ML detector:
```python
api_key = "sk-1234"
api_key = "sk-12345"
api_key = "sk-test"
api_key = "sk-mock"
database_password = "postgres"
token = "test123"
```

View file

@ -16,6 +16,8 @@
"\n",
"These are **selected examples**. LiteLLM Proxy is **OpenAI-Compatible**, it works with any project that calls OpenAI. Just change the `base_url`, `api_key` and `model`.\n",
"\n",
"Set `LITELLM_MASTER_KEY` in the notebook environment to the same key configured on the proxy before running authenticated examples\n",
"\n",
"For more examples, [go here](https://docs.litellm.ai/docs/proxy/user_keys)\n",
"\n",
"To pass provider-specific args, [go here](https://docs.litellm.ai/docs/completion/provider_specific_params#proxy-usage)\n",
@ -95,9 +97,10 @@
},
"outputs": [],
"source": [
"import os\n",
"from openai import OpenAI\n",
"client = OpenAI(\n",
" api_key=\"sk-1234\", # [OPTIONAL] set if you set one on proxy, else set \"\"\n",
" api_key=os.environ[\"LITELLM_MASTER_KEY\"],\n",
" base_url=\"http://0.0.0.0:4000\",\n",
")\n",
"\n",
@ -298,14 +301,14 @@
" engine=\"azure-gpt-3.5\", # model_name on litellm proxy\n",
" temperature=0.0,\n",
" azure_endpoint=\"http://0.0.0.0:4000\", # litellm proxy endpoint\n",
" api_key=\"sk-1234\", # litellm proxy API Key\n",
" api_key=os.environ[\"LITELLM_MASTER_KEY\"],\n",
" api_version=\"2023-07-01-preview\",\n",
")\n",
"\n",
"embed_model = AzureOpenAIEmbedding(\n",
" deployment_name=\"azure-embedding-model\",\n",
" azure_endpoint=\"http://0.0.0.0:4000\",\n",
" api_key=\"sk-1234\",\n",
" api_key=os.environ[\"LITELLM_MASTER_KEY\"],\n",
" api_version=\"2023-07-01-preview\",\n",
")\n",
"\n",
@ -341,7 +344,7 @@
"\n",
"const model = new ChatOpenAI({\n",
" modelName: \"gpt-4\",\n",
" openAIApiKey: \"sk-1234\",\n",
" openAIApiKey: process.env.LITELLM_MASTER_KEY,\n",
" modelKwargs: {\"metadata\": \"hello world\"} // 👈 PASS Additional params here\n",
"}, {\n",
" basePath: \"http://0.0.0.0:4000\",\n",
@ -372,7 +375,7 @@
"const { OpenAI } = require('openai');\n",
"\n",
"const openai = new OpenAI({\n",
" apiKey: \"sk-1234\", // This is the default and can be omitted\n",
" apiKey: process.env.LITELLM_MASTER_KEY,\n",
" baseURL: \"http://0.0.0.0:4000\"\n",
"});\n",
"\n",

View file

@ -6,7 +6,9 @@
"id": "680oRk1af-xJ"
},
"source": [
"# Environment Setup"
"# Environment Setup\n",
"\n",
"Set `LITELLM_MASTER_KEY` in the notebook environment to the same key configured on the proxy before running the cells"
]
},
{
@ -17,6 +19,7 @@
},
"outputs": [],
"source": [
"import os\n",
"import csv\n",
"from typing import Optional\n",
"import httpx\n",
@ -24,7 +27,7 @@
"import asyncio\n",
"\n",
"proxy_base_url = \"http://0.0.0.0:4000\" # 👈 SET TO PROXY URL\n",
"master_key = \"sk-1234\" # 👈 SET TO PROXY MASTER KEY"
"master_key = os.environ[\"LITELLM_MASTER_KEY\"]"
]
},
{

View file

@ -47,7 +47,7 @@ Set your environment variables:
```bash
export ANTHROPIC_API_KEY="your-anthropic-api-key"
export LITELLM_MASTER_KEY="sk-1234567890" # Generate a secure key
export LITELLM_MASTER_KEY="sk-$(openssl rand -hex 32)"
```
## Step 2: Start Proxy
@ -292,4 +292,3 @@ model_list:
- [LiteLLM Documentation](https://docs.litellm.ai/)
- [Claude Code Documentation](https://docs.anthropic.com/en/docs/claude-code/overview)
- [Anthropic's LiteLLM Configuration Guide](https://docs.anthropic.com/en/docs/claude-code/llm-gateway#litellm-configuration)

View file

@ -20,6 +20,12 @@ litellm --model claude-sonnet-4-20250514
litellm --config config.yaml
```
Generate a master key before starting the proxy:
```bash
export LITELLM_MASTER_KEY="sk-$(openssl rand -hex 32)"
```
### 3. Run the chat
**Basic Agent (no MCP):**
@ -58,7 +64,7 @@ Set these environment variables if needed:
```bash
export LITELLM_PROXY_URL="http://localhost:4000"
export LITELLM_API_KEY="sk-1234"
export LITELLM_API_KEY="$LITELLM_MASTER_KEY"
export LITELLM_MODEL="bedrock-claude-sonnet-4.5"
```
@ -98,7 +104,7 @@ The key is pointing the Agent SDK to LiteLLM instead of directly to Anthropic:
```python
# Point to LiteLLM gateway (not Anthropic)
os.environ["ANTHROPIC_BASE_URL"] = "http://localhost:4000"
os.environ["ANTHROPIC_API_KEY"] = "sk-1234" # Your LiteLLM key
os.environ["ANTHROPIC_API_KEY"] = os.environ["LITELLM_API_KEY"]
# Use any model configured in LiteLLM
options = ClaudeAgentOptions(

View file

@ -13,7 +13,7 @@ class Config:
LITELLM_PROXY_URL = os.getenv("LITELLM_PROXY_URL", "http://localhost:4000")
# LiteLLM API key (master key or virtual key)
LITELLM_API_KEY = os.getenv("LITELLM_API_KEY", "sk-1234")
LITELLM_API_KEY = os.environ["LITELLM_API_KEY"]
# Model name as configured in LiteLLM (e.g., "bedrock-claude-sonnet-4", "gpt-4", etc.)
LITELLM_MODEL = os.getenv("LITELLM_MODEL", "bedrock-claude-sonnet-4.5")

View file

@ -1,8 +1,10 @@
import os
from openai import OpenAI
client = OpenAI(
base_url="http://0.0.0.0:4000",
api_key="sk-1234",
api_key=os.environ["LITELLM_MASTER_KEY"],
)
BEDROCK_BATCH_MODEL = "bedrock/batch-anthropic.claude-3-5-sonnet-20240620-v1:0"

View file

@ -4,10 +4,12 @@ Use LiteLLM Proxy MCP Gateway to call MCP tools.
When using LiteLLM Proxy, you can use the same MCP tools across all your LLM providers.
"""
import os
import openai
client = openai.OpenAI(
api_key="sk-1234", # paste your litellm proxy api key here
api_key=os.environ["LITELLM_MASTER_KEY"],
base_url="http://localhost:4000", # paste your litellm proxy base url here
)
print("Making API request to Responses API with MCP tools")

View file

@ -15,12 +15,14 @@ pip install livekit-agents[xai] websockets
```bash
# With xAI
export XAI_API_KEY="your-xai-key"
export LITELLM_MASTER_KEY="sk-$(openssl rand -hex 32)"
litellm --config config.yaml --port 4000
```
### 3. Run the voice agent
```bash
export LITELLM_API_KEY="$LITELLM_MASTER_KEY"
python main.py
```
@ -32,7 +34,7 @@ Set these environment variables if needed:
```bash
export LITELLM_PROXY_URL="http://localhost:4000"
export LITELLM_API_KEY="sk-1234"
export LITELLM_API_KEY="$LITELLM_MASTER_KEY"
export LITELLM_MODEL="grok-voice-agent"
```
@ -59,7 +61,7 @@ model_list:
mode: realtime
general_settings:
master_key: sk-1234
master_key: os.environ/LITELLM_MASTER_KEY
```
Then start: `litellm --config config.yaml --port 4000`
@ -73,7 +75,7 @@ from livekit.plugins import xai
model = xai.realtime.RealtimeModel(
voice="ara",
api_key="sk-1234", # LiteLLM proxy key
api_key=os.environ["LITELLM_MASTER_KEY"],
base_url="http://localhost:4000", # Point to LiteLLM
)
```

View file

@ -17,4 +17,4 @@ litellm_settings:
drop_params: True
general_settings:
master_key: sk-1234 # Change this to a secure key
master_key: os.environ/LITELLM_MASTER_KEY

View file

@ -13,7 +13,7 @@ import websockets
# Configuration
PROXY_URL = os.getenv("LITELLM_PROXY_URL", "http://localhost:4000")
API_KEY = os.getenv("LITELLM_API_KEY", "sk-1234")
API_KEY = os.environ["LITELLM_API_KEY"]
MODEL = os.getenv("LITELLM_MODEL", "grok-voice-agent")

View file

@ -58,7 +58,7 @@ litellm_settings:
context_window_fallbacks: [{"gpt-3.5-turbo": ["gpt-3.5-turbo-large"]}]
general_settings:
master_key: sk-1234 # [OPTIONAL] Use to enforce auth on proxy. See - https://docs.litellm.ai/docs/proxy/virtual_keys
master_key: os.environ/LITELLM_MASTER_KEY
store_model_in_db: True
proxy_budget_rescheduler_min_time: 60
proxy_budget_rescheduler_max_time: 64

View file

@ -12,6 +12,8 @@ Step 2: reads `model_list` and loops through all models
Step 3: calls `<proxy-base-url>/model/new` for each model
"""
import os
import yaml
import requests
@ -89,7 +91,7 @@ def migrate_models(config_file, proxy_base_url):
# Usage
config_file = "config.yaml"
proxy_base_url = "http://0.0.0.0:4000"
master_key = "sk-1234"
master_key = os.environ["LITELLM_MASTER_KEY"]
print(f"config_file: {config_file}")
print(f"proxy_base_url: {proxy_base_url}")
migrate_models(config_file, proxy_base_url)

View file

@ -1,8 +1,10 @@
import base64
import os
from openai import OpenAI
import time
client = OpenAI(base_url="http://0.0.0.0:4001", api_key="sk-1234")
client = OpenAI(base_url="http://0.0.0.0:4001", api_key=os.environ["LITELLM_MASTER_KEY"])
# Function to encode the image

View file

@ -4,7 +4,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Databricks Notebook with MLFlow AutoLogging for LiteLLM Proxy calls\n"
"# Databricks Notebook with MLFlow AutoLogging for LiteLLM Proxy calls\n",
"\n",
"Set `LITELLM_PROXY_API_KEY` in the notebook environment to a key accepted by the proxy before running the model cell\n"
]
},
{
@ -160,11 +162,13 @@
},
"outputs": [],
"source": [
"import os\n",
"\n",
"model = ChatOpenAI(\n",
" openai_api_base=\"LITELLM_PROXY_BASE_URL\", # e.g.: http://0.0.0.0:4000\n",
" model = \"gpt-3.5-turbo\", # LITELLM 'model_name'\n",
" temperature=0.1, \n",
" api_key=\"LITELLM_PROXY_API_KEY\" # e.g.: \"sk-1234\"\n",
" api_key=os.environ[\"LITELLM_PROXY_API_KEY\"]\n",
")"
]
},

View file

@ -69,7 +69,7 @@ litellm --config config.yaml
```bash
curl http://0.0.0.0:4000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"model": "gpt-3.5-turbo",
"prompt_id": "hello-world-prompt",
@ -290,4 +290,3 @@ Before deploying to production:
## Questions?
This is a reference implementation for the LiteLLM Generic Prompt Management API. For questions or issues, please open an issue on the [LiteLLM GitHub repository](https://github.com/BerriAI/litellm).

View file

@ -10,6 +10,7 @@ Prerequisites:
- websockets installed: pip install websockets
Usage:
export LITELLM_MASTER_KEY="sk-$(openssl rand -hex 32)"
python nova_sonic_realtime.py
"""
@ -33,7 +34,7 @@ CHUNK_SIZE = 1024
# LiteLLM proxy configuration
LITELLM_PROXY_URL = "ws://localhost:4000/v1/realtime?model=bedrock-sonic"
LITELLM_API_KEY = "sk-12345" # Your LiteLLM API key
LITELLM_API_KEY = os.environ["LITELLM_MASTER_KEY"]
class RealtimeClient:

View file

@ -24,8 +24,8 @@ class VeoVideoGenerator:
def __init__(
self,
api_key: str,
base_url: str = "http://localhost:4000/gemini/v1beta",
api_key: str = "sk-1234",
):
"""
Initialize the Veo video generator.
@ -274,12 +274,12 @@ def main():
Configure these environment variables:
- LITELLM_BASE_URL: Your LiteLLM proxy URL (default: http://localhost:4000/gemini/v1beta)
- LITELLM_API_KEY: Your LiteLLM API key (default: sk-1234)
- LITELLM_API_KEY: API key for LiteLLM proxy authentication
"""
# Configuration from environment or defaults
base_url = os.getenv("LITELLM_BASE_URL", "http://localhost:4000/gemini/v1beta")
api_key = os.getenv("LITELLM_API_KEY", "sk-1234")
api_key = os.environ["LITELLM_API_KEY"]
print("🚀 Starting Veo Video Generation Example")
print(f"📡 Using LiteLLM proxy at: {base_url}")

View file

@ -21,7 +21,7 @@ docker build --build-arg LITELLM_RELEASE_TAG="$LITELLM_RELEASE_TAG" \
docker compose -f docker/docker-compose.tracing.yml up -d --build
```
Open `http://localhost:4002/ui/` and sign in as `admin` with password `sk-1234`. Go to **Lens > Investigations > Connect worker**, choose a model and monthly budget, then **Get install command**. Expand **Using Docker Compose or Helm?** and copy the worker token. In the same terminal, run:
Open `http://localhost:4002/ui/` and sign in as `admin` with the key saved in `.lens-dev/master_key`. Go to **Lens > Investigations > Connect worker**, choose a model and monthly budget, then **Get install command**. Expand **Using Docker Compose or Helm?** and copy the worker token. In the same terminal, run:
```bash
export LITELLM_URL=http://litellm:4000

View file

@ -9,8 +9,7 @@ services:
LITELLM_RELEASE_TAG: ${LITELLM_RELEASE_TAG:-}
command: ["--config", "/app/tracing-config.yaml", "--port", "4000"]
environment:
LITELLM_MASTER_KEY: sk-1234
LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY: "true"
LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY:?set LITELLM_MASTER_KEY}
LITELLM_SALT_KEY: sk-local-tracing-salt-key
DATABASE_URL: postgresql://litellm:litellm@db:5432/litellm
STORE_MODEL_IN_DB: "True"

View file

@ -433,7 +433,7 @@ async def new_project(
```bash
curl --location 'http://0.0.0.0:4000/project/new' \\
--header 'Authorization: Bearer sk-1234' \\
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \\
--header 'Content-Type: application/json' \\
--data '{
"project_alias": "flight-search-assistant",
@ -460,7 +460,7 @@ async def new_project(
```bash
curl --location 'http://0.0.0.0:4000/project/new' \\
--header 'Authorization: Bearer sk-1234' \\
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \\
--header 'Content-Type: application/json' \\
--data '{
"project_alias": "hotel-recommendations",
@ -648,7 +648,7 @@ async def update_project(
Example:
```bash
curl --location 'http://0.0.0.0:4000/project/update' \\
--header 'Authorization: Bearer sk-1234' \\
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \\
--header 'Content-Type: application/json' \\
--data '{
"project_id": "project-123",
@ -876,7 +876,7 @@ async def delete_project(
Example:
```bash
curl --location --request DELETE 'http://0.0.0.0:4000/project/delete' \\
--header 'Authorization: Bearer sk-1234' \\
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \\
--header 'Content-Type: application/json' \\
--data '{
"project_ids": ["project-123", "project-456"]
@ -983,7 +983,7 @@ async def project_info(
Example:
```bash
curl --location 'http://0.0.0.0:4000/project/info?project_id=project-123' \\
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import prisma_client
@ -1052,7 +1052,7 @@ async def list_projects(
Example:
```bash
curl --location 'http://0.0.0.0:4000/project/list' \\
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import prisma_client

View file

@ -94,8 +94,8 @@ async fn enforces_configured_keys_without_exposing_secrets(
#[rstest]
fn hash_token_matches_python_sha256_hexdigest() {
assert_eq!(
hash_token("sk-1234"),
"88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b"
hash_token("sk-9876"),
"595b23af2e99cee580245f388d3244a39247a25a3846e898e9c78841f8471a3e"
);
}

View file

@ -175,7 +175,7 @@ class LoggingCallbackManager:
callback_type: generic_api
endpoint: https://webhook-test.com/30343bc33591bc5e6dc44217ceae3e0a
headers:
Authorization: Bearer sk-1234
Authorization: Bearer $LITELLM_MASTER_KEY
"""
callback_config: Final = litellm.callback_settings.get(callback)

View file

@ -292,7 +292,7 @@ def _redact_sequence(values: Sequence[object], depth: int) -> Sequence[object]:
"""
masker = SensitiveDataMasker()
data = {
"api_key": "sk-1234567890abcdef",
"api_key": "sk-9876543210abcdef",
"redis_password": "very_secret_pass",
"port": 6379,
"tags": ["East US 2", "production", "test"]

View file

@ -1241,7 +1241,7 @@
"paths": {
"/access_group/list": {
"get": {
"description": "List all access groups.\n\nReturns a list of all access groups with their model names, deployment counts, shared budget\nand the spend drawn against it.\n\nExample:\n```bash\ncurl -X GET 'http://localhost:4000/access_group/list' \\\n -H 'Authorization: Bearer sk-1234'\n```\n\nReturns:\n- ListAccessGroupsResponse with all access groups",
"description": "List all access groups.\n\nReturns a list of all access groups with their model names, deployment counts, shared budget\nand the spend drawn against it.\n\nExample:\n```bash\ncurl -X GET 'http://localhost:4000/access_group/list' \\\n -H \"Authorization: Bearer $LITELLM_MASTER_KEY\"\n```\n\nReturns:\n- ListAccessGroupsResponse with all access groups",
"operationId": "list_access_groups_access_group_list_get",
"responses": {
"200": {
@ -1268,7 +1268,7 @@
},
"/access_group/new": {
"post": {
"description": "Create a new access group containing multiple model names.\n\nAn access group is a named collection of model groups that can be referenced\nby teams/keys for simplified access control.\n\nExample:\n```bash\ncurl -X POST 'http://localhost:4000/access_group/new' \\\n -H 'Authorization: Bearer sk-1234' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"access_group\": \"production-models\",\n \"model_names\": [\"gpt-4\", \"claude-3-opus\", \"gemini-pro\"]\n }'\n```\n\nParameters:\n- access_group: str - The access group name (e.g., \"production-models\")\n- model_names: List[str] - List of existing model groups to include\n\nReturns:\n- NewModelGroupResponse with the created access group details\n\nRaises:\n- HTTPException 400: If any model names don't exist\n- HTTPException 500: If database operations fail",
"description": "Create a new access group containing multiple model names.\n\nAn access group is a named collection of model groups that can be referenced\nby teams/keys for simplified access control.\n\nExample:\n```bash\ncurl -X POST 'http://localhost:4000/access_group/new' \\\n -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"access_group\": \"production-models\",\n \"model_names\": [\"gpt-4\", \"claude-3-opus\", \"gemini-pro\"]\n }'\n```\n\nParameters:\n- access_group: str - The access group name (e.g., \"production-models\")\n- model_names: List[str] - List of existing model groups to include\n\nReturns:\n- NewModelGroupResponse with the created access group details\n\nRaises:\n- HTTPException 400: If any model names don't exist\n- HTTPException 500: If database operations fail",
"operationId": "create_model_group_access_group_new_post",
"requestBody": {
"content": {
@ -1315,7 +1315,7 @@
},
"/access_group/{access_group}/budget": {
"delete": {
"description": "Clear the shared budget of an access group, leaving the group itself in place.\n\nExample:\n```bash\ncurl -X DELETE 'http://localhost:4000/access_group/production-models/budget' \\\n -H 'Authorization: Bearer sk-1234'\n```\n\nParameters:\n- access_group: str - The access group name (URL path parameter)\n\nReturns:\n- DeleteAccessGroupBudgetResponse; budget_deleted is false when there was nothing to clear\n\nRaises:\n- HTTPException 404: If access group not found",
"description": "Clear the shared budget of an access group, leaving the group itself in place.\n\nExample:\n```bash\ncurl -X DELETE 'http://localhost:4000/access_group/production-models/budget' \\\n -H \"Authorization: Bearer $LITELLM_MASTER_KEY\"\n```\n\nParameters:\n- access_group: str - The access group name (URL path parameter)\n\nReturns:\n- DeleteAccessGroupBudgetResponse; budget_deleted is false when there was nothing to clear\n\nRaises:\n- HTTPException 404: If access group not found",
"operationId": "delete_access_group_budget_access_group__access_group__budget_delete",
"parameters": [
{
@ -1361,7 +1361,7 @@
]
},
"get": {
"description": "Get the shared budget of an access group, and the spend drawn against it.\n\nExample:\n```bash\ncurl -X GET 'http://localhost:4000/access_group/production-models/budget' \\\n -H 'Authorization: Bearer sk-1234'\n```\n\nParameters:\n- access_group: str - The access group name (URL path parameter)\n\nReturns:\n- AccessGroupBudgetResponse; budget is null when the group has no budget set\n\nRaises:\n- HTTPException 404: If access group not found",
"description": "Get the shared budget of an access group, and the spend drawn against it.\n\nExample:\n```bash\ncurl -X GET 'http://localhost:4000/access_group/production-models/budget' \\\n -H \"Authorization: Bearer $LITELLM_MASTER_KEY\"\n```\n\nParameters:\n- access_group: str - The access group name (URL path parameter)\n\nReturns:\n- AccessGroupBudgetResponse; budget is null when the group has no budget set\n\nRaises:\n- HTTPException 404: If access group not found",
"operationId": "get_access_group_budget_access_group__access_group__budget_get",
"parameters": [
{
@ -1407,7 +1407,7 @@
]
},
"put": {
"description": "Set or replace the shared budget of an access group. Idempotent.\n\nEvery key that can reach a model in the group draws from this one budget.\n\nExample:\n```bash\ncurl -X PUT 'http://localhost:4000/access_group/production-models/budget' \\\n -H 'Authorization: Bearer sk-1234' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"max_budget\": 100.0,\n \"budget_duration\": \"30d\"\n }'\n```\n\nParameters:\n- access_group: str - The access group name (URL path parameter)\n- max_budget: Optional[float] - Requests fail once the group's shared spend exceeds this\n- soft_budget: Optional[float] - Fires an alert when reached; requests still succeed\n- budget_duration: Optional[str] - Frequency of resetting the group's spend (e.g. '30d')\n- budget_id: Optional[str] - Link an existing budget instead of creating one\n\nReturns:\n- AccessGroupBudgetResponse with the stored budget and current spend\n\nRaises:\n- HTTPException 400: If no budget field is given, or budget_duration cannot be parsed\n- HTTPException 404: If access group not found",
"description": "Set or replace the shared budget of an access group. Idempotent.\n\nEvery key that can reach a model in the group draws from this one budget.\n\nExample:\n```bash\ncurl -X PUT 'http://localhost:4000/access_group/production-models/budget' \\\n -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"max_budget\": 100.0,\n \"budget_duration\": \"30d\"\n }'\n```\n\nParameters:\n- access_group: str - The access group name (URL path parameter)\n- max_budget: Optional[float] - Requests fail once the group's shared spend exceeds this\n- soft_budget: Optional[float] - Fires an alert when reached; requests still succeed\n- budget_duration: Optional[str] - Frequency of resetting the group's spend (e.g. '30d')\n- budget_id: Optional[str] - Link an existing budget instead of creating one\n\nReturns:\n- AccessGroupBudgetResponse with the stored budget and current spend\n\nRaises:\n- HTTPException 400: If no budget field is given, or budget_duration cannot be parsed\n- HTTPException 404: If access group not found",
"operationId": "set_access_group_budget_access_group__access_group__budget_put",
"parameters": [
{
@ -1465,7 +1465,7 @@
},
"/access_group/{access_group}/delete": {
"delete": {
"description": "Delete an access group.\n\nRemoves the access group from all deployments that have it.\n\nExample:\n```bash\ncurl -X DELETE 'http://localhost:4000/access_group/production-models/delete' \\\n -H 'Authorization: Bearer sk-1234'\n```\n\nParameters:\n- access_group: str - The access group name (URL path parameter)\n\nReturns:\n- DeleteModelGroupResponse with deletion details\n\nRaises:\n- HTTPException 404: If access group not found",
"description": "Delete an access group.\n\nRemoves the access group from all deployments that have it.\n\nExample:\n```bash\ncurl -X DELETE 'http://localhost:4000/access_group/production-models/delete' \\\n -H \"Authorization: Bearer $LITELLM_MASTER_KEY\"\n```\n\nParameters:\n- access_group: str - The access group name (URL path parameter)\n\nReturns:\n- DeleteModelGroupResponse with deletion details\n\nRaises:\n- HTTPException 404: If access group not found",
"operationId": "delete_access_group_access_group__access_group__delete_delete",
"parameters": [
{
@ -1513,7 +1513,7 @@
},
"/access_group/{access_group}/info": {
"get": {
"description": "Get information about a specific access group.\n\nExample:\n```bash\ncurl -X GET 'http://localhost:4000/access_group/production-models/info' \\\n -H 'Authorization: Bearer sk-1234'\n```\n\nParameters:\n- access_group: str - The access group name (URL path parameter)\n\nReturns:\n- AccessGroupInfo with the access group details, its shared budget and its spend\n\nRaises:\n- HTTPException 404: If access group not found",
"description": "Get information about a specific access group.\n\nExample:\n```bash\ncurl -X GET 'http://localhost:4000/access_group/production-models/info' \\\n -H \"Authorization: Bearer $LITELLM_MASTER_KEY\"\n```\n\nParameters:\n- access_group: str - The access group name (URL path parameter)\n\nReturns:\n- AccessGroupInfo with the access group details, its shared budget and its spend\n\nRaises:\n- HTTPException 404: If access group not found",
"operationId": "get_access_group_info_access_group__access_group__info_get",
"parameters": [
{
@ -1561,7 +1561,7 @@
},
"/access_group/{access_group}/update": {
"put": {
"description": "Update an access group's model names.\n\nThis will:\n1. Remove the access group from all current deployments\n2. Add the access group to all deployments for the new model_names list\n\nExample:\n```bash\ncurl -X PUT 'http://localhost:4000/access_group/production-models/update' \\\n -H 'Authorization: Bearer sk-1234' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"model_names\": [\"gpt-4\", \"claude-3-sonnet\"]\n }'\n```\n\nParameters:\n- access_group: str - The access group name (URL path parameter)\n- model_names: List[str] - New list of model groups to include\n\nReturns:\n- NewModelGroupResponse with the updated access group details\n\nRaises:\n- HTTPException 400: If any model names don't exist\n- HTTPException 404: If access group not found",
"description": "Update an access group's model names.\n\nThis will:\n1. Remove the access group from all current deployments\n2. Add the access group to all deployments for the new model_names list\n\nExample:\n```bash\ncurl -X PUT 'http://localhost:4000/access_group/production-models/update' \\\n -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"model_names\": [\"gpt-4\", \"claude-3-sonnet\"]\n }'\n```\n\nParameters:\n- access_group: str - The access group name (URL path parameter)\n- model_names: List[str] - New list of model groups to include\n\nReturns:\n- NewModelGroupResponse with the updated access group details\n\nRaises:\n- HTTPException 400: If any model names don't exist\n- HTTPException 404: If access group not found",
"operationId": "update_access_group_access_group__access_group__update_put",
"parameters": [
{
@ -26692,7 +26692,7 @@
},
"/cursor/chat/completions": {
"post": {
"description": "Cursor BYOK endpoint. Accepts both request shapes Cursor sends to its OpenAI-compatible\nbase URL and always answers in chat completions format.\n\nCursor agent mode sends Responses API format bodies (`input`, flat tool defs, `reasoning`,\ncustom tools) to the chat/completions path while expecting chat completions responses;\nthose are routed through the Responses API pipeline and converted back. Genuine chat\ncompletions bodies (`messages` present) are routed through the standard chat completions\npipeline, after normalizing each level of the `tools` array and `tool_choice` to the chat\ncompletions shapes OpenAI requires. Cursor mixes Responses API shapes into chat bodies\nper level, independently: a flat tool def (`{\"type\": \"custom\", \"name\": \"ApplyPatch\", ...}`)\ngets nested under `custom`, and a flat grammar format\n(`{\"type\": \"grammar\", \"definition\", \"syntax\"}`) gets wrapped as\n`{\"type\": \"grammar\", \"grammar\": {...}}` wherever it appears, including inside tool defs\nCursor already sent pre-nested.\n\n```bash\ncurl -X POST http://localhost:4000/cursor/chat/completions -H \"Content-Type: application/json\" -H \"Authorization: Bearer sk-1234\" -d '{\n \"model\": \"gpt-4o\",\n \"input\": [{\"role\": \"user\", \"content\": \"Hello\"}]\n}'\nResponds back in chat completions format.\n```",
"description": "Cursor BYOK endpoint. Accepts both request shapes Cursor sends to its OpenAI-compatible\nbase URL and always answers in chat completions format.\n\nCursor agent mode sends Responses API format bodies (`input`, flat tool defs, `reasoning`,\ncustom tools) to the chat/completions path while expecting chat completions responses;\nthose are routed through the Responses API pipeline and converted back. Genuine chat\ncompletions bodies (`messages` present) are routed through the standard chat completions\npipeline, after normalizing each level of the `tools` array and `tool_choice` to the chat\ncompletions shapes OpenAI requires. Cursor mixes Responses API shapes into chat bodies\nper level, independently: a flat tool def (`{\"type\": \"custom\", \"name\": \"ApplyPatch\", ...}`)\ngets nested under `custom`, and a flat grammar format\n(`{\"type\": \"grammar\", \"definition\", \"syntax\"}`) gets wrapped as\n`{\"type\": \"grammar\", \"grammar\": {...}}` wherever it appears, including inside tool defs\nCursor already sent pre-nested.\n\n```bash\ncurl -X POST http://localhost:4000/cursor/chat/completions -H \"Content-Type: application/json\" -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" -d '{\n \"model\": \"gpt-4o\",\n \"input\": [{\"role\": \"user\", \"content\": \"Hello\"}]\n}'\nResponds back in chat completions format.\n```",
"operationId": "cursor_chat_completions_cursor_chat_completions_post",
"responses": {
"200": {
@ -28532,7 +28532,7 @@
},
"/openai/deployments/{model}/chat/completions": {
"post": {
"description": "Follows the exact same API spec as `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat`\n\n```bash\ncurl -X POST http://localhost:4000/v1/chat/completions \n-H \"Content-Type: application/json\" \n-H \"Authorization: Bearer sk-1234\" \n-d '{\n \"model\": \"gpt-4o\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Hello!\"\n }\n ]\n}'\n```",
"description": "Follows the exact same API spec as `OpenAI's Chat API https://platform.openai.com/docs/api-reference/chat`\n\n```bash\ncurl -X POST http://localhost:4000/v1/chat/completions \n-H \"Content-Type: application/json\" \n-H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \n-d '{\n \"model\": \"gpt-4o\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Hello!\"\n }\n ]\n}'\n```",
"operationId": "chat_completion_openai_deployments__model__chat_completions_post",
"parameters": [
{
@ -28665,7 +28665,7 @@
},
"/openai/deployments/{model}/completions": {
"post": {
"description": "Follows the exact same API spec as `OpenAI's Completions API https://platform.openai.com/docs/api-reference/completions`\n\n```bash\ncurl -X POST http://localhost:4000/v1/completions \n-H \"Content-Type: application/json\" \n-H \"Authorization: Bearer sk-1234\" \n-d '{\n \"model\": \"gpt-3.5-turbo-instruct\",\n \"prompt\": \"Once upon a time\",\n \"max_tokens\": 50,\n \"temperature\": 0.7\n}'\n```",
"description": "Follows the exact same API spec as `OpenAI's Completions API https://platform.openai.com/docs/api-reference/completions`\n\n```bash\ncurl -X POST http://localhost:4000/v1/completions \n-H \"Content-Type: application/json\" \n-H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \n-d '{\n \"model\": \"gpt-3.5-turbo-instruct\",\n \"prompt\": \"Once upon a time\",\n \"max_tokens\": 50,\n \"temperature\": 0.7\n}'\n```",
"operationId": "completion_openai_deployments__model__completions_post",
"parameters": [
{
@ -28718,7 +28718,7 @@
},
"/openai/deployments/{model}/embeddings": {
"post": {
"description": "Follows the exact same API spec as `OpenAI's Embeddings API https://platform.openai.com/docs/api-reference/embeddings`\n\n```bash\ncurl -X POST http://localhost:4000/v1/embeddings \n-H \"Content-Type: application/json\" \n-H \"Authorization: Bearer sk-1234\" \n-d '{\n \"model\": \"text-embedding-ada-002\",\n \"input\": \"The quick brown fox jumps over the lazy dog\"\n}'\n```",
"description": "Follows the exact same API spec as `OpenAI's Embeddings API https://platform.openai.com/docs/api-reference/embeddings`\n\n```bash\ncurl -X POST http://localhost:4000/v1/embeddings \n-H \"Content-Type: application/json\" \n-H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \n-d '{\n \"model\": \"text-embedding-ada-002\",\n \"input\": \"The quick brown fox jumps over the lazy dog\"\n}'\n```",
"operationId": "embeddings_openai_deployments__model__embeddings_post",
"parameters": [
{
@ -28771,7 +28771,7 @@
},
"/openai/deployments/{model}/images/edits": {
"post": {
"description": "Follows the OpenAI Images API spec: https://platform.openai.com/docs/api-reference/images/create\n\n```bash\ncurl -s -D >(grep -i x-request-id >&2) -o >(jq -r '.data[0].b64_json' | base64 --decode > gift-basket.png) -X POST \"http://localhost:4000/v1/images/edits\" -H \"Authorization: Bearer sk-1234\" -F \"model=gpt-image-1\" -F \"image[]=@soap.png\" -F 'prompt=Create a studio ghibli image of this'\n```",
"description": "Follows the OpenAI Images API spec: https://platform.openai.com/docs/api-reference/images/create\n\n```bash\ncurl -s -D >(grep -i x-request-id >&2) -o >(jq -r '.data[0].b64_json' | base64 --decode > gift-basket.png) -X POST \"http://localhost:4000/v1/images/edits\" -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" -F \"model=gpt-image-1\" -F \"image[]=@soap.png\" -F 'prompt=Create a studio ghibli image of this'\n```",
"operationId": "image_edit_api_openai_deployments__model__images_edits_post",
"parameters": [
{
@ -28957,7 +28957,7 @@
},
"/openai/v1/responses": {
"post": {
"description": "Follows the OpenAI Responses API spec: https://platform.openai.com/docs/api-reference/responses\n\nSupports background mode with polling_via_cache for partial response retrieval.\nWhen background=true and polling_via_cache is enabled, returns a polling_id immediately\nand streams the response in the background, updating Redis cache.\n\n```bash\n# Normal request\ncurl -X POST http://localhost:4000/v1/responses -H \"Content-Type: application/json\" -H \"Authorization: Bearer sk-1234\" -d '{\n \"model\": \"gpt-4o\",\n \"input\": \"Tell me about AI\"\n}'\n\n# Background request with polling\ncurl -X POST http://localhost:4000/v1/responses -H \"Content-Type: application/json\" -H \"Authorization: Bearer sk-1234\" -d '{\n \"model\": \"gpt-4o\",\n \"input\": \"Tell me about AI\",\n \"background\": true\n}'\n```",
"description": "Follows the OpenAI Responses API spec: https://platform.openai.com/docs/api-reference/responses\n\nSupports background mode with polling_via_cache for partial response retrieval.\nWhen background=true and polling_via_cache is enabled, returns a polling_id immediately\nand streams the response in the background, updating Redis cache.\n\n```bash\n# Normal request\ncurl -X POST http://localhost:4000/v1/responses -H \"Content-Type: application/json\" -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" -d '{\n \"model\": \"gpt-4o\",\n \"input\": \"Tell me about AI\"\n}'\n\n# Background request with polling\ncurl -X POST http://localhost:4000/v1/responses -H \"Content-Type: application/json\" -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" -d '{\n \"model\": \"gpt-4o\",\n \"input\": \"Tell me about AI\",\n \"background\": true\n}'\n```",
"operationId": "responses_api_openai_v1_responses_post",
"responses": {
"200": {
@ -28990,7 +28990,7 @@
},
"/openai/v1/responses/compact": {
"post": {
"description": "Compact a response by running a compaction pass over a conversation.\n\nReturns encrypted, opaque items that can be used to reduce context size.\n\nFollows the OpenAI Responses API spec: https://platform.openai.com/docs/api-reference/responses/compact\n\n```bash\ncurl -X POST http://localhost:4000/v1/responses/compact -H \"Content-Type: application/json\" -H \"Authorization: Bearer sk-1234\" -d '{\n \"model\": \"gpt-4o\",\n \"input\": [{\"role\": \"user\", \"content\": \"Hello\"}]\n}'\n```",
"description": "Compact a response by running a compaction pass over a conversation.\n\nReturns encrypted, opaque items that can be used to reduce context size.\n\nFollows the OpenAI Responses API spec: https://platform.openai.com/docs/api-reference/responses/compact\n\n```bash\ncurl -X POST http://localhost:4000/v1/responses/compact -H \"Content-Type: application/json\" -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" -d '{\n \"model\": \"gpt-4o\",\n \"input\": [{\"role\": \"user\", \"content\": \"Hello\"}]\n}'\n```",
"operationId": "compact_response_openai_v1_responses_compact_post",
"responses": {
"200": {
@ -29015,7 +29015,7 @@
},
"/openai/v1/responses/input_tokens": {
"post": {
"description": "Count the input tokens of a Responses API request without calling the model.\n\nFollows the OpenAI Responses API spec: https://platform.openai.com/docs/api-reference/responses/input-tokens\n\n```bash\ncurl -X POST http://localhost:4000/v1/responses/input_tokens -H \"Content-Type: application/json\" -H \"Authorization: Bearer sk-1234\" -d '{\n \"model\": \"gpt-4o\",\n \"input\": \"Hello, how are you?\"\n}'\n```\n\nReturns: `{\"object\": \"response.input_tokens\", \"input_tokens\": <count>}`",
"description": "Count the input tokens of a Responses API request without calling the model.\n\nFollows the OpenAI Responses API spec: https://platform.openai.com/docs/api-reference/responses/input-tokens\n\n```bash\ncurl -X POST http://localhost:4000/v1/responses/input_tokens -H \"Content-Type: application/json\" -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" -d '{\n \"model\": \"gpt-4o\",\n \"input\": \"Hello, how are you?\"\n}'\n```\n\nReturns: `{\"object\": \"response.input_tokens\", \"input_tokens\": <count>}`",
"operationId": "responses_input_tokens_openai_v1_responses_input_tokens_post",
"responses": {
"200": {
@ -29040,7 +29040,7 @@
},
"/openai/v1/responses/{response_id}": {
"delete": {
"description": "Delete a response by ID.\n\nSupports both:\n- Polling IDs (litellm_poll_*): Deletes from Redis cache\n- Provider response IDs: Passes through to provider API\n\nFollows the OpenAI Responses API spec: https://platform.openai.com/docs/api-reference/responses/delete\n\n```bash\ncurl -X DELETE http://localhost:4000/v1/responses/resp_abc123 -H \"Authorization: Bearer sk-1234\"\n```",
"description": "Delete a response by ID.\n\nSupports both:\n- Polling IDs (litellm_poll_*): Deletes from Redis cache\n- Provider response IDs: Passes through to provider API\n\nFollows the OpenAI Responses API spec: https://platform.openai.com/docs/api-reference/responses/delete\n\n```bash\ncurl -X DELETE http://localhost:4000/v1/responses/resp_abc123 -H \"Authorization: Bearer $LITELLM_MASTER_KEY\"\n```",
"operationId": "delete_response_openai_v1_responses__response_id__delete",
"parameters": [
{
@ -29086,7 +29086,7 @@
]
},
"get": {
"description": "Get a response by ID.\n\nSupports both:\n- Polling IDs (litellm_poll_*): Returns cumulative cached content from background responses\n- Provider response IDs: Passes through to provider API\n\nFollows the OpenAI Responses API spec: https://platform.openai.com/docs/api-reference/responses/get\n\n```bash\n# Get polling response\ncurl -X GET http://localhost:4000/v1/responses/litellm_poll_abc123 -H \"Authorization: Bearer sk-1234\"\n\n# Get provider response\ncurl -X GET http://localhost:4000/v1/responses/resp_abc123 -H \"Authorization: Bearer sk-1234\"\n```",
"description": "Get a response by ID.\n\nSupports both:\n- Polling IDs (litellm_poll_*): Returns cumulative cached content from background responses\n- Provider response IDs: Passes through to provider API\n\nFollows the OpenAI Responses API spec: https://platform.openai.com/docs/api-reference/responses/get\n\n```bash\n# Get polling response\ncurl -X GET http://localhost:4000/v1/responses/litellm_poll_abc123 -H \"Authorization: Bearer $LITELLM_MASTER_KEY\"\n\n# Get provider response\ncurl -X GET http://localhost:4000/v1/responses/resp_abc123 -H \"Authorization: Bearer $LITELLM_MASTER_KEY\"\n```",
"operationId": "get_response_openai_v1_responses__response_id__get",
"parameters": [
{
@ -29134,7 +29134,7 @@
},
"/openai/v1/responses/{response_id}/cancel": {
"post": {
"description": "Cancel a response by ID.\n\nSupports both:\n- Polling IDs (litellm_poll_*): Cancels background response and updates status in Redis\n- Provider response IDs: Passes through to provider API\n\nFollows the OpenAI Responses API spec: https://platform.openai.com/docs/api-reference/responses/cancel\n\n```bash\n# Cancel polling response\ncurl -X POST http://localhost:4000/v1/responses/litellm_poll_abc123/cancel -H \"Authorization: Bearer sk-1234\"\n\n# Cancel provider response\ncurl -X POST http://localhost:4000/v1/responses/resp_abc123/cancel -H \"Authorization: Bearer sk-1234\"\n```",
"description": "Cancel a response by ID.\n\nSupports both:\n- Polling IDs (litellm_poll_*): Cancels background response and updates status in Redis\n- Provider response IDs: Passes through to provider API\n\nFollows the OpenAI Responses API spec: https://platform.openai.com/docs/api-reference/responses/cancel\n\n```bash\n# Cancel polling response\ncurl -X POST http://localhost:4000/v1/responses/litellm_poll_abc123/cancel -H \"Authorization: Bearer $LITELLM_MASTER_KEY\"\n\n# Cancel provider response\ncurl -X POST http://localhost:4000/v1/responses/resp_abc123/cancel -H \"Authorization: Bearer $LITELLM_MASTER_KEY\"\n```",
"operationId": "cancel_response_openai_v1_responses__response_id__cancel_post",
"parameters": [
{
@ -56443,7 +56443,7 @@
"paths": {
"/v1/indexes": {
"get": {
"description": "List all vector store indexes. Proxy admin only.\n\n```bash\ncurl -L -X GET 'http://0.0.0.0:4000/v1/indexes' -H 'Authorization: Bearer sk-1234'\n```",
"description": "List all vector store indexes. Proxy admin only.\n\n```bash\ncurl -L -X GET 'http://0.0.0.0:4000/v1/indexes' -H \"Authorization: Bearer $LITELLM_MASTER_KEY\"\n```",
"operationId": "index_list_v1_indexes_get",
"responses": {
"200": {
@ -56468,7 +56468,7 @@
]
},
"post": {
"description": "Create an index. Just writes the index to the database.\n\n```bash\ncurl -L -X POST 'http://0.0.0.0:4000/v1/indexes' -H 'Content-Type: application/json' -H 'Authorization: Bearer sk-1234' -d '{\n \"index_name\": \"dall-e-3\",\n \"litellm_params\": {\n \"vector_store_index\": \"real-index-name\",\n \"vector_store_name\": \"azure-ai-search\"\n }\n }'\n```",
"description": "Create an index. Just writes the index to the database.\n\n```bash\ncurl -L -X POST 'http://0.0.0.0:4000/v1/indexes' -H 'Content-Type: application/json' -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" -d '{\n \"index_name\": \"dall-e-3\",\n \"litellm_params\": {\n \"vector_store_index\": \"real-index-name\",\n \"vector_store_name\": \"azure-ai-search\"\n }\n }'\n```",
"operationId": "index_create_v1_indexes_post",
"requestBody": {
"content": {

View file

@ -731,7 +731,7 @@ def route_in_additonal_public_routes(current_route: str):
```yaml
general_settings:
master_key: sk-1234
master_key: os.environ/LITELLM_MASTER_KEY
public_routes: ["LiteLLMRoutes.public_routes", "/spend/calculate", "/api/*"]
```
"""

View file

@ -1,4 +1,5 @@
import atexit
import hashlib
import sys
from collections.abc import Awaitable, Callable, Mapping
from dataclasses import dataclass, replace
@ -16,7 +17,9 @@ MASTER_KEY_SETTING: Final = "master_key"
MASTER_KEY_ENV_VAR: Final = "LITELLM_MASTER_KEY"
SALT_KEY_ENV_VAR: Final = "LITELLM_SALT_KEY"
MIGRATE_FROM_MASTER_KEY_ENV_VAR: Final = "LITELLM_MIGRATE_FROM_MASTER_KEY"
PUBLICLY_KNOWN_MASTER_KEYS: Final = frozenset({"sk-1234"})
PUBLICLY_KNOWN_MASTER_KEY_SHA256_DIGESTS: Final = frozenset(
{"88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b"}
)
ROTATION_DOCS_URL: Final = "https://docs.litellm.ai/docs/proxy/master_key_rotations#proxy-refuses-to-start"
_NEW_MASTER_KEY: Final = "sk-$(openssl rand -hex 32)"
GENERATE_MASTER_KEY_COMMAND: Final = f'echo "{MASTER_KEY_ENV_VAR}={_NEW_MASTER_KEY}" | tee -a .env'
@ -203,7 +206,7 @@ def _unsafe_reason(master_key: str | None) -> UnsafeMasterKeyReason | None:
stripped: Final = master_key.strip()
if not stripped:
return UnsafeMasterKeyReason.EMPTY
if stripped in PUBLICLY_KNOWN_MASTER_KEYS:
if hashlib.sha256(stripped.encode()).hexdigest() in PUBLICLY_KNOWN_MASTER_KEY_SHA256_DIGESTS:
return UnsafeMasterKeyReason.PUBLICLY_KNOWN
return None

View file

@ -506,7 +506,7 @@ def _get_bearer_token_or_received_api_key(api_key: str) -> str:
api_key = api_key.replace("bearer ", "")
elif api_key.startswith("AWS4-HMAC-SHA256"):
# Handle AWS Signature V4 format from LangChain
# Format: AWS4-HMAC-SHA256 Credential=Bearer sk-12345/date/region/service/aws4_request, SignedHeaders=..., Signature=...
# Format: AWS4-HMAC-SHA256 Credential=Bearer $LITELLM_MASTER_KEY/date/region/service/aws4_request, SignedHeaders=..., Signature=...
# Extract the Bearer token from the Credential field
match = re.search(r"Credential=Bearer\s+([^/\s,]+)", api_key)
if match:
@ -602,7 +602,7 @@ def _get_bearer_token(
api_key = api_key.replace("bearer ", "")
elif api_key.startswith("AWS4-HMAC-SHA256"):
# Handle AWS Signature V4 format from LangChain
# Format: AWS4-HMAC-SHA256 Credential=Bearer sk-12345/date/region/service/aws4_request, SignedHeaders=..., Signature=...
# Format: AWS4-HMAC-SHA256 Credential=Bearer $LITELLM_MASTER_KEY/date/region/service/aws4_request, SignedHeaders=..., Signature=...
# Extract the Bearer token from the Credential field
match = re.search(r"Credential=Bearer\s+([^/\s,]+)", api_key)
if match:
@ -1572,7 +1572,6 @@ async def _user_api_key_auth_builder(
route=route,
request=request,
)
# if user wants to pass LiteLLM_Master_Key as a custom header, example pass litellm keys as X-LiteLLM-Key: Bearer sk-1234
custom_litellm_key_header_name: Final = general_settings.get("litellm_key_header_name")
if custom_litellm_key_header_name is not None:
api_key = get_api_key_from_custom_header(

View file

@ -244,7 +244,7 @@ async def create_batch(
Example Curl
```
curl http://localhost:4000/v1/batches \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"input_file_id": "file-abc123",
@ -554,7 +554,7 @@ async def retrieve_batch(
Example Curl
```
curl http://localhost:4000/v1/batches/batch_abc123 \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
```
@ -861,7 +861,7 @@ async def list_batches(
Example Curl
```
curl http://localhost:4000/v1/batches?limit=2 \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
```
@ -1045,7 +1045,7 @@ async def cancel_batch(
Example Curl
```
curl http://localhost:4000/v1/batches/batch_abc123/cancel \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-X POST

View file

@ -133,7 +133,7 @@ async def cache_delete(request: Request):
```shell
curl -X POST "http://0.0.0.0:4000/cache/delete" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{"keys": ["key1", "key2"]}'
```
@ -226,7 +226,7 @@ async def cache_flushall():
Usage:
```
curl -X POST http://0.0.0.0:4000/cache/flushall -H "Authorization: Bearer sk-1234"
curl -X POST http://0.0.0.0:4000/cache/flushall -H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
try:

View file

@ -331,7 +331,7 @@ async def get_memory_summary(
- garbage_collector: GC status and pending object counts
Example usage:
curl http://localhost:4000/debug/memory/summary -H "Authorization: Bearer sk-1234"
curl http://localhost:4000/debug/memory/summary -H "Authorization: Bearer $LITELLM_MASTER_KEY"
For detailed analysis, call GET /debug/memory/details
For cache management, use the cache management endpoints
@ -692,7 +692,7 @@ async def get_memory_details(
- include_process_info: Include process-level memory info using psutil (default: true)
Example usage:
curl "http://localhost:4000/debug/memory/details?top_n=30" -H "Authorization: Bearer sk-1234"
curl "http://localhost:4000/debug/memory/details?top_n=30" -H "Authorization: Bearer $LITELLM_MASTER_KEY"
All memory sizes are reported in both bytes and MB.
"""
@ -754,10 +754,10 @@ async def configure_gc_thresholds_endpoint(
- generation_2: Number of gen-1 collections before gen-2 collection (default: 10)
Example for more aggressive collection:
curl -X POST "http://localhost:4000/debug/memory/gc/configure?generation_0=500" -H "Authorization: Bearer sk-1234"
curl -X POST "http://localhost:4000/debug/memory/gc/configure?generation_0=500" -H "Authorization: Bearer $LITELLM_MASTER_KEY"
Example for less aggressive collection:
curl -X POST "http://localhost:4000/debug/memory/gc/configure?generation_0=1000" -H "Authorization: Bearer sk-1234"
curl -X POST "http://localhost:4000/debug/memory/gc/configure?generation_0=1000" -H "Authorization: Bearer $LITELLM_MASTER_KEY"
Monitor memory usage with GET /debug/memory/summary after changes.
"""
@ -796,7 +796,7 @@ async def get_debug_report(
Nothing from the operator's config values, request data, or errors
Example usage:
curl http://localhost:4000/debug/report -H "Authorization: Bearer sk-1234"
curl http://localhost:4000/debug/report -H "Authorization: Bearer $LITELLM_MASTER_KEY"
"""
if not is_proxy_admin(user_api_key_dict):
raise HTTPException(status_code=403, detail="Only proxy admins can read /debug/report")

View file

@ -52,7 +52,7 @@ async def create_container(
Example:
```bash
curl -X POST "http://localhost:4000/v1/containers" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "My Container",
@ -66,7 +66,7 @@ async def create_container(
Or specify provider via header:
```bash
curl -X POST "http://localhost:4000/v1/containers" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "custom-llm-provider: azure" \
-H "Content-Type: application/json" \
-d '{
@ -187,13 +187,13 @@ async def list_containers(
Example:
```bash
curl -X GET "http://localhost:4000/v1/containers?limit=20&order=desc" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Or specify provider via header or query param:
```bash
curl -X GET "http://localhost:4000/v1/containers?custom_llm_provider=azure" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import (
@ -290,13 +290,13 @@ async def retrieve_container(
Example:
```bash
curl -X GET "http://localhost:4000/v1/containers/cntr_123" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Or specify provider via header:
```bash
curl -X GET "http://localhost:4000/v1/containers/cntr_123" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "custom-llm-provider: azure"
```
"""
@ -396,13 +396,13 @@ async def delete_container(
Example:
```bash
curl -X DELETE "http://localhost:4000/v1/containers/cntr_123" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Or specify provider via header:
```bash
curl -X DELETE "http://localhost:4000/v1/containers/cntr_123" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "custom-llm-provider: azure"
```
"""

View file

@ -87,7 +87,7 @@ async def create_fine_tuning_job(
```
curl http://localhost:4000/v1/fine_tuning/jobs \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"model": "gpt-3.5-turbo",
"training_file": "file-abc123",

View file

@ -246,7 +246,7 @@ async def create_interaction(
Example:
```bash
curl -X POST "http://localhost:4000/v1beta/interactions" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini/gemini-2.5-flash",

View file

@ -298,7 +298,7 @@ async def health_services_endpoint(
Example:
```
curl -L -X GET 'http://0.0.0.0:4000/health/services?service=datadog' \
-H 'Authorization: Bearer sk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
try:
@ -2086,7 +2086,7 @@ async def test_model_connection(
```bash
# If model is configured in proxy_config.yaml, you only need to specify the model name:
curl -X POST 'http://localhost:4000/health/test_connection' \\
-H 'Authorization: Bearer sk-1234' \\
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \\
-H 'Content-Type: application/json' \\
-d '{
"litellm_params": {
@ -2099,7 +2099,7 @@ async def test_model_connection(
# You can also override specific params or test with custom credentials:
curl -X POST 'http://localhost:4000/health/test_connection' \\
-H 'Authorization: Bearer sk-1234' \\
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \\
-H 'Content-Type: application/json' \\
-d '{
"litellm_params": {

View file

@ -292,7 +292,7 @@ class KeyManagementEventHooks:
Args:
secret_name: Name of the virtual key
secret_token: Value of the virtual key (example: sk-1234)
secret_token: Value of the virtual key (example: $LITELLM_API_KEY)
"""
if litellm._key_management_settings is not None:
if litellm._key_management_settings.store_virtual_keys is True:
@ -330,7 +330,7 @@ class KeyManagementEventHooks:
Args:
current_secret_name: Current name of the virtual key
new_secret_name: New name of the virtual key
new_secret_value: New value of the virtual key (example: sk-1234)
new_secret_value: New value of the virtual key (example: $LITELLM_API_KEY)
team_id: Optional team ID to get team-specific secret manager settings
"""
secret_manager: Final = KeyManagementEventHooks._stored_virtual_key_secret_manager()

View file

@ -294,7 +294,7 @@ class _PROXY_VirtualKeyModelMaxBudgetLimiter(RouterBudgetLimiting):
"""
Handles budgets for model + virtual key
Example: key=sk-1234567890, model=gpt-4o, max_budget=100, time_period=1d
Example: key=$LITELLM_API_KEY, model=gpt-4o, max_budget=100, time_period=1d
"""
def __init__(self, dual_cache: DualCache):
@ -497,7 +497,7 @@ class _PROXY_VirtualKeyModelMaxBudgetLimiter(RouterBudgetLimiting):
"""
Track spend for virtual key + model in DualCache
Example: key=sk-1234567890, model=gpt-4o, max_budget=100, time_period=1d
Example: key=$LITELLM_API_KEY, model=gpt-4o, max_budget=100, time_period=1d
"""
verbose_proxy_logger.debug("in RouterBudgetLimiting.async_log_success_event")
standard_logging_payload: Final[StandardLoggingPayload | None] = kwargs.get("standard_logging_object", None)

View file

@ -259,7 +259,7 @@ async def image_edit_api(
curl -s -D >(grep -i x-request-id >&2) \
-o >(jq -r '.data[0].b64_json' | base64 --decode > gift-basket.png) \
-X POST "http://localhost:4000/v1/images/edits" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-F "model=gpt-image-1" \
-F "image[]=@soap.png" \
-F 'prompt=Create a studio ghibli image of this'

View file

@ -170,7 +170,7 @@ async def block_user(data: BlockUsers):
```
curl -X POST "http://0.0.0.0:8000/user/block"
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
-d '{
"user_ids": [<user_id>, ...]
}'
@ -222,7 +222,7 @@ async def unblock_user(data: BlockUsers):
Example
```
curl -X POST "http://0.0.0.0:8000/user/unblock"
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
-d '{
"user_ids": [<user_id>, ...]
}'
@ -375,7 +375,7 @@ async def new_end_user(
Example curl:
```
curl --location 'http://0.0.0.0:4000/customer/new' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"user_id" : "ishaan-jaff-3",
@ -387,7 +387,7 @@ async def new_end_user(
# With object permissions
curl -L -X POST 'http://localhost:4000/customer/new' \
-H 'Authorization: Bearer sk-1234' \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H 'Content-Type: application/json' \
-d '{
"user_id": "user_1",
@ -558,7 +558,7 @@ async def end_user_info(
Example curl:
```
curl -X GET 'http://localhost:4000/customer/info?end_user_id=test-litellm-user-4' \
-H 'Authorization: Bearer sk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
try:
@ -638,7 +638,7 @@ async def update_end_user(
Example curl:
```
curl --location 'http://0.0.0.0:4000/customer/update' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"user_id": "test-litellm-user-4",
@ -648,7 +648,7 @@ async def update_end_user(
# Updating object permissions
curl -L -X POST 'http://localhost:4000/customer/update' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"user_id": "user_1",
@ -797,7 +797,7 @@ async def delete_end_user(
Example curl:
```
curl --location 'http://0.0.0.0:4000/customer/delete' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"user_ids" :["ishaan-jaff-5"]
@ -872,7 +872,7 @@ async def list_end_user(
Example curl:
```
curl --location --request GET 'http://0.0.0.0:4000/customer/list' \
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""

View file

@ -543,7 +543,7 @@ async def new_user(
```shell
curl -X POST "http://localhost:4000/user/new" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"username": "new_user",
"email": "new_user@example.com"
@ -952,7 +952,7 @@ async def user_info(
Example request
```
curl -X GET 'http://localhost:4000/user/info?user_id=krrish7%40berri.ai' \
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import model_max_budget_limiter, prisma_client
@ -1105,7 +1105,7 @@ async def user_info_v2(
Example request:
```
curl -X GET 'http://localhost:4000/v2/user/info?user_id=user123' \\
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import model_max_budget_limiter, prisma_client
@ -1685,7 +1685,7 @@ async def user_update(
```
curl --location 'http://0.0.0.0:4000/user/update' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"user_id": "test-litellm-user-4",
@ -1873,7 +1873,7 @@ async def bulk_user_update(
Example request for specific users:
```bash
curl --location 'http://0.0.0.0:4000/user/bulk_update' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"users": [
@ -1894,7 +1894,7 @@ async def bulk_user_update(
Example request for all users:
```bash
curl --location 'http://0.0.0.0:4000/user/bulk_update' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"all_users": true,
@ -2415,7 +2415,7 @@ async def delete_user(
```
curl --location 'http://0.0.0.0:4000/user/delete' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \

View file

@ -2028,7 +2028,7 @@ async def generate_key_fn(
```bash
curl --location 'http://0.0.0.0:4000/key/generate' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"permissions": {"allow_pii_controls": true}
@ -2226,7 +2226,7 @@ async def generate_service_account_key_fn(
```bash
curl --location 'http://0.0.0.0:4000/key/generate' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"permissions": {"allow_pii_controls": true}
@ -3440,10 +3440,10 @@ async def update_key_fn(
Example:
```bash
curl --location 'http://0.0.0.0:4000/key/update' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"key": "sk-1234",
"key": "sk-<your-virtual-key>",
"key_alias": "my-key",
"user_id": "user-1234",
"team_id": "team-1234",
@ -3666,12 +3666,12 @@ async def bulk_update_keys(
Example request:
```bash
curl --location 'http://0.0.0.0:4000/key/bulk_update' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"keys": [
{
"key": "sk-1234",
"key": "sk-<your-virtual-key>",
"max_budget": 100.0,
"team_id": "team-123",
"tags": ["production", "api"]
@ -4097,7 +4097,7 @@ async def delete_key_fn(
Example:
```bash
curl --location 'http://0.0.0.0:4000/key/delete' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"keys": ["sk-QWrxEynunsNpV1zT48HIrw"]
@ -4276,7 +4276,7 @@ async def info_key_fn_v2(
Example Curl:
```
curl -X GET "http://0.0.0.0:4000/key/info" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d {"keys": ["sk-1", "sk-2", "sk-3"]}
```
"""
@ -4405,7 +4405,7 @@ async def info_key_fn(
Example Curl:
```
curl -X GET "http://0.0.0.0:4000/key/info?key=d5345c0ecc68ae6295c69f91926b2bd379e25481a40c34b5884d157a9f65d8fa" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Example Curl - if no key is passed, it will use the Key Passed in Authorization Header
@ -5795,8 +5795,8 @@ async def regenerate_key_fn(
Example:
```bash
curl --location --request POST 'http://localhost:4000/key/sk-1234/regenerate' \
--header 'Authorization: Bearer sk-1234' \
curl --location --request POST "http://localhost:4000/key/$LITELLM_API_KEY/regenerate" \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data-raw '{
"max_budget": 100,
@ -7329,7 +7329,7 @@ async def block_key(
Example:
```bash
curl --location 'http://0.0.0.0:4000/key/block' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"key": "sk-Fn8Ej39NxjAXrvpUGKghGw"
@ -7443,7 +7443,7 @@ async def unblock_key(
Example:
```bash
curl --location 'http://0.0.0.0:4000/key/unblock' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"key": "sk-Fn8Ej39NxjAXrvpUGKghGw"
@ -7560,7 +7560,7 @@ async def key_health(
```bash
curl -X POST "http://localhost:4000/key/health" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json"
```

View file

@ -164,7 +164,7 @@ async def list_budgets(
Example curl:
```
curl --location --globoff 'http://0.0.0.0:4000/management/v1/budgets?sort=-max_budget&filter[budget_duration][in]=7d,30d&page_size=25' \
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
try:

View file

@ -188,7 +188,7 @@ async def list_spend_log_end_users(
Example curl:
```
curl --location --globoff 'http://0.0.0.0:4000/management/v1/spend_logs/end_users?filter[startTime][gte]=2026-07-23T00:00:00Z&filter[startTime][lte]=2026-07-24T00:00:00Z&page_size=50&q=acme' \
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
return await _list_spend_log_facet(

View file

@ -51,7 +51,7 @@ async def bulk_delete_team_members_action(
Example curl:
```
curl --location 'http://0.0.0.0:4000/management/v1/teams/team-1/members/bulk_delete' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{"members": [{"user_id": "user-1"}, {"user_email": "user-2@example.com"}]}'
```
@ -135,7 +135,7 @@ async def bulk_update_team_member_budgets_action(
Example curl:
```
curl --location 'http://0.0.0.0:4000/management/v1/teams/team-1/members/bulk_update' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{"members": [{"user_id": "user-1", "max_budget_in_team": 10}, {"user_email": "user-2@example.com", "max_budget_in_team": 10, "budget_duration": "30d"}]}'
```

View file

@ -55,7 +55,7 @@ async def bulk_create_users_route(
```
curl -X POST "http://localhost:4000/management/v1/users/bulk" \\
-H "Content-Type: application/json" \\
-H "Authorization: Bearer sk-1234" \\
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \\
-d '{
"users": [
{"user_email": "a@example.com", "user_role": "internal_user", "teams": ["team-1"]},
@ -136,7 +136,7 @@ async def bulk_delete_users_action(
Example curl:
```
curl --location 'http://0.0.0.0:4000/management/v1/users/bulk_delete' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{"user_ids": ["user-1", "user-2"]}'
```

View file

@ -584,7 +584,7 @@ async def create_model_group(
Example:
```bash
curl -X POST 'http://localhost:4000/access_group/new' \\
-H 'Authorization: Bearer sk-1234' \\
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \\
-H 'Content-Type: application/json' \\
-d '{
"access_group": "production-models",
@ -729,7 +729,7 @@ async def list_access_groups(
Example:
```bash
curl -X GET 'http://localhost:4000/access_group/list' \\
-H 'Authorization: Bearer sk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Returns:
@ -777,7 +777,7 @@ async def get_access_group_info(
Example:
```bash
curl -X GET 'http://localhost:4000/access_group/production-models/info' \\
-H 'Authorization: Bearer sk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Parameters:
@ -841,7 +841,7 @@ async def update_access_group(
Example:
```bash
curl -X PUT 'http://localhost:4000/access_group/production-models/update' \\
-H 'Authorization: Bearer sk-1234' \\
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \\
-H 'Content-Type: application/json' \\
-d '{
"model_names": ["gpt-4", "claude-3-sonnet"]
@ -993,7 +993,7 @@ async def delete_access_group(
Example:
```bash
curl -X DELETE 'http://localhost:4000/access_group/production-models/delete' \\
-H 'Authorization: Bearer sk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Parameters:
@ -1099,7 +1099,7 @@ async def get_access_group_budget(
Example:
```bash
curl -X GET 'http://localhost:4000/access_group/production-models/budget' \\
-H 'Authorization: Bearer sk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Parameters:
@ -1139,7 +1139,7 @@ async def set_access_group_budget(
Example:
```bash
curl -X PUT 'http://localhost:4000/access_group/production-models/budget' \\
-H 'Authorization: Bearer sk-1234' \\
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \\
-H 'Content-Type: application/json' \\
-d '{
"max_budget": 100.0,
@ -1213,7 +1213,7 @@ async def delete_access_group_budget(
Example:
```bash
curl -X DELETE 'http://localhost:4000/access_group/production-models/budget' \\
-H 'Authorization: Bearer sk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Parameters:

View file

@ -404,7 +404,7 @@ async def new_organization(
```bash
curl --location 'http://0.0.0.0:4000/organization/new' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
@ -422,7 +422,7 @@ async def new_organization(
```bash
curl --location 'http://0.0.0.0:4000/organization/new' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
@ -1111,13 +1111,13 @@ async def list_organization(
Example:
```
curl --location --request GET 'http://0.0.0.0:4000/organization/list?org_alias=my-org' \
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Example with org_id:
```
curl --location --request GET 'http://0.0.0.0:4000/organization/list?org_id=123e4567-e89b-12d3-a456-426614174000' \
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import prisma_client
@ -1311,7 +1311,7 @@ async def organization_member_add(
Example:
```
curl -X POST 'http://0.0.0.0:4000/organization/member_add' \
-H 'Authorization: Bearer sk-1234' \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H 'Content-Type: application/json' \
-d '{
"organization_id": "45e3e396-ee08-4a61-a88e-16b3ce7e0849",

View file

@ -295,7 +295,7 @@ async def add_team_callbacks(
```
curl -X POST 'http:/localhost:4000/team/dbe2f686-a686-4896-864a-4c3924458709/callback' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer sk-1234' \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"callback_name": "langfuse",
"callback_type": "success",
@ -468,7 +468,7 @@ async def delete_team_callback(
Example curl:
```
curl -X DELETE 'http://localhost:4000/team/dbe2f686-a686-4896-864a-4c3924458709/callback/langsmith' \
-H 'Authorization: Bearer sk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Covers callbacks registered through POST /team/{team_id}/callback and the Admin UI. Teams still
@ -604,7 +604,7 @@ async def disable_team_logging(
Example curl:
```
curl -X POST 'http://localhost:4000/team/dbe2f686-a686-4896-864a-4c3924458709/disable_logging' \
-H 'Authorization: Bearer sk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
@ -736,7 +736,7 @@ async def get_team_callbacks(
Example curl:
```
curl -X GET 'http://localhost:4000/team/dbe2f686-a686-4896-864a-4c3924458709/callback' \
-H 'Authorization: Bearer sk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
This will return the callback settings for the team with id dbe2f686-a686-4896-864a-4c3924458709

View file

@ -1407,7 +1407,7 @@ async def new_team(
Example Request:
```
curl --location 'http://0.0.0.0:4000/team/new' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"team_alias": "my-new-team_2",
@ -1419,7 +1419,7 @@ async def new_team(
```
curl --location 'http://0.0.0.0:4000/team/new' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"team_alias": "QA Prod Bot",
@ -2167,7 +2167,7 @@ async def update_team(
```
curl --location 'http://0.0.0.0:4000/team/update' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data-raw '{
"team_id": "8d916b1c-510d-4894-a334-1c16a93344f5",
@ -2178,7 +2178,7 @@ async def update_team(
Example - Update Team `max_budget` budget
```
curl --location 'http://0.0.0.0:4000/team/update' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data-raw '{
"team_id": "8d916b1c-510d-4894-a334-1c16a93344f5",
@ -2597,7 +2597,7 @@ async def patch_team(
```
curl --location --request PATCH 'http://0.0.0.0:4000/team/8d916b1c-510d-4894-a334-1c16a93344f5' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data-raw '{
"metadata": {"cost_center": "1234", "deprecated_key": null}
@ -3368,7 +3368,7 @@ async def team_member_add(
```
curl -X POST 'http://0.0.0.0:4000/team/member_add' \
-H 'Authorization: Bearer sk-1234' \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H 'Content-Type: application/json' \
-d '{"team_id": "45e3e396-ee08-4a61-a88e-16b3ce7e0849", "member": {"role": "user", "user_id": "krrish247652@berri.ai"}}'
@ -3549,7 +3549,7 @@ async def team_member_delete(
```
curl -X POST 'http://0.0.0.0:8000/team/member_delete' \
-H 'Authorization: Bearer sk-1234' \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H 'Content-Type: application/json' \
@ -4196,7 +4196,7 @@ async def bulk_team_member_add(
Example request:
```bash
curl --location 'http://0.0.0.0:4000/team/bulk_member_add' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"team_id": "team-1234",
@ -4329,7 +4329,7 @@ async def delete_team(
```
curl --location 'http://0.0.0.0:4000/team/delete' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data-raw '{
"team_ids": ["8d916b1c-510d-4894-a334-1c16a93344f5"]
@ -5141,7 +5141,7 @@ async def block_team(
Example:
```
curl --location 'http://0.0.0.0:4000/team/block' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"team_id": "team-1234"
@ -5196,7 +5196,7 @@ async def unblock_team(
Example:
```
curl --location 'http://0.0.0.0:4000/team/unblock' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"team_id": "team-1234"
@ -5867,7 +5867,7 @@ async def list_team(
"""
```
curl --location --request GET 'http://0.0.0.0:4000/team/list' \
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Parameters:
@ -6071,7 +6071,7 @@ async def team_model_add(
Example Request:
```
curl --location 'http://0.0.0.0:4000/team/model/add' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"team_id": "team-1234",
@ -6187,7 +6187,7 @@ async def team_model_delete(
Example Request:
```
curl --location 'http://0.0.0.0:4000/team/model/delete' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--header 'Content-Type: application/json' \
--data '{
"team_id": "team-1234",

View file

@ -272,7 +272,7 @@ async def ocr(
**1. JSON body** (Mistral OCR API compatible):
```bash
curl -X POST "http://localhost:4000/v1/ocr" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "mistral-ocr",
@ -286,7 +286,7 @@ async def ocr(
**2. Multipart form file upload**:
```bash
curl -X POST "http://localhost:4000/v1/ocr" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-F "model=mistral-ocr" \
-F "file=@document.pdf"
```

View file

@ -569,7 +569,7 @@ async def create_file(
Example Curl
```
curl http://localhost:4000/v1/files \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-F purpose="batch" \
-F file="@mydata.jsonl"
-F expires_after[anchor]="created_at" \
@ -970,7 +970,7 @@ async def get_file_content(
Example Curl
```
curl http://localhost:4000/v1/files/file-abc123/content \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
@ -1282,7 +1282,7 @@ async def get_file(
Example Curl
```
curl http://localhost:4000/v1/files/file-abc123 \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
@ -1708,7 +1708,7 @@ async def list_files(
Example Curl
```
curl http://localhost:4000/v1/files\
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""

View file

@ -12065,7 +12065,7 @@ async def chat_completion(
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"model": "gpt-4o",
@ -12229,7 +12229,7 @@ async def completion(
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"model": "gpt-3.5-turbo-instruct",
@ -12413,7 +12413,7 @@ async def embeddings(
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"model": "text-embedding-ada-002",
@ -12521,7 +12521,7 @@ async def moderations(
```
curl --location 'http://0.0.0.0:4000/moderations' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer sk-1234' \
--header "Authorization: Bearer $LITELLM_MASTER_KEY" \
--data '{"input": "Sample text goes here", "model": "text-moderation-stable"}'
```
"""
@ -14175,7 +14175,7 @@ async def supported_openai_params(model: str):
Example curl:
```
curl -X GET --location 'http://localhost:4000/utils/supported_openai_params?model=gpt-3.5-turbo-16k' \
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.litellm_core_utils.get_llm_provider_logic import declared_authenticating_provider
@ -14220,7 +14220,7 @@ async def model_info_lookup(model: str, custom_llm_provider: str | None = None):
Example curl:
```
curl -X GET --location 'http://localhost:4000/utils/model_info?model=gpt-4o&custom_llm_provider=openai' \
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
detail: Final = {"error": f"model={model}, custom_llm_provider={custom_llm_provider} is not in the model cost map"}
@ -15464,7 +15464,7 @@ async def model_info_v2(
Example request:
```
curl -X GET 'http://localhost:4000/v2/model/info?include_team_models=true&page=1&size=50' \\
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Example response:
@ -16414,7 +16414,7 @@ async def model_deprecations(
Example:
```shell
curl -X GET 'http://localhost:4000/model/deprecations' \\
-H 'Authorization: Bearer sk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
return collect_model_deprecations(llm_router=llm_router, warn_within_days=warn_within_days)
@ -16476,7 +16476,7 @@ async def model_group_info(
curl -X 'GET' \
'http://localhost:4000/model_group/info' \
-H 'accept: application/json' \
-H 'x-api-key: sk-1234'
-H "x-api-key: $LITELLM_MASTER_KEY"
```
Example Request (Specific Model Group):
@ -16484,7 +16484,7 @@ async def model_group_info(
curl -X 'GET' \
'http://localhost:4000/model_group/info?model_group=rerank-english-v3.0' \
-H 'accept: application/json' \
-H 'Authorization: Bearer sk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Example Request (Specific Wildcard Model Group): (e.g. `model_name: openai/*` on config.yaml)
@ -16492,7 +16492,7 @@ async def model_group_info(
curl -X 'GET' \
'http://localhost:4000/model_group/info?model_group=openai/tts-1'
-H 'accept: application/json' \
-H 'Authorization: Bearersk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Learn how to use and set wildcard models [here](https://docs.litellm.ai/docs/wildcard_routing)

View file

@ -550,7 +550,7 @@ async def rag_ingest(
## Form upload (for files):
```bash
curl -X POST "http://localhost:4000/v1/rag/ingest" \\
-H "Authorization: Bearer sk-1234" \\
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \\
-F file="@document.pdf" \\
-F 'ingest_options={"vector_store": {"custom_llm_provider": "openai"}}'
```
@ -558,7 +558,7 @@ async def rag_ingest(
## JSON body (for URLs):
```bash
curl -X POST "http://localhost:4000/v1/rag/ingest" \\
-H "Authorization: Bearer sk-1234" \\
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"file_url": "https://example.com/document.pdf",
@ -569,7 +569,7 @@ async def rag_ingest(
## Bedrock:
```bash
curl -X POST "http://localhost:4000/v1/rag/ingest" \\
-H "Authorization: Bearer sk-1234" \\
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \\
-F file="@document.pdf" \\
-F 'ingest_options={"vector_store": {"custom_llm_provider": "bedrock"}}'
```
@ -725,7 +725,7 @@ async def rag_query(
## Example Request:
```bash
curl -X POST "http://localhost:4000/v1/rag/query" \\
-H "Authorization: Bearer sk-1234" \\
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"model": "gpt-4o-mini",
@ -741,7 +741,7 @@ async def rag_query(
## With Reranking:
```bash
curl -X POST "http://localhost:4000/v1/rag/query" \\
-H "Authorization: Bearer sk-1234" \\
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"model": "gpt-4o-mini",

View file

@ -226,7 +226,7 @@ async def responses_api(
# Normal request
curl -X POST http://localhost:4000/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"model": "gpt-4o",
"input": "Tell me about AI"
@ -235,7 +235,7 @@ async def responses_api(
# Background request with polling
curl -X POST http://localhost:4000/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"model": "gpt-4o",
"input": "Tell me about AI",
@ -518,7 +518,7 @@ async def cursor_chat_completions(
```bash
curl -X POST http://localhost:4000/cursor/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"model": "gpt-4o",
"input": [{"role": "user", "content": "Hello"}]
@ -711,11 +711,11 @@ async def get_response(
```bash
# Get polling response
curl -X GET http://localhost:4000/v1/responses/litellm_poll_abc123 \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
# Get provider response
curl -X GET http://localhost:4000/v1/responses/resp_abc123 \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import (
@ -826,7 +826,7 @@ async def delete_response(
```bash
curl -X DELETE http://localhost:4000/v1/responses/resp_abc123 \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import (
@ -1001,7 +1001,7 @@ async def compact_response(
```bash
curl -X POST http://localhost:4000/v1/responses/compact \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"model": "gpt-4o",
"input": [{"role": "user", "content": "Hello"}]
@ -1153,7 +1153,7 @@ async def responses_input_tokens(
```bash
curl -X POST http://localhost:4000/v1/responses/input_tokens \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"model": "gpt-4o",
"input": "Hello, how are you?"
@ -1232,11 +1232,11 @@ async def cancel_response(
```bash
# Cancel polling response
curl -X POST http://localhost:4000/v1/responses/litellm_poll_abc123/cancel \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
# Cancel provider response
curl -X POST http://localhost:4000/v1/responses/resp_abc123/cancel \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import (

View file

@ -58,7 +58,7 @@ async def search(
Example with search_tool_name in URL (recommended - keeps body Perplexity-compatible):
```bash
curl -X POST "http://localhost:4000/v1/search/litellm-search" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "latest AI developments 2024",
@ -71,7 +71,7 @@ async def search(
Example with search_tool_name in body:
```bash
curl -X POST "http://localhost:4000/v1/search" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"search_tool_name": "litellm-search",
@ -270,7 +270,7 @@ async def list_search_tools(
Example:
```bash
curl -X GET "http://localhost:4000/v1/search/tools" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Response:

View file

@ -437,7 +437,7 @@ async def spend_key_fn(
Example Request:
```
curl -X GET "http://0.0.0.0:8000/spend/keys" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
@ -505,13 +505,13 @@ async def spend_user_fn(
Example Request:
```
curl -X GET "http://0.0.0.0:8000/spend/users" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
View User Table row for user_id
```
curl -X GET "http://0.0.0.0:8000/spend/users?user_id=1234" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import prisma_client
@ -576,13 +576,13 @@ async def view_spend_tags(
Example Request:
```
curl -X GET "http://0.0.0.0:8000/spend/tags" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Spend with Start Date and End Date
```
curl -X GET "http://0.0.0.0:8000/spend/tags?start_date=2022-01-01&end_date=2022-02-01" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
@ -1238,7 +1238,7 @@ async def get_spend_capture_rate(
Example:
```
curl -H "Authorization: Bearer sk-1234" \
curl -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
"http://localhost:4000/spend/capture_rate?provider=openai&start_date=2026-09-17&end_date=2026-09-23"
```
"""
@ -2158,13 +2158,13 @@ async def global_view_spend_tags(
Example Request:
```
curl -X GET "http://0.0.0.0:4000/spend/tags" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Spend with Start Date and End Date
```
curl -X GET "http://0.0.0.0:4000/spend/tags?start_date=2022-01-01&end_date=2022-02-01" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
import traceback
@ -2308,7 +2308,7 @@ async def calculate_spend(request: SpendCalculateRequest):
```
curl --location 'http://localhost:4000/spend/calculate'
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
--header 'Content-Type: application/json'
--data '{
"model": "anthropic.claude-v2",
@ -2320,7 +2320,7 @@ async def calculate_spend(request: SpendCalculateRequest):
```
curl --location 'http://localhost:4000/spend/calculate'
--header 'Authorization: Bearer sk-1234'
--header "Authorization: Bearer $LITELLM_MASTER_KEY"
--header 'Content-Type: application/json'
--data '{
"completion_response": {
@ -2594,7 +2594,7 @@ async def ui_view_spend_logs(
Example:
```
curl -X GET "http://0.0.0.0:8000/spend/logs/v2?start_date=2025-11-25%2000:00:00&end_date=2025-11-26%2023:59:59&page=1&page_size=50" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import prisma_client
@ -3506,31 +3506,31 @@ async def view_spend_logs(
Example Request for all logs
```
curl -X GET "http://0.0.0.0:8000/spend/logs" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Example Request for specific request_id
```
curl -X GET "http://0.0.0.0:8000/spend/logs?request_id=chatcmpl-6dcb2540-d3d7-4e49-bb27-291f863f112e" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Example Request for specific api_key
```
curl -X GET "http://0.0.0.0:8000/spend/logs?api_key=d5345c0ecc68ae6295c69f91926b2bd379e25481a40c34b5884d157a9f65d8fa" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Example Request for specific user_id
```
curl -X GET "http://0.0.0.0:8000/spend/logs?user_id=ishaan@berri.ai" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Example Request for date range with individual logs (unsummarized)
```
curl -X GET "http://0.0.0.0:8000/spend/logs?start_date=2024-01-01&end_date=2024-01-02&summarize=false" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import prisma_client
@ -4304,7 +4304,7 @@ async def provider_budgets() -> ProviderBudgetResponse:
```bash
curl -X GET http://localhost:4000/provider/budgets \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
Example Response

View file

@ -561,7 +561,7 @@ async def index_create(
```bash
curl -L -X POST 'http://0.0.0.0:4000/v1/indexes' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer sk-1234' \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"index_name": "dall-e-3",
"litellm_params": {
@ -622,7 +622,7 @@ async def index_list(
```bash
curl -L -X GET 'http://0.0.0.0:4000/v1/indexes' \
-H 'Authorization: Bearer sk-1234'
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import prisma_client

View file

@ -57,7 +57,7 @@ async def video_generation(
Example:
```bash
curl -X POST "http://localhost:4000/v1/videos" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "sora-2",
@ -144,7 +144,7 @@ async def video_list(
Example:
```bash
curl -X GET "http://localhost:4000/v1/videos" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import (
@ -232,7 +232,7 @@ async def video_status(
Example:
```bash
curl -X GET "http://localhost:4000/v1/videos/video_123" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import (
@ -332,7 +332,7 @@ async def video_content(
Example:
```bash
curl -X GET "http://localhost:4000/v1/videos/{video_id}/content" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
--output video.mp4
```
"""
@ -437,7 +437,7 @@ async def video_remix(
Example:
```bash
curl -X POST "http://localhost:4000/v1/videos/video_123/remix" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A new version with different colors"
@ -541,7 +541,7 @@ async def video_create_character(
Example:
```bash
curl -X POST "http://localhost:4000/v1/videos/characters" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-F "video=@character_video.mp4" \
-F "name=my_character"
```
@ -643,7 +643,7 @@ async def video_get_character(
Example:
```bash
curl -X GET "http://localhost:4000/v1/videos/characters/char_123" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
"""
from litellm.proxy.proxy_server import (
@ -748,7 +748,7 @@ async def video_edit(
Example:
```bash
curl -X POST "http://localhost:4000/v1/videos/edits" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "Make it brighter", "video": {"id": "video_123"}}'
```
@ -852,7 +852,7 @@ async def video_extension(
Example:
```bash
curl -X POST "http://localhost:4000/v1/videos/extensions" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "Continue the scene", "seconds": "5", "video": {"id": "video_123"}}'
```

View file

@ -1,14 +1,14 @@
#!/usr/bin/env bash
# QA: code interpreter sandbox stickiness via metadata.session_id
# bash qa_sticky_session.sh
# LITELLM_BASE_URL=http://localhost:4000 LITELLM_KEY=sk-1234 bash qa_sticky_session.sh
# LITELLM_BASE_URL=http://localhost:4000 LITELLM_KEY="$LITELLM_MASTER_KEY" bash qa_sticky_session.sh
set -euo pipefail
BASE="${LITELLM_BASE_URL:-http://localhost:4000}"
KEY="${LITELLM_KEY:-sk-1234}"
KEY="${LITELLM_KEY:-${LITELLM_MASTER_KEY:?set LITELLM_KEY or LITELLM_MASTER_KEY}}"
MODEL="${LITELLM_MODEL:-gpt-4o-mini}"
# proxy running at http://localhost:4000 (master key: sk-1234)
# proxy running at http://localhost:4000
SESSION_A="qa-session-$(date +%s)-A"
SESSION_B="qa-session-$(date +%s)-B"

View file

@ -366,7 +366,7 @@
<div class="connect">
<label>Proxy URL <input id="proxy-url" type="text" value="http://localhost:4000" /></label>
<label>API Key <input id="api-key" type="password" placeholder="sk-1234" /></label>
<label>API Key <input id="api-key" type="password" placeholder="LITELLM_MASTER_KEY" /></label>
<label>Router
<input id="router" type="text" value="smart-cheap-router" style="width:160px" />
</label>

View file

@ -255,7 +255,7 @@
<div class="connect">
<label>Proxy URL <input id="proxy-url" type="text" value="http://localhost:4000" /></label>
<label>Master Key <input id="api-key" type="password" placeholder="sk-1234" /></label>
<label>Master Key <input id="api-key" type="password" placeholder="LITELLM_MASTER_KEY" /></label>
<label>Avg tokens/req <input id="avg-tokens" type="number" value="500" min="1" /></label>
<label>Poll ms <input id="poll-ms" type="number" value="500" min="100" /></label>
<button id="connect-btn">Connect</button>

View file

@ -11,7 +11,7 @@ For each test case:
Run:
uv run python scripts/adaptive_router_demo/eval.py \
--proxy-url http://localhost:4000 \
--api-key sk-1234 \
--api-key "$LITELLM_MASTER_KEY" \
--router smart-cheap-router \
--judge-model smart
"""

View file

@ -23,7 +23,7 @@ Why this shape:
Run:
uv run python scripts/adaptive_router_demo/traffic.py \\
--proxy-url http://localhost:4000 \\
--api-key sk-1234 \\
--api-key "$LITELLM_MASTER_KEY" \\
--router smart-cheap-router \\
--rounds 100 \\
--rate 0.5

View file

@ -35,13 +35,12 @@ import tempfile
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Optional
from typing import Any, Final, Optional
import aiohttp
from aiohttp import web
DEFAULT_MODEL = "claude-perf-test"
DEFAULT_API_KEY = "sk-1234"
@dataclass
@ -490,7 +489,7 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("--proxy-port", type=int, default=4000)
parser.add_argument("--provider-host", default="127.0.0.1")
parser.add_argument("--provider-port", type=int, default=8098)
parser.add_argument("--api-key", default=DEFAULT_API_KEY)
parser.add_argument("--api-key", default=os.environ.get("LITELLM_MASTER_KEY"))
parser.add_argument("--requests", type=int, default=300)
parser.add_argument("--concurrency", type=int, default=20)
parser.add_argument("--warmup", type=int, default=30)
@ -524,12 +523,23 @@ def parse_args() -> argparse.Namespace:
async def async_main() -> None:
args = parse_args()
if args.no_start_proxy and not args.api_key:
raise ValueError("Set LITELLM_MASTER_KEY or pass --api-key when using --no-start-proxy")
api_key: Final = args.api_key or (
"sk-"
+ subprocess.run(
["openssl", "rand", "-hex", "16"],
capture_output=True,
check=True,
text=True,
).stdout.strip()
)
litellm_dir = Path(args.litellm_dir).resolve()
revision = get_git_revision(litellm_dir)
proxy_base_url = f"http://{args.proxy_host}:{args.proxy_port}"
proxy_url = f"{proxy_base_url}/v1/messages"
headers = {
"Authorization": f"Bearer {args.api_key}",
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
stream_payload = {
@ -557,7 +567,7 @@ async def async_main() -> None:
provider_base_url = provider.base_url
config_path = tmp_dir / "config.yaml"
write_proxy_config(config_path, provider_base_url, args.api_key)
write_proxy_config(config_path, provider_base_url, api_key)
try:
if not args.no_start_proxy:

View file

@ -29,14 +29,13 @@ import tempfile
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Optional
from typing import Any, Final, Optional
import aiohttp
from aiohttp import web
DEFAULT_MODEL = "perf-test-model"
DEFAULT_API_KEY = "sk-1234"
@dataclass
@ -646,7 +645,7 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("--proxy-port", type=int, default=4000)
parser.add_argument("--provider-host", default="127.0.0.1")
parser.add_argument("--provider-port", type=int, default=8099)
parser.add_argument("--api-key", default=DEFAULT_API_KEY)
parser.add_argument("--api-key", default=os.environ.get("LITELLM_MASTER_KEY"))
parser.add_argument("--requests", type=int, default=500)
parser.add_argument("--concurrency", type=int, default=100)
parser.add_argument("--stream-requests", type=int, default=200)
@ -695,12 +694,23 @@ def parse_args() -> argparse.Namespace:
async def async_main() -> None:
args = parse_args()
if args.no_start_proxy and not args.api_key:
raise ValueError("Set LITELLM_MASTER_KEY or pass --api-key when using --no-start-proxy")
api_key: Final = args.api_key or (
"sk-"
+ subprocess.run(
["openssl", "rand", "-hex", "16"],
capture_output=True,
check=True,
text=True,
).stdout.strip()
)
litellm_dir = Path(args.litellm_dir).resolve()
revision = get_git_revision(litellm_dir)
proxy_base_url = f"http://{args.proxy_host}:{args.proxy_port}"
proxy_url = f"{proxy_base_url}/v1/chat/completions"
headers = {
"Authorization": f"Bearer {args.api_key}",
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
provider_headers = {
@ -732,7 +742,7 @@ async def async_main() -> None:
provider_base_url = provider.base_url
config_path = tmp_dir / "config.yaml"
write_proxy_config(config_path, provider_base_url, args.api_key)
write_proxy_config(config_path, provider_base_url, api_key)
try:
if not args.no_start_proxy:

View file

@ -3,6 +3,7 @@
import argparse
import asyncio
import os
import time
import statistics
@ -17,7 +18,7 @@ REQUEST_BODY = {
}
HEADERS = {
"Authorization": "Bearer sk-1234",
"Authorization": f"Bearer {os.environ['LITELLM_MASTER_KEY']}",
"Content-Type": "application/json",
}

View file

@ -9,7 +9,7 @@ USAGE EXAMPLES:
# Set required environment variables
export LITELLM_PROXY_URL='http://localhost:4000/chat/completions'
export PROVIDER_URL='https://api.openai.com/v1/chat/completions'
export LITELLM_PROXY_API_KEY='sk-1234'
export LITELLM_PROXY_API_KEY="$LITELLM_MASTER_KEY"
export PROVIDER_API_KEY='sk-openai-key'
# Run from scripts directory
@ -537,7 +537,7 @@ Examples:
# 1. Basic usage (recommended - sequential execution)
export LITELLM_PROXY_URL='http://localhost:4000/chat/completions'
export PROVIDER_URL='https://api.openai.com/v1/chat/completions'
export LITELLM_PROXY_API_KEY='sk-1234'
export LITELLM_PROXY_API_KEY="$LITELLM_MASTER_KEY"
export PROVIDER_API_KEY='sk-openai-key'
python scripts/benchmark_proxy_vs_provider.py

View file

@ -400,7 +400,7 @@ class LiteLLMHealthCheckClient:
async def main():
"""Main entry point."""
base_url = os.environ.get("LITELLM_BASE_URL", "http://localhost:4000")
api_key = os.environ.get("LITELLM_API_KEY", "sk-1234")
api_key = os.environ["LITELLM_API_KEY"]
yaml_path = os.environ.get("LITELLM_MODELS_YAML")
custom_auth_header = os.environ.get(
"LITELLM_CUSTOM_AUTH_HEADER"

View file

@ -4,7 +4,7 @@
#
# Usage:
# $env:LITELLM_BASE_URL="https://litellm.example.com"
# $env:LITELLM_API_KEY="your-api-key"
# $env:LITELLM_API_KEY="<your-virtual-key>"
# .\run_parallel_health_checks.ps1 [num_parallel_jobs] [image_name]
#
# Defaults:
@ -17,15 +17,14 @@ param(
[string]$ContainerRuntime = "docker"
)
# Set defaults for environment variables if not provided
# Require credentials for the target proxy
if (-not $env:LITELLM_BASE_URL) {
$env:LITELLM_BASE_URL = "https://litellm-perf-cache-and-router.onrender.com"
Write-Warning "LITELLM_BASE_URL not set, using default: $env:LITELLM_BASE_URL"
}
if (-not $env:LITELLM_API_KEY) {
$env:LITELLM_API_KEY = "sk-1234"
Write-Warning "LITELLM_API_KEY not set, using default: $env:LITELLM_API_KEY"
throw "LITELLM_API_KEY must be set"
}
# Check if container runtime is available

View file

@ -5,7 +5,7 @@
#
# Usage:
# export LITELLM_BASE_URL="https://litellm.example.com"
# export LITELLM_API_KEY="your-api-key"
# export LITELLM_API_KEY="<your-virtual-key>"
# ./run_parallel_health_checks.sh [num_parallel_jobs] [image_name] [container_runtime]
#
# Defaults:
@ -20,16 +20,13 @@ NUM_PARALLEL_JOBS="${1:-16}"
IMAGE_NAME="${2:-litellm/litellm-health-check:latest}"
CONTAINER_RUNTIME="${3:-docker}"
# Set defaults for environment variables if not provided
# Require credentials for the target proxy
if [ -z "$LITELLM_BASE_URL" ]; then
export LITELLM_BASE_URL="https://litellm-perf-cache-and-router.onrender.com"
echo "Warning: LITELLM_BASE_URL not set, using default: $LITELLM_BASE_URL" >&2
fi
if [ -z "$LITELLM_API_KEY" ]; then
export LITELLM_API_KEY="sk-1234"
echo "Warning: LITELLM_API_KEY not set, using default: $LITELLM_API_KEY" >&2
fi
: "${LITELLM_API_KEY:?set LITELLM_API_KEY}"
# Check if container runtime is available
if ! command -v "$CONTAINER_RUNTIME" &> /dev/null; then

View file

@ -116,7 +116,6 @@ proxy_env() {
export LENS_WORKER_IMAGE=litellm-lens-worker:local
export LITELLM_MODE=PRODUCTION
export LITELLM_MASTER_KEY="$master_key"
if [ "$master_key" = sk-1234 ]; then export LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY=true; fi
export LITELLM_SALT_KEY=sk-local-tracing-salt-key
export DATABASE_URL="$database_url"
export STORE_MODEL_IN_DB=True

11
tests/_master_key.py Normal file
View file

@ -0,0 +1,11 @@
import hashlib
import os
import secrets
from typing import Final
_xdist_test_run_uid: Final = os.environ.get("PYTEST_XDIST_TESTRUNUID")
MASTER_KEY: Final = (
f"sk-{hashlib.sha256(_xdist_test_run_uid.encode()).hexdigest()[:32]}"
if _xdist_test_run_uid is not None
else f"sk-{secrets.token_hex(16)}"
)

View file

@ -5,6 +5,7 @@ This test ensures that the proxy starts and serves requests even with a bad lice
in ci/cd config.yml, we set the license to "bad-license"
"""
import os
import pytest
import aiohttp
from typing import Optional
@ -36,7 +37,7 @@ async def test_health_and_chat_completion():
# Make a chat completion call
url = "http://0.0.0.0:4000/chat/completions"
headers = {
"Authorization": "Bearer sk-1234",
"Authorization": f"Bearer {os.environ['LITELLM_MASTER_KEY']}",
"Content-Type": "application/json",
}
data = {

View file

@ -0,0 +1,60 @@
from __future__ import annotations
import os
import subprocess
import sys
from collections.abc import Iterator
from pathlib import Path
from typing import Final
REPO_ROOT: Final = Path(__file__).resolve().parents[2]
PUBLICLY_KNOWN_MASTER_KEY_PREFIX: Final = "sk-" + "1234"
def _file_violations(relative_path: str) -> tuple[str, ...]:
path: Final = REPO_ROOT / relative_path
if path.is_dir():
return ()
try:
contents: Final = path.read_bytes()
except FileNotFoundError:
return ()
if b"\0" in contents:
return ()
try:
text: Final = contents.decode("utf-8")
except UnicodeDecodeError:
return ()
return tuple(
f"{relative_path}:{line_number}"
for line_number, line in enumerate(text.splitlines(), start=1)
if PUBLICLY_KNOWN_MASTER_KEY_PREFIX in line
)
def _violations(tracked_paths: tuple[str, ...]) -> Iterator[str]:
for path in tracked_paths:
yield from _file_violations(path)
def main() -> int:
result: Final = subprocess.run(
["git", "-C", os.fspath(REPO_ROOT), "ls-files", "-z"],
check=True,
stdout=subprocess.PIPE,
)
tracked_paths: Final = tuple(
os.fsdecode(path) for path in result.stdout.split(b"\0") if path
)
violations: Final = tuple(_violations(tracked_paths))
for violation in violations:
print(violation)
if violations:
print(f"\n{len(violations)} tracked line(s) contain the publicly known master key prefix")
return 1
print("No tracked files contain the publicly known master key prefix")
return 0
if __name__ == "__main__":
sys.exit(main())

View file

@ -533,7 +533,7 @@ class TestSecretMasking:
environ: Final = {
"OPENAI_API_KEY": "sk-proj-0123456789",
"AWS_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG",
"LITELLM_MASTER_KEY": "sk-1234",
"LITELLM_MASTER_KEY": "sk-test",
"GOOGLE_APPLICATION_CREDENTIALS": "/secrets/vertex.json",
"KEYCLOAK_URL": "http://localhost:8080",
"E2E_MODEL": "claude-haiku-4-5",

View file

@ -13,10 +13,10 @@ The suites run against a live proxy, so bring one up first by running the litell
## Running the tests locally
1. Create a `.env` file in this directory with the provider keys the example models use, plus the master key and the Postgres/Redis coordinates your config reads back:
1. Generate a master key with `export LITELLM_MASTER_KEY="sk-$(openssl rand -hex 32)"`. Create a `.env` file in this directory with the provider keys the example models use and the Postgres/Redis coordinates your config reads back:
```bash
LITELLM_MASTER_KEY="sk-1234"
LITELLM_MASTER_KEY="sk-<your-master-key>"
DATABASE_URL="postgresql://llmproxy:dbpassword9090@localhost:5432/litellm"
REDIS_HOST="localhost"
REDIS_PORT="6379"
@ -110,7 +110,7 @@ A couple of logging destinations are configured on the proxy rather than by the
```bash
bash tests/e2e/secret_manager/backend.sh up cyberark
(set -a; . ~/.cache/litellm-e2e-secret-manager/cyberark/proxy.env; set +a; env -u OPENAI_API_KEY LITELLM_LICENSE=... \
LITELLM_MASTER_KEY=sk-1234 DATABASE_URL=... uv run litellm --config tests/e2e/gateway/secret_manager_cyberark_ci_config.yml --port 4000)
LITELLM_MASTER_KEY="$LITELLM_MASTER_KEY" DATABASE_URL=... uv run litellm --config tests/e2e/gateway/secret_manager_cyberark_ci_config.yml --port 4000)
(set -a; . ~/.cache/litellm-e2e-secret-manager/cyberark/tests.env; set +a; OPENAI_API_KEY=... \
uv run --group e2e-dev pytest tests/e2e/secret_manager/ -v)
bash tests/e2e/secret_manager/backend.sh down cyberark

View file

@ -51,7 +51,7 @@ set -Eeuo pipefail
LITELLM_REPO="${LITELLM_REPO:-${HOME}/litellm/litellm}"
WORKTREE="${LITELLM_WORKTREE:-${HOME}/litellm-cron-worktree}"
PROXY_PORT="${PROXY_PORT:-4100}"
PROXY_API_KEY="${PROXY_API_KEY:-sk-cron-matrix}"
PROXY_API_KEY="${PROXY_API_KEY:-sk-$(openssl rand -hex 16)}"
DOCS_REPO="${DOCS_REPO:-BerriAI/litellm-docs}"
DOCS_BRANCH="${DOCS_BRANCH:-main}"
DOCS_TARGET_PATH="${DOCS_TARGET_PATH:-src/data/compatibility-matrix.json}"
@ -347,9 +347,8 @@ log "starting proxy on 127.0.0.1:${PROXY_PORT}"
# exclusively by the pytest run on the same host (the health check and
# the test env set `LITELLM_PROXY_URL=http://127.0.0.1:...`),
# so there's no reason to expose it on the container's external interfaces.
# Without `--host`, `litellm` defaults to 0.0.0.0, which combined with
# the predictable default `LITELLM_MASTER_KEY=sk-cron-matrix` would
# allow anything that can reach :${PROXY_PORT} on the host to authenticate
# Without `--host`, `litellm` defaults to 0.0.0.0, so a predictable proxy key
# would allow anything that can reach :${PROXY_PORT} on the host to authenticate
# and burn upstream provider credentials.
#
# `setsid` puts the proxy in its own session+pgroup so cleanup() can

View file

@ -13,7 +13,7 @@
#
# Required env (proxy connection), same names as the rest of tests/e2e:
# LITELLM_PROXY_URL e.g. http://localhost:4000
# LITELLM_MASTER_KEY e.g. sk-1234
# LITELLM_MASTER_KEY e.g. sk-<your-master-key>
#
# Optional env (rate limits, all default to 5 req/s; 0 disables a column):
# LITELLM_COMPAT_RATE_ANTHROPIC

View file

@ -25,7 +25,7 @@ from pydantic import TypeAdapter
load_dotenv(Path(__file__).resolve().parent / ".env", override=False)
PROXY_BASE_URL = os.environ.get("LITELLM_PROXY_URL", "http://localhost:4000").rstrip("/")
MASTER_KEY = os.environ.get("LITELLM_MASTER_KEY", "sk-1234")
MASTER_KEY = os.environ["LITELLM_MASTER_KEY"]
# Control-plane (management/admin) base URL. Defaults to PROXY_BASE_URL so a
# single path-routing host (stage ALB, compose monolith) works for both planes.

View file

@ -15,7 +15,7 @@ if worker_image > /dev/null 2>&1; then
fi
qa_dir=$(mktemp -d)
master_key="sk-$(openssl rand -hex 32)"
master_key="sk-$(openssl rand -hex 16)"
compose=(docker compose -p lens-compose-ci --env-file "$qa_dir/env" -f deploy/lens/stack.yaml)
cleanup() {
"${compose[@]}" --profile lens down -v --remove-orphans >/dev/null 2>&1 || true

View file

@ -86,7 +86,6 @@ def owned_jwt_gateway(
"JWT_PUBLIC_KEY_URL": idp.jwks_url,
"JWT_ISSUER": idp.issuer,
"JWT_AUDIENCE": "litellm-e2e",
"LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY": "true",
"DISABLE_SCHEMA_UPDATE": "true",
"STORE_MODEL_IN_DB": "True",
"PYTHONPATH": str(Path(__file__).resolve().parents[3]),

View file

@ -58,7 +58,7 @@ class TestPlaceholders:
("file-XyZ12345abc", "<id>"),
("gpt-4o-mini", "gpt-4o-mini"),
("max_tokens", "max_tokens"),
("sk-1234", "sk-1234"),
("sk-9876", "sk-9876"),
],
)
def test_rewrites_exactly_the_volatile_shapes(self, raw: str, expected: str) -> None:

View file

@ -6,6 +6,7 @@ import {
INTERNAL_VIEWER_STORAGE_PATH,
TEAM_ADMIN_STORAGE_PATH,
} from "../constants";
import { masterKey } from "../helpers/traffic";
export enum Role {
ProxyAdmin = "proxy_admin",
@ -20,7 +21,7 @@ export type SeedApiRole = "proxy_admin_viewer" | "internal_user" | "internal_use
export const users: Record<Role, { email: string; password: string; seedApiRole?: SeedApiRole }> = {
[Role.ProxyAdmin]: {
email: "admin",
password: process.env.LITELLM_MASTER_KEY || "sk-1234",
password: masterKey(),
},
[Role.ProxyAdminViewer]: {
email: "adminviewer@test.local",

View file

@ -3,6 +3,7 @@ import { users, Role, STORAGE_PATHS } from "./fixtures/users";
import { ARTIFACT_DIR, UI_BASE_URL } from "./constants";
import { expectUnrestrictedDashboard, setInvitedUserPassword } from "./helpers/userOnboarding";
import { hideLiteAdmin } from "./helpers/navigation";
import { masterKey as getMasterKey } from "./helpers/traffic";
import * as fs from "fs";
import * as path from "path";
@ -22,7 +23,7 @@ async function globalSetup() {
// enable_projects_ui setting is on, and the seeded DB starts with it off.
// The proxy runs with LITELLM_LICENSE in CI, so enable it the same way
// the admin UI toggle does; the projects migration smoke needs the link.
const masterKey = process.env.LITELLM_MASTER_KEY || "sk-1234";
const masterKey = getMasterKey();
const api = await request.newContext();
const settingsRes = await api.patch(`${UI_BASE_URL}${rootPath}/update/ui_settings`, {
headers: { Authorization: `Bearer ${masterKey}` },

View file

@ -11,7 +11,13 @@ export const DEPLOYMENT_MODEL_B = "openai/fake-claude";
/** The only completion text fixtures/mock_llm_server/server.py ever returns. */
export const MOCK_RESPONSE_TEXT = "This is a mock response.";
export const masterKey = (): string => process.env.LITELLM_MASTER_KEY || "sk-1234";
export const masterKey = (): string => {
const key = process.env.LITELLM_MASTER_KEY;
if (!key) {
throw new Error("LITELLM_MASTER_KEY must be set");
}
return key;
};
export const rootPath = (): string => process.env.SERVER_ROOT_PATH ?? "";

View file

@ -144,8 +144,7 @@ else
fi
# --- Credentials ---
export LITELLM_MASTER_KEY="sk-1234"
export LITELLM_DANGEROUSLY_PERMIT_WEAK_OR_UNSET_MASTER_KEY="true"
export LITELLM_MASTER_KEY="${LITELLM_MASTER_KEY:-sk-$(openssl rand -hex 16)}"
export MOCK_LLM_URL="http://127.0.0.1:${MOCK_LLM_PORT}/v1"
export E2E_MOCK_PRESIDIO_URL="http://127.0.0.1:${MOCK_PRESIDIO_PORT}"
export DISABLE_SCHEMA_UPDATE="true"

View file

@ -4,11 +4,12 @@ import { execFileSync } from "node:child_process";
import * as path from "node:path";
import { Page } from "../../fixtures/pages";
import { navigateToPage } from "../../helpers/navigation";
import { masterKey } from "../../helpers/traffic";
test("cache leakage by model merges a deployment's resolved and requested model names into its model group", async ({
page,
}) => {
const master = process.env.LITELLM_MASTER_KEY ?? "sk-integration-master";
const master = masterKey();
const marker = `integration-browser-${randomUUID()}`;
const group = `${marker}-public`;
const deployment = `${marker}-backend`;

View file

@ -2,6 +2,7 @@ import { test, expect, type APIRequestContext } from "@playwright/test";
import { randomUUID } from "node:crypto";
import { Page } from "../../fixtures/pages";
import { dismissFeedbackPopup, navigateToPage } from "../../helpers/navigation";
import { masterKey } from "../../helpers/traffic";
/**
* Credential canary S8: what the Logs page renders for a request, including any client-side
@ -55,7 +56,7 @@ test("the Logs drawer renders the stored request without the deployment api_key"
page,
request,
}) => {
const master = process.env.LITELLM_MASTER_KEY ?? "sk-integration-master";
const master = masterKey();
const upstream = (
process.env.INTEGRATION_UPSTREAM_URL ?? "http://127.0.0.1:8190"
).replace(/\/+$/, "");

View file

@ -9,8 +9,9 @@ import { randomUUID } from "node:crypto";
import { Page } from "../../fixtures/pages";
import { navigateToPage } from "../../helpers/navigation";
import { captureRequestBody } from "../../helpers/roundTrip";
import { masterKey } from "../../helpers/traffic";
const master = process.env.LITELLM_MASTER_KEY ?? "sk-integration-master";
const master = masterKey();
const headers = { Authorization: `Bearer ${master}` };
const TOKEN = "USER_TOKEN";

View file

@ -5,12 +5,13 @@ import * as path from "node:path";
import { Page } from "../../fixtures/pages";
import { navigateToPage, openKeyDetail } from "../../helpers/navigation";
import { captureRequestBody, readBack } from "../../helpers/roundTrip";
import { masterKey } from "../../helpers/traffic";
test("project creation and explicit detachment preserve saved scope and restore serving", async ({
page,
request,
}) => {
const master = process.env.LITELLM_MASTER_KEY ?? "sk-integration-master";
const master = masterKey();
const headers = { Authorization: `Bearer ${master}` };
const prefix = `integration-browser-${randomUUID()}`;
// rebind-ok: Register cleanup after each acquisition so partial setup always unwinds in reverse order.

View file

@ -5,8 +5,9 @@ import {
Page as PlaywrightPage,
} from "@playwright/test";
import { randomUUID } from "node:crypto";
import { masterKey } from "../../helpers/traffic";
const master = process.env.LITELLM_MASTER_KEY ?? "sk-integration-master";
const master = masterKey();
const headers = { Authorization: `Bearer ${master}` };
async function createTeam(request: APIRequestContext): Promise<string> {

View file

@ -5,8 +5,9 @@ import {
Page as PlaywrightPage,
} from "@playwright/test";
import { randomUUID } from "node:crypto";
import { masterKey } from "../../helpers/traffic";
const master = process.env.LITELLM_MASTER_KEY ?? "sk-integration-master";
const master = masterKey();
const headers = { Authorization: `Bearer ${master}` };
async function createTeamCarryingAnEmptyMetadataKey(

View file

@ -4,6 +4,7 @@ import { execFileSync } from "node:child_process";
import * as path from "node:path";
import { Page } from "../../fixtures/pages";
import { dismissFeedbackPopup, navigateToPage } from "../../helpers/navigation";
import { masterKey } from "../../helpers/traffic";
/**
* The Tool Policies table gets a User column: the owner of the key that discovered the tool, shown
@ -32,7 +33,7 @@ test("the Tool Policies page names the user behind the key that discovered a too
page,
request,
}) => {
const master = process.env.LITELLM_MASTER_KEY ?? "sk-integration-master";
const master = masterKey();
const upstream = (
process.env.INTEGRATION_UPSTREAM_URL ?? "http://127.0.0.1:8190"
).replace(/\/+$/, "");

View file

@ -68,8 +68,7 @@ test.describe("Public model hub (/ui/model_hub_table)", () => {
// The page expects the proxy key as the `key` query param. Use the master
// key the e2e runner already exports — this matches what the AI Hub copy
// button hands out.
const masterKey = process.env.LITELLM_MASTER_KEY || "sk-1234";
await page.goto(`/ui/model_hub_table?key=${masterKey}`);
await page.goto(`/ui/model_hub_table?key=${masterKey()}`);
// Dismiss the feedback popup before asserting on the tab, so a popup
// race can't briefly mask the tab while we're evaluating visibility.

View file

@ -177,11 +177,11 @@ test.describe("Proxy Admin - Teams", () => {
// Restore the seeded models via API in case a prior run (or a CI retry)
// left this team mutated — the assertion below requires fake-anthropic-claude
// to be present.
const masterKey = process.env.LITELLM_MASTER_KEY || "sk-1234";
const key = masterKey();
const seededModels = ["fake-openai-gpt-4", "fake-anthropic-claude"];
const restore = async () => {
const res = await request.post("/team/update", {
headers: { Authorization: `Bearer ${masterKey}` },
headers: { Authorization: `Bearer ${key}` },
data: { team_id: E2E_TEAM_CRUD_ID, models: seededModels },
});
expect(res.ok(), `restore failed: ${res.status()} ${await res.text()}`).toBeTruthy();

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