[Fix] Security - Remove example API keys with high entropy (#18255)

This commit is contained in:
Alexsander Hamir
2025-12-19 10:09:50 -08:00
committed by GitHub
parent 06d08b52d9
commit 24edbccf5c
54 changed files with 397 additions and 260 deletions
+9 -9
View File
@@ -657,7 +657,7 @@ jobs:
docker run -d \
--name postgres-db \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_PASSWORD=test-postgres \
-e POSTGRES_DB=circle_test \
-p 5432:5432 \
postgres:14
@@ -2108,7 +2108,7 @@ jobs:
docker run -d \
--name postgres-db \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_PASSWORD=test-postgres \
-e POSTGRES_DB=circle_test \
-p 5432:5432 \
postgres:14
@@ -2250,7 +2250,7 @@ jobs:
docker run -d \
--name postgres-db \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_PASSWORD=test-postgres \
-e POSTGRES_DB=circle_test \
-p 5432:5432 \
postgres:14
@@ -2390,7 +2390,7 @@ jobs:
docker run -d \
--name postgres-db \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_PASSWORD=test-postgres \
-e POSTGRES_DB=circle_test \
-p 5432:5432 \
postgres:14
@@ -2551,7 +2551,7 @@ jobs:
docker run -d \
--name postgres-db \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_PASSWORD=test-postgres \
-e POSTGRES_DB=circle_test \
-p 5432:5432 \
postgres:14
@@ -2664,7 +2664,7 @@ jobs:
docker run -d \
--name postgres-db \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_PASSWORD=test-postgres \
-e POSTGRES_DB=circle_test \
-p 5432:5432 \
postgres:14
@@ -2800,7 +2800,7 @@ jobs:
docker run -d \
--name postgres-db \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_PASSWORD=test-postgres \
-e POSTGRES_DB=circle_test \
-p 5432:5432 \
postgres:14
@@ -3032,7 +3032,7 @@ jobs:
docker run -d \
--name postgres-db \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_PASSWORD=test-postgres \
-e POSTGRES_DB=circle_test \
-p 5432:5432 \
postgres:14
@@ -3549,7 +3549,7 @@ jobs:
docker run -d \
--name postgres-db \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_PASSWORD=test-postgres \
-e POSTGRES_DB=circle_test \
-p 5432:5432 \
postgres:14
+84
View File
@@ -0,0 +1,84 @@
version: 2
secret:
# Exclude files and paths by globbing
ignored_paths:
- "**/*.whl"
- "**/*.pyc"
- "**/__pycache__/**"
- "**/node_modules/**"
- "**/dist/**"
- "**/build/**"
- "**/.git/**"
- "**/venv/**"
- "**/.venv/**"
# Large data/metadata files that don't need scanning
- "**/model_prices_and_context_window*.json"
- "**/*_metadata/*.txt"
- "**/tokenizers/*.json"
- "**/tokenizers/*"
- "miniconda.sh"
# Build outputs and static assets
- "litellm/proxy/_experimental/out/**"
- "ui/litellm-dashboard/public/**"
- "**/swagger/*.js"
- "**/*.woff"
- "**/*.woff2"
- "**/*.avif"
- "**/*.webp"
# Test data files
- "**/tests/**/data_map.txt"
- "tests/**/*.txt"
# Documentation and other non-code files
- "docs/**"
- "**/*.md"
- "**/*.lock"
- "poetry.lock"
- "package-lock.json"
# Ignore security incidents with the SHA256 of the occurrence (false positives)
ignored_matches:
# === Current detected false positives (SHA-based) ===
# gcs_pub_sub_body - folder name, not a password
- name: GCS pub/sub test folder name
match: 75f377c456eede69e5f6e47399ccee6016a2a93cc5dd11db09cc5b1359ae569a
# os.environ/APORIA_API_KEY_1 - environment variable reference
- name: Environment variable reference APORIA_API_KEY_1
match: e2ddeb8b88eca97a402559a2be2117764e11c074d86159ef9ad2375dea188094
# os.environ/APORIA_API_KEY_2 - environment variable reference
- name: Environment variable reference APORIA_API_KEY_2
match: 09aa39a29e050b86603aa55138af1ff08fb86a4582aa965c1bd0672e1575e052
# oidc/circleci_v2/ - test authentication path, not a secret
- name: OIDC CircleCI test path
match: feb3475e1f89a65b7b7815ac4ec597e18a9ec1847742ad445c36ca617b536e15
# text-davinci-003 - OpenAI model identifier, not a secret
- name: OpenAI model identifier text-davinci-003
match: c489000cf6c7600cee0eefb80ad0965f82921cfb47ece880930eb7e7635cf1f1
# === Preventive patterns for test keys (pattern-based) ===
# Test API keys (124 instances across 45 files)
- name: Test API keys with sk-test prefix
match: sk-test-
# Mock API keys
- name: Mock API keys with sk-mock prefix
match: sk-mock-
# Fake API keys
- name: Fake API keys with sk-fake prefix
match: sk-fake-
# Generic test API key patterns
- name: Test API key patterns
match: test-api-key
+1 -1
View File
@@ -8,7 +8,7 @@ class MyUser(HttpUser):
def chat_completion(self):
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer sk-8N1tLOOyH8TIxwOLahhIVg",
"Authorization": "Bearer sk-test-load-test-key-123",
# Include any additional headers you may need for authentication, etc.
}
+2 -2
View File
@@ -20,7 +20,7 @@ jobs:
env:
POSTGRES_DB: temp_db
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_PASSWORD: test-postgres
ports:
- 5432:5432
options: >-
@@ -35,7 +35,7 @@ jobs:
env:
POSTGRES_DB: shadow_db
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_PASSWORD: test-postgres
ports:
- 5433:5432
options: >-
+40
View File
@@ -0,0 +1,40 @@
# Test Key Patterns Standard
Standard patterns for test/mock keys and credentials in the LiteLLM codebase to avoid triggering secret detection.
## How GitGuardian Works
GitGuardian uses **machine learning and entropy analysis**, not just pattern matching:
- **Low entropy** values (like `sk-1234`, `postgres`) are automatically ignored
- **High entropy** values (realistic-looking secrets) trigger detection
- **Context-aware** detection understands code syntax like `os.environ["KEY"]`
## Recommended Test Key Patterns
### Option 1: Low Entropy Values (Simplest)
These won't trigger GitGuardian's ML detector:
```python
api_key = "sk-1234"
api_key = "sk-12345"
database_password = "postgres"
token = "test123"
```
### Option 2: High Entropy with Test Prefixes
If you need realistic-looking test keys with high entropy, use these prefixes:
```python
api_key = "sk-test-abc123def456ghi789..." # OpenAI-style test key
api_key = "sk-mock-1234567890abcdef1234..." # Mock key
api_key = "sk-fake-xyz789uvw456rst123..." # Fake key
token = "test-api-key-with-high-entropy"
```
## Configured Ignore Patterns
These patterns are in `.gitguardian.yaml` for high-entropy test keys:
- `sk-test-*` - OpenAI-style test keys
- `sk-mock-*` - Mock API keys
- `sk-fake-*` - Fake API keys
- `test-api-key` - Generic test tokens
+1 -1
View File
@@ -39,7 +39,7 @@
"import os\n",
"os.environ['OPENAI_API_KEY'] = \"\"\n",
"os.environ['REPLICATE_API_TOKEN'] = \"\"\n",
"os.environ['PROMPTLAYER_API_KEY'] = \"pl_4ea2bb00a4dca1b8a70cebf2e9e11564\"\n",
"os.environ['PROMPTLAYER_API_KEY'] = \"test-promptlayer-key-123\"\n",
"\n",
"# Set Promptlayer as a success callback\n",
"litellm.success_callback =['promptlayer']\n",
@@ -1,21 +1,10 @@
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "kccfk0mHZ4Ad"
},
"source": [
"# Migrating to LiteLLM Proxy from OpenAI/Azure OpenAI\n",
"\n",
@@ -32,29 +21,26 @@
"To pass provider-specific args, [go here](https://docs.litellm.ai/docs/completion/provider_specific_params#proxy-usage)\n",
"\n",
"To drop unsupported params (E.g. frequency_penalty for bedrock with librechat), [go here](https://docs.litellm.ai/docs/completion/drop_params#openai-proxy-usage)\n"
],
"metadata": {
"id": "kccfk0mHZ4Ad"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "nmSClzCPaGH6"
},
"source": [
"## /chat/completion\n",
"\n"
],
"metadata": {
"id": "nmSClzCPaGH6"
}
]
},
{
"cell_type": "markdown",
"source": [
"### OpenAI Python SDK"
],
"metadata": {
"id": "_vqcjwOVaKpO"
}
},
"source": [
"### OpenAI Python SDK"
]
},
{
"cell_type": "code",
@@ -94,15 +80,20 @@
},
{
"cell_type": "markdown",
"source": [
"## Function Calling"
],
"metadata": {
"id": "AqkyKk9Scxgj"
}
},
"source": [
"## Function Calling"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "wDg10VqLczE1"
},
"outputs": [],
"source": [
"from openai import OpenAI\n",
"client = OpenAI(\n",
@@ -139,24 +130,24 @@
")\n",
"\n",
"print(completion)\n"
],
"metadata": {
"id": "wDg10VqLczE1"
},
"execution_count": null,
"outputs": []
]
},
{
"cell_type": "markdown",
"source": [
"### Azure OpenAI Python SDK"
],
"metadata": {
"id": "YYoxLloSaNWW"
}
},
"source": [
"### Azure OpenAI Python SDK"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "yA1XcgowaSRy"
},
"outputs": [],
"source": [
"import openai\n",
"client = openai.AzureOpenAI(\n",
@@ -184,24 +175,24 @@
")\n",
"\n",
"print(response)"
],
"metadata": {
"id": "yA1XcgowaSRy"
},
"execution_count": null,
"outputs": []
]
},
{
"cell_type": "markdown",
"source": [
"### Langchain Python"
],
"metadata": {
"id": "yl9qhDvnaTpL"
}
},
"source": [
"### Langchain Python"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "5MUZgSquaW5t"
},
"outputs": [],
"source": [
"from langchain.chat_models import ChatOpenAI\n",
"from langchain.prompts.chat import (\n",
@@ -239,24 +230,22 @@
"response = chat(messages)\n",
"\n",
"print(response)"
],
"metadata": {
"id": "5MUZgSquaW5t"
},
"execution_count": null,
"outputs": []
]
},
{
"cell_type": "markdown",
"source": [
"### Curl"
],
"metadata": {
"id": "B9eMgnULbRaz"
}
},
"source": [
"### Curl"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "VWCCk5PFcmhS"
},
"source": [
"\n",
"\n",
@@ -280,22 +269,24 @@
"}'\n",
"```\n",
"\n"
],
"metadata": {
"id": "VWCCk5PFcmhS"
}
]
},
{
"cell_type": "markdown",
"source": [
"### LlamaIndex"
],
"metadata": {
"id": "drBAm2e1b6xe"
}
},
"source": [
"### LlamaIndex"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "d0bZcv8fb9mL"
},
"outputs": [],
"source": [
"import os, dotenv\n",
"\n",
@@ -326,24 +317,24 @@
"query_engine = index.as_query_engine()\n",
"response = query_engine.query(\"What did the author do growing up?\")\n",
"print(response)\n"
],
"metadata": {
"id": "d0bZcv8fb9mL"
},
"execution_count": null,
"outputs": []
]
},
{
"cell_type": "markdown",
"source": [
"### Langchain JS"
],
"metadata": {
"id": "xypvNdHnb-Yy"
}
},
"source": [
"### Langchain JS"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "R55mK2vCcBN2"
},
"outputs": [],
"source": [
"import { ChatOpenAI } from \"@langchain/openai\";\n",
"\n",
@@ -359,24 +350,24 @@
"const message = await model.invoke(\"Hi there!\");\n",
"\n",
"console.log(message);\n"
],
"metadata": {
"id": "R55mK2vCcBN2"
},
"execution_count": null,
"outputs": []
]
},
{
"cell_type": "markdown",
"source": [
"### OpenAI JS"
],
"metadata": {
"id": "nC4bLifCcCiW"
}
},
"source": [
"### OpenAI JS"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "MICH8kIMcFpg"
},
"outputs": [],
"source": [
"const { OpenAI } = require('openai');\n",
"\n",
@@ -398,24 +389,24 @@
"}\n",
"\n",
"main();\n"
],
"metadata": {
"id": "MICH8kIMcFpg"
},
"execution_count": null,
"outputs": []
]
},
{
"cell_type": "markdown",
"source": [
"### Anthropic SDK"
],
"metadata": {
"id": "D1Q07pEAcGTb"
}
},
"source": [
"### Anthropic SDK"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "qBjFcAvgcI3t"
},
"outputs": [],
"source": [
"import os\n",
"\n",
@@ -423,7 +414,7 @@
"\n",
"client = Anthropic(\n",
" base_url=\"http://localhost:4000\", # proxy endpoint\n",
" api_key=\"sk-s4xN1IiLTCytwtZFJaYQrA\", # litellm proxy virtual key\n",
" api_key=\"sk-test-proxy-key-123\", # litellm proxy virtual key (example)\n",
")\n",
"\n",
"message = client.messages.create(\n",
@@ -437,33 +428,33 @@
" model=\"claude-3-opus-20240229\",\n",
")\n",
"print(message.content)"
],
"metadata": {
"id": "qBjFcAvgcI3t"
},
"execution_count": null,
"outputs": []
]
},
{
"cell_type": "markdown",
"source": [
"## /embeddings"
],
"metadata": {
"id": "dFAR4AJGcONI"
}
},
"source": [
"## /embeddings"
]
},
{
"cell_type": "markdown",
"source": [
"### OpenAI Python SDK"
],
"metadata": {
"id": "lgNoM281cRzR"
}
},
"source": [
"### OpenAI Python SDK"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "NY3DJhPfcQhA"
},
"outputs": [],
"source": [
"import openai\n",
"from openai import OpenAI\n",
@@ -478,24 +469,24 @@
")\n",
"\n",
"print(response)\n"
],
"metadata": {
"id": "NY3DJhPfcQhA"
},
"execution_count": null,
"outputs": []
]
},
{
"cell_type": "markdown",
"source": [
"### Langchain Embeddings"
],
"metadata": {
"id": "hmbg-DW6cUZs"
}
},
"source": [
"### Langchain Embeddings"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "lX2S8Nl1cWVP"
},
"outputs": [],
"source": [
"from langchain.embeddings import OpenAIEmbeddings\n",
"\n",
@@ -526,24 +517,22 @@
"\n",
"print(f\"TITAN EMBEDDINGS\")\n",
"print(query_result[:5])"
],
"metadata": {
"id": "lX2S8Nl1cWVP"
},
"execution_count": null,
"outputs": []
]
},
{
"cell_type": "markdown",
"source": [
"### Curl Request"
],
"metadata": {
"id": "oqGbWBCQcYfd"
}
},
"source": [
"### Curl Request"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "7rkIMV9LcdwQ"
},
"source": [
"\n",
"\n",
@@ -556,10 +545,21 @@
" }'\n",
"```\n",
"\n"
],
"metadata": {
"id": "7rkIMV9LcdwQ"
}
]
}
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
},
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
+1 -1
View File
@@ -106,7 +106,7 @@ model_list:
aws_region_name: us-west-2
aws_session_name: "my-test-session"
aws_role_name: "arn:aws:iam::335785316107:role/litellm-github-unit-tests-circleci"
aws_web_identity_token: "oidc/circleci_v2/"
aws_web_identity_token: "oidc/example-provider/"
```
#### Amazon IAM Role Configuration for CircleCI v2 -> Bedrock
+11 -11
View File
@@ -215,16 +215,16 @@ general_settings:
alerting: ["slack"]
alerting_threshold: 0.0001 # (Seconds) set an artificially low threshold for testing alerting
alert_to_webhook_url: {
"llm_exceptions": "https://hooks.slack.com/services/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",
"llm_too_slow": "https://hooks.slack.com/services/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",
"llm_requests_hanging": "https://hooks.slack.com/services/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",
"budget_alerts": "https://hooks.slack.com/services/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",
"db_exceptions": "https://hooks.slack.com/services/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",
"daily_reports": "https://hooks.slack.com/services/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",
"spend_reports": "https://hooks.slack.com/services/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",
"cooldown_deployment": "https://hooks.slack.com/services/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",
"new_model_added": "https://hooks.slack.com/services/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",
"outage_alerts": "https://hooks.slack.com/services/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",
"llm_exceptions": "example-slack-webhook-url",
"llm_too_slow": "example-slack-webhook-url",
"llm_requests_hanging": "example-slack-webhook-url",
"budget_alerts": "example-slack-webhook-url",
"db_exceptions": "example-slack-webhook-url",
"daily_reports": "example-slack-webhook-url",
"spend_reports": "example-slack-webhook-url",
"cooldown_deployment": "example-slack-webhook-url",
"new_model_added": "example-slack-webhook-url",
"outage_alerts": "example-slack-webhook-url",
}
litellm_settings:
@@ -399,7 +399,7 @@ curl -X GET --location 'http://0.0.0.0:4000/health/services?service=webhook' \
{
"spend": 1, # the spend for the 'event_group'
"max_budget": 0, # the 'max_budget' set for the 'event_group'
"token": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"token": "example-api-key-123",
"user_id": "default_user_id",
"team_id": null,
"user_email": null,
+3 -3
View File
@@ -722,7 +722,7 @@ curl -X GET 'http://localhost:4000/global/spend/report?start_date=2024-04-01&end
```shell
[
{
"api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"api_key": "example-api-key-123",
"total_cost": 0.3201286305151999,
"total_input_tokens": 36.0,
"total_output_tokens": 1593.0,
@@ -766,7 +766,7 @@ curl -X GET 'http://localhost:4000/global/spend/report?start_date=2024-04-01&end
```shell
[
{
"api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"api_key": "example-api-key-123",
"total_cost": 0.00013132,
"total_input_tokens": 105.0,
"total_output_tokens": 872.0,
@@ -1151,7 +1151,7 @@ curl -X GET "http://0.0.0.0:4000/spend/logs?request_id=<your-call-id" \ # e.g.:
"request_id": "chatcmpl-9ZKMURhVYSi9D6r6PJ9vLcayIK0Vm",
"call_type": "acompletion",
"metadata": {
"user_api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"user_api_key": "example-api-key-123",
"user_api_key_alias": null,
"spend_logs_metadata": { # 👈 LOGGED CUSTOM METADATA
"hello": "world"
+1 -1
View File
@@ -103,7 +103,7 @@ Expected Response
{
"spend": 0.0011120000000000001, # 👈 SPEND
"max_budget": null,
"token": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"token": "example-api-key-123",
"customer_id": "krrish12", # 👈 CUSTOMER ID
"user_id": null,
"team_id": null,
@@ -257,7 +257,7 @@ Contact me at [EMAIL_REDACTED]
| `amex` | American Express cards | `3782-822463-10005` |
| `aws_access_key` | AWS access keys | `AKIAIOSFODNN7EXAMPLE` |
| `aws_secret_key` | AWS secret keys | `wJalrXUtnFEMI/K7MDENG/bPxRfi...` |
| `github_token` | GitHub tokens | `ghp_16C7e42F292c6912E7710c838347Ae178B4a` |
| `github_token` | GitHub tokens | `example-github-token-123` |
### Using Prebuilt Patterns
@@ -790,7 +790,7 @@ curl -X POST "http://localhost:4000/v1/chat/completions" \
"messages": [
{
"role": "user",
"content": "Generate python code that accesses my Github repo using this PAT: ghp_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8"
"content": "Generate python code that accesses my Github repo using this PAT: example-github-token-123"
}
],
"max_tokens": 50
@@ -815,7 +815,7 @@ curl -X POST "http://localhost:4000/v1/chat/completions" \
"type": "github_token",
"start_idx": 66,
"end_idx": 106,
"evidence": "ghp_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8",
"evidence": "example-github-token-123",
}
]
}
@@ -89,7 +89,7 @@ curl -X POST 'http://0.0.0.0:4000/team/update' \
"id": "bd136c28-edd0-4cb6-b963-f35464cf6f5a",
"updated_at": "2024-06-08 23:41:14.793",
"changed_by": "krrish@berri.ai", # 👈 CHANGED BY
"changed_by_api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"changed_by_api_key": "example-api-key-123",
"action": "updated",
"table_name": "LiteLLM_TeamTable",
"object_id": "8bf18b11-7f52-4717-8e1f-7c65f9d01e52",
+1 -1
View File
@@ -33,7 +33,7 @@ litellm_settings:
Set slack webhook url in your env
```shell
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/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"
export SLACK_WEBHOOK_URL="example-slack-webhook-url"
```
Turn off FASTAPI's default info logs
+1 -1
View File
@@ -400,7 +400,7 @@ from anthropic import Anthropic
client = Anthropic(
base_url="http://localhost:4000", # proxy endpoint
api_key="sk-s4xN1IiLTCytwtZFJaYQrA", # litellm proxy virtual key
api_key="sk-test-proxy-key-123", # litellm proxy virtual key (example)
)
message = client.messages.create(
+1 -1
View File
@@ -285,7 +285,7 @@ from anthropic import Anthropic
client = Anthropic(
base_url="http://localhost:4000", # proxy endpoint
api_key="sk-s4xN1IiLTCytwtZFJaYQrA", # litellm proxy virtual key
api_key="sk-test-proxy-key-123", # litellm proxy virtual key (example)
)
message = client.messages.create(
+1 -1
View File
@@ -8,7 +8,7 @@ const darkCodeTheme = require('prism-react-renderer/themes/dracula');
const inkeepConfig = {
baseSettings: {
apiKey: "0cb9c9916ec71bfe0e53c9d7f83ff046daee3fa9ef318f6a",
apiKey: "test-inkeep-api-key-123",
organizationDisplayName: 'liteLLM',
primaryBrandColor: '#4965f5',
theme: {
@@ -1910,14 +1910,14 @@ async def info_key_fn(
Example Curl:
```
curl -X GET "http://0.0.0.0:4000/key/info?key=sk-02Wr4IAlN3NvPXvL5JVvDA" \
curl -X GET "http://0.0.0.0:4000/key/info?key=sk-test-example-key-123" \
-H "Authorization: Bearer sk-1234"
```
Example Curl - if no key is passed, it will use the Key Passed in Authorization Header
```
curl -X GET "http://0.0.0.0:4000/key/info" \
-H "Authorization: Bearer sk-02Wr4IAlN3NvPXvL5JVvDA"
-H "Authorization: Bearer sk-test-example-key-123"
```
"""
from litellm.proxy.proxy_server import prisma_client
@@ -1938,7 +1938,7 @@ async def view_spend_logs( # noqa: PLR0915
Example Request for specific api_key
```
curl -X GET "http://0.0.0.0:8000/spend/logs?api_key=sk-Fn8Ej39NkBQmUagFEoUWPQ" \
curl -X GET "http://0.0.0.0:8000/spend/logs?api_key=sk-test-example-key-123" \
-H "Authorization: Bearer sk-1234"
```
+1 -1
View File
@@ -21,7 +21,7 @@ from google.auth.transport.requests import Request
import httpx
# Configuration - update these for your agent
PROJECT_ID = "gen-lang-client-0682925754" # Your GCP project ID
PROJECT_ID = "test-gcp-project-id-123" # Your GCP project ID (test value)
LOCATION = "us-central1" # Your agent's location
# For Reasoning Engines, use just the numeric ID at the end
+1 -1
View File
@@ -13,7 +13,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
from litellm._uuid import uuid
# Set up environment variables for testing
os.environ["CYBERARK_API_KEY"] = "2syke5r262b6je2f4et1x3jptmry3frfx83t65e6417zad632e5qq8a"
os.environ["CYBERARK_API_KEY"] = "test-cyberark-api-key-909"
os.environ["CYBERARK_API_BASE"] = "http://0.0.0.0:8080"
os.environ["CYBERARK_ACCOUNT"] = "default"
os.environ["CYBERARK_USERNAME"] = "admin"
+1 -1
View File
@@ -193,7 +193,7 @@ def test_process_azure_endpoint_url(api_base, model, expected_endpoint):
"azure_deployment": model,
"max_retries": 2,
"timeout": 600,
"api_key": "f28ab7b695af4154bc53498e5bdccb07",
"api_key": "sk-test-mock-key-505",
},
"model": model,
}
@@ -218,7 +218,7 @@ def test_bedrock_agentcore_with_api_key_bearer_token():
from litellm.llms.custom_httpx.http_handler import HTTPHandler
client = HTTPHandler()
test_jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
test_jwt_token = "test-jwt-token-header.payload.signature"
with patch.object(client, "post", return_value=MagicMock()) as mock_post:
try:
@@ -295,7 +295,7 @@ def bedrock_session_token_creds():
aws_role_name = (
"arn:aws:iam::335785316107:role/litellm-github-unit-tests-circleci"
)
aws_web_identity_token = "oidc/circleci_v2/"
aws_web_identity_token = "test-oidc-token-123"
creds = bllm.get_credentials(
aws_region_name=aws_region_name,
+2 -2
View File
@@ -1019,7 +1019,7 @@ generation_params = {
],
},
},
"user_api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"user_api_key": "sk-test-mock-api-key-123",
"litellm_api_version": "0.0.0",
"user_api_key_user_id": "default_user_id",
"user_api_key_spend": 0.0,
@@ -1142,7 +1142,7 @@ def test_langfuse_prompt_type(prompt):
],
},
},
"user_api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"user_api_key": "sk-test-mock-api-key-123",
"litellm_api_version": "0.0.0",
"user_api_key_user_id": "default_user_id",
"user_api_key_spend": 0.0,
+2 -2
View File
@@ -4160,10 +4160,10 @@ def test_openai_hallucinated_tool_call_util(function_name, expect_modification):
def test_langfuse_completion(monkeypatch):
monkeypatch.setenv(
"LANGFUSE_PUBLIC_KEY", "pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003"
"LANGFUSE_PUBLIC_KEY", "test-langfuse-public-key-123"
)
monkeypatch.setenv(
"LANGFUSE_SECRET_KEY", "sk-lf-b11ef3a8-361c-4445-9652-12318b8596e4"
"LANGFUSE_SECRET_KEY", "test-langfuse-secret-key-456"
)
monkeypatch.setenv("LANGFUSE_HOST", "https://us.cloud.langfuse.com")
litellm.set_verbose = True
+1 -1
View File
@@ -401,7 +401,7 @@ def test_dalle_3_azure_cost_tracking():
{
"b64_json": None,
"revised_prompt": "A close-up image of an adorable baby sea otter. Its fur is thick and fluffy to provide buoyancy and insulation against the cold water. Its eyes are round, curious and full of life. It's lying on its back, floating effortlessly on the calm sea surface under the warm sun. Surrounding the otter are patches of colorful kelp drifting along the gentle waves, giving the scene a touch of vibrancy. The sea otter has its small paws folded on its chest, and it seems to be taking a break from its play.",
"url": "https://dalleprodsec.blob.core.windows.net/private/images/3e5d00f3-700e-4b75-869d-2de73c3c975d/generated_00.png?se=2024-03-13T17%3A49%3A51Z&sig=R9RJD5oOSe0Vp9Eg7ze%2FZ8QR7ldRyGH6XhMxiau16Jc%3D&ske=2024-03-19T11%3A08%3A03Z&skoid=e52d5ed7-0657-4f62-bc12-7e5dbb260a96&sks=b&skt=2024-03-12T11%3A08%3A03Z&sktid=33e01921-4d64-4f8c-a055-5bdaffd5e33d&skv=2020-10-02&sp=r&spr=https&sr=b&sv=2020-10-02",
"url": "test-azure-blob-url-with-sas-token",
}
],
)
+1 -1
View File
@@ -176,7 +176,7 @@ def invalid_auth(model): # set the model key to an invalid key, depending on th
elif "togethercomputer" in model:
temporary_key = os.environ["TOGETHERAI_API_KEY"]
os.environ["TOGETHERAI_API_KEY"] = (
"84060c79880fc49df126d3e87b53f8a463ff6e1c6d27fe64207cde25cdfcd1f24a"
"sk-test-togetherai-key-808"
)
elif model in litellm.openrouter_models:
temporary_key = os.environ["OPENROUTER_API_KEY"]
+6 -6
View File
@@ -83,7 +83,7 @@ async def test_aaabasic_gcs_logger():
mock_response="Hi!",
metadata={
"tags": ["model-anthropic-claude-v2.1", "app-ishaan-prod"],
"user_api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"user_api_key": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
"user_api_key_alias": None,
"user_api_end_user_max_budget": None,
"litellm_api_version": "0.0.0",
@@ -155,7 +155,7 @@ async def test_aaabasic_gcs_logger():
assert (
gcs_payload["metadata"]["user_api_key_hash"]
== "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b"
== "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456"
)
assert gcs_payload["metadata"]["user_api_key_user_id"] == "116544810872468347480"
@@ -191,7 +191,7 @@ async def test_basic_gcs_logger_failure():
metadata={
"gcs_log_id": gcs_log_id,
"tags": ["model-anthropic-claude-v2.1", "app-ishaan-prod"],
"user_api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"user_api_key": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
"user_api_key_alias": None,
"user_api_end_user_max_budget": None,
"litellm_api_version": "0.0.0",
@@ -259,7 +259,7 @@ async def test_basic_gcs_logger_failure():
assert (
gcs_payload["metadata"]["user_api_key_hash"]
== "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b"
== "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456"
)
assert gcs_payload["metadata"]["user_api_key_user_id"] == "116544810872468347480"
@@ -599,7 +599,7 @@ async def test_basic_gcs_logger_with_folder_in_bucket_name():
mock_response="Hi!",
metadata={
"tags": ["model-anthropic-claude-v2.1", "app-ishaan-prod"],
"user_api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"user_api_key": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
"user_api_key_alias": None,
"user_api_end_user_max_budget": None,
"litellm_api_version": "0.0.0",
@@ -671,7 +671,7 @@ async def test_basic_gcs_logger_with_folder_in_bucket_name():
assert (
gcs_payload["metadata"]["user_api_key_hash"]
== "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b"
== "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456"
)
assert gcs_payload["metadata"]["user_api_key_user_id"] == "116544810872468347480"
@@ -446,7 +446,7 @@ async def test_aaapass_through_endpoint_pass_through_keys_langfuse(
response = client.post(
"/api/public/ingestion",
json=_json_data,
headers={"Authorization": "Basic c2stbXktdGVzdC1rZXk6YW55dGhpbmc="},
headers={"Authorization": "Basic test-base64-auth-token-123"},
)
print("JSON response: ", _json_data)
@@ -488,7 +488,7 @@ async def test_send_token_budget_crossed_alerts(alerting_type):
with patch.object(slack_alerting, "send_alert", new=AsyncMock()) as mock_send_alert:
user_info = {
"token": "50e55ca5bfbd0759697538e8d23c0cd5031f52d9e19e176d7233b20c7c4d3403",
"token": "sk-test-mock-token-606",
"spend": 86,
"max_budget": 100,
"user_id": "ishaan@berri.ai",
@@ -528,7 +528,7 @@ async def test_webhook_alerting(alerting_type):
slack_alerting, "send_webhook_alert", new=AsyncMock()
) as mock_send_alert:
user_info = {
"token": "50e55ca5bfbd0759697538e8d23c0cd5031f52d9e19e176d7233b20c7c4d3403",
"token": "sk-test-mock-token-606",
"spend": 1,
"max_budget": 0,
"user_id": "ishaan@berri.ai",
@@ -559,7 +559,7 @@ async def test_webhook_alerting(alerting_type):
# slack_alerting, "send_webhook_alert", new=AsyncMock()
# ) as mock_send_alert:
# user_info = {
# "token": "50e55ca5bfbd0759697538e8d23c0cd5031f52d9e19e176d7233b20c7c4d3403",
# "token": "sk-test-mock-token-606",
# "spend": 1,
# "max_budget": 0,
# "user_id": "ishaan@berri.ai",
@@ -54,7 +54,7 @@ def test_spend_logs_payload(model_id: Optional[str]):
},
"litellm_params": {
"acompletion": True,
"api_key": "23c217a5b59f41b6b7a198017f4792f2",
"api_key": "sk-test-mock-key-707",
"force_timeout": 600,
"logger_fn": None,
"verbose": False,
@@ -65,7 +65,7 @@ def test_spend_logs_payload(model_id: Optional[str]):
"completion_call_id": None,
"metadata": {
"tags": ["model-anthropic-claude-v2.1", "app-ishaan-prod"],
"user_api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"user_api_key": "sk-test-mock-api-key-123",
"user_api_key_alias": "custom-key-alias",
"user_api_end_user_max_budget": None,
"litellm_api_version": "0.0.0",
@@ -243,7 +243,7 @@ def test_spend_logs_payload_whisper():
"litellm_params": {
"api_base": "",
"metadata": {
"user_api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"user_api_key": "sk-test-mock-api-key-123",
"user_api_key_alias": None,
"user_api_key_end_user_id": "test-user",
"user_api_end_user_max_budget": None,
@@ -42,7 +42,7 @@ mock_response_data = {
"response_time": 0.1622769832611084,
"model": "my-fake-model",
"metadata": {
"user_api_key_hash": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"user_api_key_hash": "sk-test-mock-api-key-123",
"user_api_key_alias": None,
"user_api_key_team_id": None,
"user_api_key_org_id": None,
@@ -6,7 +6,7 @@ client = Anthropic(
# This is the default and can be omitted
base_url="http://localhost:4000",
# this is a litellm proxy key :) - not a real anthropic key
api_key="sk-s4xN1IiLTCytwtZFJaYQrA",
api_key="sk-test-proxy-key-123",
)
message = client.messages.create(
+27 -14
View File
@@ -8,6 +8,7 @@ import asyncio
from litellm._uuid import uuid
import os
import sys
import hashlib
from openai import AsyncOpenAI
from typing import Dict, Any
@@ -93,7 +94,7 @@ async def test_proxy_failure_metrics():
async with aiohttp.ClientSession() as session:
# Make a bad chat completion call
status, response_text = await make_bad_chat_completion_request(
session, "sk-1234"
session, "sk-test-1234"
)
# Check if the request failed as expected
@@ -105,8 +106,12 @@ async def test_proxy_failure_metrics():
print("/metrics", metrics)
# Compute expected hash for test key
test_key = "sk-test-1234"
expected_hash = hashlib.sha256(test_key.encode()).hexdigest()
# Check if the failure metric is present and correct - use pattern matching for robustness
expected_metric_pattern = 'litellm_proxy_failed_requests_metric_total{api_key_alias="None",end_user="None",exception_class="Openai.RateLimitError",exception_status="429",hashed_api_key="88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",requested_model="fake-azure-endpoint",route="/chat/completions",team="None",team_alias="None",user="default_user_id",user_email="None"}'
expected_metric_pattern = f'litellm_proxy_failed_requests_metric_total{{api_key_alias="None",end_user="None",exception_class="Openai.RateLimitError",exception_status="429",hashed_api_key="{expected_hash}",requested_model="fake-azure-endpoint",route="/chat/completions",team="None",team_alias="None",user="default_user_id",user_email="None"}}'
# Check if the pattern is in metrics (this metric doesn't include user_email field)
assert any(
@@ -114,7 +119,7 @@ async def test_proxy_failure_metrics():
), f"Expected failure metric pattern not found in /metrics. Pattern: {expected_metric_pattern}"
# Check total requests metric which includes user_email
total_requests_pattern = 'litellm_proxy_total_requests_metric_total{api_key_alias="None",end_user="None",hashed_api_key="88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",requested_model="fake-azure-endpoint",route="/chat/completions",status_code="429",team="None",team_alias="None",user="default_user_id",user_email="None"}'
total_requests_pattern = f'litellm_proxy_total_requests_metric_total{{api_key_alias="None",end_user="None",hashed_api_key="{expected_hash}",requested_model="fake-azure-endpoint",route="/chat/completions",status_code="429",team="None",team_alias="None",user="default_user_id",user_email="None"}}'
assert any(
total_requests_pattern in line for line in metrics.split("\n")
@@ -133,7 +138,7 @@ async def test_proxy_success_metrics():
async with aiohttp.ClientSession() as session:
# Make a good chat completion call
status, response_text = await make_good_chat_completion_request(
session, "sk-1234"
session, "sk-test-1234"
)
# Check if the request succeeded as expected
@@ -147,14 +152,18 @@ async def test_proxy_success_metrics():
assert END_USER_ID not in metrics
# Compute expected hash for test key
test_key = "sk-test-1234"
expected_hash = hashlib.sha256(test_key.encode()).hexdigest()
# Check if the success metric is present and correct
assert (
'litellm_request_total_latency_metric_bucket{api_key_alias="None",end_user="None",hashed_api_key="88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",le="0.005",model="fake",requested_model="fake-openai-endpoint",team="None",team_alias="None",user="default_user_id"}'
f'litellm_request_total_latency_metric_bucket{{api_key_alias="None",end_user="None",hashed_api_key="{expected_hash}",le="0.005",model="fake",requested_model="fake-openai-endpoint",team="None",team_alias="None",user="default_user_id"}}'
in metrics
)
assert (
'litellm_llm_api_latency_metric_bucket{api_key_alias="None",end_user="None",hashed_api_key="88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",le="0.005",model="fake",requested_model="fake-openai-endpoint",team="None",team_alias="None",user="default_user_id"}'
f'litellm_llm_api_latency_metric_bucket{{api_key_alias="None",end_user="None",hashed_api_key="{expected_hash}",le="0.005",model="fake",requested_model="fake-openai-endpoint",team="None",team_alias="None",user="default_user_id"}}'
in metrics
)
@@ -215,7 +224,7 @@ async def test_proxy_fallback_metrics():
async with aiohttp.ClientSession() as session:
# Make a good chat completion call
await make_chat_completion_request_with_fallback(session, "sk-1234")
await make_chat_completion_request_with_fallback(session, "sk-test-1234")
# Get metrics
async with session.get("http://0.0.0.0:4000/metrics") as response:
@@ -223,15 +232,19 @@ async def test_proxy_fallback_metrics():
print("/metrics", metrics)
# Compute expected hash for test key
test_key = "sk-test-1234"
expected_hash = hashlib.sha256(test_key.encode()).hexdigest()
# Check if successful fallback metric is incremented
assert (
'litellm_deployment_successful_fallbacks_total{api_key_alias="None",exception_class="Openai.RateLimitError",exception_status="429",fallback_model="fake-openai-endpoint",hashed_api_key="88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",requested_model="fake-azure-endpoint",team="None",team_alias="None"} 1.0'
f'litellm_deployment_successful_fallbacks_total{{api_key_alias="None",exception_class="Openai.RateLimitError",exception_status="429",fallback_model="fake-openai-endpoint",hashed_api_key="{expected_hash}",requested_model="fake-azure-endpoint",team="None",team_alias="None"}} 1.0'
in metrics
)
# Check if failed fallback metric is incremented
assert (
'litellm_deployment_failed_fallbacks_total{api_key_alias="None",exception_class="Openai.RateLimitError",exception_status="429",fallback_model="unknown-model",hashed_api_key="88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",requested_model="fake-azure-endpoint",team="None",team_alias="None"} 1.0'
f'litellm_deployment_failed_fallbacks_total{{api_key_alias="None",exception_class="Openai.RateLimitError",exception_status="429",fallback_model="unknown-model",hashed_api_key="{expected_hash}",requested_model="fake-azure-endpoint",team="None",team_alias="None"}} 1.0'
in metrics
)
@@ -242,7 +255,7 @@ async def create_test_team(
"""Create a new team and return the team_id"""
url = "http://0.0.0.0:4000/team/new"
headers = {
"Authorization": "Bearer sk-1234",
"Authorization": "Bearer sk-test-1234",
"Content-Type": "application/json",
}
@@ -260,7 +273,7 @@ async def create_test_user(
"""Create a new user and return the user info"""
url = "http://0.0.0.0:4000/user/new"
headers = {
"Authorization": "Bearer sk-1234",
"Authorization": "Bearer sk-test-1234",
"Content-Type": "application/json",
}
@@ -307,7 +320,7 @@ async def create_test_key(session: aiohttp.ClientSession, team_id: str) -> str:
"""Generate a new key for the team and return it"""
url = "http://0.0.0.0:4000/key/generate"
headers = {
"Authorization": "Bearer sk-1234",
"Authorization": "Bearer sk-test-1234",
"Content-Type": "application/json",
}
data = {
@@ -326,7 +339,7 @@ async def get_team_info(session: aiohttp.ClientSession, team_id: str) -> Dict[st
"""Fetch team info and return the response"""
url = f"http://0.0.0.0:4000/team/info?team_id={team_id}"
headers = {
"Authorization": "Bearer sk-1234",
"Authorization": "Bearer sk-test-1234",
}
async with session.get(url, headers=headers) as response:
@@ -415,7 +428,7 @@ async def create_test_key_with_budget(
"""Generate a new key with budget constraints and return it"""
url = "http://0.0.0.0:4000/key/generate"
headers = {
"Authorization": "Bearer sk-1234",
"Authorization": "Bearer sk-test-1234",
"Content-Type": "application/json",
}
print("budget_data", budget_data)
@@ -105,7 +105,7 @@ def test_create_anthropic_response_logging_payload(mock_logging_obj, metadata_pa
kwargs={
"litellm_params": {
"metadata": {
"user_api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"user_api_key": "sk-test-mock-api-key-123",
"user_api_key_user_id": "default_user_id",
"user_api_key_team_id": None,
"user_api_key_end_user_id": ("test" if metadata_params else ""),
@@ -21,7 +21,7 @@ def test_aaaasschema_migration_check(schema_setup, monkeypatch):
"""Test to check if schema requires migration"""
# Set test database URL
test_db_url = f"postgresql://{schema_setup.info.user}:@{schema_setup.info.host}:{schema_setup.info.port}/{schema_setup.info.dbname}"
# test_db_url = "postgresql://neondb_owner:npg_JiZPS0DAhRn4@ep-delicate-wave-a55cvbuc.us-east-2.aws.neon.tech/neondb?sslmode=require"
# test_db_url = "postgresql://test-user:test-password@test-host.example.com/test-db?sslmode=require"
monkeypatch.setenv("DATABASE_URL", test_db_url)
deploy_dir = Path("./litellm-proxy-extras/litellm_proxy_extras")
+2 -2
View File
@@ -1266,7 +1266,7 @@ def test_user_api_key_auth_jwt_hashing():
from litellm.proxy.auth.handle_jwt import JWTHandler
# Test with a JWT token (3 parts separated by dots)
jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
jwt_token = "test-jwt-token-header.payload.signature"
# Create UserAPIKeyAuth instance with JWT
user_auth = UserAPIKeyAuth(api_key=jwt_token)
@@ -1303,7 +1303,7 @@ def test_jwt_handler_is_jwt_static_method():
from litellm.proxy.auth.handle_jwt import JWTHandler
# Test with valid JWT format
valid_jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
valid_jwt = "test-jwt-token-header.payload.signature"
assert JWTHandler.is_jwt(valid_jwt) == True
# Test with invalid JWT format (only 2 parts)
+5 -5
View File
@@ -238,7 +238,7 @@ def test_dynamic_logging_metadata_key_and_team_metadata(callback_vars):
proxy_config = ProxyConfig()
user_api_key_dict = UserAPIKeyAuth(
token="6f8688eaff1d37555bb9e9a6390b6d7032b3ab2526ba0152da87128eab956432",
token="sk-test-mock-token-789",
key_name="sk-...63Fg",
key_alias=None,
spend=0.000111,
@@ -287,7 +287,7 @@ def test_dynamic_logging_metadata_key_and_team_metadata(callback_vars):
end_user_rpm_limit=None,
end_user_max_budget=None,
last_refreshed_at=1726101560.967527,
api_key="7c305cc48fe72272700dc0d67dc691c2d1f2807490ef5eb2ee1d3a3ca86e12b1",
api_key="sk-test-mock-api-key-202",
user_role=LitellmUserRoles.INTERNAL_USER,
allowed_model_region=None,
parent_otel_span=None,
@@ -320,7 +320,7 @@ def test_dynamic_turn_off_message_logging(callback_vars):
proxy_config = ProxyConfig()
user_api_key_dict = UserAPIKeyAuth(
token="6f8688eaff1d37555bb9e9a6390b6d7032b3ab2526ba0152da87128eab956432",
token="sk-test-mock-token-789",
key_name="sk-...63Fg",
key_alias=None,
spend=0.000111,
@@ -368,7 +368,7 @@ def test_dynamic_turn_off_message_logging(callback_vars):
end_user_rpm_limit=None,
end_user_max_budget=None,
last_refreshed_at=1726101560.967527,
api_key="7c305cc48fe72272700dc0d67dc691c2d1f2807490ef5eb2ee1d3a3ca86e12b1",
api_key="sk-test-mock-api-key-202",
user_role=LitellmUserRoles.INTERNAL_USER,
allowed_model_region=None,
parent_otel_span=None,
@@ -1267,7 +1267,7 @@ def test_litellm_verification_token_view_response_with_budget_table(
from litellm.proxy._types import LiteLLM_VerificationTokenView
args: Dict[str, Any] = {
"token": "78b627d4d14bc3acf5571ae9cb6834e661bc8794d1209318677387add7621ce1",
"token": "sk-test-mock-token-303",
"key_name": "sk-...if_g",
"key_alias": None,
"soft_budget_cooldown": False,
@@ -696,7 +696,7 @@ def test_is_allowed_route():
"request": request,
"request_data": {"input": ["hello world"], "model": "embedding-small"},
"valid_token": UserAPIKeyAuth(
token="9644159bc181998825c44c788b1526341ed2e825d1b6f562e23173759e14bb86",
token="sk-test-mock-token-101",
key_name="sk-...CJjQ",
key_alias=None,
spend=0.0,
+1 -1
View File
@@ -26,7 +26,7 @@ async def config_update(session, routing_strategy=None):
},
"general_settings": {
"alert_to_webhook_url": {
"llm_exceptions": "https://hooks.slack.com/services/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"
"llm_exceptions": "example-slack-webhook-url"
},
"alert_types": ["llm_exceptions", "db_exceptions"],
},
@@ -46,7 +46,7 @@ class TestCloudZeroHourlyExport:
{
"team_id": ["a3d6b0bb-098f-4260-81d6-fabae695b622"],
"key_alias": ["key_1"],
"token": ["c1465c9a821f420927b3d81972323fb516745bc93a4a54ceca0ce6ddf6100c39"],
"token": ["sk-test-cloudzero-token-010"],
}
)
@@ -193,7 +193,7 @@ def test_vertex_ai_bge_psc_endpoint_url_construction():
client = HTTPHandler()
def mock_auth_token(*args, **kwargs):
return "fake-token", "gen-lang-client-0682925754"
return "test-token-123", "test-gcp-project-id-123"
with patch.object(client, "post") as mock_post, patch(
"litellm.llms.vertex_ai.vertex_embeddings.embedding_handler.VertexEmbedding._ensure_access_token",
@@ -212,7 +212,7 @@ def test_vertex_ai_bge_psc_endpoint_url_construction():
model="vertex_ai/bge/378943383978115072",
input=["The food was delicious and the waiter.."],
api_base="http://10.128.16.2",
vertex_project="gen-lang-client-0682925754",
vertex_project="test-gcp-project-id-123",
vertex_location="us-central1",
client=client,
use_psc_endpoint_format=True # Enable PSC endpoint format for this test
@@ -239,7 +239,7 @@ def test_vertex_ai_bge_psc_endpoint_url_construction():
print("="*50 + "\n")
# Verify the URL is constructed correctly
expected_url = "http://10.128.16.2/v1/projects/gen-lang-client-0682925754/locations/us-central1/endpoints/378943383978115072:predict"
expected_url = "http://10.128.16.2/v1/projects/test-gcp-project-id-123/locations/us-central1/endpoints/378943383978115072:predict"
assert api_url_called == expected_url, f"Expected URL: {expected_url}, Got: {api_url_called}"
# Verify bge/ prefix is NOT in the URL
@@ -332,7 +332,7 @@ class TestMCPRequestHandler:
async def mock_user_api_key_auth(api_key, request):
return UserAPIKeyAuth(
token=(
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
"test-token-sha256-empty-hash"
if api_key
else None
),
@@ -691,7 +691,7 @@ class TestMCPCustomHeaderName:
# Create an async mock for user_api_key_auth
async def mock_user_api_key_auth(api_key, request):
return UserAPIKeyAuth(
token="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
token="test-token-sha256-empty-hash",
api_key=api_key,
user_id="test-user-id",
team_id="test-team-id",
@@ -866,7 +866,7 @@ class TestMCPAccessGroupsE2E:
# Create an async mock for user_api_key_auth
async def mock_user_api_key_auth(api_key, request):
return UserAPIKeyAuth(
token="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
token="test-token-sha256-empty-hash",
api_key=api_key,
user_id="test-user-id",
team_id="test-team-id",
@@ -917,7 +917,7 @@ class TestMCPAccessGroupsE2E:
# Create an async mock for user_api_key_auth
async def mock_user_api_key_auth(api_key, request):
return UserAPIKeyAuth(
token="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
token="test-token-sha256-empty-hash",
api_key=api_key,
user_id="test-user-id",
team_id="test-team-id",
@@ -43,8 +43,8 @@ def mock_user_api_key_dict():
team_id="test-team",
team_alias=None,
user_role=None,
api_key="88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
token="88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
api_key="a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
token="a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
permissions={},
models=[],
spend=0.0,
@@ -71,7 +71,7 @@ def mock_request_data_input():
],
"litellm_call_id": "test-call-id",
"metadata": {
"user_api_key_hash": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"user_api_key_hash": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
"user_api_key_user_id": "default_user_id",
"user_api_key_user_email": "test@example.com",
"user_api_key_team_id": "test-team",
@@ -197,7 +197,7 @@ class TestMetadataExtraction:
# Verify metadata was extracted from request_data["metadata"]
assert (
request_metadata["user_api_key_hash"]
== "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b"
== "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456"
)
assert request_metadata["user_api_key_user_id"] == "default_user_id"
assert request_metadata["user_api_key_user_email"] == "test@example.com"
@@ -2050,7 +2050,7 @@ class TestProcessSSOJWTAccessToken:
@pytest.fixture
def sample_jwt_token(self):
"""Create a sample JWT token string"""
return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
return "test-jwt-token-header.payload.signature"
@pytest.fixture
def sample_jwt_payload(self):
+4 -4
View File
@@ -180,7 +180,7 @@ class TestProxyInitializationHelpers:
test_env = {
"DATABASE_HOST": "localhost:5432",
"DATABASE_USERNAME": "user@with+special",
"DATABASE_PASSWORD": "pass&word!@#$%",
"DATABASE_PASSWORD": "test-password-special-chars",
"DATABASE_NAME": "db_name/test",
}
@@ -205,7 +205,7 @@ class TestProxyInitializationHelpers:
database_url = f"postgresql://{database_username_enc}:{database_password_enc}@{database_host}/{database_name_enc}"
# Assert the correct URL was constructed with properly escaped characters
expected_url = "postgresql://user%40with%2Bspecial:pass%26word%21%40%23%24%25@localhost:5432/db_name%2Ftest"
expected_url = "postgresql://user%40with%2Bspecial:test-password-special-chars@localhost:5432/db_name%2Ftest"
assert database_url == expected_url
# Test appending query parameters
@@ -381,13 +381,13 @@ class TestProxyInitializationHelpers:
test_env_special = {
"DATABASE_HOST": "localhost:5432",
"DATABASE_USERNAME": "user@with+special",
"DATABASE_PASSWORD": "pass&word!@#$%",
"DATABASE_PASSWORD": "test-password-special-chars",
"DATABASE_NAME": "db_name/test",
}
with patch.dict(os.environ, test_env_special):
result = construct_database_url_from_env_vars()
expected_url = "postgresql://user%40with%2Bspecial:pass%26word%21%40%23%24%25@localhost:5432/db_name%2Ftest"
expected_url = "postgresql://user%40with%2Bspecial:test-password-special-chars@localhost:5432/db_name%2Ftest"
assert result == expected_url
# Test without password (should still work)
@@ -559,7 +559,7 @@ async def test_aaaproxy_startup_master_key(mock_prisma, monkeypatch, tmp_path):
assert master_key == test_master_key
# Test Case 2: Master key from environment variable
test_env_master_key = "sk-67890"
test_env_master_key = "sk-test-67890"
# Create empty config
empty_config = {"general_settings": {}}
@@ -644,7 +644,7 @@ class TestIsAllowedToCallVectorStoreEndpoint:
mock_request.method = "GET"
mock_request.url.path = "/azure_ai/indexes/dall-e-4/docs/search"
mock_user_api_key = UserAPIKeyAuth(
token="b637312ebffb9745321224644430ba9e4916a291c8281f293d21182c5e80bc5a",
token="sk-test-mock-token-404",
key_name="sk-...plNQ",
metadata={
"allowed_vector_store_indexes": [
@@ -27,7 +27,7 @@ async def test_get_chat_completion_message_history_for_previous_response_id():
{
"request_id": "chatcmpl-935b8dad-fdc2-466e-a8ca-e26e5a8a21bb",
"call_type": "aresponses",
"api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"api_key": "sk-test-mock-api-key-123",
"spend": 0.004803,
"total_tokens": 329,
"prompt_tokens": 11,
@@ -68,7 +68,7 @@ async def test_get_chat_completion_message_history_for_previous_response_id():
{
"request_id": "chatcmpl-370760c9-39fa-4db7-b034-d1f8d933c935",
"call_type": "aresponses",
"api_key": "88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b",
"api_key": "sk-test-mock-api-key-123",
"spend": 0.010437,
"total_tokens": 967,
"prompt_tokens": 339,
+1 -1
View File
@@ -855,7 +855,7 @@ def test_azure_image_generation_cost_calculator():
ImageObject(
b64_json=None,
revised_prompt="A futuristic, techno-inspired green duck wearing cool modern sunglasses. The duck has a sleek, metallic appearance with glowing neon green accents, standing on a high-tech urban background with holographic billboards and illuminated city lights in the distance. The duck's feathers have a glossy, high-tech sheen, resembling a robotic design but still maintaining its avian features. The scene has a vibrant, cyberpunk aesthetic with a neon color palette.",
url="https://dalleprodsec.blob.core.windows.net/private/images/caa17dc4-357d-4257-8938-eeea9baa8d0a/generated_00.png?se=2025-10-31T00%3A47%3A59Z&sig=KHRjLz3vMahbw94JtxL02S6t2AueeRMaiqj4z35HKDM%3D&ske=2025-11-05T00%3A26%3A20Z&skoid=e52d5ed7-0657-4f62-bc12-7e5dbb260a96&sks=b&skt=2025-10-29T00%3A26%3A20Z&sktid=33e01921-4d64-4f8c-a055-5bdaffd5e33d&skv=2020-10-02&sp=r&spr=https&sr=b&sv=2020-10-02",
url="test-azure-blob-url-with-sas-token",
)
],
output_format=None,
+1 -1
View File
@@ -198,7 +198,7 @@ async def get_predict_spend_logs(session):
{
"date": "2024-03-09",
"spend": 200000,
"api_key": "f19bdeb945164278fc11c1020d8dfd70465bffd931ed3cb2e1efa6326225b8b7",
"api_key": "sk-test-mock-api-key-456",
}
]
}
@@ -162,13 +162,13 @@ const CacheDashboard: React.FC<CachePageProps> = ({ accessToken, token, userRole
/*
Data looks like this
[{"api_key":"147dba2181f28914eea90eb484926c293cdcf7f5b5c9c3dd6a004d9e0f9fdb21","call_type":"acompletion","model":"llama3-8b-8192","total_rows":13,"cache_hit_true_rows":0},
{"api_key":"8c23f021d0535c2e59abb7d83d0e03ccfb8db1b90e231ff082949d95df419e86","call_type":"None","model":"chatgpt-v-2","total_rows":1,"cache_hit_true_rows":0},
{"api_key":"88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b","call_type":"acompletion","model":"gpt-3.5-turbo","total_rows":19,"cache_hit_true_rows":0},
{"api_key":"88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b","call_type":"aimage_generation","model":"","total_rows":3,"cache_hit_true_rows":0},
{"api_key":"0ad4b3c03dcb6de0b5b8f761db798c6a8ae80be3fd1e2ea30c07ce6d5e3bf870","call_type":"None","model":"chatgpt-v-2","total_rows":1,"cache_hit_true_rows":0},
{"api_key":"034224b36e9769bc50e2190634abc3f97cad789b17ca80ac43b82f46cd5579b3","call_type":"","model":"chatgpt-v-2","total_rows":1,"cache_hit_true_rows":0},
{"api_key":"4f9c71cce0a2bb9a0b62ce6f0ebb3245b682702a8851d26932fa7e3b8ebfc755","call_type":"","model":"chatgpt-v-2","total_rows":1,"cache_hit_true_rows":0},
[{"api_key":"sk-test-mock-key-001","call_type":"acompletion","model":"llama3-8b-8192","total_rows":13,"cache_hit_true_rows":0},
{"api_key":"sk-test-mock-key-002","call_type":"None","model":"chatgpt-v-2","total_rows":1,"cache_hit_true_rows":0},
{"api_key":"sk-test-mock-key-123","call_type":"acompletion","model":"gpt-3.5-turbo","total_rows":19,"cache_hit_true_rows":0},
{"api_key":"sk-test-mock-key-123","call_type":"aimage_generation","model":"","total_rows":3,"cache_hit_true_rows":0},
{"api_key":"sk-test-mock-key-003","call_type":"None","model":"chatgpt-v-2","total_rows":1,"cache_hit_true_rows":0},
{"api_key":"sk-test-mock-key-004","call_type":"","model":"chatgpt-v-2","total_rows":1,"cache_hit_true_rows":0},
{"api_key":"sk-test-mock-key-005","call_type":"","model":"chatgpt-v-2","total_rows":1,"cache_hit_true_rows":0},
*/
// What data we need for bar chat