mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-26 19:24:27 +00:00
Merge branch 'main' into litellm_vertex_migration
This commit is contained in:
commit
0525fb75f3
319 changed files with 23692 additions and 5152 deletions
|
@ -47,7 +47,7 @@ jobs:
|
|||
pip install opentelemetry-api==1.25.0
|
||||
pip install opentelemetry-sdk==1.25.0
|
||||
pip install opentelemetry-exporter-otlp==1.25.0
|
||||
pip install openai
|
||||
pip install openai==1.34.0
|
||||
pip install prisma
|
||||
pip install "detect_secrets==1.5.0"
|
||||
pip install "httpx==0.24.1"
|
||||
|
@ -208,6 +208,7 @@ jobs:
|
|||
-e AZURE_EUROPE_API_KEY=$AZURE_EUROPE_API_KEY \
|
||||
-e MISTRAL_API_KEY=$MISTRAL_API_KEY \
|
||||
-e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \
|
||||
-e GROQ_API_KEY=$GROQ_API_KEY \
|
||||
-e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \
|
||||
-e AWS_REGION_NAME=$AWS_REGION_NAME \
|
||||
-e AUTO_INFER_REGION=True \
|
||||
|
@ -243,7 +244,102 @@ jobs:
|
|||
command: |
|
||||
pwd
|
||||
ls
|
||||
python -m pytest -vv tests/ -x --junitxml=test-results/junit.xml --durations=5
|
||||
python -m pytest -vv tests/ -x --junitxml=test-results/junit.xml --durations=5 --ignore=tests/otel_tests
|
||||
no_output_timeout: 120m
|
||||
|
||||
# Store test results
|
||||
- store_test_results:
|
||||
path: test-results
|
||||
proxy_log_to_otel_tests:
|
||||
machine:
|
||||
image: ubuntu-2204:2023.10.1
|
||||
resource_class: xlarge
|
||||
working_directory: ~/project
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install Docker CLI (In case it's not already installed)
|
||||
command: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
|
||||
- run:
|
||||
name: Install Python 3.9
|
||||
command: |
|
||||
curl https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh --output miniconda.sh
|
||||
bash miniconda.sh -b -p $HOME/miniconda
|
||||
export PATH="$HOME/miniconda/bin:$PATH"
|
||||
conda init bash
|
||||
source ~/.bashrc
|
||||
conda create -n myenv python=3.9 -y
|
||||
conda activate myenv
|
||||
python --version
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: |
|
||||
pip install "pytest==7.3.1"
|
||||
pip install "pytest-asyncio==0.21.1"
|
||||
pip install aiohttp
|
||||
pip install openai
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install -r .circleci/requirements.txt
|
||||
pip install "pytest==7.3.1"
|
||||
pip install "pytest-mock==3.12.0"
|
||||
pip install "pytest-asyncio==0.21.1"
|
||||
pip install mypy
|
||||
pip install pyarrow
|
||||
pip install numpydoc
|
||||
pip install prisma
|
||||
pip install fastapi
|
||||
pip install jsonschema
|
||||
pip install "httpx==0.24.1"
|
||||
pip install "anyio==3.7.1"
|
||||
pip install "asyncio==3.4.3"
|
||||
pip install "PyGithub==1.59.1"
|
||||
- run:
|
||||
name: Build Docker image
|
||||
command: docker build -t my-app:latest -f Dockerfile.database .
|
||||
- run:
|
||||
name: Run Docker container
|
||||
# intentionally give bad redis credentials here
|
||||
# the OTEL test - should get this as a trace
|
||||
command: |
|
||||
docker run -d \
|
||||
-p 4000:4000 \
|
||||
-e DATABASE_URL=$PROXY_DATABASE_URL \
|
||||
-e REDIS_HOST=$REDIS_HOST \
|
||||
-e REDIS_PASSWORD=$REDIS_PASSWORD \
|
||||
-e REDIS_PORT=$REDIS_PORT \
|
||||
-e LITELLM_MASTER_KEY="sk-1234" \
|
||||
-e OPENAI_API_KEY=$OPENAI_API_KEY \
|
||||
-e LITELLM_LICENSE=$LITELLM_LICENSE \
|
||||
-e OTEL_EXPORTER="in_memory" \
|
||||
--name my-app \
|
||||
-v $(pwd)/litellm/proxy/example_config_yaml/otel_test_config.yaml:/app/config.yaml \
|
||||
my-app:latest \
|
||||
--config /app/config.yaml \
|
||||
--port 4000 \
|
||||
--detailed_debug \
|
||||
- run:
|
||||
name: Install curl and dockerize
|
||||
command: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y curl
|
||||
sudo wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz
|
||||
sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz
|
||||
sudo rm dockerize-linux-amd64-v0.6.1.tar.gz
|
||||
- run:
|
||||
name: Start outputting logs
|
||||
command: docker logs -f my-app
|
||||
background: true
|
||||
- run:
|
||||
name: Wait for app to be ready
|
||||
command: dockerize -wait http://localhost:4000 -timeout 5m
|
||||
- run:
|
||||
name: Run tests
|
||||
command: |
|
||||
pwd
|
||||
ls
|
||||
python -m pytest -vv tests/otel_tests/test_otel.py -x --junitxml=test-results/junit.xml --durations=5
|
||||
no_output_timeout: 120m
|
||||
|
||||
# Store test results
|
||||
|
@ -337,6 +433,12 @@ workflows:
|
|||
only:
|
||||
- main
|
||||
- /litellm_.*/
|
||||
- proxy_log_to_otel_tests:
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
- /litellm_.*/
|
||||
- installing_litellm_on_python:
|
||||
filters:
|
||||
branches:
|
||||
|
@ -347,6 +449,7 @@ workflows:
|
|||
requires:
|
||||
- local_testing
|
||||
- build_and_test
|
||||
- proxy_log_to_otel_tests
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# used by CI/CD testing
|
||||
openai
|
||||
openai==1.34.0
|
||||
python-dotenv
|
||||
tiktoken
|
||||
importlib_metadata
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,5 +1,7 @@
|
|||
.venv
|
||||
.env
|
||||
.newenv
|
||||
newenv/*
|
||||
litellm/proxy/myenv/*
|
||||
litellm_uuid.txt
|
||||
__pycache__/
|
||||
|
|
19
README.md
19
README.md
|
@ -8,7 +8,7 @@
|
|||
<img src="https://railway.app/button.svg" alt="Deploy on Railway">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">Call all LLM APIs using the OpenAI format [Bedrock, Huggingface, VertexAI, TogetherAI, Azure, OpenAI, etc.]
|
||||
<p align="center">Call all LLM APIs using the OpenAI format [Bedrock, Huggingface, VertexAI, TogetherAI, Azure, OpenAI, Groq etc.]
|
||||
<br>
|
||||
</p>
|
||||
<h4 align="center"><a href="https://docs.litellm.ai/docs/simple_proxy" target="_blank">OpenAI Proxy Server</a> | <a href="https://docs.litellm.ai/docs/hosted" target="_blank"> Hosted Proxy (Preview)</a> | <a href="https://docs.litellm.ai/docs/enterprise"target="_blank">Enterprise Tier</a></h4>
|
||||
|
@ -120,6 +120,7 @@ from litellm import completion
|
|||
|
||||
## set env variables for logging tools
|
||||
os.environ["LUNARY_PUBLIC_KEY"] = "your-lunary-public-key"
|
||||
os.environ["HELICONE_API_KEY"] = "your-helicone-auth-key"
|
||||
os.environ["LANGFUSE_PUBLIC_KEY"] = ""
|
||||
os.environ["LANGFUSE_SECRET_KEY"] = ""
|
||||
os.environ["ATHINA_API_KEY"] = "your-athina-api-key"
|
||||
|
@ -127,7 +128,7 @@ os.environ["ATHINA_API_KEY"] = "your-athina-api-key"
|
|||
os.environ["OPENAI_API_KEY"]
|
||||
|
||||
# set callbacks
|
||||
litellm.success_callback = ["lunary", "langfuse", "athina"] # log input/output to lunary, langfuse, supabase, athina etc
|
||||
litellm.success_callback = ["lunary", "langfuse", "athina", "helicone"] # log input/output to lunary, langfuse, supabase, athina, helicone etc
|
||||
|
||||
#openai call
|
||||
response = completion(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hi 👋 - i'm openai"}])
|
||||
|
@ -165,6 +166,10 @@ $ litellm --model huggingface/bigcode/starcoder
|
|||
|
||||
### Step 2: Make ChatCompletions Request to Proxy
|
||||
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 💡 [Use LiteLLM Proxy with Langchain (Python, JS), OpenAI SDK (Python, JS) Anthropic SDK, Mistral SDK, LlamaIndex, Instructor, Curl](https://docs.litellm.ai/docs/proxy/user_keys)
|
||||
|
||||
```python
|
||||
import openai # openai v1.0.0+
|
||||
client = openai.OpenAI(api_key="anything",base_url="http://0.0.0.0:4000") # set proxy to base_url
|
||||
|
@ -190,8 +195,15 @@ git clone https://github.com/BerriAI/litellm
|
|||
# Go to folder
|
||||
cd litellm
|
||||
|
||||
# Add the master key
|
||||
# Add the master key - you can change this after setup
|
||||
echo 'LITELLM_MASTER_KEY="sk-1234"' > .env
|
||||
|
||||
# Add the litellm salt key - you cannot change this after adding a model
|
||||
# It is used to encrypt / decrypt your LLM API Key credentials
|
||||
# We recommned - https://1password.com/password-generator/
|
||||
# password generator to get a random hash for litellm salt key
|
||||
echo 'LITELLM_SALT_KEY="sk-1234"' > .env
|
||||
|
||||
source .env
|
||||
|
||||
# Start
|
||||
|
@ -238,6 +250,7 @@ curl 'http://0.0.0.0:4000/key/generate' \
|
|||
| [cloudflare AI Workers](https://docs.litellm.ai/docs/providers/cloudflare_workers) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [cohere](https://docs.litellm.ai/docs/providers/cohere) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [anthropic](https://docs.litellm.ai/docs/providers/anthropic) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [empower](https://docs.litellm.ai/docs/providers/empower) | ✅ | ✅ | ✅ | ✅ |
|
||||
| [huggingface](https://docs.litellm.ai/docs/providers/huggingface) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [replicate](https://docs.litellm.ai/docs/providers/replicate) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [together_ai](https://docs.litellm.ai/docs/providers/togetherai) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
|
|
565
cookbook/Migrating_to_LiteLLM_Proxy_from_OpenAI_Azure_OpenAI.ipynb
vendored
Normal file
565
cookbook/Migrating_to_LiteLLM_Proxy_from_OpenAI_Azure_OpenAI.ipynb
vendored
Normal file
|
@ -0,0 +1,565 @@
|
|||
{
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"name": "python3",
|
||||
"display_name": "Python 3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# Migrating to LiteLLM Proxy from OpenAI/Azure OpenAI\n",
|
||||
"\n",
|
||||
"Covers:\n",
|
||||
"\n",
|
||||
"* /chat/completion\n",
|
||||
"* /embedding\n",
|
||||
"\n",
|
||||
"\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",
|
||||
"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",
|
||||
"\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",
|
||||
"source": [
|
||||
"## /chat/completion\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "nmSClzCPaGH6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### OpenAI Python SDK"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "_vqcjwOVaKpO"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "x1e_Ok3KZzeP"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import openai\n",
|
||||
"client = openai.OpenAI(\n",
|
||||
" api_key=\"anything\",\n",
|
||||
" base_url=\"http://0.0.0.0:4000\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# request sent to model set on litellm proxy, `litellm --model`\n",
|
||||
"response = client.chat.completions.create(\n",
|
||||
" model=\"gpt-3.5-turbo\",\n",
|
||||
" messages = [\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"this is a test request, write a short poem\"\n",
|
||||
" }\n",
|
||||
" ],\n",
|
||||
" extra_body={ # pass in any provider-specific param, if not supported by openai, https://docs.litellm.ai/docs/completion/input#provider-specific-params\n",
|
||||
" \"metadata\": { # 👈 use for logging additional params (e.g. to langfuse)\n",
|
||||
" \"generation_name\": \"ishaan-generation-openai-client\",\n",
|
||||
" \"generation_id\": \"openai-client-gen-id22\",\n",
|
||||
" \"trace_id\": \"openai-client-trace-id22\",\n",
|
||||
" \"trace_user_id\": \"openai-client-user-id2\"\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(response)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"## Function Calling"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "AqkyKk9Scxgj"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"from openai import OpenAI\n",
|
||||
"client = OpenAI(\n",
|
||||
" api_key=\"sk-1234\", # [OPTIONAL] set if you set one on proxy, else set \"\"\n",
|
||||
" base_url=\"http://0.0.0.0:4000\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"tools = [\n",
|
||||
" {\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"get_current_weather\",\n",
|
||||
" \"description\": \"Get the current weather in a given location\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\n",
|
||||
" \"location\": {\n",
|
||||
" \"type\": \"string\",\n",
|
||||
" \"description\": \"The city and state, e.g. San Francisco, CA\",\n",
|
||||
" },\n",
|
||||
" \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]},\n",
|
||||
" },\n",
|
||||
" \"required\": [\"location\"],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"messages = [{\"role\": \"user\", \"content\": \"What's the weather like in Boston today?\"}]\n",
|
||||
"completion = client.chat.completions.create(\n",
|
||||
" model=\"gpt-4o\", # use 'model_name' from config.yaml\n",
|
||||
" messages=messages,\n",
|
||||
" tools=tools,\n",
|
||||
" tool_choice=\"auto\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(completion)\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "wDg10VqLczE1"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### Azure OpenAI Python SDK"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "YYoxLloSaNWW"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import openai\n",
|
||||
"client = openai.AzureOpenAI(\n",
|
||||
" api_key=\"anything\",\n",
|
||||
" base_url=\"http://0.0.0.0:4000\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# request sent to model set on litellm proxy, `litellm --model`\n",
|
||||
"response = client.chat.completions.create(\n",
|
||||
" model=\"gpt-3.5-turbo\",\n",
|
||||
" messages = [\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"this is a test request, write a short poem\"\n",
|
||||
" }\n",
|
||||
" ],\n",
|
||||
" extra_body={ # pass in any provider-specific param, if not supported by openai, https://docs.litellm.ai/docs/completion/input#provider-specific-params\n",
|
||||
" \"metadata\": { # 👈 use for logging additional params (e.g. to langfuse)\n",
|
||||
" \"generation_name\": \"ishaan-generation-openai-client\",\n",
|
||||
" \"generation_id\": \"openai-client-gen-id22\",\n",
|
||||
" \"trace_id\": \"openai-client-trace-id22\",\n",
|
||||
" \"trace_user_id\": \"openai-client-user-id2\"\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(response)"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "yA1XcgowaSRy"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### Langchain Python"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "yl9qhDvnaTpL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"from langchain.chat_models import ChatOpenAI\n",
|
||||
"from langchain.prompts.chat import (\n",
|
||||
" ChatPromptTemplate,\n",
|
||||
" HumanMessagePromptTemplate,\n",
|
||||
" SystemMessagePromptTemplate,\n",
|
||||
")\n",
|
||||
"from langchain.schema import HumanMessage, SystemMessage\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = \"anything\"\n",
|
||||
"\n",
|
||||
"chat = ChatOpenAI(\n",
|
||||
" openai_api_base=\"http://0.0.0.0:4000\",\n",
|
||||
" model = \"gpt-3.5-turbo\",\n",
|
||||
" temperature=0.1,\n",
|
||||
" extra_body={\n",
|
||||
" \"metadata\": {\n",
|
||||
" \"generation_name\": \"ishaan-generation-langchain-client\",\n",
|
||||
" \"generation_id\": \"langchain-client-gen-id22\",\n",
|
||||
" \"trace_id\": \"langchain-client-trace-id22\",\n",
|
||||
" \"trace_user_id\": \"langchain-client-user-id2\"\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"messages = [\n",
|
||||
" SystemMessage(\n",
|
||||
" content=\"You are a helpful assistant that im using to make a test request to.\"\n",
|
||||
" ),\n",
|
||||
" HumanMessage(\n",
|
||||
" content=\"test from litellm. tell me why it's amazing in 1 sentence\"\n",
|
||||
" ),\n",
|
||||
"]\n",
|
||||
"response = chat(messages)\n",
|
||||
"\n",
|
||||
"print(response)"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "5MUZgSquaW5t"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### Curl"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "B9eMgnULbRaz"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"curl -X POST 'http://0.0.0.0:4000/chat/completions' \\\n",
|
||||
" -H 'Content-Type: application/json' \\\n",
|
||||
" -d '{\n",
|
||||
" \"model\": \"gpt-3.5-turbo\",\n",
|
||||
" \"messages\": [\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"what llm are you\"\n",
|
||||
" }\n",
|
||||
" ],\n",
|
||||
" \"metadata\": {\n",
|
||||
" \"generation_name\": \"ishaan-test-generation\",\n",
|
||||
" \"generation_id\": \"gen-id22\",\n",
|
||||
" \"trace_id\": \"trace-id22\",\n",
|
||||
" \"trace_user_id\": \"user-id2\"\n",
|
||||
" }\n",
|
||||
"}'\n",
|
||||
"```\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "VWCCk5PFcmhS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### LlamaIndex"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "drBAm2e1b6xe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import os, dotenv\n",
|
||||
"\n",
|
||||
"from llama_index.llms import AzureOpenAI\n",
|
||||
"from llama_index.embeddings import AzureOpenAIEmbedding\n",
|
||||
"from llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n",
|
||||
"\n",
|
||||
"llm = AzureOpenAI(\n",
|
||||
" 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_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_version=\"2023-07-01-preview\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"documents = SimpleDirectoryReader(\"llama_index_data\").load_data()\n",
|
||||
"service_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\n",
|
||||
"index = VectorStoreIndex.from_documents(documents, service_context=service_context)\n",
|
||||
"\n",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import { ChatOpenAI } from \"@langchain/openai\";\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"const model = new ChatOpenAI({\n",
|
||||
" modelName: \"gpt-4\",\n",
|
||||
" openAIApiKey: \"sk-1234\",\n",
|
||||
" modelKwargs: {\"metadata\": \"hello world\"} // 👈 PASS Additional params here\n",
|
||||
"}, {\n",
|
||||
" basePath: \"http://0.0.0.0:4000\",\n",
|
||||
"});\n",
|
||||
"\n",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"const { OpenAI } = require('openai');\n",
|
||||
"\n",
|
||||
"const openai = new OpenAI({\n",
|
||||
" apiKey: \"sk-1234\", // This is the default and can be omitted\n",
|
||||
" baseURL: \"http://0.0.0.0:4000\"\n",
|
||||
"});\n",
|
||||
"\n",
|
||||
"async function main() {\n",
|
||||
" const chatCompletion = await openai.chat.completions.create({\n",
|
||||
" messages: [{ role: 'user', content: 'Say this is a test' }],\n",
|
||||
" model: 'gpt-3.5-turbo',\n",
|
||||
" }, {\"metadata\": {\n",
|
||||
" \"generation_name\": \"ishaan-generation-openaijs-client\",\n",
|
||||
" \"generation_id\": \"openaijs-client-gen-id22\",\n",
|
||||
" \"trace_id\": \"openaijs-client-trace-id22\",\n",
|
||||
" \"trace_user_id\": \"openaijs-client-user-id2\"\n",
|
||||
" }});\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"main();\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "MICH8kIMcFpg"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### Anthropic SDK"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "D1Q07pEAcGTb"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"from anthropic import Anthropic\n",
|
||||
"\n",
|
||||
"client = Anthropic(\n",
|
||||
" base_url=\"http://localhost:4000\", # proxy endpoint\n",
|
||||
" api_key=\"sk-s4xN1IiLTCytwtZFJaYQrA\", # litellm proxy virtual key\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"message = client.messages.create(\n",
|
||||
" max_tokens=1024,\n",
|
||||
" messages=[\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"Hello, Claude\",\n",
|
||||
" }\n",
|
||||
" ],\n",
|
||||
" 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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### OpenAI Python SDK"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "lgNoM281cRzR"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import openai\n",
|
||||
"from openai import OpenAI\n",
|
||||
"\n",
|
||||
"# set base_url to your proxy server\n",
|
||||
"# set api_key to send to proxy server\n",
|
||||
"client = OpenAI(api_key=\"<proxy-api-key>\", base_url=\"http://0.0.0.0:4000\")\n",
|
||||
"\n",
|
||||
"response = client.embeddings.create(\n",
|
||||
" input=[\"hello from litellm\"],\n",
|
||||
" model=\"text-embedding-ada-002\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(response)\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "NY3DJhPfcQhA"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### Langchain Embeddings"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "hmbg-DW6cUZs"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"from langchain.embeddings import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"embeddings = OpenAIEmbeddings(model=\"sagemaker-embeddings\", openai_api_base=\"http://0.0.0.0:4000\", openai_api_key=\"temp-key\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"text = \"This is a test document.\"\n",
|
||||
"\n",
|
||||
"query_result = embeddings.embed_query(text)\n",
|
||||
"\n",
|
||||
"print(f\"SAGEMAKER EMBEDDINGS\")\n",
|
||||
"print(query_result[:5])\n",
|
||||
"\n",
|
||||
"embeddings = OpenAIEmbeddings(model=\"bedrock-embeddings\", openai_api_base=\"http://0.0.0.0:4000\", openai_api_key=\"temp-key\")\n",
|
||||
"\n",
|
||||
"text = \"This is a test document.\"\n",
|
||||
"\n",
|
||||
"query_result = embeddings.embed_query(text)\n",
|
||||
"\n",
|
||||
"print(f\"BEDROCK EMBEDDINGS\")\n",
|
||||
"print(query_result[:5])\n",
|
||||
"\n",
|
||||
"embeddings = OpenAIEmbeddings(model=\"bedrock-titan-embeddings\", openai_api_base=\"http://0.0.0.0:4000\", openai_api_key=\"temp-key\")\n",
|
||||
"\n",
|
||||
"text = \"This is a test document.\"\n",
|
||||
"\n",
|
||||
"query_result = embeddings.embed_query(text)\n",
|
||||
"\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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"\n",
|
||||
"\n",
|
||||
"```curl\n",
|
||||
"curl -X POST 'http://0.0.0.0:4000/embeddings' \\\n",
|
||||
" -H 'Content-Type: application/json' \\\n",
|
||||
" -d ' {\n",
|
||||
" \"model\": \"text-embedding-ada-002\",\n",
|
||||
" \"input\": [\"write a litellm poem\"]\n",
|
||||
" }'\n",
|
||||
"```\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "7rkIMV9LcdwQ"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -18,13 +18,13 @@ type: application
|
|||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.2.0
|
||||
version: 0.2.1
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: v1.35.38
|
||||
appVersion: v1.41.8
|
||||
|
||||
dependencies:
|
||||
- name: "postgresql"
|
||||
|
|
|
@ -6,10 +6,14 @@ services:
|
|||
args:
|
||||
target: runtime
|
||||
image: ghcr.io/berriai/litellm:main-stable
|
||||
#########################################
|
||||
## Uncomment these lines to start proxy with a config.yaml file ##
|
||||
# volumes:
|
||||
###############################################
|
||||
ports:
|
||||
- "4000:4000" # Map the container port to the host, change the host port if necessary
|
||||
environment:
|
||||
DATABASE_URL: "postgresql://postgres:example@db:5432/postgres"
|
||||
DATABASE_URL: "postgresql://llmproxy:dbpassword9090@db:5432/litellm"
|
||||
STORE_MODEL_IN_DB: "True" # allows adding models to proxy via UI
|
||||
env_file:
|
||||
- .env # Load local .env file
|
||||
|
@ -19,11 +23,31 @@ services:
|
|||
image: postgres
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_PASSWORD: example
|
||||
POSTGRES_DB: litellm
|
||||
POSTGRES_USER: llmproxy
|
||||
POSTGRES_PASSWORD: dbpassword9090
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready"]
|
||||
test: ["CMD-SHELL", "pg_isready -d litellm -U llmproxy"]
|
||||
interval: 1s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus
|
||||
volumes:
|
||||
- prometheus_data:/prometheus
|
||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
ports:
|
||||
- "9090:9090"
|
||||
command:
|
||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||
- '--storage.tsdb.path=/prometheus'
|
||||
- '--storage.tsdb.retention.time=15d'
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
prometheus_data:
|
||||
driver: local
|
||||
|
||||
|
||||
# ...rest of your docker-compose config if any
|
54
docs/my-website/docs/anthropic_completion.md
Normal file
54
docs/my-website/docs/anthropic_completion.md
Normal file
|
@ -0,0 +1,54 @@
|
|||
# [BETA] Anthropic `/v1/messages`
|
||||
|
||||
Call 100+ LLMs in the Anthropic format.
|
||||
|
||||
|
||||
1. Setup config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: my-test-model
|
||||
litellm_params:
|
||||
model: gpt-3.5-turbo
|
||||
```
|
||||
|
||||
2. Start proxy
|
||||
|
||||
```bash
|
||||
litellm --config /path/to/config.yaml
|
||||
```
|
||||
|
||||
3. Test it!
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/v1/messages' \
|
||||
-H 'x-api-key: sk-1234' \
|
||||
-H 'content-type: application/json' \
|
||||
-D '{
|
||||
"model": "my-test-model",
|
||||
"max_tokens": 1024,
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello, world"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
## Test with Anthropic SDK
|
||||
|
||||
```python
|
||||
import os
|
||||
from anthropic import Anthropic
|
||||
|
||||
client = Anthropic(api_key="sk-1234", base_url="http://0.0.0.0:4000") # 👈 CONNECT TO PROXY
|
||||
|
||||
message = client.messages.create(
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello, Claude",
|
||||
}
|
||||
],
|
||||
model="my-test-model", # 👈 set 'model_name'
|
||||
)
|
||||
print(message.content)
|
||||
```
|
|
@ -6,6 +6,7 @@ import TabItem from '@theme/TabItem';
|
|||
Covers Threads, Messages, Assistants.
|
||||
|
||||
LiteLLM currently covers:
|
||||
- Create Assistants
|
||||
- Get Assistants
|
||||
- Create Thread
|
||||
- Get Thread
|
||||
|
@ -25,9 +26,38 @@ Call an existing Assistant.
|
|||
|
||||
- Run the Assistant on the Thread to generate a response by calling the model and the tools.
|
||||
|
||||
### SDK + PROXY
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
**Create an Assistant**
|
||||
|
||||
|
||||
```python
|
||||
import litellm
|
||||
import os
|
||||
|
||||
# setup env
|
||||
os.environ["OPENAI_API_KEY"] = "sk-.."
|
||||
|
||||
assistant = litellm.create_assistants(
|
||||
custom_llm_provider="openai",
|
||||
model="gpt-4-turbo",
|
||||
instructions="You are a personal math tutor. When asked a question, write and run Python code to answer the question.",
|
||||
name="Math Tutor",
|
||||
tools=[{"type": "code_interpreter"}],
|
||||
)
|
||||
|
||||
### ASYNC USAGE ###
|
||||
# assistant = await litellm.acreate_assistants(
|
||||
# custom_llm_provider="openai",
|
||||
# model="gpt-4-turbo",
|
||||
# instructions="You are a personal math tutor. When asked a question, write and run Python code to answer the question.",
|
||||
# name="Math Tutor",
|
||||
# tools=[{"type": "code_interpreter"}],
|
||||
# )
|
||||
```
|
||||
|
||||
**Get the Assistant**
|
||||
|
||||
```python
|
||||
|
@ -145,6 +175,22 @@ $ litellm --config /path/to/config.yaml
|
|||
# RUNNING on http://0.0.0.0:4000
|
||||
```
|
||||
|
||||
|
||||
**Create the Assistant**
|
||||
|
||||
```bash
|
||||
curl "http://localhost:4000/v1/assistants" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"instructions": "You are a personal math tutor. When asked a question, write and run Python code to answer the question.",
|
||||
"name": "Math Tutor",
|
||||
"tools": [{"type": "code_interpreter"}],
|
||||
"model": "gpt-4-turbo"
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
**Get the Assistant**
|
||||
|
||||
```bash
|
||||
|
@ -236,3 +282,31 @@ curl -X POST 'http://0.0.0.0:4000/threads/{thread_id}/runs' \
|
|||
</Tabs>
|
||||
|
||||
## [👉 Proxy API Reference](https://litellm-api.up.railway.app/#/assistants)
|
||||
|
||||
## OpenAI-Compatible APIs
|
||||
|
||||
To call openai-compatible Assistants API's (eg. Astra Assistants API), just add `openai/` to the model name:
|
||||
|
||||
|
||||
**config**
|
||||
```yaml
|
||||
assistant_settings:
|
||||
custom_llm_provider: openai
|
||||
litellm_params:
|
||||
api_key: os.environ/ASTRA_API_KEY
|
||||
api_base: os.environ/ASTRA_API_BASE
|
||||
```
|
||||
|
||||
**curl**
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:4000/v1/assistants" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"instructions": "You are a personal math tutor. When asked a question, write and run Python code to answer the question.",
|
||||
"name": "Math Tutor",
|
||||
"tools": [{"type": "code_interpreter"}],
|
||||
"model": "openai/<my-astra-model-name>"
|
||||
}'
|
||||
```
|
|
@ -1,7 +1,7 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Audio Transcription
|
||||
# Speech to Text
|
||||
|
||||
Use this to loadbalance across Azure + OpenAI.
|
||||
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Batches API
|
||||
# [BETA] Batches API
|
||||
|
||||
Covers Batches, Files
|
||||
|
||||
|
||||
## Quick Start
|
||||
|
||||
Call an existing Assistant.
|
||||
|
||||
- Create File for Batch Completion
|
||||
|
||||
- Create Batch Request
|
||||
|
@ -18,6 +16,47 @@ Call an existing Assistant.
|
|||
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="proxy" label="LiteLLM PROXY Server">
|
||||
|
||||
```bash
|
||||
$ export OPENAI_API_KEY="sk-..."
|
||||
|
||||
$ litellm
|
||||
|
||||
# RUNNING on http://0.0.0.0:4000
|
||||
```
|
||||
|
||||
**Create File for Batch Completion**
|
||||
|
||||
```shell
|
||||
curl http://localhost:4000/v1/files \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-F purpose="batch" \
|
||||
-F file="@mydata.jsonl"
|
||||
```
|
||||
|
||||
**Create Batch Request**
|
||||
|
||||
```bash
|
||||
curl http://localhost:4000/v1/batches \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"input_file_id": "file-abc123",
|
||||
"endpoint": "/v1/chat/completions",
|
||||
"completion_window": "24h"
|
||||
}'
|
||||
```
|
||||
|
||||
**Retrieve the Specific Batch**
|
||||
|
||||
```bash
|
||||
curl http://localhost:4000/v1/batches/batch_abc123 \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
**Create File for Batch Completion**
|
||||
|
@ -78,47 +117,7 @@ print("file content = ", file_content)
|
|||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
```bash
|
||||
$ export OPENAI_API_KEY="sk-..."
|
||||
|
||||
$ litellm
|
||||
|
||||
# RUNNING on http://0.0.0.0:4000
|
||||
```
|
||||
|
||||
**Create File for Batch Completion**
|
||||
|
||||
```shell
|
||||
curl https://api.openai.com/v1/files \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-F purpose="batch" \
|
||||
-F file="@mydata.jsonl"
|
||||
```
|
||||
|
||||
**Create Batch Request**
|
||||
|
||||
```bash
|
||||
curl http://localhost:4000/v1/batches \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"input_file_id": "file-abc123",
|
||||
"endpoint": "/v1/chat/completions",
|
||||
"completion_window": "24h"
|
||||
}'
|
||||
```
|
||||
|
||||
**Retrieve the Specific Batch**
|
||||
|
||||
```bash
|
||||
curl http://localhost:4000/v1/batches/batch_abc123 \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## [👉 Proxy API Reference](https://litellm-api.up.railway.app/#/batch)
|
||||
|
|
|
@ -229,399 +229,3 @@ def completion(
|
|||
|
||||
- `hf_model_name`: *string (optional)* - [Sagemaker Only] The corresponding huggingface name of the model, used to pull the right chat template for the model.
|
||||
|
||||
|
||||
## Provider-specific Params
|
||||
Providers might offer params not supported by OpenAI (e.g. top_k). You can pass those in 2 ways:
|
||||
- via completion(): We'll pass the non-openai param, straight to the provider as part of the request body.
|
||||
- e.g. `completion(model="claude-instant-1", top_k=3)`
|
||||
- via provider-specific config variable (e.g. `litellm.OpenAIConfig()`).
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["OPENAI_API_KEY"] = "your-openai-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.OpenAIConfig(max_tokens=10)
|
||||
|
||||
response_2 = litellm.completion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="openai-text" label="OpenAI Text Completion">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["OPENAI_API_KEY"] = "your-openai-key"
|
||||
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="text-davinci-003",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.OpenAITextCompletionConfig(max_tokens=10)
|
||||
response_2 = litellm.completion(
|
||||
model="text-davinci-003",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="azure-openai" label="Azure OpenAI">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["AZURE_API_BASE"] = "your-azure-api-base"
|
||||
os.environ["AZURE_API_TYPE"] = "azure" # [OPTIONAL]
|
||||
os.environ["AZURE_API_VERSION"] = "2023-07-01-preview" # [OPTIONAL]
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="azure/chatgpt-v-2",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.AzureOpenAIConfig(max_tokens=10)
|
||||
response_2 = litellm.completion(
|
||||
model="azure/chatgpt-v-2",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="anthropic" label="Anthropic">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["ANTHROPIC_API_KEY"] = "your-anthropic-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="claude-instant-1",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.AnthropicConfig(max_tokens_to_sample=200)
|
||||
response_2 = litellm.completion(
|
||||
model="claude-instant-1",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="huggingface" label="Huggingface">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["HUGGINGFACE_API_KEY"] = "your-huggingface-key" #[OPTIONAL]
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="huggingface/mistralai/Mistral-7B-Instruct-v0.1",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
api_base="https://your-huggingface-api-endpoint",
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.HuggingfaceConfig(max_new_tokens=200)
|
||||
response_2 = litellm.completion(
|
||||
model="huggingface/mistralai/Mistral-7B-Instruct-v0.1",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
api_base="https://your-huggingface-api-endpoint"
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="together_ai" label="TogetherAI">
|
||||
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["TOGETHERAI_API_KEY"] = "your-togetherai-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="together_ai/togethercomputer/llama-2-70b-chat",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.TogetherAIConfig(max_tokens_to_sample=200)
|
||||
response_2 = litellm.completion(
|
||||
model="together_ai/togethercomputer/llama-2-70b-chat",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="ollama" label="Ollama">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="ollama/llama2",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.OllamConfig(num_predict=200)
|
||||
response_2 = litellm.completion(
|
||||
model="ollama/llama2",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="replicate" label="Replicate">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["REPLICATE_API_KEY"] = "your-replicate-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="replicate/meta/llama-2-70b-chat:02e509c789964a7ea8736978a43525956ef40397be9033abf9fd2badfe68c9e3",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.ReplicateConfig(max_new_tokens=200)
|
||||
response_2 = litellm.completion(
|
||||
model="replicate/meta/llama-2-70b-chat:02e509c789964a7ea8736978a43525956ef40397be9033abf9fd2badfe68c9e3",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="petals" label="Petals">
|
||||
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="petals/petals-team/StableBeluga2",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
api_base="https://chat.petals.dev/api/v1/generate",
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.PetalsConfig(max_new_tokens=10)
|
||||
response_2 = litellm.completion(
|
||||
model="petals/petals-team/StableBeluga2",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
api_base="https://chat.petals.dev/api/v1/generate",
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="palm" label="Palm">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["PALM_API_KEY"] = "your-palm-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="palm/chat-bison",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.PalmConfig(maxOutputTokens=10)
|
||||
response_2 = litellm.completion(
|
||||
model="palm/chat-bison",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="ai21" label="AI21">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["AI21_API_KEY"] = "your-ai21-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="j2-mid",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.AI21Config(maxOutputTokens=10)
|
||||
response_2 = litellm.completion(
|
||||
model="j2-mid",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="cohere" label="Cohere">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["COHERE_API_KEY"] = "your-cohere-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="command-nightly",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.CohereConfig(max_tokens=200)
|
||||
response_2 = litellm.completion(
|
||||
model="command-nightly",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
[**Check out the tutorial!**](../tutorials/provider_specific_params.md)
|
||||
|
|
137
docs/my-website/docs/completion/json_mode.md
Normal file
137
docs/my-website/docs/completion/json_mode.md
Normal file
|
@ -0,0 +1,137 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# JSON Mode
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
import os
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = ""
|
||||
|
||||
response = completion(
|
||||
model="gpt-4o-mini",
|
||||
response_format={ "type": "json_object" },
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant designed to output JSON."},
|
||||
{"role": "user", "content": "Who won the world series in 2020?"}
|
||||
]
|
||||
)
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
```bash
|
||||
curl http://0.0.0.0:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $LITELLM_KEY" \
|
||||
-d '{
|
||||
"model": "gpt-4o-mini",
|
||||
"response_format": { "type": "json_object" },
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a helpful assistant designed to output JSON."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Who won the world series in 2020?"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Check Model Support
|
||||
|
||||
Call `litellm.get_supported_openai_params` to check if a model/provider supports `response_format`.
|
||||
|
||||
```python
|
||||
from litellm import get_supported_openai_params
|
||||
|
||||
params = get_supported_openai_params(model="anthropic.claude-3", custom_llm_provider="bedrock")
|
||||
|
||||
assert "response_format" in params
|
||||
```
|
||||
|
||||
## Validate JSON Schema
|
||||
|
||||
For VertexAI models, LiteLLM supports passing the `response_schema` and validating the JSON output.
|
||||
|
||||
This works across Gemini (`vertex_ai_beta/`) + Anthropic (`vertex_ai/`) models.
|
||||
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```python
|
||||
# !gcloud auth application-default login - run this to add vertex credentials to your env
|
||||
|
||||
from litellm import completion
|
||||
|
||||
messages = [{"role": "user", "content": "List 5 cookie recipes"}]
|
||||
|
||||
response_schema = {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"recipe_name": {
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
"required": ["recipe_name"],
|
||||
},
|
||||
}
|
||||
|
||||
resp = completion(
|
||||
model="vertex_ai_beta/gemini-1.5-pro",
|
||||
messages=messages,
|
||||
response_format={
|
||||
"type": "json_object",
|
||||
"response_schema": response_schema,
|
||||
"enforce_validation": True, # client-side json schema validation
|
||||
},
|
||||
vertex_location="us-east5",
|
||||
)
|
||||
|
||||
print("Received={}".format(resp))
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
```bash
|
||||
curl http://0.0.0.0:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $LITELLM_API_KEY" \
|
||||
-d '{
|
||||
"model": "vertex_ai_beta/gemini-1.5-pro",
|
||||
"messages": [{"role": "user", "content": "List 5 cookie recipes"}]
|
||||
"response_format": {
|
||||
"type": "json_object",
|
||||
"enforce_validation: true,
|
||||
"response_schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"recipe_name": {
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
"required": ["recipe_name"],
|
||||
},
|
||||
}
|
||||
},
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
436
docs/my-website/docs/completion/provider_specific_params.md
Normal file
436
docs/my-website/docs/completion/provider_specific_params.md
Normal file
|
@ -0,0 +1,436 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Provider-specific Params
|
||||
|
||||
Providers might offer params not supported by OpenAI (e.g. top_k). LiteLLM treats any non-openai param, as a provider-specific param, and passes it to the provider in the request body, as a kwarg. [**See Reserved Params**](https://github.com/BerriAI/litellm/blob/aa2fd29e48245f360e771a8810a69376464b195e/litellm/main.py#L700)
|
||||
|
||||
You can pass those in 2 ways:
|
||||
- via completion(): We'll pass the non-openai param, straight to the provider as part of the request body.
|
||||
- e.g. `completion(model="claude-instant-1", top_k=3)`
|
||||
- via provider-specific config variable (e.g. `litellm.OpenAIConfig()`).
|
||||
|
||||
## SDK Usage
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["OPENAI_API_KEY"] = "your-openai-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.OpenAIConfig(max_tokens=10)
|
||||
|
||||
response_2 = litellm.completion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="openai-text" label="OpenAI Text Completion">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["OPENAI_API_KEY"] = "your-openai-key"
|
||||
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="text-davinci-003",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.OpenAITextCompletionConfig(max_tokens=10)
|
||||
response_2 = litellm.completion(
|
||||
model="text-davinci-003",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="azure-openai" label="Azure OpenAI">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["AZURE_API_BASE"] = "your-azure-api-base"
|
||||
os.environ["AZURE_API_TYPE"] = "azure" # [OPTIONAL]
|
||||
os.environ["AZURE_API_VERSION"] = "2023-07-01-preview" # [OPTIONAL]
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="azure/chatgpt-v-2",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.AzureOpenAIConfig(max_tokens=10)
|
||||
response_2 = litellm.completion(
|
||||
model="azure/chatgpt-v-2",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="anthropic" label="Anthropic">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["ANTHROPIC_API_KEY"] = "your-anthropic-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="claude-instant-1",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.AnthropicConfig(max_tokens_to_sample=200)
|
||||
response_2 = litellm.completion(
|
||||
model="claude-instant-1",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="huggingface" label="Huggingface">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["HUGGINGFACE_API_KEY"] = "your-huggingface-key" #[OPTIONAL]
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="huggingface/mistralai/Mistral-7B-Instruct-v0.1",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
api_base="https://your-huggingface-api-endpoint",
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.HuggingfaceConfig(max_new_tokens=200)
|
||||
response_2 = litellm.completion(
|
||||
model="huggingface/mistralai/Mistral-7B-Instruct-v0.1",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
api_base="https://your-huggingface-api-endpoint"
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="together_ai" label="TogetherAI">
|
||||
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["TOGETHERAI_API_KEY"] = "your-togetherai-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="together_ai/togethercomputer/llama-2-70b-chat",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.TogetherAIConfig(max_tokens_to_sample=200)
|
||||
response_2 = litellm.completion(
|
||||
model="together_ai/togethercomputer/llama-2-70b-chat",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="ollama" label="Ollama">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="ollama/llama2",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.OllamConfig(num_predict=200)
|
||||
response_2 = litellm.completion(
|
||||
model="ollama/llama2",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="replicate" label="Replicate">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["REPLICATE_API_KEY"] = "your-replicate-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="replicate/meta/llama-2-70b-chat:02e509c789964a7ea8736978a43525956ef40397be9033abf9fd2badfe68c9e3",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.ReplicateConfig(max_new_tokens=200)
|
||||
response_2 = litellm.completion(
|
||||
model="replicate/meta/llama-2-70b-chat:02e509c789964a7ea8736978a43525956ef40397be9033abf9fd2badfe68c9e3",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="petals" label="Petals">
|
||||
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="petals/petals-team/StableBeluga2",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
api_base="https://chat.petals.dev/api/v1/generate",
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.PetalsConfig(max_new_tokens=10)
|
||||
response_2 = litellm.completion(
|
||||
model="petals/petals-team/StableBeluga2",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
api_base="https://chat.petals.dev/api/v1/generate",
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="palm" label="Palm">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["PALM_API_KEY"] = "your-palm-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="palm/chat-bison",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.PalmConfig(maxOutputTokens=10)
|
||||
response_2 = litellm.completion(
|
||||
model="palm/chat-bison",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="ai21" label="AI21">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["AI21_API_KEY"] = "your-ai21-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="j2-mid",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.AI21Config(maxOutputTokens=10)
|
||||
response_2 = litellm.completion(
|
||||
model="j2-mid",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="cohere" label="Cohere">
|
||||
|
||||
```python
|
||||
import litellm, os
|
||||
|
||||
# set env variables
|
||||
os.environ["COHERE_API_KEY"] = "your-cohere-key"
|
||||
|
||||
## SET MAX TOKENS - via completion()
|
||||
response_1 = litellm.completion(
|
||||
model="command-nightly",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
max_tokens=10
|
||||
)
|
||||
|
||||
response_1_text = response_1.choices[0].message.content
|
||||
|
||||
## SET MAX TOKENS - via config
|
||||
litellm.CohereConfig(max_tokens=200)
|
||||
response_2 = litellm.completion(
|
||||
model="command-nightly",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
)
|
||||
|
||||
response_2_text = response_2.choices[0].message.content
|
||||
|
||||
## TEST OUTPUT
|
||||
assert len(response_2_text) > len(response_1_text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
[**Check out the tutorial!**](../tutorials/provider_specific_params.md)
|
||||
|
||||
|
||||
## Proxy Usage
|
||||
|
||||
**via Config**
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: llama-3-8b-instruct
|
||||
litellm_params:
|
||||
model: predibase/llama-3-8b-instruct
|
||||
api_key: os.environ/PREDIBASE_API_KEY
|
||||
tenant_id: os.environ/PREDIBASE_TENANT_ID
|
||||
max_tokens: 256
|
||||
adapter_base: <my-special_base> # 👈 PROVIDER-SPECIFIC PARAM
|
||||
```
|
||||
|
||||
**via Request**
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/chat/completions' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-D '{
|
||||
"model": "llama-3-8b-instruct",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What'\''s the weather like in Boston today?"
|
||||
}
|
||||
],
|
||||
"adapater_id": "my-special-adapter-id" # 👈 PROVIDER-SPECIFIC PARAM
|
||||
}'
|
||||
```
|
42
docs/my-website/docs/data_security.md
Normal file
42
docs/my-website/docs/data_security.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
# Data Privacy and Security
|
||||
|
||||
## Security Measures
|
||||
|
||||
### LiteLLM Cloud
|
||||
|
||||
- We encrypt all data stored using your `LITELLM_MASTER_KEY` and in transit using TLS.
|
||||
- Our database and application run on GCP, AWS infrastructure, partly managed by NeonDB.
|
||||
- US data region: Northern California (AWS/GCP `us-west-1`) & Virginia (AWS `us-east-1`)
|
||||
- EU data region Germany/Frankfurt (AWS/GCP `eu-central-1`)
|
||||
- All users have access to SSO (Single Sign-On) through OAuth 2.0 with Google, Okta, Microsoft, KeyCloak.
|
||||
- Audit Logs with retention policy
|
||||
- Control Allowed IP Addresses that can access your Cloud LiteLLM Instance
|
||||
|
||||
For security inquiries, please contact us at support@berri.ai
|
||||
|
||||
## Self-hosted Instances LiteLLM
|
||||
|
||||
- ** No data or telemetry is stored on LiteLLM Servers when you self host **
|
||||
- For installation and configuration, see: [Self-hosting guided](../docs/proxy/deploy.md)
|
||||
- **Telemetry** We run no telemetry when you self host LiteLLM
|
||||
|
||||
For security inquiries, please contact us at support@berri.ai
|
||||
|
||||
### Supported data regions for LiteLLM Cloud
|
||||
|
||||
LiteLLM supports the following data regions:
|
||||
|
||||
- US, Northern California (AWS/GCP `us-west-1`)
|
||||
- Europe, Frankfurt, Germany (AWS/GCP `eu-central-1`)
|
||||
|
||||
All data, user accounts, and infrastructure are completely separated between these two regions
|
||||
|
||||
### Security Vulnerability Reporting Guidelines
|
||||
|
||||
We value the security community's role in protecting our systems and users. To report a security vulnerability:
|
||||
|
||||
- Email support@berri.ai with details
|
||||
- Include steps to reproduce the issue
|
||||
- Provide any relevant additional information
|
||||
|
||||
We'll review all reports promptly. Note that we don't currently offer a bug bounty program.
|
|
@ -85,6 +85,17 @@ print(query_result[:5])
|
|||
</Tabs>
|
||||
|
||||
## Input Params for `litellm.embedding()`
|
||||
|
||||
|
||||
:::info
|
||||
|
||||
Any non-openai params, will be treated as provider-specific params, and sent in the request body as kwargs to the provider.
|
||||
|
||||
[**See Reserved Params**](https://github.com/BerriAI/litellm/blob/2f5f85cb52f36448d1f8bbfbd3b8af8167d0c4c8/litellm/main.py#L3130)
|
||||
|
||||
[**See Example**](#example)
|
||||
:::
|
||||
|
||||
### Required Fields
|
||||
|
||||
- `model`: *string* - ID of the model to use. `model='text-embedding-ada-002'`
|
||||
|
@ -363,3 +374,66 @@ All models listed here https://docs.voyageai.com/embeddings/#models-and-specific
|
|||
| voyage-01 | `embedding(model="voyage/voyage-01", input)` |
|
||||
| voyage-lite-01 | `embedding(model="voyage/voyage-lite-01", input)` |
|
||||
| voyage-lite-01-instruct | `embedding(model="voyage/voyage-lite-01-instruct", input)` |
|
||||
|
||||
## Provider-specific Params
|
||||
|
||||
|
||||
:::info
|
||||
|
||||
Any non-openai params, will be treated as provider-specific params, and sent in the request body as kwargs to the provider.
|
||||
|
||||
[**See Reserved Params**](https://github.com/BerriAI/litellm/blob/2f5f85cb52f36448d1f8bbfbd3b8af8167d0c4c8/litellm/main.py#L3130)
|
||||
:::
|
||||
|
||||
### **Example**
|
||||
|
||||
Cohere v3 Models have a required parameter: `input_type`, it can be one of the following four values:
|
||||
|
||||
- `input_type="search_document"`: (default) Use this for texts (documents) you want to store in your vector database
|
||||
- `input_type="search_query"`: Use this for search queries to find the most relevant documents in your vector database
|
||||
- `input_type="classification"`: Use this if you use the embeddings as an input for a classification system
|
||||
- `input_type="clustering"`: Use this if you use the embeddings for text clustering
|
||||
|
||||
https://txt.cohere.com/introducing-embed-v3/
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```python
|
||||
from litellm import embedding
|
||||
os.environ["COHERE_API_KEY"] = "cohere key"
|
||||
|
||||
# cohere call
|
||||
response = embedding(
|
||||
model="embed-english-v3.0",
|
||||
input=["good morning from litellm", "this is another item"],
|
||||
input_type="search_document" # 👈 PROVIDER-SPECIFIC PARAM
|
||||
)
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
**via config**
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: "cohere-embed"
|
||||
litellm_params:
|
||||
model: embed-english-v3.0
|
||||
input_type: search_document # 👈 PROVIDER-SPECIFIC PARAM
|
||||
```
|
||||
|
||||
**via request**
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/v1/embeddings' \
|
||||
-H 'Authorization: Bearer sk-54d77cd67b9febbb' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"model": "cohere-embed",
|
||||
"input": ["Are you authorized to work in United States of America?"],
|
||||
"input_type": "search_document" # 👈 PROVIDER-SPECIFIC PARAM
|
||||
}'
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
|
@ -7,13 +7,11 @@ Interested in Enterprise? Schedule a meeting with us here 👉
|
|||
|
||||
:::
|
||||
|
||||
## [AWS Marketplace Listing](https://aws.amazon.com/marketplace/pp/prodview-gdm3gswgjhgjo?sr=0-1&ref_=beagle&applicationId=AWSMPContessa)
|
||||
|
||||
Deploy managed LiteLLM Proxy within your VPC.
|
||||
|
||||
Includes all enterprise features.
|
||||
|
||||
[**View Listing**](https://aws.amazon.com/marketplace/pp/prodview-gdm3gswgjhgjo?sr=0-1&ref_=beagle&applicationId=AWSMPContessa)
|
||||
[**View AWS Marketplace Listing**](https://aws.amazon.com/marketplace/pp/prodview-gdm3gswgjhgjo?sr=0-1&ref_=beagle&applicationId=AWSMPContessa)
|
||||
|
||||
[**Get early access**](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat)
|
||||
|
||||
|
@ -26,7 +24,10 @@ This covers:
|
|||
- ✅ [JWT-Auth](../docs/proxy/token_auth.md)
|
||||
- ✅ [Control available public, private routes](./proxy/enterprise#control-available-public-private-routes)
|
||||
- ✅ [[BETA] AWS Key Manager v2 - Key Decryption](./proxy/enterprise#beta-aws-key-manager---key-decryption)
|
||||
- ✅ IP address‑based access control lists
|
||||
- ✅ Track Request IP Address
|
||||
- ✅ [Use LiteLLM keys/authentication on Pass Through Endpoints](./proxy/pass_through#✨-enterprise---use-litellm-keysauthentication-on-pass-through-endpoints)
|
||||
- ✅ Set Max Request / File Size on Requests
|
||||
- ✅ [Enforce Required Params for LLM Requests (ex. Reject requests missing ["metadata"]["generation_name"])](./proxy/enterprise#enforce-required-params-for-llm-requests)
|
||||
- **Spend Tracking**
|
||||
- ✅ [Tracking Spend for Custom Tags](./proxy/enterprise#tracking-spend-for-custom-tags)
|
||||
|
|
|
@ -87,13 +87,14 @@ from litellm import completion
|
|||
|
||||
## set env variables for logging tools
|
||||
os.environ["LUNARY_PUBLIC_KEY"] = "your-lunary-public-key"
|
||||
os.environ["HELICONE_API_KEY"] = "your-helicone-key"
|
||||
os.environ["LANGFUSE_PUBLIC_KEY"] = ""
|
||||
os.environ["LANGFUSE_SECRET_KEY"] = ""
|
||||
|
||||
os.environ["OPENAI_API_KEY"]
|
||||
|
||||
# set callbacks
|
||||
litellm.success_callback = ["lunary", "langfuse"] # log input/output to langfuse, lunary, supabase
|
||||
litellm.success_callback = ["lunary", "langfuse", "helicone"] # log input/output to langfuse, lunary, supabase, helicone
|
||||
|
||||
#openai call
|
||||
response = completion(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hi 👋 - i'm openai"}])
|
||||
|
|
|
@ -21,6 +21,14 @@ See our status page for [**live reliability**](https://status.litellm.ai/)
|
|||
- **Reliable**: Our hosted proxy is tested on 1k requests per second, making it reliable for high load.
|
||||
- **Secure**: LiteLLM is currently undergoing SOC-2 compliance, to make sure your data is as secure as possible.
|
||||
|
||||
## Data Privacy & Security
|
||||
|
||||
You can find our [data privacy & security policy for cloud litellm here](../docs/data_security#litellm-cloud)
|
||||
|
||||
## Supported data regions for LiteLLM Cloud
|
||||
|
||||
You can find [supported data regions litellm here](../docs/data_security#supported-data-regions-for-litellm-cloud)
|
||||
|
||||
### Pricing
|
||||
|
||||
Pricing is based on usage. We can figure out a price that works for your team, on the call.
|
||||
|
|
|
@ -14,7 +14,76 @@ response = image_generation(prompt="A cute baby sea otter", model="dall-e-3")
|
|||
print(f"response: {response}")
|
||||
```
|
||||
|
||||
### Input Params for `litellm.image_generation()`
|
||||
## Proxy Usage
|
||||
|
||||
### Setup config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: dall-e-2 ### RECEIVED MODEL NAME ###
|
||||
litellm_params: # all params accepted by litellm.image_generation()
|
||||
model: azure/dall-e-2 ### MODEL NAME sent to `litellm.image_generation()` ###
|
||||
api_base: https://my-endpoint-europe-berri-992.openai.azure.com/
|
||||
api_key: "os.environ/AZURE_API_KEY_EU" # does os.getenv("AZURE_API_KEY_EU")
|
||||
rpm: 6 # [OPTIONAL] Rate limit for this deployment: in requests per minute (rpm)
|
||||
|
||||
```
|
||||
|
||||
### Start proxy
|
||||
|
||||
```bash
|
||||
litellm --config /path/to/config.yaml
|
||||
|
||||
# RUNNING on http://0.0.0.0:4000
|
||||
```
|
||||
|
||||
### Test
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="curl" label="Curl">
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/v1/images/generations' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-D '{
|
||||
"model": "dall-e-2",
|
||||
"prompt": "A cute baby sea otter",
|
||||
"n": 1,
|
||||
"size": "1024x1024"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="openai" label="OpenAI">
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
client = openai.OpenAI(
|
||||
api_key="sk-1234",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
|
||||
image = client.images.generate(
|
||||
prompt="A cute baby sea otter",
|
||||
model="dall-e-3",
|
||||
)
|
||||
|
||||
print(image)
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Input Params for `litellm.image_generation()`
|
||||
|
||||
:::info
|
||||
|
||||
Any non-openai params, will be treated as provider-specific params, and sent in the request body as kwargs to the provider.
|
||||
|
||||
[**See Reserved Params**](https://github.com/BerriAI/litellm/blob/2f5f85cb52f36448d1f8bbfbd3b8af8167d0c4c8/litellm/main.py#L4082)
|
||||
:::
|
||||
|
||||
### Required Fields
|
||||
|
||||
- `prompt`: *string* - A text description of the desired image(s).
|
||||
|
|
|
@ -310,6 +310,7 @@ LiteLLM exposes pre defined callbacks to send data to Lunary, Langfuse, Helicone
|
|||
from litellm import completion
|
||||
|
||||
## set env variables for logging tools
|
||||
os.environ["HELICONE_API_KEY"] = "your-helicone-key"
|
||||
os.environ["LANGFUSE_PUBLIC_KEY"] = ""
|
||||
os.environ["LANGFUSE_SECRET_KEY"] = ""
|
||||
os.environ["LUNARY_PUBLIC_KEY"] = "your-lunary-public-key"
|
||||
|
@ -317,7 +318,7 @@ os.environ["LUNARY_PUBLIC_KEY"] = "your-lunary-public-key"
|
|||
os.environ["OPENAI_API_KEY"]
|
||||
|
||||
# set callbacks
|
||||
litellm.success_callback = ["lunary", "langfuse"] # log input/output to lunary, langfuse, supabase
|
||||
litellm.success_callback = ["lunary", "langfuse", "helicone"] # log input/output to lunary, langfuse, supabase, helicone
|
||||
|
||||
#openai call
|
||||
response = completion(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hi 👋 - i'm openai"}])
|
||||
|
|
72
docs/my-website/docs/observability/arize_integration.md
Normal file
72
docs/my-website/docs/observability/arize_integration.md
Normal file
|
@ -0,0 +1,72 @@
|
|||
import Image from '@theme/IdealImage';
|
||||
|
||||
# 🔥 Arize AI - Logging LLM Input/Output
|
||||
|
||||
AI Observability and Evaluation Platform
|
||||
|
||||
:::tip
|
||||
|
||||
This is community maintained, Please make an issue if you run into a bug
|
||||
https://github.com/BerriAI/litellm
|
||||
|
||||
:::
|
||||
|
||||
|
||||
|
||||
## Pre-Requisites
|
||||
Make an account on [Arize AI](https://app.arize.com/auth/login)
|
||||
|
||||
## Quick Start
|
||||
Use just 2 lines of code, to instantly log your responses **across all providers** with arize
|
||||
|
||||
|
||||
```python
|
||||
litellm.callbacks = ["arize"]
|
||||
```
|
||||
```python
|
||||
import litellm
|
||||
import os
|
||||
|
||||
os.environ["ARIZE_SPACE_KEY"] = ""
|
||||
os.environ["ARIZE_API_KEY"] = "" # defaults to litellm-completion
|
||||
|
||||
# LLM API Keys
|
||||
os.environ['OPENAI_API_KEY']=""
|
||||
|
||||
# set arize as a callback, litellm will send the data to arize
|
||||
litellm.callbacks = ["arize"]
|
||||
|
||||
# openai call
|
||||
response = litellm.completion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "Hi 👋 - i'm openai"}
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
### Using with LiteLLM Proxy
|
||||
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-4
|
||||
litellm_params:
|
||||
model: openai/fake
|
||||
api_key: fake-key
|
||||
api_base: https://exampleopenaiendpoint-production.up.railway.app/
|
||||
|
||||
litellm_settings:
|
||||
callbacks: ["arize"]
|
||||
|
||||
environment_variables:
|
||||
ARIZE_SPACE_KEY: "d0*****"
|
||||
ARIZE_API_KEY: "141a****"
|
||||
```
|
||||
|
||||
## Support & Talk to Founders
|
||||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
147
docs/my-website/docs/observability/braintrust.md
Normal file
147
docs/my-website/docs/observability/braintrust.md
Normal file
|
@ -0,0 +1,147 @@
|
|||
import Image from '@theme/IdealImage';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# ⚡️ Braintrust - Evals + Logging
|
||||
|
||||
[Braintrust](https://www.braintrust.dev/) manages evaluations, logging, prompt playground, to data management for AI products.
|
||||
|
||||
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
# pip install langfuse
|
||||
import litellm
|
||||
import os
|
||||
|
||||
# set env
|
||||
os.environ["BRAINTRUST_API_KEY"] = ""
|
||||
os.environ['OPENAI_API_KEY']=""
|
||||
|
||||
# set braintrust as a callback, litellm will send the data to braintrust
|
||||
litellm.callbacks = ["braintrust"]
|
||||
|
||||
# openai call
|
||||
response = litellm.completion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "Hi 👋 - i'm openai"}
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
|
||||
## OpenAI Proxy Usage
|
||||
|
||||
1. Add keys to env
|
||||
```env
|
||||
BRAINTRUST_API_KEY=""
|
||||
```
|
||||
|
||||
2. Add braintrust to callbacks
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-3.5-turbo
|
||||
litellm_params:
|
||||
model: gpt-3.5-turbo
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
|
||||
|
||||
litellm_settings:
|
||||
callbacks: ["braintrust"]
|
||||
```
|
||||
|
||||
3. Test it!
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/chat/completions' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-D '{
|
||||
"model": "groq-llama3",
|
||||
"messages": [
|
||||
{ "role": "system", "content": "Use your tools smartly"},
|
||||
{ "role": "user", "content": "What time is it now? Use your tool"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
## Advanced - pass Project ID
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```python
|
||||
response = litellm.completion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "Hi 👋 - i'm openai"}
|
||||
],
|
||||
metadata={
|
||||
"project_id": "my-special-project"
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
**Curl**
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/chat/completions' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-D '{
|
||||
"model": "groq-llama3",
|
||||
"messages": [
|
||||
{ "role": "system", "content": "Use your tools smartly"},
|
||||
{ "role": "user", "content": "What time is it now? Use your tool"}
|
||||
],
|
||||
"metadata": {
|
||||
"project_id": "my-special-project"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
**OpenAI SDK**
|
||||
|
||||
```python
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="anything",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# request sent to model set on litellm proxy, `litellm --model`
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
],
|
||||
extra_body={ # pass in any provider-specific param, if not supported by openai, https://docs.litellm.ai/docs/completion/input#provider-specific-params
|
||||
"metadata": { # 👈 use for logging additional params (e.g. to langfuse)
|
||||
"project_id": "my-special-project"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
For more examples, [**Click Here**](../proxy/user_keys.md#chatcompletions)
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Full API Spec
|
||||
|
||||
Here's everything you can pass in metadata for a braintrust request
|
||||
|
||||
`braintrust_*` - any metadata field starting with `braintrust_` will be passed as metadata to the logging request
|
||||
|
||||
`project_id` - set the project id for a braintrust call. Default is `litellm`.
|
|
@ -7,15 +7,17 @@ liteLLM provides `input_callbacks`, `success_callbacks` and `failure_callbacks`,
|
|||
liteLLM supports:
|
||||
|
||||
- [Custom Callback Functions](https://docs.litellm.ai/docs/observability/custom_callback)
|
||||
- [Lunary](https://lunary.ai/docs)
|
||||
- [Langfuse](https://langfuse.com/docs)
|
||||
- [Helicone](https://docs.helicone.ai/introduction)
|
||||
- [Traceloop](https://traceloop.com/docs)
|
||||
- [Lunary](https://lunary.ai/docs)
|
||||
- [Athina](https://docs.athina.ai/)
|
||||
- [Sentry](https://docs.sentry.io/platforms/python/)
|
||||
- [PostHog](https://posthog.com/docs/libraries/python)
|
||||
- [Slack](https://slack.dev/bolt-python/concepts)
|
||||
|
||||
This is **not** an extensive list. Please check the dropdown for all logging integrations.
|
||||
|
||||
### Quick Start
|
||||
|
||||
```python
|
||||
|
|
|
@ -1,64 +1,170 @@
|
|||
# Helicone Tutorial
|
||||
# 🧊 Helicone - OSS LLM Observability Platform
|
||||
|
||||
:::tip
|
||||
|
||||
This is community maintained, Please make an issue if you run into a bug
|
||||
This is community maintained. Please make an issue if you run into a bug:
|
||||
https://github.com/BerriAI/litellm
|
||||
|
||||
:::
|
||||
|
||||
[Helicone](https://helicone.ai/) is an open source observability platform that proxies your LLM requests and provides key insights into your usage, spend, latency and more.
|
||||
|
||||
[Helicone](https://helicone.ai/) is an open source observability platform that proxies your OpenAI traffic and provides you key insights into your spend, latency and usage.
|
||||
## Using Helicone with LiteLLM
|
||||
|
||||
## Use Helicone to log requests across all LLM Providers (OpenAI, Azure, Anthropic, Cohere, Replicate, PaLM)
|
||||
liteLLM provides `success_callbacks` and `failure_callbacks`, making it easy for you to send data to a particular provider depending on the status of your responses.
|
||||
LiteLLM provides `success_callbacks` and `failure_callbacks`, allowing you to easily log data to Helicone based on the status of your responses.
|
||||
|
||||
In this case, we want to log requests to Helicone when a request succeeds.
|
||||
### Supported LLM Providers
|
||||
|
||||
Helicone can log requests across [various LLM providers](https://docs.helicone.ai/getting-started/quick-start), including:
|
||||
|
||||
- OpenAI
|
||||
- Azure
|
||||
- Anthropic
|
||||
- Gemini
|
||||
- Groq
|
||||
- Cohere
|
||||
- Replicate
|
||||
- And more
|
||||
|
||||
### Integration Methods
|
||||
|
||||
There are two main approaches to integrate Helicone with LiteLLM:
|
||||
|
||||
1. Using callbacks
|
||||
2. Using Helicone as a proxy
|
||||
|
||||
Let's explore each method in detail.
|
||||
|
||||
### Approach 1: Use Callbacks
|
||||
Use just 1 line of code, to instantly log your responses **across all providers** with helicone:
|
||||
|
||||
Use just 1 line of code to instantly log your responses **across all providers** with Helicone:
|
||||
|
||||
```python
|
||||
litellm.success_callback = ["helicone"]
|
||||
```
|
||||
|
||||
Complete code
|
||||
```python
|
||||
from litellm import completion
|
||||
|
||||
## set env variables
|
||||
os.environ["HELICONE_API_KEY"] = "your-helicone-key"
|
||||
os.environ["OPENAI_API_KEY"], os.environ["COHERE_API_KEY"] = "", ""
|
||||
|
||||
# set callbacks
|
||||
litellm.success_callback=["helicone"]
|
||||
|
||||
#openai call
|
||||
response = completion(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hi 👋 - i'm openai"}])
|
||||
|
||||
#cohere call
|
||||
response = completion(model="command-nightly", messages=[{"role": "user", "content": "Hi 👋 - i'm cohere"}])
|
||||
```
|
||||
|
||||
### Approach 2: [OpenAI + Azure only] Use Helicone as a proxy
|
||||
Helicone provides advanced functionality like caching, etc. Helicone currently supports this for Azure and OpenAI.
|
||||
|
||||
If you want to use Helicone to proxy your OpenAI/Azure requests, then you can -
|
||||
|
||||
- Set helicone as your base url via: `litellm.api_url`
|
||||
- Pass in helicone request headers via: `litellm.headers`
|
||||
|
||||
Complete Code
|
||||
|
||||
```python
|
||||
import litellm
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
litellm.api_base = "https://oai.hconeai.com/v1"
|
||||
litellm.headers = {"Helicone-Auth": f"Bearer {os.getenv('HELICONE_API_KEY')}"}
|
||||
## Set env variables
|
||||
os.environ["HELICONE_API_KEY"] = "your-helicone-key"
|
||||
os.environ["OPENAI_API_KEY"] = "your-openai-key"
|
||||
|
||||
response = litellm.completion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "user", "content": "how does a court case get to the Supreme Court?"}]
|
||||
# Set callbacks
|
||||
litellm.success_callback = ["helicone"]
|
||||
|
||||
# OpenAI call
|
||||
response = completion(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hi 👋 - I'm OpenAI"}],
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
### Approach 2: Use Helicone as a proxy
|
||||
|
||||
Helicone's proxy provides [advanced functionality](https://docs.helicone.ai/getting-started/proxy-vs-async) like caching, rate limiting, LLM security through [PromptArmor](https://promptarmor.com/) and more.
|
||||
|
||||
To use Helicone as a proxy for your LLM requests:
|
||||
|
||||
1. Set Helicone as your base URL via: litellm.api_base
|
||||
2. Pass in Helicone request headers via: litellm.metadata
|
||||
|
||||
Complete Code:
|
||||
|
||||
```python
|
||||
import os
|
||||
import litellm
|
||||
from litellm import completion
|
||||
|
||||
litellm.api_base = "https://oai.hconeai.com/v1"
|
||||
litellm.headers = {
|
||||
"Helicone-Auth": f"Bearer {os.getenv('HELICONE_API_KEY')}", # Authenticate to send requests to Helicone API
|
||||
}
|
||||
|
||||
response = litellm.completion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "user", "content": "How does a court case get to the Supreme Court?"}]
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
### Advanced Usage
|
||||
|
||||
You can add custom metadata and properties to your requests using Helicone headers. Here are some examples:
|
||||
|
||||
```python
|
||||
litellm.metadata = {
|
||||
"Helicone-Auth": f"Bearer {os.getenv('HELICONE_API_KEY')}", # Authenticate to send requests to Helicone API
|
||||
"Helicone-User-Id": "user-abc", # Specify the user making the request
|
||||
"Helicone-Property-App": "web", # Custom property to add additional information
|
||||
"Helicone-Property-Custom": "any-value", # Add any custom property
|
||||
"Helicone-Prompt-Id": "prompt-supreme-court", # Assign an ID to associate this prompt with future versions
|
||||
"Helicone-Cache-Enabled": "true", # Enable caching of responses
|
||||
"Cache-Control": "max-age=3600", # Set cache limit to 1 hour
|
||||
"Helicone-RateLimit-Policy": "10;w=60;s=user", # Set rate limit policy
|
||||
"Helicone-Retry-Enabled": "true", # Enable retry mechanism
|
||||
"helicone-retry-num": "3", # Set number of retries
|
||||
"helicone-retry-factor": "2", # Set exponential backoff factor
|
||||
"Helicone-Model-Override": "gpt-3.5-turbo-0613", # Override the model used for cost calculation
|
||||
"Helicone-Session-Id": "session-abc-123", # Set session ID for tracking
|
||||
"Helicone-Session-Path": "parent-trace/child-trace", # Set session path for hierarchical tracking
|
||||
"Helicone-Omit-Response": "false", # Include response in logging (default behavior)
|
||||
"Helicone-Omit-Request": "false", # Include request in logging (default behavior)
|
||||
"Helicone-LLM-Security-Enabled": "true", # Enable LLM security features
|
||||
"Helicone-Moderations-Enabled": "true", # Enable content moderation
|
||||
"Helicone-Fallbacks": '["gpt-3.5-turbo", "gpt-4"]', # Set fallback models
|
||||
}
|
||||
```
|
||||
|
||||
### Caching and Rate Limiting
|
||||
|
||||
Enable caching and set up rate limiting policies:
|
||||
|
||||
```python
|
||||
litellm.metadata = {
|
||||
"Helicone-Auth": f"Bearer {os.getenv('HELICONE_API_KEY')}", # Authenticate to send requests to Helicone API
|
||||
"Helicone-Cache-Enabled": "true", # Enable caching of responses
|
||||
"Cache-Control": "max-age=3600", # Set cache limit to 1 hour
|
||||
"Helicone-RateLimit-Policy": "100;w=3600;s=user", # Set rate limit policy
|
||||
}
|
||||
```
|
||||
|
||||
### Session Tracking and Tracing
|
||||
|
||||
Track multi-step and agentic LLM interactions using session IDs and paths:
|
||||
|
||||
```python
|
||||
litellm.metadata = {
|
||||
"Helicone-Auth": f"Bearer {os.getenv('HELICONE_API_KEY')}", # Authenticate to send requests to Helicone API
|
||||
"Helicone-Session-Id": "session-abc-123", # The session ID you want to track
|
||||
"Helicone-Session-Path": "parent-trace/child-trace", # The path of the session
|
||||
}
|
||||
```
|
||||
|
||||
- `Helicone-Session-Id`: Use this to specify the unique identifier for the session you want to track. This allows you to group related requests together.
|
||||
- `Helicone-Session-Path`: This header defines the path of the session, allowing you to represent parent and child traces. For example, "parent/child" represents a child trace of a parent trace.
|
||||
|
||||
By using these two headers, you can effectively group and visualize multi-step LLM interactions, gaining insights into complex AI workflows.
|
||||
|
||||
### Retry and Fallback Mechanisms
|
||||
|
||||
Set up retry mechanisms and fallback options:
|
||||
|
||||
```python
|
||||
litellm.metadata = {
|
||||
"Helicone-Auth": f"Bearer {os.getenv('HELICONE_API_KEY')}", # Authenticate to send requests to Helicone API
|
||||
"Helicone-Retry-Enabled": "true", # Enable retry mechanism
|
||||
"helicone-retry-num": "3", # Set number of retries
|
||||
"helicone-retry-factor": "2", # Set exponential backoff factor
|
||||
"Helicone-Fallbacks": '["gpt-3.5-turbo", "gpt-4"]', # Set fallback models
|
||||
}
|
||||
```
|
||||
|
||||
> **Supported Headers** - For a full list of supported Helicone headers and their descriptions, please refer to the [Helicone documentation](https://docs.helicone.ai/getting-started/quick-start).
|
||||
> By utilizing these headers and metadata options, you can gain deeper insights into your LLM usage, optimize performance, and better manage your AI workflows with Helicone and LiteLLM.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Image from '@theme/IdealImage';
|
||||
|
||||
# Langsmith - Logging LLM Input/Output
|
||||
# 🦜 Langsmith - Logging LLM Input/Output
|
||||
|
||||
|
||||
:::tip
|
||||
|
@ -14,7 +14,7 @@ https://github.com/BerriAI/litellm
|
|||
An all-in-one developer platform for every step of the application lifecycle
|
||||
https://smith.langchain.com/
|
||||
|
||||
<Image img={require('../../img/langsmith.png')} />
|
||||
<Image img={require('../../img/langsmith_new.png')} />
|
||||
|
||||
:::info
|
||||
We want to learn how we can make the callbacks better! Meet the LiteLLM [founders](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version) or
|
||||
|
@ -56,7 +56,7 @@ response = litellm.completion(
|
|||
```
|
||||
|
||||
## Advanced
|
||||
### Set Custom Project & Run names
|
||||
### Set Langsmith fields - Custom Projec, Run names, tags
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
@ -77,6 +77,7 @@ response = litellm.completion(
|
|||
metadata={
|
||||
"run_name": "litellmRUN", # langsmith run name
|
||||
"project_name": "litellm-completion", # langsmith project name
|
||||
"tags": ["model1", "prod-2"] # tags to log on langsmith
|
||||
}
|
||||
)
|
||||
print(response)
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
import Image from '@theme/IdealImage';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Raw Request/Response Logging
|
||||
|
||||
|
||||
## Logging
|
||||
See the raw request/response sent by LiteLLM in your logging provider (OTEL/Langfuse/etc.).
|
||||
|
||||
**on SDK**
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```python
|
||||
# pip install langfuse
|
||||
import litellm
|
||||
|
@ -34,13 +40,85 @@ response = litellm.completion(
|
|||
)
|
||||
```
|
||||
|
||||
**on Proxy**
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
|
||||
```yaml
|
||||
litellm_settings:
|
||||
log_raw_request_response: True
|
||||
```
|
||||
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
**Expected Log**
|
||||
|
||||
<Image img={require('../../img/raw_request_log.png')}/>
|
||||
|
||||
|
||||
## Return Raw Response Headers
|
||||
|
||||
Return raw response headers from llm provider.
|
||||
|
||||
Currently only supported for openai.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```python
|
||||
import litellm
|
||||
import os
|
||||
|
||||
litellm.return_response_headers = True
|
||||
|
||||
## set ENV variables
|
||||
os.environ["OPENAI_API_KEY"] = "your-api-key"
|
||||
|
||||
response = litellm.completion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}]
|
||||
)
|
||||
|
||||
print(response._hidden_params)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
1. Setup config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-3.5-turbo
|
||||
litellm_params:
|
||||
model: gpt-3.5-turbo
|
||||
api_key: os.environ/GROQ_API_KEY
|
||||
|
||||
litellm_settings:
|
||||
return_response_headers: true
|
||||
```
|
||||
|
||||
2. Test it!
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/chat/completions' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-D '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
{ "role": "system", "content": "Use your tools smartly"},
|
||||
{ "role": "user", "content": "What time is it now? Use your tool"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
**Expected Response**
|
||||
|
||||
<Image img={require('../../img/raw_response_headers.png')}/>
|
97
docs/my-website/docs/observability/scrub_data.md
Normal file
97
docs/my-website/docs/observability/scrub_data.md
Normal file
|
@ -0,0 +1,97 @@
|
|||
# Scrub Logged Data
|
||||
|
||||
Redact messages / mask PII before sending data to logging integrations (langfuse/etc.).
|
||||
|
||||
See our [**Presidio PII Masking**](https://github.com/BerriAI/litellm/blob/a176feeacc5fdf504747978d82056eb84679c4be/litellm/proxy/hooks/presidio_pii_masking.py#L286) for reference.
|
||||
|
||||
1. Setup a custom callback
|
||||
|
||||
```python
|
||||
from litellm.integrations.custom_logger import CustomLogger
|
||||
|
||||
class MyCustomHandler(CustomLogger):
|
||||
async def async_logging_hook(
|
||||
self, kwargs: dict, result: Any, call_type: str
|
||||
) -> Tuple[dict, Any]:
|
||||
"""
|
||||
For masking logged request/response. Return a modified version of the request/result.
|
||||
|
||||
Called before `async_log_success_event`.
|
||||
"""
|
||||
if (
|
||||
call_type == "completion" or call_type == "acompletion"
|
||||
): # /chat/completions requests
|
||||
messages: Optional[List] = kwargs.get("messages", None)
|
||||
|
||||
kwargs["messages"] = [{"role": "user", "content": "MASK_THIS_ASYNC_VALUE"}]
|
||||
|
||||
return kwargs, responses
|
||||
|
||||
def logging_hook(
|
||||
self, kwargs: dict, result: Any, call_type: str
|
||||
) -> Tuple[dict, Any]:
|
||||
"""
|
||||
For masking logged request/response. Return a modified version of the request/result.
|
||||
|
||||
Called before `log_success_event`.
|
||||
"""
|
||||
if (
|
||||
call_type == "completion" or call_type == "acompletion"
|
||||
): # /chat/completions requests
|
||||
messages: Optional[List] = kwargs.get("messages", None)
|
||||
|
||||
kwargs["messages"] = [{"role": "user", "content": "MASK_THIS_SYNC_VALUE"}]
|
||||
|
||||
return kwargs, responses
|
||||
|
||||
|
||||
customHandler = MyCustomHandler()
|
||||
```
|
||||
|
||||
|
||||
2. Connect custom handler to LiteLLM
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
litellm.callbacks = [customHandler]
|
||||
```
|
||||
|
||||
3. Test it!
|
||||
|
||||
```python
|
||||
# pip install langfuse
|
||||
|
||||
import os
|
||||
import litellm
|
||||
from litellm import completion
|
||||
|
||||
os.environ["LANGFUSE_PUBLIC_KEY"] = ""
|
||||
os.environ["LANGFUSE_SECRET_KEY"] = ""
|
||||
# Optional, defaults to https://cloud.langfuse.com
|
||||
os.environ["LANGFUSE_HOST"] # optional
|
||||
# LLM API Keys
|
||||
os.environ['OPENAI_API_KEY']=""
|
||||
|
||||
litellm.callbacks = [customHandler]
|
||||
litellm.success_callback = ["langfuse"]
|
||||
|
||||
|
||||
|
||||
## sync
|
||||
response = completion(model="gpt-3.5-turbo", messages=[{ "role": "user", "content": "Hi 👋 - i'm openai"}],
|
||||
stream=True)
|
||||
for chunk in response:
|
||||
continue
|
||||
|
||||
|
||||
## async
|
||||
import asyncio
|
||||
|
||||
def async completion():
|
||||
response = await acompletion(model="gpt-3.5-turbo", messages=[{ "role": "user", "content": "Hi 👋 - i'm openai"}],
|
||||
stream=True)
|
||||
async for chunk in response:
|
||||
continue
|
||||
asyncio.run(completion())
|
||||
```
|
223
docs/my-website/docs/oidc.md
Normal file
223
docs/my-website/docs/oidc.md
Normal file
|
@ -0,0 +1,223 @@
|
|||
# OpenID Connect (OIDC)
|
||||
LiteLLM supports using OpenID Connect (OIDC) for authentication to upstream services . This allows you to avoid storing sensitive credentials in your configuration files.
|
||||
|
||||
|
||||
## OIDC Identity Provider (IdP)
|
||||
|
||||
LiteLLM supports the following OIDC identity providers:
|
||||
|
||||
| Provider | Config Name | Custom Audiences |
|
||||
| -------------------------| ------------ | ---------------- |
|
||||
| Google Cloud Run | `google` | Yes |
|
||||
| CircleCI v1 | `circleci` | No |
|
||||
| CircleCI v2 | `circleci_v2`| No |
|
||||
| GitHub Actions | `github` | Yes |
|
||||
| Azure Kubernetes Service | `azure` | No |
|
||||
|
||||
If you would like to use a different OIDC provider, please open an issue on GitHub.
|
||||
|
||||
|
||||
## OIDC Connect Relying Party (RP)
|
||||
|
||||
LiteLLM supports the following OIDC relying parties / clients:
|
||||
|
||||
- Amazon Bedrock
|
||||
- Azure OpenAI
|
||||
- _(Coming soon) Google Cloud Vertex AI_
|
||||
|
||||
|
||||
### Configuring OIDC
|
||||
|
||||
Wherever a secret key can be used, OIDC can be used in-place. The general format is:
|
||||
|
||||
```
|
||||
oidc/config_name_here/audience_here
|
||||
```
|
||||
|
||||
For providers that do not use the `audience` parameter, you can (and should) omit it:
|
||||
|
||||
```
|
||||
oidc/config_name_here/
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Google Cloud Run -> Amazon Bedrock
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: claude-3-haiku-20240307
|
||||
litellm_params:
|
||||
model: bedrock/anthropic.claude-3-haiku-20240307-v1:0
|
||||
aws_region_name: us-west-2
|
||||
aws_session_name: "litellm"
|
||||
aws_role_name: "arn:aws:iam::YOUR_THING_HERE:role/litellm-google-demo"
|
||||
aws_web_identity_token: "oidc/google/https://example.com"
|
||||
```
|
||||
|
||||
### CircleCI v2 -> Amazon Bedrock
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: command-r
|
||||
litellm_params:
|
||||
model: bedrock/cohere.command-r-v1:0
|
||||
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/"
|
||||
```
|
||||
|
||||
#### Amazon IAM Role Configuration for CircleCI v2 -> Bedrock
|
||||
|
||||
The configuration below is only an example. You should adjust the permissions and trust relationship to match your specific use case.
|
||||
|
||||
Permissions:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "VisualEditor0",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"bedrock:InvokeModel",
|
||||
"bedrock:InvokeModelWithResponseStream"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:bedrock:*::foundation-model/anthropic.claude-3-haiku-20240307-v1:0",
|
||||
"arn:aws:bedrock:*::foundation-model/cohere.command-r-v1:0"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
See https://docs.aws.amazon.com/bedrock/latest/userguide/security_iam_id-based-policy-examples.html for more examples.
|
||||
|
||||
Trust Relationship:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Federated": "arn:aws:iam::335785316107:oidc-provider/oidc.circleci.com/org/c5a99188-154f-4f69-8da2-b442b1bf78dd"
|
||||
},
|
||||
"Action": "sts:AssumeRoleWithWebIdentity",
|
||||
"Condition": {
|
||||
"StringEquals": {
|
||||
"oidc.circleci.com/org/c5a99188-154f-4f69-8da2-b442b1bf78dd:aud": "c5a99188-154f-4f69-8da2-b442b1bf78dd"
|
||||
},
|
||||
"ForAnyValue:StringLike": {
|
||||
"oidc.circleci.com/org/c5a99188-154f-4f69-8da2-b442b1bf78dd:sub": [
|
||||
"org/c5a99188-154f-4f69-8da2-b442b1bf78dd/project/*/user/*/vcs-origin/github.com/BerriAI/litellm/vcs-ref/refs/heads/main",
|
||||
"org/c5a99188-154f-4f69-8da2-b442b1bf78dd/project/*/user/*/vcs-origin/github.com/BerriAI/litellm/vcs-ref/refs/heads/litellm_*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This trust relationship restricts CircleCI to only assume the role on the main branch and branches that start with `litellm_`.
|
||||
|
||||
For CircleCI (v1 and v2), you also need to add your organization's OIDC provider in your AWS IAM settings. See https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html for more information.
|
||||
|
||||
:::tip
|
||||
|
||||
You should _never_ need to create an IAM user. If you did, you're not using OIDC correctly. You should only be creating a role with permissions and a trust relationship to your OIDC provider.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
### Google Cloud Run -> Azure OpenAI
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-4o-2024-05-13
|
||||
litellm_params:
|
||||
model: azure/gpt-4o-2024-05-13
|
||||
azure_ad_token: "oidc/google/https://example.com"
|
||||
api_version: "2024-06-01"
|
||||
api_base: "https://demo-here.openai.azure.com"
|
||||
model_info:
|
||||
base_model: azure/gpt-4o-2024-05-13
|
||||
```
|
||||
|
||||
For Azure OpenAI, you need to define `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, and optionally `AZURE_AUTHORITY_HOST` in your environment.
|
||||
|
||||
```bash
|
||||
export AZURE_CLIENT_ID="91a43c21-cf21-4f34-9085-331015ea4f91" # Azure AD Application (Client) ID
|
||||
export AZURE_TENANT_ID="f3b1cf79-eba8-40c3-8120-cb26aca169c2" # Will be the same across of all your Azure AD applications
|
||||
export AZURE_AUTHORITY_HOST="https://login.microsoftonline.com" # 👈 Optional, defaults to "https://login.microsoftonline.com"
|
||||
```
|
||||
|
||||
:::tip
|
||||
|
||||
You can find `AZURE_CLIENT_ID` by visiting `https://login.microsoftonline.com/YOUR_DOMAIN_HERE/v2.0/.well-known/openid-configuration` and looking for the UUID in the `issuer` field.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
:::tip
|
||||
|
||||
Don't set `AZURE_AUTHORITY_HOST` in your environment unless you need to override the default value. This way, if the default value changes in the future, you won't need to update your environment.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
:::tip
|
||||
|
||||
By default, Azure AD applications use the audience `api://AzureADTokenExchange`. We recommend setting the audience to something more specific to your application.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
#### Azure AD Application Configuration
|
||||
|
||||
Unfortunately, Azure is bit more complicated to set up than other OIDC relying parties like AWS. Basically, you have to:
|
||||
|
||||
1. Create an Azure application.
|
||||
2. Add a federated credential for the OIDC IdP you're using (e.g. Google Cloud Run).
|
||||
3. Add the Azure application to resource group that contains the Azure OpenAI resource(s).
|
||||
4. Give the Azure application the necessary role to access the Azure OpenAI resource(s).
|
||||
|
||||
The custom role below is the recommended minimum permissions for the Azure application to access Azure OpenAI resources. You should adjust the permissions to match your specific use case.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "/subscriptions/24ebb700-ec2f-417f-afad-78fe15dcc91f/providers/Microsoft.Authorization/roleDefinitions/baf42808-99ff-466d-b9da-f95bb0422c5f",
|
||||
"properties": {
|
||||
"roleName": "invoke-only",
|
||||
"description": "",
|
||||
"assignableScopes": [
|
||||
"/subscriptions/24ebb700-ec2f-417f-afad-78fe15dcc91f/resourceGroups/your-openai-group-name"
|
||||
],
|
||||
"permissions": [
|
||||
{
|
||||
"actions": [],
|
||||
"notActions": [],
|
||||
"dataActions": [
|
||||
"Microsoft.CognitiveServices/accounts/OpenAI/deployments/audio/action",
|
||||
"Microsoft.CognitiveServices/accounts/OpenAI/deployments/search/action",
|
||||
"Microsoft.CognitiveServices/accounts/OpenAI/deployments/completions/action",
|
||||
"Microsoft.CognitiveServices/accounts/OpenAI/deployments/chat/completions/action",
|
||||
"Microsoft.CognitiveServices/accounts/OpenAI/deployments/extensions/chat/completions/action",
|
||||
"Microsoft.CognitiveServices/accounts/OpenAI/deployments/embeddings/action",
|
||||
"Microsoft.CognitiveServices/accounts/OpenAI/images/generations/action"
|
||||
],
|
||||
"notDataActions": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
_Note: Your UUIDs will be different._
|
||||
|
||||
Please contact us for paid enterprise support if you need help setting up Azure AD applications.
|
|
@ -22,6 +22,7 @@ Anthropic API fails requests when `max_tokens` are not passed. Due to this litel
|
|||
import os
|
||||
|
||||
os.environ["ANTHROPIC_API_KEY"] = "your-api-key"
|
||||
# os.environ["ANTHROPIC_API_BASE"] = "" # [OPTIONAL] or 'ANTHROPIC_BASE_URL'
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
@ -55,7 +56,7 @@ for chunk in response:
|
|||
print(chunk["choices"][0]["delta"]["content"]) # same as openai format
|
||||
```
|
||||
|
||||
## OpenAI Proxy Usage
|
||||
## Usage with LiteLLM Proxy
|
||||
|
||||
Here's how to call Anthropic with the LiteLLM Proxy Server
|
||||
|
||||
|
@ -68,14 +69,6 @@ export ANTHROPIC_API_KEY="your-api-key"
|
|||
### 2. Start the proxy
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="cli" label="cli">
|
||||
|
||||
```bash
|
||||
$ litellm --model claude-3-opus-20240229
|
||||
|
||||
# Server running on http://0.0.0.0:4000
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="config" label="config.yaml">
|
||||
|
||||
```yaml
|
||||
|
@ -90,6 +83,55 @@ model_list:
|
|||
litellm --config /path/to/config.yaml
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="config-all" label="config - default all Anthropic Model">
|
||||
|
||||
Use this if you want to make requests to `claude-3-haiku-20240307`,`claude-3-opus-20240229`,`claude-2.1` without defining them on the config.yaml
|
||||
|
||||
#### Required env variables
|
||||
```
|
||||
ANTHROPIC_API_KEY=sk-ant****
|
||||
```
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: "*"
|
||||
litellm_params:
|
||||
model: "*"
|
||||
```
|
||||
|
||||
```bash
|
||||
litellm --config /path/to/config.yaml
|
||||
```
|
||||
|
||||
Example Request for this config.yaml
|
||||
|
||||
**Ensure you use `anthropic/` prefix to route the request to Anthropic API**
|
||||
|
||||
```shell
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data ' {
|
||||
"model": "anthropic/claude-3-haiku-20240307",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "what llm are you"
|
||||
}
|
||||
]
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="cli" label="cli">
|
||||
|
||||
```bash
|
||||
$ litellm --model claude-3-opus-20240229
|
||||
|
||||
# Server running on http://0.0.0.0:4000
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### 3. Test it
|
||||
|
@ -183,6 +225,19 @@ print(response)
|
|||
| claude-instant-1.2 | `completion('claude-instant-1.2', messages)` | `os.environ['ANTHROPIC_API_KEY']` |
|
||||
| claude-instant-1 | `completion('claude-instant-1', messages)` | `os.environ['ANTHROPIC_API_KEY']` |
|
||||
|
||||
## Passing Extra Headers to Anthropic API
|
||||
|
||||
Pass `extra_headers: dict` to `litellm.completion`
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
messages = [{"role": "user", "content": "What is Anthropic?"}]
|
||||
response = completion(
|
||||
model="claude-3-5-sonnet-20240620",
|
||||
messages=messages,
|
||||
extra_headers={"anthropic-beta": "max-tokens-3-5-sonnet-2024-07-15"}
|
||||
)
|
||||
```
|
||||
## Advanced
|
||||
|
||||
## Usage - Function Calling
|
||||
|
|
|
@ -40,36 +40,36 @@ response = completion(
|
|||
|
||||
Here's how to call Anthropic with the LiteLLM Proxy Server
|
||||
|
||||
### 1. Save key in your environment
|
||||
|
||||
```bash
|
||||
export AWS_ACCESS_KEY_ID=""
|
||||
export AWS_SECRET_ACCESS_KEY=""
|
||||
export AWS_REGION_NAME=""
|
||||
```
|
||||
|
||||
### 2. Start the proxy
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="cli" label="CLI">
|
||||
|
||||
```bash
|
||||
$ litellm --model anthropic.claude-3-sonnet-20240229-v1:0
|
||||
|
||||
# Server running on http://0.0.0.0:4000
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="config" label="config.yaml">
|
||||
### 1. Setup config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: bedrock-claude-v1
|
||||
litellm_params:
|
||||
model: bedrock/anthropic.claude-instant-v1
|
||||
aws_access_key_id: os.environ/CUSTOM_AWS_ACCESS_KEY_ID
|
||||
aws_secret_access_key: os.environ/CUSTOM_AWS_SECRET_ACCESS_KEY
|
||||
aws_region_name: os.environ/CUSTOM_AWS_REGION_NAME
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
All possible auth params:
|
||||
|
||||
```
|
||||
aws_access_key_id: Optional[str],
|
||||
aws_secret_access_key: Optional[str],
|
||||
aws_session_token: Optional[str],
|
||||
aws_region_name: Optional[str],
|
||||
aws_session_name: Optional[str],
|
||||
aws_profile_name: Optional[str],
|
||||
aws_role_name: Optional[str],
|
||||
aws_web_identity_token: Optional[str],
|
||||
```
|
||||
|
||||
### 2. Start the proxy
|
||||
|
||||
```bash
|
||||
litellm --config /path/to/config.yaml
|
||||
```
|
||||
### 3. Test it
|
||||
|
||||
|
||||
|
@ -623,7 +623,7 @@ response = litellm.embedding(
|
|||
|
||||
|
||||
## Supported AWS Bedrock Models
|
||||
Here's an example of using a bedrock model with LiteLLM
|
||||
Here's an example of using a bedrock model with LiteLLM. For a complete list, refer to the [model cost map](https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json)
|
||||
|
||||
| Model Name | Command |
|
||||
|----------------------------|------------------------------------------------------------------|
|
||||
|
@ -641,6 +641,7 @@ Here's an example of using a bedrock model with LiteLLM
|
|||
| Cohere Command | `completion(model='bedrock/cohere.command-text-v14', messages=messages)` | `os.environ['AWS_ACCESS_KEY_ID']`, `os.environ['AWS_SECRET_ACCESS_KEY']`, `os.environ['AWS_REGION_NAME']` |
|
||||
| AI21 J2-Mid | `completion(model='bedrock/ai21.j2-mid-v1', messages=messages)` | `os.environ['AWS_ACCESS_KEY_ID']`, `os.environ['AWS_SECRET_ACCESS_KEY']`, `os.environ['AWS_REGION_NAME']` |
|
||||
| AI21 J2-Ultra | `completion(model='bedrock/ai21.j2-ultra-v1', messages=messages)` | `os.environ['AWS_ACCESS_KEY_ID']`, `os.environ['AWS_SECRET_ACCESS_KEY']`, `os.environ['AWS_REGION_NAME']` |
|
||||
| AI21 Jamba-Instruct | `completion(model='bedrock/ai21.jamba-instruct-v1:0', messages=messages)` | `os.environ['AWS_ACCESS_KEY_ID']`, `os.environ['AWS_SECRET_ACCESS_KEY']`, `os.environ['AWS_REGION_NAME']` |
|
||||
| Meta Llama 2 Chat 13b | `completion(model='bedrock/meta.llama2-13b-chat-v1', messages=messages)` | `os.environ['AWS_ACCESS_KEY_ID']`, `os.environ['AWS_SECRET_ACCESS_KEY']`, `os.environ['AWS_REGION_NAME']` |
|
||||
| Meta Llama 2 Chat 70b | `completion(model='bedrock/meta.llama2-70b-chat-v1', messages=messages)` | `os.environ['AWS_ACCESS_KEY_ID']`, `os.environ['AWS_SECRET_ACCESS_KEY']`, `os.environ['AWS_REGION_NAME']` |
|
||||
| Mistral 7B Instruct | `completion(model='bedrock/mistral.mistral-7b-instruct-v0:2', messages=messages)` | `os.environ['AWS_ACCESS_KEY_ID']`, `os.environ['AWS_SECRET_ACCESS_KEY']`, `os.environ['AWS_REGION_NAME']` |
|
||||
|
|
|
@ -68,7 +68,7 @@ response = embedding(
|
|||
```
|
||||
|
||||
### Setting - Input Type for v3 models
|
||||
v3 Models have a required parameter: `input_type`, it can be one of the following four values:
|
||||
v3 Models have a required parameter: `input_type`. LiteLLM defaults to `search_document`. It can be one of the following four values:
|
||||
|
||||
- `input_type="search_document"`: (default) Use this for texts (documents) you want to store in your vector database
|
||||
- `input_type="search_query"`: Use this for search queries to find the most relevant documents in your vector database
|
||||
|
@ -76,6 +76,8 @@ v3 Models have a required parameter: `input_type`, it can be one of the followin
|
|||
- `input_type="clustering"`: Use this if you use the embeddings for text clustering
|
||||
|
||||
https://txt.cohere.com/introducing-embed-v3/
|
||||
|
||||
|
||||
```python
|
||||
from litellm import embedding
|
||||
os.environ["COHERE_API_KEY"] = "cohere key"
|
||||
|
|
167
docs/my-website/docs/providers/custom_llm_server.md
Normal file
167
docs/my-website/docs/providers/custom_llm_server.md
Normal file
|
@ -0,0 +1,167 @@
|
|||
# Custom API Server (Custom Format)
|
||||
|
||||
Call your custom torch-serve / internal LLM APIs via LiteLLM
|
||||
|
||||
:::info
|
||||
|
||||
For calling an openai-compatible endpoint, [go here](./openai_compatible.md)
|
||||
:::
|
||||
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
import litellm
|
||||
from litellm import CustomLLM, completion, get_llm_provider
|
||||
|
||||
|
||||
class MyCustomLLM(CustomLLM):
|
||||
def completion(self, *args, **kwargs) -> litellm.ModelResponse:
|
||||
return litellm.completion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "user", "content": "Hello world"}],
|
||||
mock_response="Hi!",
|
||||
) # type: ignore
|
||||
|
||||
litellm.custom_provider_map = [ # 👈 KEY STEP - REGISTER HANDLER
|
||||
{"provider": "my-custom-llm", "custom_handler": my_custom_llm}
|
||||
]
|
||||
|
||||
resp = completion(
|
||||
model="my-custom-llm/my-fake-model",
|
||||
messages=[{"role": "user", "content": "Hello world!"}],
|
||||
)
|
||||
|
||||
assert resp.choices[0].message.content == "Hi!"
|
||||
```
|
||||
|
||||
## OpenAI Proxy Usage
|
||||
|
||||
1. Setup your `custom_handler.py` file
|
||||
|
||||
```python
|
||||
import litellm
|
||||
from litellm import CustomLLM, completion, get_llm_provider
|
||||
|
||||
|
||||
class MyCustomLLM(CustomLLM):
|
||||
def completion(self, *args, **kwargs) -> litellm.ModelResponse:
|
||||
return litellm.completion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "user", "content": "Hello world"}],
|
||||
mock_response="Hi!",
|
||||
) # type: ignore
|
||||
|
||||
async def acompletion(self, *args, **kwargs) -> litellm.ModelResponse:
|
||||
return litellm.completion(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "user", "content": "Hello world"}],
|
||||
mock_response="Hi!",
|
||||
) # type: ignore
|
||||
|
||||
|
||||
my_custom_llm = MyCustomLLM()
|
||||
```
|
||||
|
||||
2. Add to `config.yaml`
|
||||
|
||||
In the config below, we pass
|
||||
|
||||
python_filename: `custom_handler.py`
|
||||
custom_handler_instance_name: `my_custom_llm`. This is defined in Step 1
|
||||
|
||||
custom_handler: `custom_handler.my_custom_llm`
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: "test-model"
|
||||
litellm_params:
|
||||
model: "openai/text-embedding-ada-002"
|
||||
- model_name: "my-custom-model"
|
||||
litellm_params:
|
||||
model: "my-custom-llm/my-model"
|
||||
|
||||
litellm_settings:
|
||||
custom_provider_map:
|
||||
- {"provider": "my-custom-llm", "custom_handler": custom_handler.my_custom_llm}
|
||||
```
|
||||
|
||||
```bash
|
||||
litellm --config /path/to/config.yaml
|
||||
```
|
||||
|
||||
3. Test it!
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/chat/completions' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-d '{
|
||||
"model": "my-custom-model",
|
||||
"messages": [{"role": "user", "content": "Say \"this is a test\" in JSON!"}],
|
||||
}'
|
||||
```
|
||||
|
||||
Expected Response
|
||||
|
||||
```
|
||||
{
|
||||
"id": "chatcmpl-06f1b9cd-08bc-43f7-9814-a69173921216",
|
||||
"choices": [
|
||||
{
|
||||
"finish_reason": "stop",
|
||||
"index": 0,
|
||||
"message": {
|
||||
"content": "Hi!",
|
||||
"role": "assistant",
|
||||
"tool_calls": null,
|
||||
"function_call": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"created": 1721955063,
|
||||
"model": "gpt-3.5-turbo",
|
||||
"object": "chat.completion",
|
||||
"system_fingerprint": null,
|
||||
"usage": {
|
||||
"prompt_tokens": 10,
|
||||
"completion_tokens": 20,
|
||||
"total_tokens": 30
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Custom Handler Spec
|
||||
|
||||
```python
|
||||
from litellm.types.utils import GenericStreamingChunk, ModelResponse
|
||||
from typing import Iterator, AsyncIterator
|
||||
from litellm.llms.base import BaseLLM
|
||||
|
||||
class CustomLLMError(Exception): # use this for all your exceptions
|
||||
def __init__(
|
||||
self,
|
||||
status_code,
|
||||
message,
|
||||
):
|
||||
self.status_code = status_code
|
||||
self.message = message
|
||||
super().__init__(
|
||||
self.message
|
||||
) # Call the base class constructor with the parameters it needs
|
||||
|
||||
class CustomLLM(BaseLLM):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
def completion(self, *args, **kwargs) -> ModelResponse:
|
||||
raise CustomLLMError(status_code=500, message="Not implemented yet!")
|
||||
|
||||
def streaming(self, *args, **kwargs) -> Iterator[GenericStreamingChunk]:
|
||||
raise CustomLLMError(status_code=500, message="Not implemented yet!")
|
||||
|
||||
async def acompletion(self, *args, **kwargs) -> ModelResponse:
|
||||
raise CustomLLMError(status_code=500, message="Not implemented yet!")
|
||||
|
||||
async def astreaming(self, *args, **kwargs) -> AsyncIterator[GenericStreamingChunk]:
|
||||
raise CustomLLMError(status_code=500, message="Not implemented yet!")
|
||||
```
|
|
@ -1,129 +0,0 @@
|
|||
# Custom API Server (OpenAI Format)
|
||||
|
||||
LiteLLM allows you to call your custom endpoint in the OpenAI ChatCompletion format
|
||||
|
||||
## API KEYS
|
||||
No api keys required
|
||||
|
||||
## Set up your Custom API Server
|
||||
Your server should have the following Endpoints:
|
||||
|
||||
Here's an example OpenAI proxy server with routes: https://replit.com/@BerriAI/openai-proxy#main.py
|
||||
|
||||
### Required Endpoints
|
||||
- POST `/chat/completions` - chat completions endpoint
|
||||
|
||||
### Optional Endpoints
|
||||
- POST `/completions` - completions endpoint
|
||||
- Get `/models` - available models on server
|
||||
- POST `/embeddings` - creates an embedding vector representing the input text.
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
### Call `/chat/completions`
|
||||
In order to use your custom OpenAI Chat Completion proxy with LiteLLM, ensure you set
|
||||
|
||||
* `api_base` to your proxy url, example "https://openai-proxy.berriai.repl.co"
|
||||
* `custom_llm_provider` to `openai` this ensures litellm uses the `openai.ChatCompletion` to your api_base
|
||||
|
||||
```python
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
## set ENV variables
|
||||
os.environ["OPENAI_API_KEY"] = "anything" #key is not used for proxy
|
||||
|
||||
messages = [{ "content": "Hello, how are you?","role": "user"}]
|
||||
|
||||
response = completion(
|
||||
model="command-nightly",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
api_base="https://openai-proxy.berriai.repl.co",
|
||||
custom_llm_provider="openai" # litellm will use the openai.ChatCompletion to make the request
|
||||
|
||||
)
|
||||
print(response)
|
||||
```
|
||||
|
||||
#### Response
|
||||
```json
|
||||
{
|
||||
"object":
|
||||
"chat.completion",
|
||||
"choices": [{
|
||||
"finish_reason": "stop",
|
||||
"index": 0,
|
||||
"message": {
|
||||
"content":
|
||||
"The sky, a canvas of blue,\nA work of art, pure and true,\nA",
|
||||
"role": "assistant"
|
||||
}
|
||||
}],
|
||||
"id":
|
||||
"chatcmpl-7fbd6077-de10-4cb4-a8a4-3ef11a98b7c8",
|
||||
"created":
|
||||
1699290237.408061,
|
||||
"model":
|
||||
"togethercomputer/llama-2-70b-chat",
|
||||
"usage": {
|
||||
"completion_tokens": 18,
|
||||
"prompt_tokens": 14,
|
||||
"total_tokens": 32
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Call `/completions`
|
||||
In order to use your custom OpenAI Completion proxy with LiteLLM, ensure you set
|
||||
|
||||
* `api_base` to your proxy url, example "https://openai-proxy.berriai.repl.co"
|
||||
* `custom_llm_provider` to `text-completion-openai` this ensures litellm uses the `openai.Completion` to your api_base
|
||||
|
||||
```python
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
## set ENV variables
|
||||
os.environ["OPENAI_API_KEY"] = "anything" #key is not used for proxy
|
||||
|
||||
messages = [{ "content": "Hello, how are you?","role": "user"}]
|
||||
|
||||
response = completion(
|
||||
model="command-nightly",
|
||||
messages=[{ "content": "Hello, how are you?","role": "user"}],
|
||||
api_base="https://openai-proxy.berriai.repl.co",
|
||||
custom_llm_provider="text-completion-openai" # litellm will use the openai.Completion to make the request
|
||||
|
||||
)
|
||||
print(response)
|
||||
```
|
||||
|
||||
#### Response
|
||||
```json
|
||||
{
|
||||
"warning":
|
||||
"This model version is deprecated. Migrate before January 4, 2024 to avoid disruption of service. Learn more https://platform.openai.com/docs/deprecations",
|
||||
"id":
|
||||
"cmpl-8HxHqF5dymQdALmLplS0dWKZVFe3r",
|
||||
"object":
|
||||
"text_completion",
|
||||
"created":
|
||||
1699290166,
|
||||
"model":
|
||||
"text-davinci-003",
|
||||
"choices": [{
|
||||
"text":
|
||||
"\n\nThe weather in San Francisco varies depending on what time of year and time",
|
||||
"index": 0,
|
||||
"logprobs": None,
|
||||
"finish_reason": "length"
|
||||
}],
|
||||
"usage": {
|
||||
"prompt_tokens": 7,
|
||||
"completion_tokens": 16,
|
||||
"total_tokens": 23
|
||||
}
|
||||
}
|
||||
```
|
89
docs/my-website/docs/providers/empower.md
Normal file
89
docs/my-website/docs/providers/empower.md
Normal file
|
@ -0,0 +1,89 @@
|
|||
# Empower
|
||||
LiteLLM supports all models on Empower.
|
||||
|
||||
## API Keys
|
||||
|
||||
```python
|
||||
import os
|
||||
os.environ["EMPOWER_API_KEY"] = "your-api-key"
|
||||
```
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
import os
|
||||
|
||||
os.environ["EMPOWER_API_KEY"] = "your-api-key"
|
||||
|
||||
messages = [{"role": "user", "content": "Write me a poem about the blue sky"}]
|
||||
|
||||
response = completion(model="empower/empower-functions", messages=messages)
|
||||
print(response)
|
||||
```
|
||||
|
||||
## Example Usage - Streaming
|
||||
```python
|
||||
from litellm import completion
|
||||
import os
|
||||
|
||||
os.environ["EMPOWER_API_KEY"] = "your-api-key"
|
||||
|
||||
messages = [{"role": "user", "content": "Write me a poem about the blue sky"}]
|
||||
|
||||
response = completion(model="empower/empower-functions", messages=messages, streaming=True)
|
||||
for chunk in response:
|
||||
print(chunk['choices'][0]['delta'])
|
||||
|
||||
```
|
||||
|
||||
## Example Usage - Automatic Tool Calling
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
import os
|
||||
|
||||
os.environ["EMPOWER_API_KEY"] = "your-api-key"
|
||||
|
||||
messages = [{"role": "user", "content": "What's the weather like in San Francisco, Tokyo, and Paris?"}]
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_current_weather",
|
||||
"description": "Get the current weather in a given location",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA",
|
||||
},
|
||||
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
|
||||
},
|
||||
"required": ["location"],
|
||||
},
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
response = completion(
|
||||
model="empower/empower-functions-small",
|
||||
messages=messages,
|
||||
tools=tools,
|
||||
tool_choice="auto", # auto is default, but we'll be explicit
|
||||
)
|
||||
print("\nLLM Response:\n", response)
|
||||
```
|
||||
|
||||
## Empower Models
|
||||
liteLLM supports `non-streaming` and `streaming` requests to all models on https://empower.dev/
|
||||
|
||||
Example Empower Usage - Note: liteLLM supports all models deployed on Empower
|
||||
|
||||
|
||||
### Empower LLMs - Automatic Tool Using models
|
||||
| Model Name | Function Call | Required OS Variables |
|
||||
|-----------------------------------|------------------------------------------------------------------------|---------------------------------|
|
||||
| empower/empower-functions | `completion('empower/empower-functions', messages)` | `os.environ['TOGETHERAI_API_KEY']` |
|
||||
| empower/empower-functions-small | `completion('empower/empower-functions-small', messages)` | `os.environ['TOGETHERAI_API_KEY']` |
|
||||
|
|
@ -1,7 +1,12 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Fireworks AI
|
||||
https://fireworks.ai/
|
||||
|
||||
:::info
|
||||
**We support ALL Fireworks AI models, just set `fireworks_ai/` as a prefix when sending completion requests**
|
||||
:::
|
||||
|
||||
## API Key
|
||||
```python
|
||||
|
@ -16,7 +21,7 @@ import os
|
|||
|
||||
os.environ['FIREWORKS_AI_API_KEY'] = ""
|
||||
response = completion(
|
||||
model="fireworks_ai/mixtral-8x7b-instruct",
|
||||
model="fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct",
|
||||
messages=[
|
||||
{"role": "user", "content": "hello from litellm"}
|
||||
],
|
||||
|
@ -31,7 +36,7 @@ import os
|
|||
|
||||
os.environ['FIREWORKS_AI_API_KEY'] = ""
|
||||
response = completion(
|
||||
model="fireworks_ai/mixtral-8x7b-instruct",
|
||||
model="fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct",
|
||||
messages=[
|
||||
{"role": "user", "content": "hello from litellm"}
|
||||
],
|
||||
|
@ -43,8 +48,103 @@ for chunk in response:
|
|||
```
|
||||
|
||||
|
||||
## Usage with LiteLLM Proxy
|
||||
|
||||
### 1. Set Fireworks AI Models on config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: fireworks-llama-v3-70b-instruct
|
||||
litellm_params:
|
||||
model: fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct
|
||||
api_key: "os.environ/FIREWORKS_AI_API_KEY"
|
||||
```
|
||||
|
||||
### 2. Start Proxy
|
||||
|
||||
```
|
||||
litellm --config config.yaml
|
||||
```
|
||||
|
||||
### 3. Test it
|
||||
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Curl" label="Curl Request">
|
||||
|
||||
```shell
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data ' {
|
||||
"model": "fireworks-llama-v3-70b-instruct",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "what llm are you"
|
||||
}
|
||||
]
|
||||
}
|
||||
'
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="openai" label="OpenAI v1.0.0+">
|
||||
|
||||
```python
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="anything",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# request sent to model set on litellm proxy, `litellm --model`
|
||||
response = client.chat.completions.create(model="fireworks-llama-v3-70b-instruct", messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
])
|
||||
|
||||
print(response)
|
||||
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="langchain" label="Langchain">
|
||||
|
||||
```python
|
||||
from langchain.chat_models import ChatOpenAI
|
||||
from langchain.prompts.chat import (
|
||||
ChatPromptTemplate,
|
||||
HumanMessagePromptTemplate,
|
||||
SystemMessagePromptTemplate,
|
||||
)
|
||||
from langchain.schema import HumanMessage, SystemMessage
|
||||
|
||||
chat = ChatOpenAI(
|
||||
openai_api_base="http://0.0.0.0:4000", # set openai_api_base to the LiteLLM Proxy
|
||||
model = "fireworks-llama-v3-70b-instruct",
|
||||
temperature=0.1
|
||||
)
|
||||
|
||||
messages = [
|
||||
SystemMessage(
|
||||
content="You are a helpful assistant that im using to make a test request to."
|
||||
),
|
||||
HumanMessage(
|
||||
content="test from litellm. tell me why it's amazing in 1 sentence"
|
||||
),
|
||||
]
|
||||
response = chat(messages)
|
||||
|
||||
print(response)
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Supported Models - ALL Fireworks AI Models Supported!
|
||||
|
||||
:::info
|
||||
We support ALL Fireworks AI models, just set `fireworks_ai/` as a prefix when sending completion requests
|
||||
:::
|
||||
|
||||
| Model Name | Function Call |
|
||||
|--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
|
60
docs/my-website/docs/providers/friendliai.md
Normal file
60
docs/my-website/docs/providers/friendliai.md
Normal file
|
@ -0,0 +1,60 @@
|
|||
# FriendliAI
|
||||
https://suite.friendli.ai/
|
||||
|
||||
**We support ALL FriendliAI models, just set `friendliai/` as a prefix when sending completion requests**
|
||||
|
||||
## API Key
|
||||
```python
|
||||
# env variable
|
||||
os.environ['FRIENDLI_TOKEN']
|
||||
os.environ['FRIENDLI_API_BASE'] # Optional. Set this when using dedicated endpoint.
|
||||
```
|
||||
|
||||
## Sample Usage
|
||||
```python
|
||||
from litellm import completion
|
||||
import os
|
||||
|
||||
os.environ['FRIENDLI_TOKEN'] = ""
|
||||
response = completion(
|
||||
model="friendliai/mixtral-8x7b-instruct-v0-1",
|
||||
messages=[
|
||||
{"role": "user", "content": "hello from litellm"}
|
||||
],
|
||||
)
|
||||
print(response)
|
||||
```
|
||||
|
||||
## Sample Usage - Streaming
|
||||
```python
|
||||
from litellm import completion
|
||||
import os
|
||||
|
||||
os.environ['FRIENDLI_TOKEN'] = ""
|
||||
response = completion(
|
||||
model="friendliai/mixtral-8x7b-instruct-v0-1",
|
||||
messages=[
|
||||
{"role": "user", "content": "hello from litellm"}
|
||||
],
|
||||
stream=True
|
||||
)
|
||||
|
||||
for chunk in response:
|
||||
print(chunk)
|
||||
```
|
||||
|
||||
|
||||
## Supported Models
|
||||
### Serverless Endpoints
|
||||
We support ALL FriendliAI AI models, just set `friendliai/` as a prefix when sending completion requests
|
||||
|
||||
| Model Name | Function Call |
|
||||
|--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| mixtral-8x7b-instruct | `completion(model="friendliai/mixtral-8x7b-instruct-v0-1", messages)` |
|
||||
| meta-llama-3-8b-instruct | `completion(model="friendliai/meta-llama-3-8b-instruct", messages)` |
|
||||
| meta-llama-3-70b-instruct | `completion(model="friendliai/meta-llama-3-70b-instruct", messages)` |
|
||||
|
||||
### Dedicated Endpoints
|
||||
```
|
||||
model="friendliai/$ENDPOINT_ID:$ADAPTER_ROUTE"
|
||||
```
|
|
@ -1,3 +1,6 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Groq
|
||||
https://groq.com/
|
||||
|
||||
|
@ -20,7 +23,7 @@ import os
|
|||
|
||||
os.environ['GROQ_API_KEY'] = ""
|
||||
response = completion(
|
||||
model="groq/llama2-70b-4096",
|
||||
model="groq/llama3-8b-8192",
|
||||
messages=[
|
||||
{"role": "user", "content": "hello from litellm"}
|
||||
],
|
||||
|
@ -35,7 +38,7 @@ import os
|
|||
|
||||
os.environ['GROQ_API_KEY'] = ""
|
||||
response = completion(
|
||||
model="groq/llama2-70b-4096",
|
||||
model="groq/llama3-8b-8192",
|
||||
messages=[
|
||||
{"role": "user", "content": "hello from litellm"}
|
||||
],
|
||||
|
@ -47,11 +50,109 @@ for chunk in response:
|
|||
```
|
||||
|
||||
|
||||
|
||||
## Usage with LiteLLM Proxy
|
||||
|
||||
### 1. Set Groq Models on config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: groq-llama3-8b-8192 # Model Alias to use for requests
|
||||
litellm_params:
|
||||
model: groq/llama3-8b-8192
|
||||
api_key: "os.environ/GROQ_API_KEY" # ensure you have `GROQ_API_KEY` in your .env
|
||||
```
|
||||
|
||||
### 2. Start Proxy
|
||||
|
||||
```
|
||||
litellm --config config.yaml
|
||||
```
|
||||
|
||||
### 3. Test it
|
||||
|
||||
Make request to litellm proxy
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Curl" label="Curl Request">
|
||||
|
||||
```shell
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data ' {
|
||||
"model": "groq-llama3-8b-8192",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "what llm are you"
|
||||
}
|
||||
]
|
||||
}
|
||||
'
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="openai" label="OpenAI v1.0.0+">
|
||||
|
||||
```python
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="anything",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
response = client.chat.completions.create(model="groq-llama3-8b-8192", messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
])
|
||||
|
||||
print(response)
|
||||
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="langchain" label="Langchain">
|
||||
|
||||
```python
|
||||
from langchain.chat_models import ChatOpenAI
|
||||
from langchain.prompts.chat import (
|
||||
ChatPromptTemplate,
|
||||
HumanMessagePromptTemplate,
|
||||
SystemMessagePromptTemplate,
|
||||
)
|
||||
from langchain.schema import HumanMessage, SystemMessage
|
||||
|
||||
chat = ChatOpenAI(
|
||||
openai_api_base="http://0.0.0.0:4000", # set openai_api_base to the LiteLLM Proxy
|
||||
model = "groq-llama3-8b-8192",
|
||||
temperature=0.1
|
||||
)
|
||||
|
||||
messages = [
|
||||
SystemMessage(
|
||||
content="You are a helpful assistant that im using to make a test request to."
|
||||
),
|
||||
HumanMessage(
|
||||
content="test from litellm. tell me why it's amazing in 1 sentence"
|
||||
),
|
||||
]
|
||||
response = chat(messages)
|
||||
|
||||
print(response)
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
## Supported Models - ALL Groq Models Supported!
|
||||
We support ALL Groq models, just set `groq/` as a prefix when sending completion requests
|
||||
|
||||
| Model Name | Function Call |
|
||||
| Model Name | Usage |
|
||||
|--------------------|---------------------------------------------------------|
|
||||
| llama-3.1-8b-instant | `completion(model="groq/llama-3.1-8b-instant", messages)` |
|
||||
| llama-3.1-70b-versatile | `completion(model="groq/llama-3.1-70b-versatile", messages)` |
|
||||
| llama-3.1-405b-reasoning | `completion(model="groq/llama-3.1-405b-reasoning", messages)` |
|
||||
| llama3-8b-8192 | `completion(model="groq/llama3-8b-8192", messages)` |
|
||||
| llama3-70b-8192 | `completion(model="groq/llama3-70b-8192", messages)` |
|
||||
| llama2-70b-4096 | `completion(model="groq/llama2-70b-4096", messages)` |
|
||||
|
@ -114,7 +215,7 @@ tools = [
|
|||
}
|
||||
]
|
||||
response = litellm.completion(
|
||||
model="groq/llama2-70b-4096",
|
||||
model="groq/llama3-8b-8192",
|
||||
messages=messages,
|
||||
tools=tools,
|
||||
tool_choice="auto", # auto is default, but we'll be explicit
|
||||
|
@ -154,7 +255,7 @@ if tool_calls:
|
|||
) # extend conversation with function response
|
||||
print(f"messages: {messages}")
|
||||
second_response = litellm.completion(
|
||||
model="groq/llama2-70b-4096", messages=messages
|
||||
model="groq/llama3-8b-8192", messages=messages
|
||||
) # get a new response from the model where it can see the function response
|
||||
print("second response\n", second_response)
|
||||
```
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Mistral AI API
|
||||
https://docs.mistral.ai/api/
|
||||
|
||||
|
@ -41,18 +44,120 @@ for chunk in response:
|
|||
```
|
||||
|
||||
|
||||
|
||||
## Usage with LiteLLM Proxy
|
||||
|
||||
### 1. Set Mistral Models on config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: mistral-small-latest
|
||||
litellm_params:
|
||||
model: mistral/mistral-small-latest
|
||||
api_key: "os.environ/MISTRAL_API_KEY" # ensure you have `MISTRAL_API_KEY` in your .env
|
||||
```
|
||||
|
||||
### 2. Start Proxy
|
||||
|
||||
```
|
||||
litellm --config config.yaml
|
||||
```
|
||||
|
||||
### 3. Test it
|
||||
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Curl" label="Curl Request">
|
||||
|
||||
```shell
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data ' {
|
||||
"model": "mistral-small-latest",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "what llm are you"
|
||||
}
|
||||
]
|
||||
}
|
||||
'
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="openai" label="OpenAI v1.0.0+">
|
||||
|
||||
```python
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="anything",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
response = client.chat.completions.create(model="mistral-small-latest", messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
])
|
||||
|
||||
print(response)
|
||||
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="langchain" label="Langchain">
|
||||
|
||||
```python
|
||||
from langchain.chat_models import ChatOpenAI
|
||||
from langchain.prompts.chat import (
|
||||
ChatPromptTemplate,
|
||||
HumanMessagePromptTemplate,
|
||||
SystemMessagePromptTemplate,
|
||||
)
|
||||
from langchain.schema import HumanMessage, SystemMessage
|
||||
|
||||
chat = ChatOpenAI(
|
||||
openai_api_base="http://0.0.0.0:4000", # set openai_api_base to the LiteLLM Proxy
|
||||
model = "mistral-small-latest",
|
||||
temperature=0.1
|
||||
)
|
||||
|
||||
messages = [
|
||||
SystemMessage(
|
||||
content="You are a helpful assistant that im using to make a test request to."
|
||||
),
|
||||
HumanMessage(
|
||||
content="test from litellm. tell me why it's amazing in 1 sentence"
|
||||
),
|
||||
]
|
||||
response = chat(messages)
|
||||
|
||||
print(response)
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Supported Models
|
||||
|
||||
:::info
|
||||
All models listed here https://docs.mistral.ai/platform/endpoints are supported. We actively maintain the list of models, pricing, token window, etc. [here](https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json).
|
||||
|
||||
:::
|
||||
|
||||
|
||||
| Model Name | Function Call |
|
||||
|----------------|--------------------------------------------------------------|
|
||||
| Mistral Small | `completion(model="mistral/mistral-small-latest", messages)` |
|
||||
| Mistral Medium | `completion(model="mistral/mistral-medium-latest", messages)`|
|
||||
| Mistral Large | `completion(model="mistral/mistral-large-latest", messages)` |
|
||||
| Mistral Large 2 | `completion(model="mistral/mistral-large-2407", messages)` |
|
||||
| Mistral Large Latest | `completion(model="mistral/mistral-large-latest", messages)` |
|
||||
| Mistral 7B | `completion(model="mistral/open-mistral-7b", messages)` |
|
||||
| Mixtral 8x7B | `completion(model="mistral/open-mixtral-8x7b", messages)` |
|
||||
| Mixtral 8x22B | `completion(model="mistral/open-mixtral-8x22b", messages)` |
|
||||
| Codestral | `completion(model="mistral/codestral-latest", messages)` |
|
||||
| Mistral NeMo | `completion(model="mistral/open-mistral-nemo", messages)` |
|
||||
| Mistral NeMo 2407 | `completion(model="mistral/open-mistral-nemo-2407", messages)` |
|
||||
| Codestral Mamba | `completion(model="mistral/open-codestral-mamba", messages)` |
|
||||
| Codestral Mamba | `completion(model="mistral/codestral-mamba-latest"", messages)` |
|
||||
|
||||
## Function Calling
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Ollama
|
||||
LiteLLM supports all models from [Ollama](https://github.com/ollama/ollama)
|
||||
|
||||
|
@ -84,6 +87,120 @@ response = completion(
|
|||
)
|
||||
```
|
||||
|
||||
## Example Usage - Tool Calling
|
||||
|
||||
To use ollama tool calling, pass `tools=[{..}]` to `litellm.completion()`
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
import litellm
|
||||
|
||||
## [OPTIONAL] REGISTER MODEL - not all ollama models support function calling, litellm defaults to json mode tool calls if native tool calling not supported.
|
||||
|
||||
# litellm.register_model(model_cost={
|
||||
# "ollama_chat/llama3.1": {
|
||||
# "supports_function_calling": true
|
||||
# },
|
||||
# })
|
||||
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_current_weather",
|
||||
"description": "Get the current weather in a given location",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA",
|
||||
},
|
||||
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
|
||||
},
|
||||
"required": ["location"],
|
||||
},
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
messages = [{"role": "user", "content": "What's the weather like in Boston today?"}]
|
||||
|
||||
|
||||
response = completion(
|
||||
model="ollama_chat/llama3.1",
|
||||
messages=messages,
|
||||
tools=tools
|
||||
)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
1. Setup config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: "llama3.1"
|
||||
litellm_params:
|
||||
model: "ollama_chat/llama3.1"
|
||||
model_info:
|
||||
supports_function_calling: true
|
||||
```
|
||||
|
||||
2. Start proxy
|
||||
|
||||
```bash
|
||||
litellm --config /path/to/config.yaml
|
||||
```
|
||||
|
||||
3. Test it!
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/chat/completions' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-d '{
|
||||
"model": "llama3.1",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What'\''s the weather like in Boston today?"
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_current_weather",
|
||||
"description": "Get the current weather in a given location",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string",
|
||||
"enum": ["celsius", "fahrenheit"]
|
||||
}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"tool_choice": "auto",
|
||||
"stream": true
|
||||
}'
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Using ollama `api/chat`
|
||||
In order to send ollama requests to `POST /api/chat` on your ollama server, set the model prefix to `ollama_chat`
|
||||
|
||||
|
|
|
@ -163,6 +163,8 @@ os.environ["OPENAI_API_BASE"] = "openaiai-api-base" # OPTIONAL
|
|||
|
||||
| Model Name | Function Call |
|
||||
|-----------------------|-----------------------------------------------------------------|
|
||||
| gpt-4o-mini | `response = completion(model="gpt-4o-mini", messages=messages)` |
|
||||
| gpt-4o-mini-2024-07-18 | `response = completion(model="gpt-4o-mini-2024-07-18", messages=messages)` |
|
||||
| gpt-4o | `response = completion(model="gpt-4o", messages=messages)` |
|
||||
| gpt-4o-2024-05-13 | `response = completion(model="gpt-4o-2024-05-13", messages=messages)` |
|
||||
| gpt-4-turbo | `response = completion(model="gpt-4-turbo", messages=messages)` |
|
||||
|
@ -236,6 +238,104 @@ response = completion(
|
|||
|
||||
## Advanced
|
||||
|
||||
### Getting OpenAI API Response Headers
|
||||
|
||||
Set `litellm.return_response_headers = True` to get raw response headers from OpenAI
|
||||
|
||||
You can expect to always get the `_response_headers` field from `litellm.completion()`, `litellm.embedding()` functions
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="litellm.completion" label="litellm.completion">
|
||||
|
||||
```python
|
||||
litellm.return_response_headers = True
|
||||
|
||||
# /chat/completion
|
||||
response = completion(
|
||||
model="gpt-4o-mini",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "hi",
|
||||
}
|
||||
],
|
||||
)
|
||||
print(f"response: {response}")
|
||||
print("_response_headers=", response._response_headers)
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="litellm.completion - streaming" label="litellm.completion + stream">
|
||||
|
||||
```python
|
||||
litellm.return_response_headers = True
|
||||
|
||||
# /chat/completion
|
||||
response = completion(
|
||||
model="gpt-4o-mini",
|
||||
stream=True,
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "hi",
|
||||
}
|
||||
],
|
||||
)
|
||||
print(f"response: {response}")
|
||||
print("response_headers=", response._response_headers)
|
||||
for chunk in response:
|
||||
print(chunk)
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="litellm.embedding" label="litellm.embedding">
|
||||
|
||||
```python
|
||||
litellm.return_response_headers = True
|
||||
|
||||
# embedding
|
||||
embedding_response = litellm.embedding(
|
||||
model="text-embedding-ada-002",
|
||||
input="hello",
|
||||
)
|
||||
|
||||
embedding_response_headers = embedding_response._response_headers
|
||||
print("embedding_response_headers=", embedding_response_headers)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
Expected Response Headers from OpenAI
|
||||
|
||||
```json
|
||||
{
|
||||
"date": "Sat, 20 Jul 2024 22:05:23 GMT",
|
||||
"content-type": "application/json",
|
||||
"transfer-encoding": "chunked",
|
||||
"connection": "keep-alive",
|
||||
"access-control-allow-origin": "*",
|
||||
"openai-model": "text-embedding-ada-002",
|
||||
"openai-organization": "*****",
|
||||
"openai-processing-ms": "20",
|
||||
"openai-version": "2020-10-01",
|
||||
"strict-transport-security": "max-age=15552000; includeSubDomains; preload",
|
||||
"x-ratelimit-limit-requests": "5000",
|
||||
"x-ratelimit-limit-tokens": "5000000",
|
||||
"x-ratelimit-remaining-requests": "4999",
|
||||
"x-ratelimit-remaining-tokens": "4999999",
|
||||
"x-ratelimit-reset-requests": "12ms",
|
||||
"x-ratelimit-reset-tokens": "0s",
|
||||
"x-request-id": "req_cc37487bfd336358231a17034bcfb4d9",
|
||||
"cf-cache-status": "DYNAMIC",
|
||||
"set-cookie": "__cf_bm=E_FJY8fdAIMBzBE2RZI2.OkMIO3lf8Hz.ydBQJ9m3q8-1721513123-1.0.1.1-6OK0zXvtd5s9Jgqfz66cU9gzQYpcuh_RLaUZ9dOgxR9Qeq4oJlu.04C09hOTCFn7Hg.k.2tiKLOX24szUE2shw; path=/; expires=Sat, 20-Jul-24 22:35:23 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None, *cfuvid=SDndIImxiO3U0aBcVtoy1TBQqYeQtVDo1L6*Nlpp7EU-1721513123215-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None",
|
||||
"x-content-type-options": "nosniff",
|
||||
"server": "cloudflare",
|
||||
"cf-ray": "8a66409b4f8acee9-SJC",
|
||||
"content-encoding": "br",
|
||||
"alt-svc": "h3=\":443\"; ma=86400"
|
||||
}
|
||||
```
|
||||
|
||||
### Parallel Function calling
|
||||
See a detailed walthrough of parallel function calling with litellm [here](https://docs.litellm.ai/docs/completion/function_call)
|
||||
```python
|
||||
|
|
|
@ -63,6 +63,14 @@ Here's how to call an OpenAI-Compatible Endpoint with the LiteLLM Proxy Server
|
|||
api_key: api-key # api key to send your model
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
If you see `Not Found Error` when testing make sure your `api_base` has the `/v1` postfix
|
||||
|
||||
Example: `http://vllm-endpoint.xyz/v1`
|
||||
|
||||
:::
|
||||
|
||||
2. Start the proxy
|
||||
|
||||
```bash
|
||||
|
|
|
@ -10,7 +10,7 @@ import TabItem from '@theme/TabItem';
|
|||
|
||||
## 🆕 `vertex_ai_beta/` route
|
||||
|
||||
New `vertex_ai_beta/` route. Adds support for system messages, tool_choice params, etc. by moving to httpx client (instead of vertex sdk).
|
||||
New `vertex_ai_beta/` route. Adds support for system messages, tool_choice params, etc. by moving to httpx client (instead of vertex sdk). This implementation uses [VertexAI's REST API](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference#syntax).
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
|
@ -330,6 +330,103 @@ Return a `list[Recipe]`
|
|||
completion(model="vertex_ai_beta/gemini-1.5-flash-preview-0514", messages=messages, response_format={ "type": "json_object" })
|
||||
```
|
||||
|
||||
### **Grounding**
|
||||
|
||||
Add Google Search Result grounding to vertex ai calls.
|
||||
|
||||
[**Relevant VertexAI Docs**](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/grounding#examples)
|
||||
|
||||
See the grounding metadata with `response_obj._hidden_params["vertex_ai_grounding_metadata"]`
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
|
||||
## SETUP ENVIRONMENT
|
||||
# !gcloud auth application-default login - run this to add vertex credentials to your env
|
||||
|
||||
tools = [{"googleSearchRetrieval": {}}] # 👈 ADD GOOGLE SEARCH
|
||||
|
||||
resp = litellm.completion(
|
||||
model="vertex_ai_beta/gemini-1.0-pro-001",
|
||||
messages=[{"role": "user", "content": "Who won the world cup?"}],
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
print(resp)
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
```bash
|
||||
curl http://0.0.0.0:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $OPENAI_API_KEY" \
|
||||
-d '{
|
||||
"model": "gpt-4o",
|
||||
"messages": [{"role": "user", "content": "Who won the world cup?"}],
|
||||
"tools": [
|
||||
{
|
||||
"googleSearchResults": {}
|
||||
}
|
||||
]
|
||||
}'
|
||||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
#### **Moving from Vertex AI SDK to LiteLLM (GROUNDING)**
|
||||
|
||||
|
||||
If this was your initial VertexAI Grounding code,
|
||||
|
||||
```python
|
||||
import vertexai
|
||||
|
||||
vertexai.init(project=project_id, location="us-central1")
|
||||
|
||||
model = GenerativeModel("gemini-1.5-flash-001")
|
||||
|
||||
# Use Google Search for grounding
|
||||
tool = Tool.from_google_search_retrieval(grounding.GoogleSearchRetrieval(disable_attributon=False))
|
||||
|
||||
prompt = "When is the next total solar eclipse in US?"
|
||||
response = model.generate_content(
|
||||
prompt,
|
||||
tools=[tool],
|
||||
generation_config=GenerationConfig(
|
||||
temperature=0.0,
|
||||
),
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
then, this is what it looks like now
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
|
||||
|
||||
# !gcloud auth application-default login - run this to add vertex credentials to your env
|
||||
|
||||
tools = [{"googleSearchRetrieval": {"disable_attributon": False}}] # 👈 ADD GOOGLE SEARCH
|
||||
|
||||
resp = litellm.completion(
|
||||
model="vertex_ai_beta/gemini-1.0-pro-001",
|
||||
messages=[{"role": "user", "content": "Who won the world cup?"}],
|
||||
tools=tools,
|
||||
vertex_project="project-id"
|
||||
)
|
||||
|
||||
print(resp)
|
||||
```
|
||||
|
||||
|
||||
## Pre-requisites
|
||||
* `pip install google-cloud-aiplatform` (pre-installed on proxy docker image)
|
||||
* Authentication:
|
||||
|
@ -652,6 +749,85 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Llama 3 API
|
||||
|
||||
| Model Name | Function Call |
|
||||
|------------------|--------------------------------------|
|
||||
| meta/llama3-405b-instruct-maas | `completion('vertex_ai/meta/llama3-405b-instruct-maas', messages)` |
|
||||
|
||||
### Usage
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
import os
|
||||
|
||||
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = ""
|
||||
|
||||
model = "meta/llama3-405b-instruct-maas"
|
||||
|
||||
vertex_ai_project = "your-vertex-project" # can also set this as os.environ["VERTEXAI_PROJECT"]
|
||||
vertex_ai_location = "your-vertex-location" # can also set this as os.environ["VERTEXAI_LOCATION"]
|
||||
|
||||
response = completion(
|
||||
model="vertex_ai/" + model,
|
||||
messages=[{"role": "user", "content": "hi"}],
|
||||
temperature=0.7,
|
||||
vertex_ai_project=vertex_ai_project,
|
||||
vertex_ai_location=vertex_ai_location,
|
||||
)
|
||||
print("\nModel Response", response)
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="Proxy">
|
||||
|
||||
**1. Add to config**
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: anthropic-llama
|
||||
litellm_params:
|
||||
model: vertex_ai/meta/llama3-405b-instruct-maas
|
||||
vertex_ai_project: "my-test-project"
|
||||
vertex_ai_location: "us-east-1"
|
||||
- model_name: anthropic-llama
|
||||
litellm_params:
|
||||
model: vertex_ai/meta/llama3-405b-instruct-maas
|
||||
vertex_ai_project: "my-test-project"
|
||||
vertex_ai_location: "us-west-1"
|
||||
```
|
||||
|
||||
**2. Start proxy**
|
||||
|
||||
```bash
|
||||
litellm --config /path/to/config.yaml
|
||||
|
||||
# RUNNING at http://0.0.0.0:4000
|
||||
```
|
||||
|
||||
**3. Test it!**
|
||||
|
||||
```bash
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"model": "anthropic-llama", # 👈 the 'model_name' in config
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "what llm are you"
|
||||
}
|
||||
],
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Model Garden
|
||||
| Model Name | Function Call |
|
||||
|------------------|--------------------------------------|
|
||||
|
@ -825,9 +1001,11 @@ assert isinstance(
|
|||
|
||||
Pass any file supported by Vertex AI, through LiteLLM.
|
||||
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
### **Using `gs://`**
|
||||
```python
|
||||
from litellm import completion
|
||||
|
||||
|
@ -840,7 +1018,7 @@ response = completion(
|
|||
{"type": "text", "text": "You are a very professional document summarization specialist. Please summarize the given document."},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": "gs://cloud-samples-data/generative-ai/pdf/2403.05530.pdf",
|
||||
"image_url": "gs://cloud-samples-data/generative-ai/pdf/2403.05530.pdf", # 👈 PDF
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@ -849,7 +1027,41 @@ response = completion(
|
|||
)
|
||||
|
||||
print(response.choices[0])
|
||||
```
|
||||
|
||||
### **using base64**
|
||||
```python
|
||||
from litellm import completion
|
||||
import base64
|
||||
import requests
|
||||
|
||||
# URL of the file
|
||||
url = "https://storage.googleapis.com/cloud-samples-data/generative-ai/pdf/2403.05530.pdf"
|
||||
|
||||
# Download the file
|
||||
response = requests.get(url)
|
||||
file_data = response.content
|
||||
|
||||
encoded_file = base64.b64encode(file_data).decode("utf-8")
|
||||
|
||||
response = completion(
|
||||
model="vertex_ai/gemini-1.5-flash",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "You are a very professional document summarization specialist. Please summarize the given document."},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": f"data:application/pdf;base64,{encoded_file}", # 👈 PDF
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
max_tokens=300,
|
||||
)
|
||||
|
||||
print(response.choices[0])
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="proxy" lable="PROXY">
|
||||
|
@ -871,6 +1083,7 @@ litellm --config /path/to/config.yaml
|
|||
|
||||
3. Test it!
|
||||
|
||||
**Using `gs://`**
|
||||
```bash
|
||||
curl http://0.0.0.0:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
|
@ -887,8 +1100,8 @@ curl http://0.0.0.0:4000/v1/chat/completions \
|
|||
},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": "gs://cloud-samples-data/generative-ai/pdf/2403.05530.pdf",
|
||||
},
|
||||
"image_url": "gs://cloud-samples-data/generative-ai/pdf/2403.05530.pdf" # 👈 PDF
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -898,6 +1111,33 @@ curl http://0.0.0.0:4000/v1/chat/completions \
|
|||
|
||||
```
|
||||
|
||||
|
||||
```bash
|
||||
curl http://0.0.0.0:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer <YOUR-LITELLM-KEY>" \
|
||||
-d '{
|
||||
"model": "gemini-1.5-flash",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "You are a very professional document summarization specialist. Please summarize the given document"
|
||||
},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": "data:application/pdf;base64,{encoded_file}" # 👈 PDF
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"max_tokens": 300
|
||||
}'
|
||||
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
|
@ -119,8 +119,8 @@ All Possible Alert Types
|
|||
|
||||
```python
|
||||
AlertType = Literal[
|
||||
"llm_exceptions",
|
||||
"llm_too_slow",
|
||||
"llm_exceptions", # LLM API Exceptions
|
||||
"llm_too_slow", # LLM Responses slower than alerting_threshold
|
||||
"llm_requests_hanging",
|
||||
"budget_alerts",
|
||||
"db_exceptions",
|
||||
|
@ -133,6 +133,61 @@ AlertType = Literal[
|
|||
|
||||
```
|
||||
|
||||
## Advanced - set specific slack channels per alert type
|
||||
|
||||
Use this if you want to set specific channels per alert type
|
||||
|
||||
**This allows you to do the following**
|
||||
```
|
||||
llm_exceptions -> go to slack channel #llm-exceptions
|
||||
spend_reports -> go to slack channel #llm-spend-reports
|
||||
```
|
||||
|
||||
Set `alert_to_webhook_url` on your config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-4
|
||||
litellm_params:
|
||||
model: openai/fake
|
||||
api_key: fake-key
|
||||
api_base: https://exampleopenaiendpoint-production.up.railway.app/
|
||||
|
||||
general_settings:
|
||||
master_key: sk-1234
|
||||
alerting: ["slack"]
|
||||
alerting_threshold: 0.0001 # (Seconds) set an artifically 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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",
|
||||
}
|
||||
|
||||
litellm_settings:
|
||||
success_callback: ["langfuse"]
|
||||
```
|
||||
|
||||
Test it - send a valid llm request - expect to see a `llm_too_slow` alert in it's own slack channel
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-4",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello, Claude gm!"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
## Advanced - Using MS Teams Webhooks
|
||||
|
||||
|
|
|
@ -59,6 +59,8 @@ litellm_settings:
|
|||
cache_params: # set cache params for redis
|
||||
type: redis
|
||||
ttl: 600 # will be cached on redis for 600s
|
||||
# default_in_memory_ttl: Optional[float], default is None. time in seconds.
|
||||
# default_in_redis_ttl: Optional[float], default is None. time in seconds.
|
||||
```
|
||||
|
||||
|
||||
|
@ -294,6 +296,11 @@ The proxy support 4 cache-controls:
|
|||
|
||||
**Turn off caching**
|
||||
|
||||
Set `no-cache=True`, this will not return a cached response
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI Python SDK">
|
||||
|
||||
```python
|
||||
import os
|
||||
from openai import OpenAI
|
||||
|
@ -319,9 +326,81 @@ chat_completion = client.chat.completions.create(
|
|||
}
|
||||
)
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
```shell
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"cache": {"no-cache": True},
|
||||
"messages": [
|
||||
{"role": "user", "content": "Say this is a test"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
**Turn on caching**
|
||||
|
||||
By default cache is always on
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI Python SDK">
|
||||
|
||||
```python
|
||||
import os
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
# This is the default and can be omitted
|
||||
api_key=os.environ.get("OPENAI_API_KEY"),
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
chat_completion = client.chat.completions.create(
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Say this is a test",
|
||||
}
|
||||
],
|
||||
model="gpt-3.5-turbo"
|
||||
)
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="curl on" label="curl">
|
||||
|
||||
```shell
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Say this is a test"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
**Set `ttl`**
|
||||
|
||||
Set `ttl=600`, this will caches response for 10 minutes (600 seconds)
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI Python SDK">
|
||||
|
||||
```python
|
||||
import os
|
||||
from openai import OpenAI
|
||||
|
@ -347,6 +426,35 @@ chat_completion = client.chat.completions.create(
|
|||
}
|
||||
)
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="curl on" label="curl">
|
||||
|
||||
```shell
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"cache": {"ttl": 600},
|
||||
"messages": [
|
||||
{"role": "user", "content": "Say this is a test"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
**Set `s-maxage`**
|
||||
|
||||
Set `s-maxage`, this will only get responses cached within last 10 minutes
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI Python SDK">
|
||||
|
||||
```python
|
||||
import os
|
||||
|
@ -373,6 +481,27 @@ chat_completion = client.chat.completions.create(
|
|||
}
|
||||
)
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="curl on" label="curl">
|
||||
|
||||
```shell
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"cache": {"s-maxage": 600},
|
||||
"messages": [
|
||||
{"role": "user", "content": "Say this is a test"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
### Turn on / off caching per Key.
|
||||
|
||||
|
@ -486,6 +615,11 @@ litellm_settings:
|
|||
|
||||
```yaml
|
||||
cache_params:
|
||||
# ttl
|
||||
ttl: Optional[float]
|
||||
default_in_memory_ttl: Optional[float]
|
||||
default_in_redis_ttl: Optional[float]
|
||||
|
||||
# Type of cache (options: "local", "redis", "s3")
|
||||
type: s3
|
||||
|
||||
|
@ -501,6 +635,8 @@ cache_params:
|
|||
host: localhost # Redis server hostname or IP address
|
||||
port: "6379" # Redis server port (as a string)
|
||||
password: secret_password # Redis server password
|
||||
namespace: Optional[str] = None,
|
||||
|
||||
|
||||
# S3 cache parameters
|
||||
s3_bucket_name: your_s3_bucket_name # Name of the S3 bucket
|
||||
|
|
|
@ -60,6 +60,13 @@ model_list:
|
|||
model_info:
|
||||
version: 2
|
||||
|
||||
# Use this if you want to make requests to `claude-3-haiku-20240307`,`claude-3-opus-20240229`,`claude-2.1` without defining them on the config.yaml
|
||||
# Default models
|
||||
# Works for ALL Providers and needs the default provider credentials in .env
|
||||
- model_name: "*"
|
||||
litellm_params:
|
||||
model: "*"
|
||||
|
||||
litellm_settings: # module level litellm settings - https://github.com/BerriAI/litellm/blob/main/litellm/__init__.py
|
||||
drop_params: True
|
||||
success_callback: ["langfuse"] # OPTIONAL - if you want to start sending LLM Logs to Langfuse. Make sure to set `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` in your env
|
||||
|
@ -288,7 +295,7 @@ Dynamically call any model from any given provider without the need to predefine
|
|||
model_list:
|
||||
- model_name: "*" # all requests where model not in your config go to this deployment
|
||||
litellm_params:
|
||||
model: "openai/*" # passes our validation check that a real provider is given
|
||||
model: "*" # passes our validation check that a real provider is given
|
||||
```
|
||||
|
||||
2. Start LiteLLM proxy
|
||||
|
@ -639,6 +646,36 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
}'
|
||||
```
|
||||
|
||||
## ✨ IP Address Filtering
|
||||
|
||||
:::info
|
||||
|
||||
You need a LiteLLM License to unlock this feature. [Grab time](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat), to get one today!
|
||||
|
||||
:::
|
||||
|
||||
Restrict which IP's can call the proxy endpoints.
|
||||
|
||||
```yaml
|
||||
general_settings:
|
||||
allowed_ips: ["192.168.1.1"]
|
||||
```
|
||||
|
||||
**Expected Response** (if IP not listed)
|
||||
|
||||
```bash
|
||||
{
|
||||
"error": {
|
||||
"message": "Access forbidden: IP address not allowed.",
|
||||
"type": "auth_error",
|
||||
"param": "None",
|
||||
"code": 403
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Disable Swagger UI
|
||||
|
||||
To disable the Swagger docs from the base url, set
|
||||
|
|
|
@ -231,7 +231,7 @@ curl -X POST 'http://localhost:4000/customer/new' \
|
|||
```python
|
||||
from openai import OpenAI
|
||||
client = OpenAI(
|
||||
base_url="<your_proxy_base_url",
|
||||
base_url="<your_proxy_base_url>",
|
||||
api_key="<your_proxy_key>"
|
||||
)
|
||||
|
||||
|
|
|
@ -35,6 +35,22 @@ $ litellm --detailed_debug
|
|||
os.environ["LITELLM_LOG"] = "DEBUG"
|
||||
```
|
||||
|
||||
### Debug Logs
|
||||
|
||||
Run the proxy with `--detailed_debug` to view detailed debug logs
|
||||
```shell
|
||||
litellm --config /path/to/config.yaml --detailed_debug
|
||||
```
|
||||
|
||||
When making requests you should see the POST request sent by LiteLLM to the LLM on the Terminal output
|
||||
```shell
|
||||
POST Request Sent from LiteLLM:
|
||||
curl -X POST \
|
||||
https://api.openai.com/v1/chat/completions \
|
||||
-H 'content-type: application/json' -H 'Authorization: Bearer sk-qnWGUIW9****************************************' \
|
||||
-d '{"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "this is a test request, write a short poem"}]}'
|
||||
```
|
||||
|
||||
## JSON LOGS
|
||||
|
||||
Set `JSON_LOGS="True"` in your env:
|
||||
|
|
|
@ -17,8 +17,15 @@ git clone https://github.com/BerriAI/litellm
|
|||
# Go to folder
|
||||
cd litellm
|
||||
|
||||
# Add the master key
|
||||
# Add the master key - you can change this after setup
|
||||
echo 'LITELLM_MASTER_KEY="sk-1234"' > .env
|
||||
|
||||
# Add the litellm salt key - you cannot change this after adding a model
|
||||
# It is used to encrypt / decrypt your LLM API Key credentials
|
||||
# We recommned - https://1password.com/password-generator/
|
||||
# password generator to get a random hash for litellm salt key
|
||||
echo 'LITELLM_SALT_KEY="sk-1234"' > .env
|
||||
|
||||
source .env
|
||||
|
||||
# Start
|
||||
|
@ -247,6 +254,15 @@ Your OpenAI proxy server is now running on `http://127.0.0.1:4000`.
|
|||
|
||||
**That's it ! That's the quick start to deploy litellm**
|
||||
|
||||
## Use with Langchain, OpenAI SDK, LlamaIndex, Instructor, Curl
|
||||
|
||||
:::info
|
||||
💡 Go here 👉 [to make your first LLM API Request](user_keys)
|
||||
|
||||
LiteLLM is compatible with several SDKs - including OpenAI SDK, Anthropic SDK, Mistral SDK, LLamaIndex, Langchain (Js, Python)
|
||||
|
||||
:::
|
||||
|
||||
## Options to deploy LiteLLM
|
||||
|
||||
| Docs | When to Use |
|
||||
|
|
|
@ -18,16 +18,20 @@ Features:
|
|||
- ✅ [JWT-Auth](../docs/proxy/token_auth.md)
|
||||
- ✅ [Control available public, private routes](#control-available-public-private-routes)
|
||||
- ✅ [[BETA] AWS Key Manager v2 - Key Decryption](#beta-aws-key-manager---key-decryption)
|
||||
- ✅ IP address‑based access control lists
|
||||
- ✅ Track Request IP Address
|
||||
- ✅ [Use LiteLLM keys/authentication on Pass Through Endpoints](pass_through#✨-enterprise---use-litellm-keysauthentication-on-pass-through-endpoints)
|
||||
- ✅ [Set Max Request Size / File Size on Requests](#set-max-request--response-size-on-litellm-proxy)
|
||||
- ✅ [Enforce Required Params for LLM Requests (ex. Reject requests missing ["metadata"]["generation_name"])](#enforce-required-params-for-llm-requests)
|
||||
- **Spend Tracking**
|
||||
- **Enterprise Spend Tracking Features**
|
||||
- ✅ [Tracking Spend for Custom Tags](#tracking-spend-for-custom-tags)
|
||||
- ✅ [API Endpoints to get Spend Reports per Team, API Key, Customer](cost_tracking.md#✨-enterprise-api-endpoints-to-get-spend)
|
||||
- ✅ [`/spend/report` API endpoint](cost_tracking.md#✨-enterprise-api-endpoints-to-get-spend)
|
||||
- **Advanced Metrics**
|
||||
- ✅ [`x-ratelimit-remaining-requests`, `x-ratelimit-remaining-tokens` for LLM APIs on Prometheus](prometheus#✨-enterprise-llm-remaining-requests-and-remaining-tokens)
|
||||
- **Guardrails, PII Masking, Content Moderation**
|
||||
- ✅ [Content Moderation with LLM Guard, LlamaGuard, Secret Detection, Google Text Moderations](#content-moderation)
|
||||
- ✅ [Prompt Injection Detection (with LakeraAI API)](#prompt-injection-detection---lakeraai)
|
||||
- ✅ [Prompt Injection Detection (with Aporio API)](#prompt-injection-detection---aporio-ai)
|
||||
- ✅ [Switch LakeraAI on / off per request](guardrails#control-guardrails-onoff-per-request)
|
||||
- ✅ Reject calls from Blocked User list
|
||||
- ✅ Reject calls (incoming / outgoing) with Banned Keywords (e.g. competitors)
|
||||
|
@ -111,7 +115,7 @@ client = openai.OpenAI(
|
|||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# request sent to model set on litellm proxy, `litellm --model`
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages = [
|
||||
|
@ -122,7 +126,7 @@ response = client.chat.completions.create(
|
|||
],
|
||||
extra_body={
|
||||
"metadata": {
|
||||
"tags": ["model-anthropic-claude-v2.1", "app-ishaan-prod"]
|
||||
"tags": ["model-anthropic-claude-v2.1", "app-ishaan-prod"] # 👈 Key Change
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -131,6 +135,43 @@ print(response)
|
|||
```
|
||||
</TabItem>
|
||||
|
||||
|
||||
<TabItem value="openai js" label="OpenAI JS">
|
||||
|
||||
```js
|
||||
const openai = require('openai');
|
||||
|
||||
async function runOpenAI() {
|
||||
const client = new openai.OpenAI({
|
||||
apiKey: 'sk-1234',
|
||||
baseURL: 'http://0.0.0.0:4000'
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.chat.completions.create({
|
||||
model: 'gpt-3.5-turbo',
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: "this is a test request, write a short poem"
|
||||
},
|
||||
],
|
||||
metadata: {
|
||||
tags: ["model-anthropic-claude-v2.1", "app-ishaan-prod"] // 👈 Key Change
|
||||
}
|
||||
});
|
||||
console.log(response);
|
||||
} catch (error) {
|
||||
console.log("got this exception from server");
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Call the asynchronous function
|
||||
runOpenAI();
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="Curl" label="Curl Request">
|
||||
|
||||
Pass `metadata` as part of the request body
|
||||
|
@ -265,6 +306,45 @@ print(response)
|
|||
```
|
||||
</TabItem>
|
||||
|
||||
|
||||
<TabItem value="openai js" label="OpenAI JS">
|
||||
|
||||
```js
|
||||
const openai = require('openai');
|
||||
|
||||
async function runOpenAI() {
|
||||
const client = new openai.OpenAI({
|
||||
apiKey: 'sk-1234',
|
||||
baseURL: 'http://0.0.0.0:4000'
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await client.chat.completions.create({
|
||||
model: 'gpt-3.5-turbo',
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: "this is a test request, write a short poem"
|
||||
},
|
||||
],
|
||||
metadata: {
|
||||
spend_logs_metadata: { // 👈 Key Change
|
||||
hello: "world"
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log(response);
|
||||
} catch (error) {
|
||||
console.log("got this exception from server");
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Call the asynchronous function
|
||||
runOpenAI();
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="Curl" label="Curl Request">
|
||||
|
||||
Pass `metadata` as part of the request body
|
||||
|
@ -950,6 +1030,72 @@ curl --location 'http://localhost:4000/chat/completions' \
|
|||
Need to control LakeraAI per Request ? Doc here 👉: [Switch LakerAI on / off per request](prompt_injection.md#✨-enterprise-switch-lakeraai-on--off-per-api-call)
|
||||
:::
|
||||
|
||||
## Prompt Injection Detection - Aporio AI
|
||||
|
||||
Use this if you want to reject /chat/completion calls that have prompt injection attacks with [AporioAI](https://www.aporia.com/)
|
||||
|
||||
#### Usage
|
||||
|
||||
Step 1. Add env
|
||||
|
||||
```env
|
||||
APORIO_API_KEY="eyJh****"
|
||||
APORIO_API_BASE="https://gr..."
|
||||
```
|
||||
|
||||
Step 2. Add `aporio_prompt_injection` to your callbacks
|
||||
|
||||
```yaml
|
||||
litellm_settings:
|
||||
callbacks: ["aporio_prompt_injection"]
|
||||
```
|
||||
|
||||
That's it, start your proxy
|
||||
|
||||
Test it with this request -> expect it to get rejected by LiteLLM Proxy
|
||||
|
||||
```shell
|
||||
curl --location 'http://localhost:4000/chat/completions' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"model": "llama3",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "You suck!"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Response**
|
||||
|
||||
```
|
||||
{
|
||||
"error": {
|
||||
"message": {
|
||||
"error": "Violated guardrail policy",
|
||||
"aporio_ai_response": {
|
||||
"action": "block",
|
||||
"revised_prompt": null,
|
||||
"revised_response": "Profanity detected: Message blocked because it includes profanity. Please rephrase.",
|
||||
"explain_log": null
|
||||
}
|
||||
},
|
||||
"type": "None",
|
||||
"param": "None",
|
||||
"code": 400
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
Need to control AporioAI per Request ? Doc here 👉: [Create a guardrail](./guardrails.md)
|
||||
:::
|
||||
|
||||
|
||||
## Swagger Docs - Custom Routes + Branding
|
||||
|
||||
:::info
|
||||
|
@ -1057,10 +1203,10 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
### Using via API
|
||||
|
||||
|
||||
**Block all calls for a user id**
|
||||
**Block all calls for a customer id**
|
||||
|
||||
```
|
||||
curl -X POST "http://0.0.0.0:4000/user/block" \
|
||||
curl -X POST "http://0.0.0.0:4000/customer/block" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-D '{
|
||||
"user_ids": [<user_id>, ...]
|
||||
|
@ -1077,6 +1223,8 @@ curl -X POST "http://0.0.0.0:4000/user/unblock" \
|
|||
}'
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Enable Banned Keywords List
|
||||
|
||||
```yaml
|
||||
|
@ -1140,3 +1288,52 @@ How it works?
|
|||
|
||||
**Note:** Setting an environment variable within a Python script using os.environ will not make that variable accessible via SSH sessions or any other new processes that are started independently of the Python script. Environment variables set this way only affect the current process and its child processes.
|
||||
|
||||
|
||||
## Set Max Request / Response Size on LiteLLM Proxy
|
||||
|
||||
Use this if you want to set a maximum request / response size for your proxy server. If a request size is above the size it gets rejected + slack alert triggered
|
||||
|
||||
#### Usage
|
||||
**Step 1.** Set `max_request_size_mb` and `max_response_size_mb`
|
||||
|
||||
For this example we set a very low limit on `max_request_size_mb` and expect it to get rejected
|
||||
|
||||
:::info
|
||||
In production we recommend setting a `max_request_size_mb` / `max_response_size_mb` around `32 MB`
|
||||
|
||||
:::
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: fake-openai-endpoint
|
||||
litellm_params:
|
||||
model: openai/fake
|
||||
api_key: fake-key
|
||||
api_base: https://exampleopenaiendpoint-production.up.railway.app/
|
||||
general_settings:
|
||||
master_key: sk-1234
|
||||
|
||||
# Security controls
|
||||
max_request_size_mb: 0.000000001 # 👈 Key Change - Max Request Size in MB. Set this very low for testing
|
||||
max_response_size_mb: 100 # 👈 Key Change - Max Response Size in MB
|
||||
```
|
||||
|
||||
**Step 2.** Test it with `/chat/completions` request
|
||||
|
||||
```shell
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "fake-openai-endpoint",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello, Claude!"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Response from request**
|
||||
We expect this to fail since the request size is over `max_request_size_mb`
|
||||
```shell
|
||||
{"error":{"message":"Request size is too large. Request size is 0.0001125335693359375 MB. Max size is 1e-09 MB","type":"bad_request_error","param":"content-length","code":400}}
|
||||
```
|
||||
|
|
|
@ -217,10 +217,10 @@ If you need to switch `pii_masking` off for an API Key set `"permissions": {"pii
|
|||
<TabItem value="/key/generate" label="/key/generate">
|
||||
|
||||
```shell
|
||||
curl --location 'http://0.0.0.0:4000/key/generate' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
curl -X POST 'http://0.0.0.0:4000/key/generate' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-D '{
|
||||
"permissions": {"pii_masking": true}
|
||||
}'
|
||||
```
|
||||
|
@ -266,6 +266,54 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
}'
|
||||
```
|
||||
|
||||
## Disable team from turning on/off guardrails
|
||||
|
||||
|
||||
### 1. Disable team from modifying guardrails
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/team/update' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-D '{
|
||||
"team_id": "4198d93c-d375-4c83-8d5a-71e7c5473e50",
|
||||
"metadata": {"guardrails": {"modify_guardrails": false}}
|
||||
}'
|
||||
```
|
||||
|
||||
### 2. Try to disable guardrails for a call
|
||||
|
||||
```bash
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer $LITELLM_VIRTUAL_KEY' \
|
||||
--data '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Think of 10 random colors."
|
||||
}
|
||||
],
|
||||
"metadata": {"guardrails": {"hide_secrets": false}}
|
||||
}'
|
||||
```
|
||||
|
||||
### 3. Get 403 Error
|
||||
|
||||
```
|
||||
{
|
||||
"error": {
|
||||
"message": {
|
||||
"error": "Your team does not have permission to modify guardrails."
|
||||
},
|
||||
"type": "auth_error",
|
||||
"param": "None",
|
||||
"code": 403
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Expect to NOT see `+1 412-612-9992` in your server logs on your callback.
|
||||
|
||||
:::info
|
||||
|
@ -277,6 +325,22 @@ The `pii_masking` guardrail ran on this request because api key=sk-jNm1Zar7XfNdZ
|
|||
|
||||
## Spec for `guardrails` on litellm config
|
||||
|
||||
```yaml
|
||||
litellm_settings:
|
||||
guardrails:
|
||||
- string: GuardrailItemSpec
|
||||
```
|
||||
|
||||
- `string` - Your custom guardrail name
|
||||
|
||||
- `GuardrailItemSpec`:
|
||||
- `callbacks`: List[str], list of supported guardrail callbacks.
|
||||
- Full List: presidio, lakera_prompt_injection, hide_secrets, llmguard_moderations, llamaguard_moderations, google_text_moderation
|
||||
- `default_on`: bool, will run on all llm requests when true
|
||||
- `logging_only`: Optional[bool], if true, run guardrail only on logged output, not on the actual LLM API call. Currently only supported for presidio pii masking. Requires `default_on` to be True as well.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
litellm_settings:
|
||||
guardrails:
|
||||
|
@ -286,19 +350,12 @@ litellm_settings:
|
|||
- hide_secrets:
|
||||
callbacks: [hide_secrets]
|
||||
default_on: true
|
||||
- pii_masking:
|
||||
callback: ["presidio"]
|
||||
default_on: true
|
||||
logging_only: true
|
||||
- your-custom-guardrail
|
||||
callbacks: [hide_secrets]
|
||||
default_on: false
|
||||
```
|
||||
|
||||
|
||||
### `guardrails`: List of guardrail configurations to be applied to LLM requests.
|
||||
|
||||
#### Guardrail: `prompt_injection`: Configuration for detecting and preventing prompt injection attacks.
|
||||
|
||||
- `callbacks`: List of LiteLLM callbacks used for this guardrail. [Can be one of `[lakera_prompt_injection, hide_secrets, presidio, llmguard_moderations, llamaguard_moderations, google_text_moderation]`](enterprise#content-moderation)
|
||||
- `default_on`: Boolean flag determining if this guardrail runs on all LLM requests by default.
|
||||
#### Guardrail: `your-custom-guardrail`: Configuration for a user-defined custom guardrail.
|
||||
|
||||
- `callbacks`: List of callbacks for this custom guardrail. Can be one of `[lakera_prompt_injection, hide_secrets, presidio, llmguard_moderations, llamaguard_moderations, google_text_moderation]`
|
||||
- `default_on`: Boolean flag determining if this custom guardrail runs by default, set to false.
|
||||
|
|
|
@ -41,28 +41,6 @@ litellm --health
|
|||
}
|
||||
```
|
||||
|
||||
### Background Health Checks
|
||||
|
||||
You can enable model health checks being run in the background, to prevent each model from being queried too frequently via `/health`.
|
||||
|
||||
Here's how to use it:
|
||||
1. in the config.yaml add:
|
||||
```
|
||||
general_settings:
|
||||
background_health_checks: True # enable background health checks
|
||||
health_check_interval: 300 # frequency of background health checks
|
||||
```
|
||||
|
||||
2. Start server
|
||||
```
|
||||
$ litellm /path/to/config.yaml
|
||||
```
|
||||
|
||||
3. Query health endpoint:
|
||||
```
|
||||
curl --location 'http://0.0.0.0:4000/health'
|
||||
```
|
||||
|
||||
### Embedding Models
|
||||
|
||||
We need some way to know if the model is an embedding model when running checks, if you have this in your config, specifying mode it makes an embedding health check
|
||||
|
@ -112,6 +90,66 @@ model_list:
|
|||
mode: completion # 👈 ADD THIS
|
||||
```
|
||||
|
||||
### Speech to Text Models
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: whisper
|
||||
litellm_params:
|
||||
model: whisper-1
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
model_info:
|
||||
mode: audio_transcription
|
||||
```
|
||||
|
||||
|
||||
### Text to Speech Models
|
||||
|
||||
```yaml
|
||||
# OpenAI Text to Speech Models
|
||||
- model_name: tts
|
||||
litellm_params:
|
||||
model: openai/tts-1
|
||||
api_key: "os.environ/OPENAI_API_KEY"
|
||||
model_info:
|
||||
mode: audio_speech
|
||||
```
|
||||
|
||||
## Background Health Checks
|
||||
|
||||
You can enable model health checks being run in the background, to prevent each model from being queried too frequently via `/health`.
|
||||
|
||||
Here's how to use it:
|
||||
1. in the config.yaml add:
|
||||
```
|
||||
general_settings:
|
||||
background_health_checks: True # enable background health checks
|
||||
health_check_interval: 300 # frequency of background health checks
|
||||
```
|
||||
|
||||
2. Start server
|
||||
```
|
||||
$ litellm /path/to/config.yaml
|
||||
```
|
||||
|
||||
3. Query health endpoint:
|
||||
```
|
||||
curl --location 'http://0.0.0.0:4000/health'
|
||||
```
|
||||
|
||||
### Hide details
|
||||
|
||||
The health check response contains details like endpoint URLs, error messages,
|
||||
and other LiteLLM params. While this is useful for debugging, it can be
|
||||
problematic when exposing the proxy server to a broad audience.
|
||||
|
||||
You can hide these details by setting the `health_check_details` setting to `False`.
|
||||
|
||||
```yaml
|
||||
general_settings:
|
||||
health_check_details: False
|
||||
```
|
||||
|
||||
## `/health/readiness`
|
||||
|
||||
Unprotected endpoint for checking if proxy is ready to accept requests
|
||||
|
@ -119,30 +157,32 @@ Unprotected endpoint for checking if proxy is ready to accept requests
|
|||
Example Request:
|
||||
|
||||
```bash
|
||||
curl --location 'http://0.0.0.0:4000/health/readiness'
|
||||
curl http://0.0.0.0:4000/health/readiness
|
||||
```
|
||||
|
||||
Example Response:
|
||||
|
||||
*If proxy connected to a database*
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "healthy",
|
||||
"status": "connected",
|
||||
"db": "connected",
|
||||
"litellm_version":"1.19.2",
|
||||
"cache": null,
|
||||
"litellm_version": "1.40.21",
|
||||
"success_callbacks": [
|
||||
"langfuse",
|
||||
"_PROXY_track_cost_callback",
|
||||
"response_taking_too_long_callback",
|
||||
"_PROXY_MaxParallelRequestsHandler",
|
||||
"_PROXY_MaxBudgetLimiter",
|
||||
"_PROXY_CacheControlCheck",
|
||||
"ServiceLogging"
|
||||
],
|
||||
"last_updated": "2024-07-10T18:59:10.616968"
|
||||
}
|
||||
```
|
||||
|
||||
*If proxy not connected to a database*
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "healthy",
|
||||
"db": "Not connected",
|
||||
"litellm_version":"1.19.2",
|
||||
}
|
||||
```
|
||||
If the proxy is not connected to a database, then the `"db"` field will be `"Not
|
||||
connected"` instead of `"connected"` and the `"last_updated"` field will not be present.
|
||||
|
||||
## `/health/liveliness`
|
||||
|
||||
|
|
|
@ -1,28 +1,69 @@
|
|||
# 🪢 Logging
|
||||
|
||||
Log Proxy input, output, and exceptions using:
|
||||
|
||||
- Langfuse
|
||||
- OpenTelemetry
|
||||
- Custom Callbacks
|
||||
- Langsmith
|
||||
- DataDog
|
||||
- DynamoDB
|
||||
- s3 Bucket
|
||||
- etc.
|
||||
|
||||
import Image from '@theme/IdealImage';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
## Getting the LiteLLM Call ID
|
||||
|
||||
# 🪢 Logging - Langfuse, OpenTelemetry, Custom Callbacks, DataDog, s3 Bucket, Sentry, Athina, Azure Content-Safety
|
||||
LiteLLM generates a unique `call_id` for each request. This `call_id` can be
|
||||
used to track the request across the system. This can be very useful for finding
|
||||
the info for a particular request in a logging system like one of the systems
|
||||
mentioned in this page.
|
||||
|
||||
Log Proxy Input, Output, Exceptions using Langfuse, OpenTelemetry, Custom Callbacks, DataDog, DynamoDB, s3 Bucket
|
||||
```shell
|
||||
curl -i -sSL --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [{"role": "user", "content": "what llm are you"}]
|
||||
}' | grep 'x-litellm'
|
||||
```
|
||||
|
||||
## Table of Contents
|
||||
The output of this is:
|
||||
|
||||
- [Logging to Langfuse](#logging-proxy-inputoutput---langfuse)
|
||||
- [Logging with OpenTelemetry (OpenTelemetry)](#logging-proxy-inputoutput-in-opentelemetry-format)
|
||||
- [Async Custom Callbacks](#custom-callback-class-async)
|
||||
- [Async Custom Callback APIs](#custom-callback-apis-async)
|
||||
- [Logging to Galileo](#logging-llm-io-to-galileo)
|
||||
- [Logging to OpenMeter](#logging-proxy-inputoutput---langfuse)
|
||||
- [Logging to s3 Buckets](#logging-proxy-inputoutput---s3-buckets)
|
||||
- [Logging to DataDog](#logging-proxy-inputoutput---datadog)
|
||||
- [Logging to DynamoDB](#logging-proxy-inputoutput---dynamodb)
|
||||
- [Logging to Sentry](#logging-proxy-inputoutput---sentry)
|
||||
- [Logging to Athina](#logging-proxy-inputoutput-athina)
|
||||
- [(BETA) Moderation with Azure Content-Safety](#moderation-with-azure-content-safety)
|
||||
```output
|
||||
x-litellm-call-id: b980db26-9512-45cc-b1da-c511a363b83f
|
||||
x-litellm-model-id: cb41bc03f4c33d310019bae8c5afdb1af0a8f97b36a234405a9807614988457c
|
||||
x-litellm-model-api-base: https://x-example-1234.openai.azure.com
|
||||
x-litellm-version: 1.40.21
|
||||
x-litellm-response-cost: 2.85e-05
|
||||
x-litellm-key-tpm-limit: None
|
||||
x-litellm-key-rpm-limit: None
|
||||
```
|
||||
|
||||
A number of these headers could be useful for troubleshooting, but the
|
||||
`x-litellm-call-id` is the one that is most useful for tracking a request across
|
||||
components in your system, including in logging tools.
|
||||
|
||||
## Redacting UserAPIKeyInfo
|
||||
|
||||
Redact information about the user api key (hashed token, user_id, team id, etc.), from logs.
|
||||
|
||||
Currently supported for Langfuse, OpenTelemetry, Logfire, ArizeAI logging.
|
||||
|
||||
```yaml
|
||||
litellm_settings:
|
||||
callbacks: ["langfuse"]
|
||||
redact_user_api_key_info: true
|
||||
```
|
||||
|
||||
Removes any field with `user_api_key_*` from metadata.
|
||||
|
||||
## Logging Proxy Input/Output - Langfuse
|
||||
|
||||
We will use the `--config` to set `litellm.success_callback = ["langfuse"]` this will log all successfull LLM calls to langfuse. Make sure to set `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` in your environment
|
||||
|
||||
**Step 1** Install langfuse
|
||||
|
@ -32,6 +73,7 @@ pip install langfuse>=2.0.0
|
|||
```
|
||||
|
||||
**Step 2**: Create a `config.yaml` file and set `litellm_settings`: `success_callback`
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-3.5-turbo
|
||||
|
@ -42,6 +84,7 @@ litellm_settings:
|
|||
```
|
||||
|
||||
**Step 3**: Set required env variables for logging to langfuse
|
||||
|
||||
```shell
|
||||
export LANGFUSE_PUBLIC_KEY="pk_kk"
|
||||
export LANGFUSE_SECRET_KEY="sk_ss"
|
||||
|
@ -52,11 +95,13 @@ export LANGFUSE_HOST="https://xxx.langfuse.com"
|
|||
**Step 4**: Start the proxy, make a test request
|
||||
|
||||
Start proxy
|
||||
|
||||
```shell
|
||||
litellm --config config.yaml --debug
|
||||
```
|
||||
|
||||
Test Request
|
||||
|
||||
```
|
||||
litellm --test
|
||||
```
|
||||
|
@ -67,7 +112,6 @@ Expected output on Langfuse
|
|||
|
||||
### Logging Metadata to Langfuse
|
||||
|
||||
|
||||
<Tabs>
|
||||
|
||||
<TabItem value="Curl" label="Curl Request">
|
||||
|
@ -93,6 +137,7 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
}
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="openai" label="OpenAI v1.0.0+">
|
||||
|
||||
|
@ -126,6 +171,7 @@ response = client.chat.completions.create(
|
|||
|
||||
print(response)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="langchain" label="Langchain">
|
||||
|
||||
|
@ -168,9 +214,11 @@ print(response)
|
|||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
### Team based Logging to Langfuse
|
||||
|
||||
[👉 Tutorial - Allow each team to use their own Langfuse Project / custom callbacks](team_logging)
|
||||
<!--
|
||||
|
||||
**Example:**
|
||||
|
||||
This config would send langfuse logs to 2 different langfuse projects, based on the team id
|
||||
|
@ -197,7 +245,7 @@ curl -X POST 'http://0.0.0.0:4000/key/generate' \
|
|||
-d '{"team_id": "ishaans-secret-project"}'
|
||||
```
|
||||
|
||||
All requests made with these keys will log data to their team-specific logging.
|
||||
All requests made with these keys will log data to their team-specific logging. -->
|
||||
|
||||
### Redacting Messages, Response Content from Langfuse Logging
|
||||
|
||||
|
@ -257,6 +305,7 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
}
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="openai" label="OpenAI v1.0.0+">
|
||||
|
||||
|
@ -287,6 +336,7 @@ response = client.chat.completions.create(
|
|||
|
||||
print(response)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="langchain" label="Langchain">
|
||||
|
||||
|
@ -332,7 +382,6 @@ You will see `raw_request` in your Langfuse Metadata. This is the RAW CURL comma
|
|||
|
||||
<Image img={require('../../img/debug_langfuse.png')} />
|
||||
|
||||
|
||||
## Logging Proxy Input/Output in OpenTelemetry format
|
||||
|
||||
:::info
|
||||
|
@ -348,10 +397,8 @@ OTEL_SERVICE_NAME=<your-service-name>` # default="litellm"
|
|||
|
||||
<Tabs>
|
||||
|
||||
|
||||
<TabItem value="Console Exporter" label="Log to console">
|
||||
|
||||
|
||||
**Step 1:** Set callbacks and env vars
|
||||
|
||||
Add the following to your env
|
||||
|
@ -367,7 +414,6 @@ litellm_settings:
|
|||
callbacks: ["otel"]
|
||||
```
|
||||
|
||||
|
||||
**Step 2**: Start the proxy, make a test request
|
||||
|
||||
Start proxy
|
||||
|
@ -427,7 +473,6 @@ This is the Span from OTEL Logging
|
|||
|
||||
</TabItem>
|
||||
|
||||
|
||||
<TabItem value="Honeycomb" label="Log to Honeycomb">
|
||||
|
||||
#### Quick Start - Log to Honeycomb
|
||||
|
@ -449,7 +494,6 @@ litellm_settings:
|
|||
callbacks: ["otel"]
|
||||
```
|
||||
|
||||
|
||||
**Step 2**: Start the proxy, make a test request
|
||||
|
||||
Start proxy
|
||||
|
@ -474,10 +518,8 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
}'
|
||||
```
|
||||
|
||||
|
||||
</TabItem>
|
||||
|
||||
|
||||
<TabItem value="otel-col" label="Log to OTEL HTTP Collector">
|
||||
|
||||
#### Quick Start - Log to OTEL Collector
|
||||
|
@ -499,7 +541,6 @@ litellm_settings:
|
|||
callbacks: ["otel"]
|
||||
```
|
||||
|
||||
|
||||
**Step 2**: Start the proxy, make a test request
|
||||
|
||||
Start proxy
|
||||
|
@ -526,7 +567,6 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
|
||||
</TabItem>
|
||||
|
||||
|
||||
<TabItem value="otel-col-grpc" label="Log to OTEL GRPC Collector">
|
||||
|
||||
#### Quick Start - Log to OTEL GRPC Collector
|
||||
|
@ -548,7 +588,6 @@ litellm_settings:
|
|||
callbacks: ["otel"]
|
||||
```
|
||||
|
||||
|
||||
**Step 2**: Start the proxy, make a test request
|
||||
|
||||
Start proxy
|
||||
|
@ -573,7 +612,6 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
}'
|
||||
```
|
||||
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="traceloop" label="Log to Traceloop Cloud">
|
||||
|
@ -596,7 +634,6 @@ environment_variables:
|
|||
TRACELOOP_API_KEY: "XXXXX"
|
||||
```
|
||||
|
||||
|
||||
**Step 3**: Start the proxy, make a test request
|
||||
|
||||
Start proxy
|
||||
|
@ -632,11 +669,15 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
❓ Use this when you want to **pass information about the incoming request in a distributed tracing system**
|
||||
|
||||
✅ Key change: Pass the **`traceparent` header** in your requests. [Read more about traceparent headers here](https://uptrace.dev/opentelemetry/opentelemetry-traceparent.html#what-is-traceparent-header)
|
||||
|
||||
```curl
|
||||
traceparent: 00-80e1afed08e019fc1110464cfa66635c-7a085853722dc6d2-01
|
||||
```
|
||||
|
||||
Example Usage
|
||||
|
||||
1. Make Request to LiteLLM Proxy with `traceparent` header
|
||||
|
||||
```python
|
||||
import openai
|
||||
import uuid
|
||||
|
@ -660,7 +701,6 @@ response = client.chat.completions.create(
|
|||
)
|
||||
|
||||
print(response)
|
||||
|
||||
```
|
||||
|
||||
```shell
|
||||
|
@ -674,12 +714,12 @@ Search for Trace=`80e1afed08e019fc1110464cfa66635c` on your OTEL Collector
|
|||
|
||||
<Image img={require('../../img/otel_parent.png')} />
|
||||
|
||||
|
||||
|
||||
## Custom Callback Class [Async]
|
||||
|
||||
Use this when you want to run custom callbacks in `python`
|
||||
|
||||
#### Step 1 - Create your custom `litellm` callback class
|
||||
|
||||
We use `litellm.integrations.custom_logger` for this, **more details about litellm custom callbacks [here](https://docs.litellm.ai/docs/observability/custom_callback)**
|
||||
|
||||
Define your custom callback class in a python file.
|
||||
|
@ -782,16 +822,17 @@ proxy_handler_instance = MyCustomHandler()
|
|||
```
|
||||
|
||||
#### Step 2 - Pass your custom callback class in `config.yaml`
|
||||
|
||||
We pass the custom callback class defined in **Step1** to the config.yaml.
|
||||
Set `callbacks` to `python_filename.logger_instance_name`
|
||||
|
||||
In the config below, we pass
|
||||
|
||||
- python_filename: `custom_callbacks.py`
|
||||
- logger_instance_name: `proxy_handler_instance`. This is defined in Step 1
|
||||
|
||||
`callbacks: custom_callbacks.proxy_handler_instance`
|
||||
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-3.5-turbo
|
||||
|
@ -804,6 +845,7 @@ litellm_settings:
|
|||
```
|
||||
|
||||
#### Step 3 - Start proxy + test request
|
||||
|
||||
```shell
|
||||
litellm --config proxy_config.yaml
|
||||
```
|
||||
|
@ -825,6 +867,7 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
```
|
||||
|
||||
#### Resulting Log on Proxy
|
||||
|
||||
```shell
|
||||
On Success
|
||||
Model: gpt-3.5-turbo,
|
||||
|
@ -877,7 +920,6 @@ class MyCustomHandler(CustomLogger):
|
|||
"max_tokens": 10
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### Logging `model_info` set in config.yaml
|
||||
|
@ -895,11 +937,13 @@ class MyCustomHandler(CustomLogger):
|
|||
```
|
||||
|
||||
**Expected Output**
|
||||
|
||||
```json
|
||||
{'mode': 'embedding', 'input_cost_per_token': 0.002}
|
||||
```
|
||||
|
||||
### Logging responses from proxy
|
||||
|
||||
Both `/chat/completions` and `/embeddings` responses are available as `response_obj`
|
||||
|
||||
**Note: for `/chat/completions`, both `stream=True` and `non stream` responses are available as `response_obj`**
|
||||
|
@ -913,6 +957,7 @@ class MyCustomHandler(CustomLogger):
|
|||
```
|
||||
|
||||
**Expected Output /chat/completion [for both `stream` and `non-stream` responses]**
|
||||
|
||||
```json
|
||||
ModelResponse(
|
||||
id='chatcmpl-8Tfu8GoMElwOZuj2JlHBhNHG01PPo',
|
||||
|
@ -939,6 +984,7 @@ ModelResponse(
|
|||
```
|
||||
|
||||
**Expected Output /embeddings**
|
||||
|
||||
```json
|
||||
{
|
||||
'model': 'ada',
|
||||
|
@ -958,7 +1004,6 @@ ModelResponse(
|
|||
}
|
||||
```
|
||||
|
||||
|
||||
## Custom Callback APIs [Async]
|
||||
|
||||
:::info
|
||||
|
@ -968,10 +1013,12 @@ This is an Enterprise only feature [Get Started with Enterprise here](https://gi
|
|||
:::
|
||||
|
||||
Use this if you:
|
||||
|
||||
- Want to use custom callbacks written in a non Python programming language
|
||||
- Want your callbacks to run on a different microservice
|
||||
|
||||
#### Step 1. Create your generic logging API endpoint
|
||||
|
||||
Set up a generic API endpoint that can receive data in JSON format. The data will be included within a "data" field.
|
||||
|
||||
Your server should support the following Request format:
|
||||
|
@ -1034,11 +1081,8 @@ async def log_event(request: Request):
|
|||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="127.0.0.1", port=4000)
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### Step 2. Set your `GENERIC_LOGGER_ENDPOINT` to the endpoint + route we should send callback logs to
|
||||
|
||||
```shell
|
||||
|
@ -1048,6 +1092,7 @@ os.environ["GENERIC_LOGGER_ENDPOINT"] = "http://localhost:4000/log-event"
|
|||
#### Step 3. Create a `config.yaml` file and set `litellm_settings`: `success_callback` = ["generic"]
|
||||
|
||||
Example litellm proxy config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-3.5-turbo
|
||||
|
@ -1059,8 +1104,98 @@ litellm_settings:
|
|||
|
||||
Start the LiteLLM Proxy and make a test request to verify the logs reached your callback API
|
||||
|
||||
## Logging LLM IO to Langsmith
|
||||
|
||||
1. Set `success_callback: ["langsmith"]` on litellm config.yaml
|
||||
|
||||
If you're using a custom LangSmith instance, you can set the
|
||||
`LANGSMITH_BASE_URL` environment variable to point to your instance.
|
||||
|
||||
```yaml
|
||||
litellm_settings:
|
||||
success_callback: ["langsmith"]
|
||||
|
||||
environment_variables:
|
||||
LANGSMITH_API_KEY: "lsv2_pt_xxxxxxxx"
|
||||
LANGSMITH_PROJECT: "litellm-proxy"
|
||||
|
||||
LANGSMITH_BASE_URL: "https://api.smith.langchain.com" # (Optional - only needed if you have a custom Langsmith instance)
|
||||
```
|
||||
|
||||
|
||||
2. Start Proxy
|
||||
|
||||
```
|
||||
litellm --config /path/to/config.yaml
|
||||
```
|
||||
|
||||
3. Test it!
|
||||
|
||||
```bash
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data ' {
|
||||
"model": "fake-openai-endpoint",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello, Claude gm!"
|
||||
}
|
||||
],
|
||||
}
|
||||
'
|
||||
```
|
||||
Expect to see your log on Langfuse
|
||||
<Image img={require('../../img/langsmith_new.png')} />
|
||||
|
||||
|
||||
## Logging LLM IO to Arize AI
|
||||
|
||||
1. Set `success_callback: ["arize"]` on litellm config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-4
|
||||
litellm_params:
|
||||
model: openai/fake
|
||||
api_key: fake-key
|
||||
api_base: https://exampleopenaiendpoint-production.up.railway.app/
|
||||
|
||||
litellm_settings:
|
||||
callbacks: ["arize"]
|
||||
|
||||
environment_variables:
|
||||
ARIZE_SPACE_KEY: "d0*****"
|
||||
ARIZE_API_KEY: "141a****"
|
||||
```
|
||||
|
||||
2. Start Proxy
|
||||
|
||||
```
|
||||
litellm --config /path/to/config.yaml
|
||||
```
|
||||
|
||||
3. Test it!
|
||||
|
||||
```bash
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data ' {
|
||||
"model": "fake-openai-endpoint",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello, Claude gm!"
|
||||
}
|
||||
],
|
||||
}
|
||||
'
|
||||
```
|
||||
Expect to see your log on Langfuse
|
||||
<Image img={require('../../img/langsmith_new.png')} />
|
||||
|
||||
## Logging LLM IO to Galileo
|
||||
|
||||
[BETA]
|
||||
|
||||
Log LLM I/O on [www.rungalileo.io](https://www.rungalileo.io/)
|
||||
|
@ -1083,6 +1218,7 @@ export GALILEO_PASSWORD=""
|
|||
### Quick Start
|
||||
|
||||
1. Add to Config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- litellm_params:
|
||||
|
@ -1118,7 +1254,6 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
'
|
||||
```
|
||||
|
||||
|
||||
🎉 That's it - Expect to see your Logs on your Galileo Dashboard
|
||||
|
||||
## Logging Proxy Cost + Usage - OpenMeter
|
||||
|
@ -1136,6 +1271,7 @@ export OPENMETER_API_KEY=""
|
|||
### Quick Start
|
||||
|
||||
1. Add to Config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- litellm_params:
|
||||
|
@ -1171,13 +1307,14 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
'
|
||||
```
|
||||
|
||||
|
||||
<Image img={require('../../img/openmeter_img_2.png')} />
|
||||
|
||||
## Logging Proxy Input/Output - DataDog
|
||||
|
||||
We will use the `--config` to set `litellm.success_callback = ["datadog"]` this will log all successfull LLM calls to DataDog
|
||||
|
||||
**Step 1**: Create a `config.yaml` file and set `litellm_settings`: `success_callback`
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-3.5-turbo
|
||||
|
@ -1197,6 +1334,7 @@ DD_SITE="us5.datadoghq.com" # your datadog base url
|
|||
**Step 3**: Start the proxy, make a test request
|
||||
|
||||
Start proxy
|
||||
|
||||
```shell
|
||||
litellm --config config.yaml --debug
|
||||
```
|
||||
|
@ -1224,10 +1362,10 @@ Expected output on Datadog
|
|||
|
||||
<Image img={require('../../img/dd_small1.png')} />
|
||||
|
||||
|
||||
## Logging Proxy Input/Output - s3 Buckets
|
||||
|
||||
We will use the `--config` to set
|
||||
|
||||
- `litellm.success_callback = ["s3"]`
|
||||
|
||||
This will log all successfull LLM calls to s3 Bucket
|
||||
|
@ -1241,6 +1379,7 @@ AWS_REGION_NAME = ""
|
|||
```
|
||||
|
||||
**Step 2**: Create a `config.yaml` file and set `litellm_settings`: `success_callback`
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-3.5-turbo
|
||||
|
@ -1260,11 +1399,13 @@ litellm_settings:
|
|||
**Step 3**: Start the proxy, make a test request
|
||||
|
||||
Start proxy
|
||||
|
||||
```shell
|
||||
litellm --config config.yaml --debug
|
||||
```
|
||||
|
||||
Test Request
|
||||
|
||||
```shell
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
|
@ -1284,6 +1425,7 @@ Your logs should be available on the specified s3 Bucket
|
|||
## Logging Proxy Input/Output - DynamoDB
|
||||
|
||||
We will use the `--config` to set
|
||||
|
||||
- `litellm.success_callback = ["dynamodb"]`
|
||||
- `litellm.dynamodb_table_name = "your-table-name"`
|
||||
|
||||
|
@ -1298,6 +1440,7 @@ AWS_REGION_NAME = ""
|
|||
```
|
||||
|
||||
**Step 2**: Create a `config.yaml` file and set `litellm_settings`: `success_callback`
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-3.5-turbo
|
||||
|
@ -1311,11 +1454,13 @@ litellm_settings:
|
|||
**Step 3**: Start the proxy, make a test request
|
||||
|
||||
Start proxy
|
||||
|
||||
```shell
|
||||
litellm --config config.yaml --debug
|
||||
```
|
||||
|
||||
Test Request
|
||||
|
||||
```shell
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
|
@ -1403,19 +1548,18 @@ Your logs should be available on DynamoDB
|
|||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## Logging Proxy Input/Output - Sentry
|
||||
|
||||
If api calls fail (llm/database) you can log those to Sentry:
|
||||
|
||||
**Step 1** Install Sentry
|
||||
|
||||
```shell
|
||||
pip install --upgrade sentry-sdk
|
||||
```
|
||||
|
||||
**Step 2**: Save your Sentry_DSN and add `litellm_settings`: `failure_callback`
|
||||
|
||||
```shell
|
||||
export SENTRY_DSN="your-sentry-dsn"
|
||||
```
|
||||
|
@ -1435,11 +1579,13 @@ general_settings:
|
|||
**Step 3**: Start the proxy, make a test request
|
||||
|
||||
Start proxy
|
||||
|
||||
```shell
|
||||
litellm --config config.yaml --debug
|
||||
```
|
||||
|
||||
Test Request
|
||||
|
||||
```
|
||||
litellm --test
|
||||
```
|
||||
|
@ -1457,6 +1603,7 @@ ATHINA_API_KEY = "your-athina-api-key"
|
|||
```
|
||||
|
||||
**Step 2**: Create a `config.yaml` file and set `litellm_settings`: `success_callback`
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-3.5-turbo
|
||||
|
@ -1469,11 +1616,13 @@ litellm_settings:
|
|||
**Step 3**: Start the proxy, make a test request
|
||||
|
||||
Start proxy
|
||||
|
||||
```shell
|
||||
litellm --config config.yaml --debug
|
||||
```
|
||||
|
||||
Test Request
|
||||
|
||||
```
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
|
@ -1505,6 +1654,7 @@ AZURE_CONTENT_SAFETY_KEY = "<your-azure-content-safety-key>"
|
|||
```
|
||||
|
||||
**Step 2**: Create a `config.yaml` file and set `litellm_settings`: `success_callback`
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-3.5-turbo
|
||||
|
@ -1520,11 +1670,13 @@ litellm_settings:
|
|||
**Step 3**: Start the proxy, make a test request
|
||||
|
||||
Start proxy
|
||||
|
||||
```shell
|
||||
litellm --config config.yaml --debug
|
||||
```
|
||||
|
||||
Test Request
|
||||
|
||||
```
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
|
@ -1541,6 +1693,7 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
|
||||
An HTTP 400 error will be returned if the content is detected with a value greater than the threshold set in the `config.yaml`.
|
||||
The details of the response will describe:
|
||||
|
||||
- The `source` : input text or llm generated text
|
||||
- The `category` : the category of the content that triggered the moderation
|
||||
- The `severity` : the severity from 0 to 10
|
||||
|
|
|
@ -15,9 +15,9 @@ model_list:
|
|||
metadata: "here's additional metadata on the model" # returned via GET /model/info
|
||||
```
|
||||
|
||||
## Get Model Information
|
||||
## Get Model Information - `/model/info`
|
||||
|
||||
Retrieve detailed information about each model listed in the `/models` endpoint, including descriptions from the `config.yaml` file, and additional model info (e.g. max tokens, cost per input token, etc.) pulled the model_info you set and the litellm model cost map. Sensitive details like API keys are excluded for security purposes.
|
||||
Retrieve detailed information about each model listed in the `/model/info` endpoint, including descriptions from the `config.yaml` file, and additional model info (e.g. max tokens, cost per input token, etc.) pulled the model_info you set and the litellm model cost map. Sensitive details like API keys are excluded for security purposes.
|
||||
|
||||
<Tabs
|
||||
defaultValue="curl"
|
||||
|
|
|
@ -156,6 +156,8 @@ POST /api/public/ingestion HTTP/1.1" 207 Multi-Status
|
|||
|
||||
Use this if you want the pass through endpoint to honour LiteLLM keys/authentication
|
||||
|
||||
This also enforces the key's rpm limits on pass-through endpoints.
|
||||
|
||||
Usage - set `auth: true` on the config
|
||||
```yaml
|
||||
general_settings:
|
||||
|
@ -218,3 +220,148 @@ general_settings:
|
|||
* `LANGFUSE_PUBLIC_KEY` *string*: Your Langfuse account public key - only set this when forwarding to Langfuse.
|
||||
* `LANGFUSE_SECRET_KEY` *string*: Your Langfuse account secret key - only set this when forwarding to Langfuse.
|
||||
* `<your-custom-header>` *string*: Pass any custom header key/value pair
|
||||
|
||||
|
||||
## Custom Chat Endpoints (Anthropic/Bedrock/Vertex)
|
||||
|
||||
Allow developers to call the proxy with Anthropic/boto3/etc. client sdk's.
|
||||
|
||||
Test our [Anthropic Adapter](../anthropic_completion.md) for reference [**Code**](https://github.com/BerriAI/litellm/blob/fd743aaefd23ae509d8ca64b0c232d25fe3e39ee/litellm/adapters/anthropic_adapter.py#L50)
|
||||
|
||||
### 1. Write an Adapter
|
||||
|
||||
Translate the request/response from your custom API schema to the OpenAI schema (used by litellm.completion()) and back.
|
||||
|
||||
For provider-specific params 👉 [**Provider-Specific Params**](../completion/provider_specific_params.md)
|
||||
|
||||
```python
|
||||
from litellm import adapter_completion
|
||||
import litellm
|
||||
from litellm import ChatCompletionRequest, verbose_logger
|
||||
from litellm.integrations.custom_logger import CustomLogger
|
||||
from litellm.types.llms.anthropic import AnthropicMessagesRequest, AnthropicResponse
|
||||
import os
|
||||
|
||||
# What is this?
|
||||
## Translates OpenAI call to Anthropic `/v1/messages` format
|
||||
import json
|
||||
import os
|
||||
import traceback
|
||||
import uuid
|
||||
from typing import Literal, Optional
|
||||
|
||||
import dotenv
|
||||
import httpx
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
###################
|
||||
# CUSTOM ADAPTER ##
|
||||
###################
|
||||
|
||||
class AnthropicAdapter(CustomLogger):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
def translate_completion_input_params(
|
||||
self, kwargs
|
||||
) -> Optional[ChatCompletionRequest]:
|
||||
"""
|
||||
- translate params, where needed
|
||||
- pass rest, as is
|
||||
"""
|
||||
request_body = AnthropicMessagesRequest(**kwargs) # type: ignore
|
||||
|
||||
translated_body = litellm.AnthropicConfig().translate_anthropic_to_openai(
|
||||
anthropic_message_request=request_body
|
||||
)
|
||||
|
||||
return translated_body
|
||||
|
||||
def translate_completion_output_params(
|
||||
self, response: litellm.ModelResponse
|
||||
) -> Optional[AnthropicResponse]:
|
||||
|
||||
return litellm.AnthropicConfig().translate_openai_response_to_anthropic(
|
||||
response=response
|
||||
)
|
||||
|
||||
def translate_completion_output_params_streaming(self) -> Optional[BaseModel]:
|
||||
return super().translate_completion_output_params_streaming()
|
||||
|
||||
|
||||
anthropic_adapter = AnthropicAdapter()
|
||||
|
||||
###########
|
||||
# TEST IT #
|
||||
###########
|
||||
|
||||
## register CUSTOM ADAPTER
|
||||
litellm.adapters = [{"id": "anthropic", "adapter": anthropic_adapter}]
|
||||
|
||||
## set ENV variables
|
||||
os.environ["OPENAI_API_KEY"] = "your-openai-key"
|
||||
os.environ["COHERE_API_KEY"] = "your-cohere-key"
|
||||
|
||||
messages = [{ "content": "Hello, how are you?","role": "user"}]
|
||||
|
||||
# openai call
|
||||
response = adapter_completion(model="gpt-3.5-turbo", messages=messages, adapter_id="anthropic")
|
||||
|
||||
# cohere call
|
||||
response = adapter_completion(model="command-nightly", messages=messages, adapter_id="anthropic")
|
||||
print(response)
|
||||
```
|
||||
|
||||
### 2. Create new endpoint
|
||||
|
||||
We pass the custom callback class defined in Step1 to the config.yaml. Set callbacks to python_filename.logger_instance_name
|
||||
|
||||
In the config below, we pass
|
||||
|
||||
python_filename: `custom_callbacks.py`
|
||||
logger_instance_name: `anthropic_adapter`. This is defined in Step 1
|
||||
|
||||
`target: custom_callbacks.proxy_handler_instance`
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: my-fake-claude-endpoint
|
||||
litellm_params:
|
||||
model: gpt-3.5-turbo
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
|
||||
|
||||
general_settings:
|
||||
master_key: sk-1234
|
||||
pass_through_endpoints:
|
||||
- path: "/v1/messages" # route you want to add to LiteLLM Proxy Server
|
||||
target: custom_callbacks.anthropic_adapter # Adapter to use for this route
|
||||
headers:
|
||||
litellm_user_api_key: "x-api-key" # Field in headers, containing LiteLLM Key
|
||||
```
|
||||
|
||||
### 3. Test it!
|
||||
|
||||
**Start proxy**
|
||||
|
||||
```bash
|
||||
litellm --config /path/to/config.yaml
|
||||
```
|
||||
|
||||
**Curl**
|
||||
|
||||
```bash
|
||||
curl --location 'http://0.0.0.0:4000/v1/messages' \
|
||||
-H 'x-api-key: sk-1234' \
|
||||
-H 'anthropic-version: 2023-06-01' \ # ignored
|
||||
-H 'content-type: application/json' \
|
||||
-D '{
|
||||
"model": "my-fake-claude-endpoint",
|
||||
"max_tokens": 1024,
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello, world"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
|
|
|
@ -180,3 +180,59 @@ chat_completion = client.chat.completions.create(
|
|||
"_response_ms": 1753.426
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Turn on for logging only
|
||||
|
||||
Only apply PII Masking before logging to Langfuse, etc.
|
||||
|
||||
Not on the actual llm api request / response.
|
||||
|
||||
:::note
|
||||
This is currently only applied for
|
||||
- `/chat/completion` requests
|
||||
- on 'success' logging
|
||||
|
||||
:::
|
||||
|
||||
1. Setup config.yaml
|
||||
```yaml
|
||||
litellm_settings:
|
||||
presidio_logging_only: true
|
||||
|
||||
model_list:
|
||||
- model_name: gpt-3.5-turbo
|
||||
litellm_params:
|
||||
model: gpt-3.5-turbo
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
```
|
||||
|
||||
2. Start proxy
|
||||
|
||||
```bash
|
||||
litellm --config /path/to/config.yaml
|
||||
```
|
||||
|
||||
3. Test it!
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/chat/completions' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-D '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hi, my name is Jane!"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
**Expected Logged Response**
|
||||
|
||||
```
|
||||
Hi, my name is <PERSON>!
|
||||
```
|
|
@ -68,6 +68,14 @@ router_settings:
|
|||
redis_host: os.environ/REDIS_HOST
|
||||
redis_port: os.environ/REDIS_PORT
|
||||
redis_password: os.environ/REDIS_PASSWORD
|
||||
|
||||
litellm_settings:
|
||||
cache: True
|
||||
cache_params:
|
||||
type: redis
|
||||
host: os.environ/REDIS_HOST
|
||||
port: os.environ/REDIS_PORT
|
||||
password: os.environ/REDIS_PASSWORD
|
||||
```
|
||||
|
||||
## 4. Disable 'load_dotenv'
|
||||
|
|
|
@ -255,6 +255,12 @@ litellm --config your_config.yaml
|
|||
|
||||
## Using LiteLLM Proxy - Curl Request, OpenAI Package, Langchain
|
||||
|
||||
:::info
|
||||
LiteLLM is compatible with several SDKs - including OpenAI SDK, Anthropic SDK, Mistral SDK, LLamaIndex, Langchain (Js, Python)
|
||||
|
||||
[More examples here](user_keys)
|
||||
:::
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Curl" label="Curl Request">
|
||||
|
||||
|
@ -382,6 +388,34 @@ print(response)
|
|||
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="anthropic-py" label="Anthropic Python SDK">
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
from anthropic import Anthropic
|
||||
|
||||
client = Anthropic(
|
||||
base_url="http://localhost:4000", # proxy endpoint
|
||||
api_key="sk-s4xN1IiLTCytwtZFJaYQrA", # litellm proxy virtual key
|
||||
)
|
||||
|
||||
message = client.messages.create(
|
||||
max_tokens=1024,
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello, Claude",
|
||||
}
|
||||
],
|
||||
model="claude-3-opus-20240229",
|
||||
)
|
||||
print(message.content)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
[**More Info**](./configs.md)
|
||||
|
@ -396,165 +430,6 @@ print(response)
|
|||
- POST `/key/generate` - generate a key to access the proxy
|
||||
|
||||
|
||||
## Using with OpenAI compatible projects
|
||||
Set `base_url` to the LiteLLM Proxy server
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI v1.0.0+">
|
||||
|
||||
```python
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="anything",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# request sent to model set on litellm proxy, `litellm --model`
|
||||
response = client.chat.completions.create(model="gpt-3.5-turbo", messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
])
|
||||
|
||||
print(response)
|
||||
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="librechat" label="LibreChat">
|
||||
|
||||
#### Start the LiteLLM proxy
|
||||
```shell
|
||||
litellm --model gpt-3.5-turbo
|
||||
|
||||
#INFO: Proxy running on http://0.0.0.0:4000
|
||||
```
|
||||
|
||||
#### 1. Clone the repo
|
||||
|
||||
```shell
|
||||
git clone https://github.com/danny-avila/LibreChat.git
|
||||
```
|
||||
|
||||
|
||||
#### 2. Modify Librechat's `docker-compose.yml`
|
||||
LiteLLM Proxy is running on port `4000`, set `4000` as the proxy below
|
||||
```yaml
|
||||
OPENAI_REVERSE_PROXY=http://host.docker.internal:4000/v1/chat/completions
|
||||
```
|
||||
|
||||
#### 3. Save fake OpenAI key in Librechat's `.env`
|
||||
|
||||
Copy Librechat's `.env.example` to `.env` and overwrite the default OPENAI_API_KEY (by default it requires the user to pass a key).
|
||||
```env
|
||||
OPENAI_API_KEY=sk-1234
|
||||
```
|
||||
|
||||
#### 4. Run LibreChat:
|
||||
```shell
|
||||
docker compose up
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="continue-dev" label="ContinueDev">
|
||||
|
||||
Continue-Dev brings ChatGPT to VSCode. See how to [install it here](https://continue.dev/docs/quickstart).
|
||||
|
||||
In the [config.py](https://continue.dev/docs/reference/Models/openai) set this as your default model.
|
||||
```python
|
||||
default=OpenAI(
|
||||
api_key="IGNORED",
|
||||
model="fake-model-name",
|
||||
context_length=2048, # customize if needed for your model
|
||||
api_base="http://localhost:4000" # your proxy server url
|
||||
),
|
||||
```
|
||||
|
||||
Credits [@vividfog](https://github.com/ollama/ollama/issues/305#issuecomment-1751848077) for this tutorial.
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="aider" label="Aider">
|
||||
|
||||
```shell
|
||||
$ pip install aider
|
||||
|
||||
$ aider --openai-api-base http://0.0.0.0:4000 --openai-api-key fake-key
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="autogen" label="AutoGen">
|
||||
|
||||
```python
|
||||
pip install pyautogen
|
||||
```
|
||||
|
||||
```python
|
||||
from autogen import AssistantAgent, UserProxyAgent, oai
|
||||
config_list=[
|
||||
{
|
||||
"model": "my-fake-model",
|
||||
"api_base": "http://localhost:4000", #litellm compatible endpoint
|
||||
"api_type": "open_ai",
|
||||
"api_key": "NULL", # just a placeholder
|
||||
}
|
||||
]
|
||||
|
||||
response = oai.Completion.create(config_list=config_list, prompt="Hi")
|
||||
print(response) # works fine
|
||||
|
||||
llm_config={
|
||||
"config_list": config_list,
|
||||
}
|
||||
|
||||
assistant = AssistantAgent("assistant", llm_config=llm_config)
|
||||
user_proxy = UserProxyAgent("user_proxy")
|
||||
user_proxy.initiate_chat(assistant, message="Plot a chart of META and TESLA stock price change YTD.", config_list=config_list)
|
||||
```
|
||||
|
||||
Credits [@victordibia](https://github.com/microsoft/autogen/issues/45#issuecomment-1749921972) for this tutorial.
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="guidance" label="guidance">
|
||||
A guidance language for controlling large language models.
|
||||
https://github.com/guidance-ai/guidance
|
||||
|
||||
**NOTE:** Guidance sends additional params like `stop_sequences` which can cause some models to fail if they don't support it.
|
||||
|
||||
**Fix**: Start your proxy using the `--drop_params` flag
|
||||
|
||||
```shell
|
||||
litellm --model ollama/codellama --temperature 0.3 --max_tokens 2048 --drop_params
|
||||
```
|
||||
|
||||
```python
|
||||
import guidance
|
||||
|
||||
# set api_base to your proxy
|
||||
# set api_key to anything
|
||||
gpt4 = guidance.llms.OpenAI("gpt-4", api_base="http://0.0.0.0:4000", api_key="anything")
|
||||
|
||||
experts = guidance('''
|
||||
{{#system~}}
|
||||
You are a helpful and terse assistant.
|
||||
{{~/system}}
|
||||
|
||||
{{#user~}}
|
||||
I want a response to the following question:
|
||||
{{query}}
|
||||
Name 3 world-class experts (past or present) who would be great at answering this?
|
||||
Don't answer the question yet.
|
||||
{{~/user}}
|
||||
|
||||
{{#assistant~}}
|
||||
{{gen 'expert_names' temperature=0 max_tokens=300}}
|
||||
{{~/assistant}}
|
||||
''', llm=gpt4)
|
||||
|
||||
result = experts(query='How can I be more productive?')
|
||||
print(result)
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Debugging Proxy
|
||||
|
||||
Events that occur during normal operation
|
||||
|
|
|
@ -31,8 +31,19 @@ model_list:
|
|||
api_base: https://openai-france-1234.openai.azure.com/
|
||||
api_key: <your-azure-api-key>
|
||||
rpm: 1440
|
||||
routing_strategy: simple-shuffle # Literal["simple-shuffle", "least-busy", "usage-based-routing","latency-based-routing"], default="simple-shuffle"
|
||||
model_group_alias: {"gpt-4": "gpt-3.5-turbo"} # all requests with `gpt-4` will be routed to models with `gpt-3.5-turbo`
|
||||
num_retries: 2
|
||||
timeout: 30 # 30 seconds
|
||||
redis_host: <your redis host> # set this when using multiple litellm proxy deployments, load balancing state stored in redis
|
||||
redis_password: <your redis password>
|
||||
redis_port: 1992
|
||||
```
|
||||
|
||||
:::info
|
||||
Detailed information about [routing strategies can be found here](../routing)
|
||||
:::
|
||||
|
||||
#### Step 2: Start Proxy with config
|
||||
|
||||
```shell
|
||||
|
@ -434,6 +445,33 @@ litellm_settings:
|
|||
|
||||
|
||||
|
||||
### Default Fallbacks
|
||||
|
||||
You can also set default_fallbacks, in case a specific model group is misconfigured / bad.
|
||||
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-3.5-turbo-small
|
||||
litellm_params:
|
||||
model: azure/chatgpt-v-2
|
||||
api_base: os.environ/AZURE_API_BASE
|
||||
api_key: os.environ/AZURE_API_KEY
|
||||
api_version: "2023-07-01-preview"
|
||||
|
||||
- model_name: claude-opus
|
||||
litellm_params:
|
||||
model: claude-3-opus-20240229
|
||||
api_key: os.environ/ANTHROPIC_API_KEY
|
||||
|
||||
litellm_settings:
|
||||
default_fallbacks: ["claude-opus"]
|
||||
```
|
||||
|
||||
This will default to claude-opus in case any model fails.
|
||||
|
||||
A model-specific fallbacks (e.g. {"gpt-3.5-turbo-small": ["claude-opus"]}) overrides default fallback.
|
||||
|
||||
### Test Fallbacks!
|
||||
|
||||
Check if your fallbacks are working as expected.
|
||||
|
|
|
@ -4,7 +4,7 @@ import TabItem from '@theme/TabItem';
|
|||
|
||||
# 🤗 UI - Self-Serve
|
||||
|
||||
Allow users to create their own keys on [Proxy UI](./ui.md).
|
||||
## Allow users to create their own keys on [Proxy UI](./ui.md).
|
||||
|
||||
1. Add user with permissions to a team on proxy
|
||||
|
||||
|
@ -125,6 +125,41 @@ LiteLLM Enterprise: Enable [SSO login](./ui.md#setup-ssoauth-for-ui)
|
|||
|
||||
<Image img={require('../../img/ui_self_serve_create_key.png')} style={{ width: '800px', height: 'auto' }} />
|
||||
|
||||
## Allow users to View Usage, Caching Analytics
|
||||
|
||||
1. Go to Internal Users -> +Invite User
|
||||
|
||||
Set their role to `Admin Viewer` - this means they can only view usage, caching analytics
|
||||
|
||||
<Image img={require('../../img/ui_invite_user.png')} style={{ width: '800px', height: 'auto' }} />
|
||||
<br />
|
||||
|
||||
2. Share invitation link with user
|
||||
|
||||
|
||||
<Image img={require('../../img/ui_invite_link.png')} style={{ width: '800px', height: 'auto' }} />
|
||||
<br />
|
||||
|
||||
3. User logs in via email + password auth
|
||||
|
||||
<Image img={require('../../img/ui_clean_login.png')} style={{ width: '500px', height: 'auto' }} />
|
||||
<br />
|
||||
|
||||
4. User can now view Usage, Caching Analytics
|
||||
|
||||
<Image img={require('../../img/ui_usage.png')} style={{ width: '800px', height: 'auto' }} />
|
||||
|
||||
|
||||
## Available Roles
|
||||
Here's the available UI roles for a LiteLLM Internal User:
|
||||
|
||||
**Admin Roles:**
|
||||
- `proxy_admin`: admin over the platform
|
||||
- `proxy_admin_viewer`: can login, view all keys, view all spend. **Cannot** create/delete keys, add new users.
|
||||
|
||||
**Internal User Roles:**
|
||||
- `internal_user`: can login, view/create/delete their own keys, view their spend. **Cannot** add new users.
|
||||
- `internal_user_viewer`: can login, view their own keys, view their own spend. **Cannot** create/delete keys, add new users.
|
||||
|
||||
## Advanced
|
||||
### Setting custom logout URLs
|
||||
|
|
|
@ -8,6 +8,7 @@ Define your custom callback class in a python file.
|
|||
```python
|
||||
from litellm.integrations.custom_logger import CustomLogger
|
||||
import litellm
|
||||
import logging
|
||||
|
||||
# This file includes the custom callbacks for LiteLLM Proxy
|
||||
# Once defined, these can be passed in proxy_config.yaml
|
||||
|
@ -25,9 +26,9 @@ class MyCustomHandler(CustomLogger):
|
|||
datefmt='%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
|
||||
response_cost = litellm.completion_cost(completion_response=completion_response)
|
||||
response_cost: Optional[float] = kwargs.get("response_cost", None)
|
||||
print("regular response_cost", response_cost)
|
||||
logging.info(f"Model {completion_response.model} Cost: ${response_cost:.8f}")
|
||||
logging.info(f"Model {response_obj.model} Cost: ${response_cost:.8f}")
|
||||
except:
|
||||
pass
|
||||
|
||||
|
|
133
docs/my-website/docs/proxy/tag_routing.md
Normal file
133
docs/my-website/docs/proxy/tag_routing.md
Normal file
|
@ -0,0 +1,133 @@
|
|||
# 💸 Tag Based Routing
|
||||
|
||||
Route requests based on tags.
|
||||
This is useful for implementing free / paid tiers for users
|
||||
|
||||
### 1. Define tags on config.yaml
|
||||
|
||||
- A request with `tags=["free"]` will get routed to `openai/fake`
|
||||
- A request with `tags=["paid"]` will get routed to `openai/gpt-4o`
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-4
|
||||
litellm_params:
|
||||
model: openai/fake
|
||||
api_key: fake-key
|
||||
api_base: https://exampleopenaiendpoint-production.up.railway.app/
|
||||
tags: ["free"] # 👈 Key Change
|
||||
- model_name: gpt-4
|
||||
litellm_params:
|
||||
model: openai/gpt-4o
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
tags: ["paid"] # 👈 Key Change
|
||||
|
||||
router_settings:
|
||||
enable_tag_filtering: True # 👈 Key Change
|
||||
general_settings:
|
||||
master_key: sk-1234
|
||||
```
|
||||
|
||||
### 2. Make Request with `tags=["free"]`
|
||||
|
||||
This request includes "tags": ["free"], which routes it to `openai/fake`
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-4",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello, Claude gm!"}
|
||||
],
|
||||
"tags": ["free"]
|
||||
}'
|
||||
```
|
||||
**Expected Response**
|
||||
|
||||
Expect to see the following response header when this works
|
||||
```shell
|
||||
x-litellm-model-api-base: https://exampleopenaiendpoint-production.up.railway.app/
|
||||
```
|
||||
|
||||
Response
|
||||
```shell
|
||||
{
|
||||
"id": "chatcmpl-33c534e3d70148218e2d62496b81270b",
|
||||
"choices": [
|
||||
{
|
||||
"finish_reason": "stop",
|
||||
"index": 0,
|
||||
"message": {
|
||||
"content": "\n\nHello there, how may I assist you today?",
|
||||
"role": "assistant",
|
||||
"tool_calls": null,
|
||||
"function_call": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"created": 1677652288,
|
||||
"model": "gpt-3.5-turbo-0125",
|
||||
"object": "chat.completion",
|
||||
"system_fingerprint": "fp_44709d6fcb",
|
||||
"usage": {
|
||||
"completion_tokens": 12,
|
||||
"prompt_tokens": 9,
|
||||
"total_tokens": 21
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 3. Make Request with `tags=["paid"]`
|
||||
|
||||
This request includes "tags": ["paid"], which routes it to `openai/gpt-4`
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-4",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello, Claude gm!"}
|
||||
],
|
||||
"tags": ["paid"]
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Response**
|
||||
|
||||
Expect to see the following response header when this works
|
||||
```shell
|
||||
x-litellm-model-api-base: https://api.openai.com
|
||||
```
|
||||
|
||||
Response
|
||||
```shell
|
||||
{
|
||||
"id": "chatcmpl-9maCcqQYTqdJrtvfakIawMOIUbEZx",
|
||||
"choices": [
|
||||
{
|
||||
"finish_reason": "stop",
|
||||
"index": 0,
|
||||
"message": {
|
||||
"content": "Good morning! How can I assist you today?",
|
||||
"role": "assistant",
|
||||
"tool_calls": null,
|
||||
"function_call": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"created": 1721365934,
|
||||
"model": "gpt-4o-2024-05-13",
|
||||
"object": "chat.completion",
|
||||
"system_fingerprint": "fp_c4e5b6fa31",
|
||||
"usage": {
|
||||
"completion_tokens": 10,
|
||||
"prompt_tokens": 12,
|
||||
"total_tokens": 22
|
||||
}
|
||||
}
|
||||
```
|
|
@ -71,7 +71,13 @@ curl --location 'http://0.0.0.0:4000/v1/chat/completions' \
|
|||
}'
|
||||
```
|
||||
|
||||
## Team Based Logging
|
||||
|
||||
[👉 Tutorial - Allow each team to use their own Langfuse Project / custom callbacks](team_logging.md)
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
## Logging / Caching
|
||||
|
||||
Turn on/off logging and caching for a specific team id.
|
||||
|
@ -102,4 +108,4 @@ curl -X POST 'http://0.0.0.0:4000/key/generate' \
|
|||
-d '{"team_id": "ishaans-secret-project"}'
|
||||
```
|
||||
|
||||
All requests made with these keys will log data to their team-specific logging.
|
||||
All requests made with these keys will log data to their team-specific logging. -->
|
||||
|
|
144
docs/my-website/docs/proxy/team_logging.md
Normal file
144
docs/my-website/docs/proxy/team_logging.md
Normal file
|
@ -0,0 +1,144 @@
|
|||
import Image from '@theme/IdealImage';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# 👥📊 Team Based Logging
|
||||
|
||||
Allow each team to use their own Langfuse Project / custom callbacks
|
||||
|
||||
**This allows you to do the following**
|
||||
```
|
||||
Team 1 -> Logs to Langfuse Project 1
|
||||
Team 2 -> Logs to Langfuse Project 2
|
||||
Team 3 -> Disabled Logging (for GDPR compliance)
|
||||
```
|
||||
|
||||
## Set Callbacks Per Team
|
||||
|
||||
### 1. Set callback for team
|
||||
|
||||
We make a request to `POST /team/{team_id}/callback` to add a callback for
|
||||
|
||||
```shell
|
||||
curl -X POST 'http:/localhost:4000/team/dbe2f686-a686-4896-864a-4c3924458709/callback' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-d '{
|
||||
"callback_name": "langfuse",
|
||||
"callback_type": "success",
|
||||
"callback_vars": {
|
||||
"langfuse_public_key": "pk",
|
||||
"langfuse_secret_key": "sk_",
|
||||
"langfuse_host": "https://cloud.langfuse.com"
|
||||
}
|
||||
|
||||
}'
|
||||
```
|
||||
|
||||
#### Supported Values
|
||||
|
||||
| Field | Supported Values | Notes |
|
||||
|-------|------------------|-------|
|
||||
| `callback_name` | `"langfuse"` | Currently only supports "langfuse" |
|
||||
| `callback_type` | `"success"`, `"failure"`, `"success_and_failure"` | |
|
||||
| `callback_vars` | | dict of callback settings |
|
||||
| `langfuse_public_key` | string | Required |
|
||||
| `langfuse_secret_key` | string | Required |
|
||||
| `langfuse_host` | string | Optional (defaults to https://cloud.langfuse.com) |
|
||||
|
||||
### 2. Create key for team
|
||||
|
||||
All keys created for team `dbe2f686-a686-4896-864a-4c3924458709` will log to langfuse project specified on [Step 1. Set callback for team](#1-set-callback-for-team)
|
||||
|
||||
|
||||
```shell
|
||||
curl --location 'http://0.0.0.0:4000/key/generate' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"team_id": "dbe2f686-a686-4896-864a-4c3924458709"
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
### 3. Make `/chat/completion` request for team
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-KbUuE0WNptC0jXapyMmLBA" \
|
||||
-d '{
|
||||
"model": "gpt-4",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello, Claude gm!"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
Expect this to be logged on the langfuse project specified on [Step 1. Set callback for team](#1-set-callback-for-team)
|
||||
|
||||
|
||||
## Disable Logging for a Team
|
||||
|
||||
To disable logging for a specific team, you can use the following endpoint:
|
||||
|
||||
`POST /team/{team_id}/disable_logging`
|
||||
|
||||
This endpoint removes all success and failure callbacks for the specified team, effectively disabling logging.
|
||||
|
||||
### Step 1. Disable logging for team
|
||||
|
||||
```shell
|
||||
curl -X POST 'http://localhost:4000/team/YOUR_TEAM_ID/disable_logging' \
|
||||
-H 'Authorization: Bearer YOUR_API_KEY'
|
||||
```
|
||||
Replace YOUR_TEAM_ID with the actual team ID
|
||||
|
||||
**Response**
|
||||
A successful request will return a response similar to this:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Logging disabled for team YOUR_TEAM_ID",
|
||||
"data": {
|
||||
"team_id": "YOUR_TEAM_ID",
|
||||
"success_callbacks": [],
|
||||
"failure_callbacks": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2. Test it - `/chat/completions`
|
||||
|
||||
Use a key generated for team = `team_id` - you should see no logs on your configured success callback (eg. Langfuse)
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-KbUuE0WNptC0jXapyMmLBA" \
|
||||
-d '{
|
||||
"model": "gpt-4",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello, Claude gm!"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### Debugging / Troubleshooting
|
||||
|
||||
- Check active callbacks for team using `GET /team/{team_id}/callback`
|
||||
|
||||
Use this to check what success/failure callbacks are active for team=`team_id`
|
||||
|
||||
```shell
|
||||
curl -X GET 'http://localhost:4000/team/dbe2f686-a686-4896-864a-4c3924458709/callback' \
|
||||
-H 'Authorization: Bearer sk-1234'
|
||||
```
|
||||
|
||||
## Team Logging Endpoints
|
||||
|
||||
- [`POST /team/{team_id}/callback` Add a success/failure callback to a team](https://litellm-api.up.railway.app/#/team%20management/add_team_callbacks_team__team_id__callback_post)
|
||||
- [`GET /team/{team_id}/callback` - Get the success/failure callbacks and variables for a team](https://litellm-api.up.railway.app/#/team%20management/get_team_callbacks_team__team_id__callback_get)
|
||||
|
||||
|
||||
|
|
@ -1,7 +1,39 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Use with Langchain, OpenAI SDK, LlamaIndex, Instructor, Curl
|
||||
# 💡 Migrating from OpenAI (Langchain, OpenAI SDK, LlamaIndex, Instructor, Curl)
|
||||
|
||||
LiteLLM Proxy is **OpenAI-Compatible**, and supports:
|
||||
* /chat/completions
|
||||
* /embeddings
|
||||
* /completions
|
||||
* /image/generations
|
||||
* /moderations
|
||||
* /audio/transcriptions
|
||||
* /audio/speech
|
||||
* [Assistants API endpoints](https://docs.litellm.ai/docs/assistants)
|
||||
* [Batches API endpoints](https://docs.litellm.ai/docs/batches)
|
||||
|
||||
LiteLLM Proxy is **Azure OpenAI-compatible**:
|
||||
* /chat/completions
|
||||
* /completions
|
||||
* /embeddings
|
||||
|
||||
LiteLLM Proxy is **Anthropic-compatible**:
|
||||
* /messages
|
||||
|
||||
This doc covers:
|
||||
|
||||
* /chat/completion
|
||||
* /embedding
|
||||
|
||||
|
||||
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`.
|
||||
|
||||
To pass provider-specific args, [go here](https://docs.litellm.ai/docs/completion/provider_specific_params#proxy-usage)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
:::info
|
||||
|
||||
|
@ -48,6 +80,39 @@ response = client.chat.completions.create(
|
|||
}
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="azureopenai" label="AzureOpenAI Python">
|
||||
|
||||
Set `extra_body={"metadata": { }}` to `metadata` you want to pass
|
||||
|
||||
```python
|
||||
import openai
|
||||
client = openai.AzureOpenAI(
|
||||
api_key="anything",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# request sent to model set on litellm proxy, `litellm --model`
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
],
|
||||
extra_body={ # pass in any provider-specific param, if not supported by openai, https://docs.litellm.ai/docs/completion/input#provider-specific-params
|
||||
"metadata": { # 👈 use for logging additional params (e.g. to langfuse)
|
||||
"generation_name": "ishaan-generation-openai-client",
|
||||
"generation_id": "openai-client-gen-id22",
|
||||
"trace_id": "openai-client-trace-id22",
|
||||
"trace_user_id": "openai-client-user-id2"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
</TabItem>
|
||||
|
@ -174,6 +239,81 @@ console.log(message);
|
|||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="openai JS" label="OpenAI JS">
|
||||
|
||||
```js
|
||||
const { OpenAI } = require('openai');
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: "sk-1234", // This is the default and can be omitted
|
||||
baseURL: "http://0.0.0.0:4000"
|
||||
});
|
||||
|
||||
async function main() {
|
||||
const chatCompletion = await openai.chat.completions.create({
|
||||
messages: [{ role: 'user', content: 'Say this is a test' }],
|
||||
model: 'gpt-3.5-turbo',
|
||||
}, {"metadata": {
|
||||
"generation_name": "ishaan-generation-openaijs-client",
|
||||
"generation_id": "openaijs-client-gen-id22",
|
||||
"trace_id": "openaijs-client-trace-id22",
|
||||
"trace_user_id": "openaijs-client-user-id2"
|
||||
}});
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="anthropic-py" label="Anthropic Python SDK">
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
from anthropic import Anthropic
|
||||
|
||||
client = Anthropic(
|
||||
base_url="http://localhost:4000", # proxy endpoint
|
||||
api_key="sk-s4xN1IiLTCytwtZFJaYQrA", # litellm proxy virtual key
|
||||
)
|
||||
|
||||
message = client.messages.create(
|
||||
max_tokens=1024,
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello, Claude",
|
||||
}
|
||||
],
|
||||
model="claude-3-opus-20240229",
|
||||
)
|
||||
print(message.content)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="mistral-py" label="Mistral Python SDK">
|
||||
|
||||
```python
|
||||
import os
|
||||
from mistralai.client import MistralClient
|
||||
from mistralai.models.chat_completion import ChatMessage
|
||||
|
||||
|
||||
client = MistralClient(api_key="sk-1234", endpoint="http://0.0.0.0:4000")
|
||||
chat_response = client.chat(
|
||||
model="mistral-small-latest",
|
||||
messages=[
|
||||
{"role": "user", "content": "this is a test request, write a short poem"}
|
||||
],
|
||||
)
|
||||
print(chat_response.choices[0].message.content)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="instructor" label="Instructor">
|
||||
|
||||
```python
|
||||
|
@ -506,6 +646,166 @@ curl --location 'http://0.0.0.0:4000/moderations' \
|
|||
```
|
||||
|
||||
|
||||
## Using with OpenAI compatible projects
|
||||
Set `base_url` to the LiteLLM Proxy server
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI v1.0.0+">
|
||||
|
||||
```python
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="anything",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# request sent to model set on litellm proxy, `litellm --model`
|
||||
response = client.chat.completions.create(model="gpt-3.5-turbo", messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
])
|
||||
|
||||
print(response)
|
||||
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="librechat" label="LibreChat">
|
||||
|
||||
#### Start the LiteLLM proxy
|
||||
```shell
|
||||
litellm --model gpt-3.5-turbo
|
||||
|
||||
#INFO: Proxy running on http://0.0.0.0:4000
|
||||
```
|
||||
|
||||
#### 1. Clone the repo
|
||||
|
||||
```shell
|
||||
git clone https://github.com/danny-avila/LibreChat.git
|
||||
```
|
||||
|
||||
|
||||
#### 2. Modify Librechat's `docker-compose.yml`
|
||||
LiteLLM Proxy is running on port `4000`, set `4000` as the proxy below
|
||||
```yaml
|
||||
OPENAI_REVERSE_PROXY=http://host.docker.internal:4000/v1/chat/completions
|
||||
```
|
||||
|
||||
#### 3. Save fake OpenAI key in Librechat's `.env`
|
||||
|
||||
Copy Librechat's `.env.example` to `.env` and overwrite the default OPENAI_API_KEY (by default it requires the user to pass a key).
|
||||
```env
|
||||
OPENAI_API_KEY=sk-1234
|
||||
```
|
||||
|
||||
#### 4. Run LibreChat:
|
||||
```shell
|
||||
docker compose up
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="continue-dev" label="ContinueDev">
|
||||
|
||||
Continue-Dev brings ChatGPT to VSCode. See how to [install it here](https://continue.dev/docs/quickstart).
|
||||
|
||||
In the [config.py](https://continue.dev/docs/reference/Models/openai) set this as your default model.
|
||||
```python
|
||||
default=OpenAI(
|
||||
api_key="IGNORED",
|
||||
model="fake-model-name",
|
||||
context_length=2048, # customize if needed for your model
|
||||
api_base="http://localhost:4000" # your proxy server url
|
||||
),
|
||||
```
|
||||
|
||||
Credits [@vividfog](https://github.com/ollama/ollama/issues/305#issuecomment-1751848077) for this tutorial.
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="aider" label="Aider">
|
||||
|
||||
```shell
|
||||
$ pip install aider
|
||||
|
||||
$ aider --openai-api-base http://0.0.0.0:4000 --openai-api-key fake-key
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="autogen" label="AutoGen">
|
||||
|
||||
```python
|
||||
pip install pyautogen
|
||||
```
|
||||
|
||||
```python
|
||||
from autogen import AssistantAgent, UserProxyAgent, oai
|
||||
config_list=[
|
||||
{
|
||||
"model": "my-fake-model",
|
||||
"api_base": "http://localhost:4000", #litellm compatible endpoint
|
||||
"api_type": "open_ai",
|
||||
"api_key": "NULL", # just a placeholder
|
||||
}
|
||||
]
|
||||
|
||||
response = oai.Completion.create(config_list=config_list, prompt="Hi")
|
||||
print(response) # works fine
|
||||
|
||||
llm_config={
|
||||
"config_list": config_list,
|
||||
}
|
||||
|
||||
assistant = AssistantAgent("assistant", llm_config=llm_config)
|
||||
user_proxy = UserProxyAgent("user_proxy")
|
||||
user_proxy.initiate_chat(assistant, message="Plot a chart of META and TESLA stock price change YTD.", config_list=config_list)
|
||||
```
|
||||
|
||||
Credits [@victordibia](https://github.com/microsoft/autogen/issues/45#issuecomment-1749921972) for this tutorial.
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="guidance" label="guidance">
|
||||
A guidance language for controlling large language models.
|
||||
https://github.com/guidance-ai/guidance
|
||||
|
||||
**NOTE:** Guidance sends additional params like `stop_sequences` which can cause some models to fail if they don't support it.
|
||||
|
||||
**Fix**: Start your proxy using the `--drop_params` flag
|
||||
|
||||
```shell
|
||||
litellm --model ollama/codellama --temperature 0.3 --max_tokens 2048 --drop_params
|
||||
```
|
||||
|
||||
```python
|
||||
import guidance
|
||||
|
||||
# set api_base to your proxy
|
||||
# set api_key to anything
|
||||
gpt4 = guidance.llms.OpenAI("gpt-4", api_base="http://0.0.0.0:4000", api_key="anything")
|
||||
|
||||
experts = guidance('''
|
||||
{{#system~}}
|
||||
You are a helpful and terse assistant.
|
||||
{{~/system}}
|
||||
|
||||
{{#user~}}
|
||||
I want a response to the following question:
|
||||
{{query}}
|
||||
Name 3 world-class experts (past or present) who would be great at answering this?
|
||||
Don't answer the question yet.
|
||||
{{~/user}}
|
||||
|
||||
{{#assistant~}}
|
||||
{{gen 'expert_names' temperature=0 max_tokens=300}}
|
||||
{{~/assistant}}
|
||||
''', llm=gpt4)
|
||||
|
||||
result = experts(query='How can I be more productive?')
|
||||
print(result)
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Advanced
|
||||
|
||||
### (BETA) Batch Completions - pass multiple models
|
||||
|
|
|
@ -347,6 +347,70 @@ curl --location 'http://localhost:4000/key/generate' \
|
|||
"max_budget": 0,}'
|
||||
```
|
||||
|
||||
## Advanced - Pass LiteLLM Key in custom header
|
||||
|
||||
Use this to make LiteLLM proxy look for the virtual key in a custom header instead of the default `"Authorization"` header
|
||||
|
||||
**Step 1** Define `litellm_key_header_name` name on litellm config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: fake-openai-endpoint
|
||||
litellm_params:
|
||||
model: openai/fake
|
||||
api_key: fake-key
|
||||
api_base: https://exampleopenaiendpoint-production.up.railway.app/
|
||||
|
||||
general_settings:
|
||||
master_key: sk-1234
|
||||
litellm_key_header_name: "X-Litellm-Key" # 👈 Key Change
|
||||
|
||||
```
|
||||
|
||||
**Step 2** Test it
|
||||
|
||||
In this request, litellm will use the Virtual key in the `X-Litellm-Key` header
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
```shell
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Litellm-Key: Bearer sk-1234" \
|
||||
-H "Authorization: Bearer bad-key" \
|
||||
-d '{
|
||||
"model": "fake-openai-endpoint",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello, Claude gm!"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Response**
|
||||
|
||||
Expect to see a successfull response from the litellm proxy since the key passed in `X-Litellm-Key` is valid
|
||||
```shell
|
||||
{"id":"chatcmpl-f9b2b79a7c30477ab93cd0e717d1773e","choices":[{"finish_reason":"stop","index":0,"message":{"content":"\n\nHello there, how may I assist you today?","role":"assistant","tool_calls":null,"function_call":null}}],"created":1677652288,"model":"gpt-3.5-turbo-0125","object":"chat.completion","system_fingerprint":"fp_44709d6fcb","usage":{"completion_tokens":12,"prompt_tokens":9,"total_tokens":21}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="python" label="OpenAI Python SDK">
|
||||
|
||||
```python
|
||||
client = openai.OpenAI(
|
||||
api_key="not-used",
|
||||
base_url="https://api-gateway-url.com/llmservc/api/litellmp",
|
||||
default_headers={
|
||||
"Authorization": f"Bearer {API_GATEWAY_TOKEN}", # (optional) For your API Gateway
|
||||
"X-Litellm-Key": f"Bearer sk-1234" # For LiteLLM Proxy
|
||||
}
|
||||
)
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Advanced - Custom Auth
|
||||
|
||||
You can now override the default api key auth.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# [OLD PROXY 👉 [**NEW** proxy here](./simple_proxy)] Local OpenAI Proxy Server
|
||||
# [OLD PROXY 👉 [NEW proxy here](./simple_proxy)] Local OpenAI Proxy Server
|
||||
|
||||
A fast, and lightweight OpenAI-compatible server to call 100+ LLM APIs.
|
||||
|
||||
|
|
|
@ -110,3 +110,32 @@ response = speech(
|
|||
)
|
||||
response.stream_to_file(speech_file_path)
|
||||
```
|
||||
|
||||
## ✨ Enterprise LiteLLM Proxy - Set Max Request File Size
|
||||
|
||||
Use this when you want to limit the file size for requests sent to `audio/transcriptions`
|
||||
|
||||
```yaml
|
||||
- model_name: whisper
|
||||
litellm_params:
|
||||
model: whisper-1
|
||||
api_key: sk-*******
|
||||
max_file_size_mb: 0.00001 # 👈 max file size in MB (Set this intentionally very small for testing)
|
||||
model_info:
|
||||
mode: audio_transcription
|
||||
```
|
||||
|
||||
Make a test Request with a valid file
|
||||
```shell
|
||||
curl --location 'http://localhost:4000/v1/audio/transcriptions' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--form 'file=@"/Users/ishaanjaffer/Github/litellm/tests/gettysburg.wav"' \
|
||||
--form 'model="whisper"'
|
||||
```
|
||||
|
||||
|
||||
Expect to see the follow response
|
||||
|
||||
```shell
|
||||
{"error":{"message":"File size is too large. Please check your file size. Passed file size: 0.7392807006835938 MB. Max file size: 0.0001 MB","type":"bad_request","param":"file","code":500}}%
|
||||
```
|
|
@ -11,7 +11,7 @@ const config = {
|
|||
favicon: '/img/favicon.ico',
|
||||
|
||||
// Set the production url of your site here
|
||||
url: 'https://litellm.vercel.app/',
|
||||
url: 'https://docs.litellm.ai/',
|
||||
// Set the /<baseUrl>/ pathname under which your site is served
|
||||
// For GitHub pages deployment, it is often '/<projectName>/'
|
||||
baseUrl: '/',
|
||||
|
@ -28,6 +28,24 @@ const config = {
|
|||
},
|
||||
|
||||
plugins: [
|
||||
[
|
||||
require.resolve("@getcanary/docusaurus-pagefind"),
|
||||
{
|
||||
indexOnly: true,
|
||||
styles: {
|
||||
"--canary-color-primary-c": 0.1,
|
||||
"--canary-color-primary-h": 270,
|
||||
},
|
||||
pagefind: {
|
||||
ranking: {
|
||||
pageLength: 0.9,
|
||||
termFrequency: 1.0,
|
||||
termSimilarity: 1.0,
|
||||
termSaturation: 1.5,
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
[
|
||||
'@docusaurus/plugin-ideal-image',
|
||||
{
|
||||
|
@ -117,6 +135,11 @@ const config = {
|
|||
label: '🚀 Hosted',
|
||||
to: "docs/hosted"
|
||||
},
|
||||
{
|
||||
href: 'https://models.litellm.ai/',
|
||||
label: '💸 LLM Model Cost Map',
|
||||
position: 'right',
|
||||
},
|
||||
{
|
||||
href: 'https://github.com/BerriAI/litellm',
|
||||
label: 'GitHub',
|
||||
|
|
BIN
docs/my-website/img/langsmith_new.png
Normal file
BIN
docs/my-website/img/langsmith_new.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 353 KiB |
BIN
docs/my-website/img/raw_response_headers.png
Normal file
BIN
docs/my-website/img/raw_response_headers.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 117 KiB |
BIN
docs/my-website/img/ui_invite_link.png
Normal file
BIN
docs/my-website/img/ui_invite_link.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 88 KiB |
BIN
docs/my-website/img/ui_invite_user.png
Normal file
BIN
docs/my-website/img/ui_invite_user.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 207 KiB |
BIN
docs/my-website/img/ui_usage.png
Normal file
BIN
docs/my-website/img/ui_usage.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 181 KiB |
|
@ -18,10 +18,11 @@
|
|||
"@docusaurus/plugin-google-gtag": "^2.4.1",
|
||||
"@docusaurus/plugin-ideal-image": "^2.4.1",
|
||||
"@docusaurus/preset-classic": "2.4.1",
|
||||
"@getcanary/docusaurus-pagefind": "^0.0.12",
|
||||
"@getcanary/web": "^0.0.55",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.2.1",
|
||||
"docusaurus": "^1.14.7",
|
||||
"docusaurus-lunr-search": "^2.4.1",
|
||||
"prism-react-renderer": "^1.3.5",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
|
|
|
@ -37,25 +37,27 @@ const sidebars = {
|
|||
href: "https://litellm-api.up.railway.app/",
|
||||
},
|
||||
"proxy/enterprise",
|
||||
"proxy/user_keys",
|
||||
"proxy/demo",
|
||||
"proxy/configs",
|
||||
"proxy/reliability",
|
||||
"proxy/cost_tracking",
|
||||
"proxy/self_serve",
|
||||
"proxy/users",
|
||||
"proxy/team_budgets",
|
||||
"proxy/customers",
|
||||
"proxy/billing",
|
||||
"proxy/user_keys",
|
||||
"proxy/virtual_keys",
|
||||
"proxy/guardrails",
|
||||
"proxy/token_auth",
|
||||
"proxy/alerting",
|
||||
{
|
||||
type: "category",
|
||||
label: "🪢 Logging",
|
||||
items: ["proxy/logging", "proxy/streaming_logging"],
|
||||
},
|
||||
"proxy/team_logging",
|
||||
"proxy/guardrails",
|
||||
"proxy/tag_routing",
|
||||
"proxy/users",
|
||||
"proxy/team_budgets",
|
||||
"proxy/customers",
|
||||
"proxy/billing",
|
||||
"proxy/token_auth",
|
||||
"proxy/alerting",
|
||||
"proxy/ui",
|
||||
"proxy/prometheus",
|
||||
"proxy/pass_through",
|
||||
|
@ -90,6 +92,8 @@ const sidebars = {
|
|||
},
|
||||
items: [
|
||||
"completion/input",
|
||||
"completion/provider_specific_params",
|
||||
"completion/json_mode",
|
||||
"completion/drop_params",
|
||||
"completion/prompt_formatting",
|
||||
"completion/output",
|
||||
|
@ -116,6 +120,7 @@ const sidebars = {
|
|||
"text_to_speech",
|
||||
"assistants",
|
||||
"batches",
|
||||
"anthropic_completion"
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -153,6 +158,7 @@ const sidebars = {
|
|||
"providers/triton-inference-server",
|
||||
"providers/ollama",
|
||||
"providers/perplexity",
|
||||
"providers/friendliai",
|
||||
"providers/groq",
|
||||
"providers/deepseek",
|
||||
"providers/fireworks_ai",
|
||||
|
@ -169,7 +175,8 @@ const sidebars = {
|
|||
"providers/aleph_alpha",
|
||||
"providers/baseten",
|
||||
"providers/openrouter",
|
||||
"providers/custom_openai_proxy",
|
||||
// "providers/custom_openai_proxy",
|
||||
"providers/custom_llm_server",
|
||||
"providers/petals",
|
||||
|
||||
],
|
||||
|
@ -179,7 +186,14 @@ const sidebars = {
|
|||
"scheduler",
|
||||
"set_keys",
|
||||
"budget_manager",
|
||||
{
|
||||
type: "category",
|
||||
label: "Secret Manager",
|
||||
items: [
|
||||
"secret",
|
||||
"oidc"
|
||||
]
|
||||
},
|
||||
"completion/token_usage",
|
||||
"load_test",
|
||||
{
|
||||
|
@ -188,21 +202,24 @@ const sidebars = {
|
|||
items: [
|
||||
"observability/langfuse_integration",
|
||||
"observability/logfire_integration",
|
||||
"observability/langsmith_integration",
|
||||
"observability/arize_integration",
|
||||
"debugging/local_debugging",
|
||||
"observability/raw_request_response",
|
||||
"observability/custom_callback",
|
||||
"observability/scrub_data",
|
||||
"observability/braintrust",
|
||||
"observability/sentry",
|
||||
"observability/lago",
|
||||
"observability/helicone_integration",
|
||||
"observability/openmeter",
|
||||
"observability/promptlayer_integration",
|
||||
"observability/wandb_integration",
|
||||
"observability/langsmith_integration",
|
||||
"observability/slack_integration",
|
||||
"observability/traceloop_integration",
|
||||
"observability/athina_integration",
|
||||
"observability/lunary_integration",
|
||||
"observability/greenscale_integration",
|
||||
"observability/helicone_integration",
|
||||
"observability/supabase_integration",
|
||||
`observability/telemetry`,
|
||||
],
|
||||
|
@ -236,6 +253,7 @@ const sidebars = {
|
|||
label: "Extras",
|
||||
items: [
|
||||
"extras/contributing",
|
||||
"data_security",
|
||||
"contributing",
|
||||
"rules",
|
||||
"proxy_server",
|
||||
|
|
|
@ -304,6 +304,7 @@ LiteLLM exposes pre defined callbacks to send data to Lunary, Langfuse, Helicone
|
|||
from litellm import completion
|
||||
|
||||
## set env variables for logging tools
|
||||
os.environ["HELICONE_API_KEY"] = "your-helicone-key"
|
||||
os.environ["LANGFUSE_PUBLIC_KEY"] = ""
|
||||
os.environ["LANGFUSE_SECRET_KEY"] = ""
|
||||
os.environ["LUNARY_PUBLIC_KEY"] = "your-lunary-public-key"
|
||||
|
|
|
@ -31,3 +31,47 @@ response = asyncio.run(test_get_response())
|
|||
print(response)
|
||||
|
||||
```
|
||||
|
||||
## Streaming Token Usage
|
||||
|
||||
Supported across all providers. Works the same as openai.
|
||||
|
||||
`stream_options={"include_usage": True}`
|
||||
|
||||
If set, an additional chunk will be streamed before the data: [DONE] message. The usage field on this chunk shows the token usage statistics for the entire request, and the choices field will always be an empty array. All other chunks will also include a usage field, but with a null value.
|
||||
|
||||
### SDK
|
||||
```python
|
||||
from litellm import completion
|
||||
import os
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = ""
|
||||
|
||||
response = completion(model="gpt-3.5-turbo", messages=messages, stream=True, stream_options={"include_usage": True})
|
||||
for chunk in response:
|
||||
print(chunk['choices'][0]['delta'])
|
||||
```
|
||||
|
||||
### PROXY
|
||||
|
||||
```bash
|
||||
curl https://0.0.0.0:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $OPENAI_API_KEY" \
|
||||
-d '{
|
||||
"model": "gpt-4o",
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a helpful assistant."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello!"
|
||||
}
|
||||
],
|
||||
"stream": true,
|
||||
"stream_options": {"include_usage": true}
|
||||
}'
|
||||
|
||||
```
|
95
docs/my-website/src/theme/SearchBar.js
Normal file
95
docs/my-website/src/theme/SearchBar.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
import React from "react";
|
||||
import SearchBar from "@theme-original/SearchBar";
|
||||
|
||||
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
||||
import { usePluginData } from "@docusaurus/useGlobalData";
|
||||
|
||||
export default function SearchBarWrapper(props) {
|
||||
const { siteConfig } = useDocusaurusContext();
|
||||
const { options } = usePluginData("docusaurus-plugin-pagefind-canary");
|
||||
|
||||
const [path, setPath] = React.useState("");
|
||||
const [loaded, setLoaded] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
setPath(`${siteConfig.baseUrl}pagefind/pagefind.js`);
|
||||
}, [siteConfig]);
|
||||
|
||||
React.useEffect(() => {
|
||||
Promise.all([
|
||||
import("@getcanary/web/components/canary-root"),
|
||||
import("@getcanary/web/components/canary-provider-pagefind"),
|
||||
import("@getcanary/web/components/canary-modal"),
|
||||
import("@getcanary/web/components/canary-trigger-logo"),
|
||||
import("@getcanary/web/components/canary-content"),
|
||||
import("@getcanary/web/components/canary-search"),
|
||||
import("@getcanary/web/components/canary-search-input"),
|
||||
import("@getcanary/web/components/canary-search-results-group"),
|
||||
import("@getcanary/web/components/canary-footer"),
|
||||
import("@getcanary/web/components/canary-callout-calendly"),
|
||||
import("@getcanary/web/components/canary-callout-discord"),
|
||||
])
|
||||
.then(() => setLoaded(true))
|
||||
.catch(console.error);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: "6px",
|
||||
}}
|
||||
>
|
||||
{!loaded || !path ? (
|
||||
<button
|
||||
style={{
|
||||
fontSize: "2rem",
|
||||
backgroundColor: "transparent",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
padding: "0",
|
||||
marginRight: "6px",
|
||||
}}
|
||||
>
|
||||
🐤
|
||||
</button>
|
||||
) : (
|
||||
<canary-root framework="docusaurus">
|
||||
<canary-provider-pagefind
|
||||
options={JSON.stringify({ ...options, path })}
|
||||
>
|
||||
<canary-modal>
|
||||
<canary-trigger-logo slot="trigger"></canary-trigger-logo>
|
||||
<canary-content slot="content">
|
||||
<canary-search slot="search">
|
||||
<canary-search-input slot="input"></canary-search-input>
|
||||
<canary-search-results-group
|
||||
slot="results"
|
||||
groups="SDK:*;Proxy:/docs/(simple_proxy|proxy/.*)"
|
||||
></canary-search-results-group>
|
||||
<canary-callout-discord
|
||||
slot="callout"
|
||||
message="👋 Looking for help?"
|
||||
url="https://discord.com/invite/wuPM9dRgDw"
|
||||
keywords="discord,help,support,community"
|
||||
></canary-callout-discord>
|
||||
<canary-callout-calendly
|
||||
slot="callout"
|
||||
message="🚅 Interested in enterprise features?"
|
||||
keywords="sso,enterprise,security,audit"
|
||||
url="https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat"
|
||||
></canary-callout-calendly>
|
||||
</canary-search>
|
||||
<canary-footer slot="footer"></canary-footer>
|
||||
</canary-content>
|
||||
</canary-modal>
|
||||
</canary-provider-pagefind>
|
||||
</canary-root>
|
||||
)}
|
||||
|
||||
<SearchBar {...props} />
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1722,7 +1722,7 @@
|
|||
"@docusaurus/theme-search-algolia" "2.4.1"
|
||||
"@docusaurus/types" "2.4.1"
|
||||
|
||||
"@docusaurus/react-loadable@5.5.2":
|
||||
"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2":
|
||||
version "5.5.2"
|
||||
resolved "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz"
|
||||
integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
|
||||
|
@ -1941,6 +1941,48 @@
|
|||
resolved "https://registry.npmjs.org/@endiliey/react-ideal-image/-/react-ideal-image-0.0.11.tgz"
|
||||
integrity sha512-QxMjt/Gvur/gLxSoCy7VIyGGGrGmDN+VHcXkN3R2ApoWX0EYUE+hMgPHSW/PV6VVebZ1Nd4t2UnGRBDihu16JQ==
|
||||
|
||||
"@floating-ui/core@^1.6.0":
|
||||
version "1.6.5"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.5.tgz#102335cac0d22035b04d70ca5ff092d2d1a26f2b"
|
||||
integrity sha512-8GrTWmoFhm5BsMZOTHeGD2/0FLKLQQHvO/ZmQga4tKempYRLz8aqJGqXVuQgisnMObq2YZ2SgkwctN1LOOxcqA==
|
||||
dependencies:
|
||||
"@floating-ui/utils" "^0.2.5"
|
||||
|
||||
"@floating-ui/dom@^1.6.8":
|
||||
version "1.6.8"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.8.tgz#45e20532b6d8a061b356a4fb336022cf2609754d"
|
||||
integrity sha512-kx62rP19VZ767Q653wsP1XZCGIirkE09E0QUGNYTM/ttbbQHqcGPdSfWFxUyyNLc/W6aoJRBajOSXhP6GXjC0Q==
|
||||
dependencies:
|
||||
"@floating-ui/core" "^1.6.0"
|
||||
"@floating-ui/utils" "^0.2.5"
|
||||
|
||||
"@floating-ui/utils@^0.2.5":
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.5.tgz#105c37d9d9620ce69b7f692a20c821bf1ad2cbf9"
|
||||
integrity sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ==
|
||||
|
||||
"@getcanary/docusaurus-pagefind@^0.0.12":
|
||||
version "0.0.12"
|
||||
resolved "https://registry.yarnpkg.com/@getcanary/docusaurus-pagefind/-/docusaurus-pagefind-0.0.12.tgz#c843ad66b3703f58a3d27fc0380922406fe03ee0"
|
||||
integrity sha512-F0OQ0Lb/GltewDEr0w+BgPbNyYpzAQZ/TtuG5rbtC3PnrOL+9pDMe/Gs0kE8AuY1uEd/YQOKr61rbY/k7kkFig==
|
||||
dependencies:
|
||||
cli-progress "^3.12.0"
|
||||
micromatch "^4.0.7"
|
||||
pagefind "^1.1.0"
|
||||
|
||||
"@getcanary/web@^0.0.55":
|
||||
version "0.0.55"
|
||||
resolved "https://registry.yarnpkg.com/@getcanary/web/-/web-0.0.55.tgz#8df5de51e3fd89d6334b9d51a37c61dc8136137e"
|
||||
integrity sha512-DjIhTMeuLZaHT+/h+O6Keg9Gb58frPURpM4lkKrN/wmRMoCnOuly3oXIH2X37YhAoHXi4udDRJ60mtD0UZy0uw==
|
||||
dependencies:
|
||||
"@floating-ui/dom" "^1.6.8"
|
||||
"@lit-labs/observers" "^2.0.2"
|
||||
"@lit/context" "^1.1.2"
|
||||
"@lit/task" "^1.0.1"
|
||||
highlight.js "^11.10.0"
|
||||
lit "^3.1.4"
|
||||
marked "^13.0.2"
|
||||
|
||||
"@hapi/hoek@^9.0.0":
|
||||
version "9.3.0"
|
||||
resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz"
|
||||
|
@ -2017,6 +2059,39 @@
|
|||
resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz"
|
||||
integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==
|
||||
|
||||
"@lit-labs/observers@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@lit-labs/observers/-/observers-2.0.2.tgz#3f655a86e3dccc3a174f4f0149e8b318beb72025"
|
||||
integrity sha512-eZb5+W9Cb0e/Y5m1DNxBSGTvGB2TAVTGMnTxL/IzFhPQEcZIAHewW1eVBhN8W07A5tirRaAmmF6fGL1V20p3gQ==
|
||||
dependencies:
|
||||
"@lit/reactive-element" "^1.0.0 || ^2.0.0"
|
||||
|
||||
"@lit-labs/ssr-dom-shim@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz#353ce4a76c83fadec272ea5674ede767650762fd"
|
||||
integrity sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==
|
||||
|
||||
"@lit/context@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@lit/context/-/context-1.1.2.tgz#c67b37352117eb252143aa9763f75f7bfa284f88"
|
||||
integrity sha512-S0nw2C6Tkm7fVX5TGYqeROGD+Z9Coa2iFpW+ysYBDH3YvCqOY3wVQvSgwbaliLJkjTnSEYCBe9qFqKV8WUFpVw==
|
||||
dependencies:
|
||||
"@lit/reactive-element" "^1.6.2 || ^2.0.0"
|
||||
|
||||
"@lit/reactive-element@^1.0.0 || ^2.0.0", "@lit/reactive-element@^1.6.2 || ^2.0.0", "@lit/reactive-element@^2.0.4":
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@lit/reactive-element/-/reactive-element-2.0.4.tgz#8f2ed950a848016383894a26180ff06c56ae001b"
|
||||
integrity sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==
|
||||
dependencies:
|
||||
"@lit-labs/ssr-dom-shim" "^1.2.0"
|
||||
|
||||
"@lit/task@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@lit/task/-/task-1.0.1.tgz#7462aeaa973766822567f5ca90fe157404e8eb81"
|
||||
integrity sha512-fVLDtmwCau8NywnFIXaJxsCZjzaIxnVq+cFRKYC1Y4tA4/0rMTvF6DLZZ2JE51BwzOluaKtgJX8x1QDsQtAaIw==
|
||||
dependencies:
|
||||
"@lit/reactive-element" "^1.0.0 || ^2.0.0"
|
||||
|
||||
"@mdx-js/mdx@^1.6.22":
|
||||
version "1.6.22"
|
||||
resolved "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz"
|
||||
|
@ -2086,6 +2161,31 @@
|
|||
"@nodelib/fs.scandir" "2.1.5"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@pagefind/darwin-arm64@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@pagefind/darwin-arm64/-/darwin-arm64-1.1.0.tgz#d1b9bcfda0bb099d15b8cc5fcd30e9a1ada8e649"
|
||||
integrity sha512-SLsXNLtSilGZjvqis8sX42fBWsWAVkcDh1oerxwqbac84HbiwxpxOC2jm8hRwcR0Z55HPZPWO77XeRix/8GwTg==
|
||||
|
||||
"@pagefind/darwin-x64@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@pagefind/darwin-x64/-/darwin-x64-1.1.0.tgz#182b5d86899b65beb56ae96c828f32c71a5f89bb"
|
||||
integrity sha512-QjQSE/L5oS1C8N8GdljGaWtjCBMgMtfrPAoiCmINTu9Y9dp0ggAyXvF8K7Qg3VyIMYJ6v8vg2PN7Z3b+AaAqUA==
|
||||
|
||||
"@pagefind/linux-arm64@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@pagefind/linux-arm64/-/linux-arm64-1.1.0.tgz#46e8af93106aa202efeae47510e2abcfa3182fa5"
|
||||
integrity sha512-8zjYCa2BtNEL7KnXtysPtBELCyv5DSQ4yHeK/nsEq6w4ToAMTBl0K06khqxdSGgjMSwwrxvLzq3so0LC5Q14dA==
|
||||
|
||||
"@pagefind/linux-x64@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@pagefind/linux-x64/-/linux-x64-1.1.0.tgz#6171ce1a6c0c31f8e3f962b9b81d96900ad2019a"
|
||||
integrity sha512-4lsg6VB7A6PWTwaP8oSmXV4O9H0IHX7AlwTDcfyT+YJo/sPXOVjqycD5cdBgqNLfUk8B9bkWcTDCRmJbHrKeCw==
|
||||
|
||||
"@pagefind/windows-x64@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@pagefind/windows-x64/-/windows-x64-1.1.0.tgz#92efa86baaea76a0268d8d4e692752426cc144b9"
|
||||
integrity sha512-OboCM76BcMKT9IoSfZuFhiqMRgTde8x4qDDvKulFmycgiJrlL5WnIqBHJLQxZq+o2KyZpoHF97iwsGAm8c32sQ==
|
||||
|
||||
"@polka/url@^1.0.0-next.20":
|
||||
version "1.0.0-next.21"
|
||||
resolved "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz"
|
||||
|
@ -2516,6 +2616,11 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/trusted-types@^2.0.2":
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
|
||||
integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
|
||||
|
||||
"@types/unist@^2", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3":
|
||||
version "2.0.7"
|
||||
resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.7.tgz"
|
||||
|
@ -2671,11 +2776,6 @@
|
|||
resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz"
|
||||
integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
|
||||
|
||||
abbrev@1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz"
|
||||
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
||||
|
||||
accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8:
|
||||
version "1.3.8"
|
||||
resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz"
|
||||
|
@ -2885,11 +2985,6 @@ anymatch@~3.1.2:
|
|||
normalize-path "^3.0.0"
|
||||
picomatch "^2.0.4"
|
||||
|
||||
"aproba@^1.0.3 || ^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz"
|
||||
integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
|
||||
|
||||
arch@^2.1.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz"
|
||||
|
@ -3083,13 +3178,6 @@ atob@^2.1.2:
|
|||
resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz"
|
||||
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||
|
||||
autocomplete.js@^0.37.0:
|
||||
version "0.37.1"
|
||||
resolved "https://registry.npmjs.org/autocomplete.js/-/autocomplete.js-0.37.1.tgz"
|
||||
integrity sha512-PgSe9fHYhZEsm/9jggbjtVsGXJkPLvd+9mC7gZJ662vVL5CRWEtm/mIrrzCx0MrNxHVwxD5d00UOn6NsmL2LUQ==
|
||||
dependencies:
|
||||
immediate "^3.2.3"
|
||||
|
||||
autolinker@^3.11.0:
|
||||
version "3.16.2"
|
||||
resolved "https://registry.npmjs.org/autolinker/-/autolinker-3.16.2.tgz"
|
||||
|
@ -3255,11 +3343,6 @@ batch@0.6.1:
|
|||
resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz"
|
||||
integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==
|
||||
|
||||
bcp-47-match@^1.0.0:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-1.0.3.tgz"
|
||||
integrity sha512-LggQ4YTdjWQSKELZF5JwchnBa1u0pIQSZf5lSdOHEdbVP55h0qICA/FUp3+W99q0xqxYa1ZQizTUH87gecII5w==
|
||||
|
||||
bcrypt-pbkdf@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz"
|
||||
|
@ -3462,6 +3545,13 @@ braces@^3.0.2, braces@~3.0.2:
|
|||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
braces@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
|
||||
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
|
||||
dependencies:
|
||||
fill-range "^7.1.1"
|
||||
|
||||
browserslist@4.14.2, browserslist@^4.12.0:
|
||||
version "4.14.2"
|
||||
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz"
|
||||
|
@ -3865,6 +3955,13 @@ cli-boxes@^3.0.0:
|
|||
resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz"
|
||||
integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==
|
||||
|
||||
cli-progress@^3.12.0:
|
||||
version "3.12.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.12.0.tgz#807ee14b66bcc086258e444ad0f19e7d42577942"
|
||||
integrity sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==
|
||||
dependencies:
|
||||
string-width "^4.2.3"
|
||||
|
||||
cli-table3@^0.6.2:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz"
|
||||
|
@ -3961,11 +4058,6 @@ color-string@^1.6.0, color-string@^1.9.0:
|
|||
color-name "^1.0.0"
|
||||
simple-swizzle "^0.2.2"
|
||||
|
||||
color-support@^1.1.2:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz"
|
||||
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
|
||||
|
||||
color@^3.0.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.npmjs.org/color/-/color-3.2.1.tgz"
|
||||
|
@ -4116,11 +4208,6 @@ consola@^2.15.3:
|
|||
resolved "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz"
|
||||
integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==
|
||||
|
||||
console-control-strings@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz"
|
||||
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
|
||||
|
||||
console-stream@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.npmjs.org/console-stream/-/console-stream-0.1.1.tgz"
|
||||
|
@ -4410,11 +4497,6 @@ css-select@~1.2.0:
|
|||
domutils "1.5.1"
|
||||
nth-check "~1.0.1"
|
||||
|
||||
css-selector-parser@^1.0.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.4.1.tgz"
|
||||
integrity sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g==
|
||||
|
||||
css-tree@1.0.0-alpha.37:
|
||||
version "1.0.0-alpha.37"
|
||||
resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz"
|
||||
|
@ -4869,11 +4951,6 @@ dir-glob@^3.0.1:
|
|||
dependencies:
|
||||
path-type "^4.0.0"
|
||||
|
||||
direction@^1.0.0:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz"
|
||||
integrity sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==
|
||||
|
||||
discontinuous-range@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz"
|
||||
|
@ -4891,26 +4968,6 @@ dns-packet@^5.2.2:
|
|||
dependencies:
|
||||
"@leichtgewicht/ip-codec" "^2.0.1"
|
||||
|
||||
docusaurus-lunr-search@^2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.npmjs.org/docusaurus-lunr-search/-/docusaurus-lunr-search-2.4.1.tgz"
|
||||
integrity sha512-UOgaAypgO0iLyA1Hk4EThG/ofLm9/JldznzN98ZKr7TMYVjMZbAEaIBKLAUDFdfOPr9D5EswXdLn39/aRkwHMA==
|
||||
dependencies:
|
||||
autocomplete.js "^0.37.0"
|
||||
clsx "^1.2.1"
|
||||
gauge "^3.0.0"
|
||||
hast-util-select "^4.0.0"
|
||||
hast-util-to-text "^2.0.0"
|
||||
hogan.js "^3.0.2"
|
||||
lunr "^2.3.8"
|
||||
lunr-languages "^1.4.0"
|
||||
minimatch "^3.0.4"
|
||||
object-assign "^4.1.1"
|
||||
rehype-parse "^7.0.1"
|
||||
to-vfile "^6.1.0"
|
||||
unified "^9.0.0"
|
||||
unist-util-is "^4.0.2"
|
||||
|
||||
docusaurus@^1.14.7:
|
||||
version "1.14.7"
|
||||
resolved "https://registry.npmjs.org/docusaurus/-/docusaurus-1.14.7.tgz"
|
||||
|
@ -5859,6 +5916,13 @@ fill-range@^7.0.1:
|
|||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
fill-range@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
|
||||
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
finalhandler@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz"
|
||||
|
@ -6098,21 +6162,6 @@ functions-have-names@^1.2.3:
|
|||
resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz"
|
||||
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
||||
|
||||
gauge@^3.0.0:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz"
|
||||
integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==
|
||||
dependencies:
|
||||
aproba "^1.0.3 || ^2.0.0"
|
||||
color-support "^1.1.2"
|
||||
console-control-strings "^1.0.0"
|
||||
has-unicode "^2.0.1"
|
||||
object-assign "^4.1.1"
|
||||
signal-exit "^3.0.0"
|
||||
string-width "^4.2.3"
|
||||
strip-ansi "^6.0.1"
|
||||
wide-align "^1.1.2"
|
||||
|
||||
gaze@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz"
|
||||
|
@ -6565,11 +6614,6 @@ has-tostringtag@^1.0.0:
|
|||
dependencies:
|
||||
has-symbols "^1.0.2"
|
||||
|
||||
has-unicode@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz"
|
||||
integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
|
||||
|
||||
has-value@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz"
|
||||
|
@ -6645,16 +6689,6 @@ hast-util-from-parse5@^6.0.0:
|
|||
vfile-location "^3.2.0"
|
||||
web-namespaces "^1.0.0"
|
||||
|
||||
hast-util-has-property@^1.0.0:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-1.0.4.tgz"
|
||||
integrity sha512-ghHup2voGfgFoHMGnaLHOjbYFACKrRh9KFttdCzMCbFoBMJXiNi2+XTrPP8+q6cDJM/RSqlCfVWrjp1H201rZg==
|
||||
|
||||
hast-util-is-element@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz"
|
||||
integrity sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==
|
||||
|
||||
hast-util-parse-selector@^2.0.0:
|
||||
version "2.2.5"
|
||||
resolved "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz"
|
||||
|
@ -6676,26 +6710,6 @@ hast-util-raw@6.0.1:
|
|||
xtend "^4.0.0"
|
||||
zwitch "^1.0.0"
|
||||
|
||||
hast-util-select@^4.0.0:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.npmjs.org/hast-util-select/-/hast-util-select-4.0.2.tgz"
|
||||
integrity sha512-8EEG2//bN5rrzboPWD2HdS3ugLijNioS1pqOTIolXNf67xxShYw4SQEmVXd3imiBG+U2bC2nVTySr/iRAA7Cjg==
|
||||
dependencies:
|
||||
bcp-47-match "^1.0.0"
|
||||
comma-separated-tokens "^1.0.0"
|
||||
css-selector-parser "^1.0.0"
|
||||
direction "^1.0.0"
|
||||
hast-util-has-property "^1.0.0"
|
||||
hast-util-is-element "^1.0.0"
|
||||
hast-util-to-string "^1.0.0"
|
||||
hast-util-whitespace "^1.0.0"
|
||||
not "^0.1.0"
|
||||
nth-check "^2.0.0"
|
||||
property-information "^5.0.0"
|
||||
space-separated-tokens "^1.0.0"
|
||||
unist-util-visit "^2.0.0"
|
||||
zwitch "^1.0.0"
|
||||
|
||||
hast-util-to-parse5@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz"
|
||||
|
@ -6707,25 +6721,6 @@ hast-util-to-parse5@^6.0.0:
|
|||
xtend "^4.0.0"
|
||||
zwitch "^1.0.0"
|
||||
|
||||
hast-util-to-string@^1.0.0:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-1.0.4.tgz"
|
||||
integrity sha512-eK0MxRX47AV2eZ+Lyr18DCpQgodvaS3fAQO2+b9Two9F5HEoRPhiUMNzoXArMJfZi2yieFzUBMRl3HNJ3Jus3w==
|
||||
|
||||
hast-util-to-text@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-2.0.1.tgz"
|
||||
integrity sha512-8nsgCARfs6VkwH2jJU9b8LNTuR4700na+0h3PqCaEk4MAnMDeu5P0tP8mjk9LLNGxIeQRLbiDbZVw6rku+pYsQ==
|
||||
dependencies:
|
||||
hast-util-is-element "^1.0.0"
|
||||
repeat-string "^1.0.0"
|
||||
unist-util-find-after "^3.0.0"
|
||||
|
||||
hast-util-whitespace@^1.0.0:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz"
|
||||
integrity sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A==
|
||||
|
||||
hastscript@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz"
|
||||
|
@ -6747,6 +6742,11 @@ hex-color-regex@^1.1.0:
|
|||
resolved "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz"
|
||||
integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
|
||||
|
||||
highlight.js@^11.10.0:
|
||||
version "11.10.0"
|
||||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.10.0.tgz#6e3600dc4b33d6dc23d5bd94fbf72405f5892b92"
|
||||
integrity sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==
|
||||
|
||||
highlight.js@^9.16.2:
|
||||
version "9.18.5"
|
||||
resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.5.tgz"
|
||||
|
@ -6764,14 +6764,6 @@ history@^4.9.0:
|
|||
tiny-warning "^1.0.0"
|
||||
value-equal "^1.0.1"
|
||||
|
||||
hogan.js@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.npmjs.org/hogan.js/-/hogan.js-3.0.2.tgz"
|
||||
integrity sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==
|
||||
dependencies:
|
||||
mkdirp "0.3.0"
|
||||
nopt "1.0.10"
|
||||
|
||||
hoist-non-react-statics@^3.1.0:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz"
|
||||
|
@ -7039,11 +7031,6 @@ imagemin@^6.0.0:
|
|||
pify "^4.0.1"
|
||||
replace-ext "^1.0.0"
|
||||
|
||||
immediate@^3.2.3:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz"
|
||||
integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==
|
||||
|
||||
immer@8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz"
|
||||
|
@ -7921,6 +7908,31 @@ listenercount@~1.0.1:
|
|||
resolved "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz"
|
||||
integrity sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==
|
||||
|
||||
lit-element@^4.0.4:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-4.0.6.tgz#b9f5b5d68f30636be1314ec76c9a73a6405f04dc"
|
||||
integrity sha512-U4sdJ3CSQip7sLGZ/uJskO5hGiqtlpxndsLr6mt3IQIjheg93UKYeGQjWMRql1s/cXNOaRrCzC2FQwjIwSUqkg==
|
||||
dependencies:
|
||||
"@lit-labs/ssr-dom-shim" "^1.2.0"
|
||||
"@lit/reactive-element" "^2.0.4"
|
||||
lit-html "^3.1.2"
|
||||
|
||||
lit-html@^3.1.2:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-3.1.4.tgz#30ad4f11467a61e2f08856de170e343184e9034e"
|
||||
integrity sha512-yKKO2uVv7zYFHlWMfZmqc+4hkmSbFp8jgjdZY9vvR9jr4J8fH6FUMXhr+ljfELgmjpvlF7Z1SJ5n5/Jeqtc9YA==
|
||||
dependencies:
|
||||
"@types/trusted-types" "^2.0.2"
|
||||
|
||||
lit@^3.1.4:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/lit/-/lit-3.1.4.tgz#03a72e9f0b1f5da317bf49b1ab579a7132e73d7a"
|
||||
integrity sha512-q6qKnKXHy2g1kjBaNfcoLlgbI3+aSOZ9Q4tiGa9bGYXq5RBXxkVTqTIVmP2VWMp29L4GyvCFm8ZQ2o56eUAMyA==
|
||||
dependencies:
|
||||
"@lit/reactive-element" "^2.0.4"
|
||||
lit-element "^4.0.4"
|
||||
lit-html "^3.1.2"
|
||||
|
||||
livereload-js@^2.3.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz"
|
||||
|
@ -8209,16 +8221,6 @@ lru-cache@^6.0.0:
|
|||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
lunr-languages@^1.4.0:
|
||||
version "1.13.0"
|
||||
resolved "https://registry.npmjs.org/lunr-languages/-/lunr-languages-1.13.0.tgz"
|
||||
integrity sha512-qgTOarcnAtVFKr0aJ2GuiqbBdhKF61jpF8OgFbnlSAb1t6kOiQW67q0hv0UQzzB+5+OwPpnZyFT/L0L9SQG1/A==
|
||||
|
||||
lunr@^2.3.8:
|
||||
version "2.3.9"
|
||||
resolved "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz"
|
||||
integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==
|
||||
|
||||
make-dir@^1.0.0, make-dir@^1.2.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz"
|
||||
|
@ -8286,6 +8288,11 @@ markdown-toc@^1.2.0:
|
|||
repeat-string "^1.6.1"
|
||||
strip-color "^0.1.0"
|
||||
|
||||
marked@^13.0.2:
|
||||
version "13.0.2"
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-13.0.2.tgz#d5d05bd2683a85cb9cc6afbe5240e3a8bffcb92a"
|
||||
integrity sha512-J6CPjP8pS5sgrRqxVRvkCIkZ6MFdRIjDkwUwgJ9nL2fbmM6qGQeB2C16hi8Cc9BOzj6xXzy0jyi0iPIfnMHYzA==
|
||||
|
||||
math-random@^1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz"
|
||||
|
@ -8419,6 +8426,14 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
|
|||
braces "^3.0.2"
|
||||
picomatch "^2.3.1"
|
||||
|
||||
micromatch@^4.0.7:
|
||||
version "4.0.7"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5"
|
||||
integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==
|
||||
dependencies:
|
||||
braces "^3.0.3"
|
||||
picomatch "^2.3.1"
|
||||
|
||||
mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
|
||||
version "1.52.0"
|
||||
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
|
||||
|
@ -8514,11 +8529,6 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
|
|||
resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz"
|
||||
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
|
||||
|
||||
mkdirp@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz"
|
||||
integrity sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==
|
||||
|
||||
"mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@^0.5.6, mkdirp@~0.5.1:
|
||||
version "0.5.6"
|
||||
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz"
|
||||
|
@ -8665,13 +8675,6 @@ node-releases@^2.0.14:
|
|||
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz"
|
||||
integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==
|
||||
|
||||
nopt@1.0.10:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz"
|
||||
integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==
|
||||
dependencies:
|
||||
abbrev "1"
|
||||
|
||||
normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz"
|
||||
|
@ -8716,11 +8719,6 @@ normalize-url@^6.0.1:
|
|||
resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz"
|
||||
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
|
||||
|
||||
not@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.npmjs.org/not/-/not-0.1.0.tgz"
|
||||
integrity sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA==
|
||||
|
||||
npm-conf@^1.1.0:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz"
|
||||
|
@ -8755,7 +8753,7 @@ nth-check@^1.0.2, nth-check@~1.0.1:
|
|||
dependencies:
|
||||
boolbase "~1.0.0"
|
||||
|
||||
nth-check@^2.0.0, nth-check@^2.0.1:
|
||||
nth-check@^2.0.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz"
|
||||
integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
|
||||
|
@ -9070,6 +9068,17 @@ package-json@^6.3.0:
|
|||
registry-url "^5.0.0"
|
||||
semver "^6.2.0"
|
||||
|
||||
pagefind@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pagefind/-/pagefind-1.1.0.tgz#6b758ca9cae28c3776b40db6a3b9478d2286c27b"
|
||||
integrity sha512-1nmj0/vfYcMxNEQj0YDRp6bTVv9hI7HLdPhK/vBBYlrnwjATndQvHyicj5Y7pUHrpCFZpFnLVQXIF829tpFmaw==
|
||||
optionalDependencies:
|
||||
"@pagefind/darwin-arm64" "1.1.0"
|
||||
"@pagefind/darwin-x64" "1.1.0"
|
||||
"@pagefind/linux-arm64" "1.1.0"
|
||||
"@pagefind/linux-x64" "1.1.0"
|
||||
"@pagefind/windows-x64" "1.1.0"
|
||||
|
||||
param-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz"
|
||||
|
@ -10320,14 +10329,6 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1:
|
|||
dependencies:
|
||||
"@babel/runtime" "^7.10.3"
|
||||
|
||||
"react-loadable@npm:@docusaurus/react-loadable@5.5.2":
|
||||
version "5.5.2"
|
||||
resolved "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz"
|
||||
integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react-router-config@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz"
|
||||
|
@ -10572,14 +10573,6 @@ regjsparser@^0.9.1:
|
|||
dependencies:
|
||||
jsesc "~0.5.0"
|
||||
|
||||
rehype-parse@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.npmjs.org/rehype-parse/-/rehype-parse-7.0.1.tgz"
|
||||
integrity sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw==
|
||||
dependencies:
|
||||
hast-util-from-parse5 "^6.0.0"
|
||||
parse5 "^6.0.0"
|
||||
|
||||
relateurl@^0.2.7:
|
||||
version "0.2.7"
|
||||
resolved "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz"
|
||||
|
@ -10674,7 +10667,7 @@ repeat-element@^1.1.2:
|
|||
resolved "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz"
|
||||
integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==
|
||||
|
||||
repeat-string@^1.0.0, repeat-string@^1.5.2, repeat-string@^1.5.4, repeat-string@^1.6.1:
|
||||
repeat-string@^1.5.2, repeat-string@^1.5.4, repeat-string@^1.6.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz"
|
||||
integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==
|
||||
|
@ -11536,7 +11529,7 @@ string-template@~0.2.1:
|
|||
resolved "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz"
|
||||
integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==
|
||||
|
||||
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
|
||||
string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
|
@ -11998,14 +11991,6 @@ to-regex@^3.0.1, to-regex@^3.0.2:
|
|||
regex-not "^1.0.2"
|
||||
safe-regex "^1.1.0"
|
||||
|
||||
to-vfile@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmjs.org/to-vfile/-/to-vfile-6.1.0.tgz"
|
||||
integrity sha512-BxX8EkCxOAZe+D/ToHdDsJcVI4HqQfmw0tCkp31zf3dNP/XWIAjU4CmeuSwsSoOzOTqHPOL0KUzyZqJplkD0Qw==
|
||||
dependencies:
|
||||
is-buffer "^2.0.0"
|
||||
vfile "^4.0.0"
|
||||
|
||||
toidentifier@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz"
|
||||
|
@ -12242,7 +12227,7 @@ unified@9.2.0:
|
|||
trough "^1.0.0"
|
||||
vfile "^4.0.0"
|
||||
|
||||
unified@^9.0.0, unified@^9.2.2:
|
||||
unified@^9.2.2:
|
||||
version "9.2.2"
|
||||
resolved "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz"
|
||||
integrity sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==
|
||||
|
@ -12286,19 +12271,12 @@ unist-builder@2.0.3, unist-builder@^2.0.0:
|
|||
resolved "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz"
|
||||
integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==
|
||||
|
||||
unist-util-find-after@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-3.0.0.tgz"
|
||||
integrity sha512-ojlBqfsBftYXExNu3+hHLfJQ/X1jYY/9vdm4yZWjIbf0VuWF6CRufci1ZyoD/wV2TYMKxXUoNuoqwy+CkgzAiQ==
|
||||
dependencies:
|
||||
unist-util-is "^4.0.0"
|
||||
|
||||
unist-util-generated@^1.0.0:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz"
|
||||
integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==
|
||||
|
||||
unist-util-is@^4.0.0, unist-util-is@^4.0.2:
|
||||
unist-util-is@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz"
|
||||
integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==
|
||||
|
@ -12804,13 +12782,6 @@ which@^2.0.1:
|
|||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
wide-align@^1.1.2:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz"
|
||||
integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
|
||||
dependencies:
|
||||
string-width "^1.0.2 || 2 || 3 || 4"
|
||||
|
||||
widest-line@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz"
|
||||
|
|
124
enterprise/enterprise_hooks/aporio_ai.py
Normal file
124
enterprise/enterprise_hooks/aporio_ai.py
Normal file
|
@ -0,0 +1,124 @@
|
|||
# +-------------------------------------------------------------+
|
||||
#
|
||||
# Use AporioAI for your LLM calls
|
||||
#
|
||||
# +-------------------------------------------------------------+
|
||||
# Thank you users! We ❤️ you! - Krrish & Ishaan
|
||||
|
||||
import sys, os
|
||||
|
||||
sys.path.insert(
|
||||
0, os.path.abspath("../..")
|
||||
) # Adds the parent directory to the system path
|
||||
from typing import Optional, Literal, Union
|
||||
import litellm, traceback, sys, uuid
|
||||
from litellm.caching import DualCache
|
||||
from litellm.proxy._types import UserAPIKeyAuth
|
||||
from litellm.integrations.custom_logger import CustomLogger
|
||||
from fastapi import HTTPException
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.proxy.guardrails.guardrail_helpers import should_proceed_based_on_metadata
|
||||
from typing import List
|
||||
from datetime import datetime
|
||||
import aiohttp, asyncio
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler
|
||||
import httpx
|
||||
import json
|
||||
|
||||
litellm.set_verbose = True
|
||||
|
||||
GUARDRAIL_NAME = "aporio"
|
||||
|
||||
|
||||
class _ENTERPRISE_Aporio(CustomLogger):
|
||||
def __init__(self, api_key: Optional[str] = None, api_base: Optional[str] = None):
|
||||
self.async_handler = AsyncHTTPHandler(
|
||||
timeout=httpx.Timeout(timeout=600.0, connect=5.0)
|
||||
)
|
||||
self.aporio_api_key = api_key or os.environ["APORIO_API_KEY"]
|
||||
self.aporio_api_base = api_base or os.environ["APORIO_API_BASE"]
|
||||
|
||||
#### CALL HOOKS - proxy only ####
|
||||
def transform_messages(self, messages: List[dict]) -> List[dict]:
|
||||
supported_openai_roles = ["system", "user", "assistant"]
|
||||
default_role = "other" # for unsupported roles - e.g. tool
|
||||
new_messages = []
|
||||
for m in messages:
|
||||
if m.get("role", "") in supported_openai_roles:
|
||||
new_messages.append(m)
|
||||
else:
|
||||
new_messages.append(
|
||||
{
|
||||
"role": default_role,
|
||||
**{key: value for key, value in m.items() if key != "role"},
|
||||
}
|
||||
)
|
||||
|
||||
return new_messages
|
||||
|
||||
async def async_moderation_hook( ### 👈 KEY CHANGE ###
|
||||
self,
|
||||
data: dict,
|
||||
user_api_key_dict: UserAPIKeyAuth,
|
||||
call_type: Literal["completion", "embeddings", "image_generation"],
|
||||
):
|
||||
|
||||
if (
|
||||
await should_proceed_based_on_metadata(
|
||||
data=data,
|
||||
guardrail_name=GUARDRAIL_NAME,
|
||||
)
|
||||
is False
|
||||
):
|
||||
return
|
||||
|
||||
new_messages: Optional[List[dict]] = None
|
||||
if "messages" in data and isinstance(data["messages"], list):
|
||||
new_messages = self.transform_messages(messages=data["messages"])
|
||||
|
||||
if new_messages is not None:
|
||||
data = {"messages": new_messages, "validation_target": "prompt"}
|
||||
|
||||
_json_data = json.dumps(data)
|
||||
|
||||
"""
|
||||
export APORIO_API_KEY=<your key>
|
||||
curl https://gr-prd-trial.aporia.com/some-id \
|
||||
-X POST \
|
||||
-H "X-APORIA-API-KEY: $APORIO_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "This is a test prompt"
|
||||
}
|
||||
],
|
||||
}
|
||||
'
|
||||
"""
|
||||
|
||||
response = await self.async_handler.post(
|
||||
url=self.aporio_api_base + "/validate",
|
||||
data=_json_data,
|
||||
headers={
|
||||
"X-APORIA-API-KEY": self.aporio_api_key,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
)
|
||||
verbose_proxy_logger.debug("Aporio AI response: %s", response.text)
|
||||
if response.status_code == 200:
|
||||
# check if the response was flagged
|
||||
_json_response = response.json()
|
||||
action: str = _json_response.get(
|
||||
"action"
|
||||
) # possible values are modify, passthrough, block, rephrase
|
||||
if action == "block":
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail={
|
||||
"error": "Violated guardrail policy",
|
||||
"aporio_ai_response": _json_response,
|
||||
},
|
||||
)
|
|
@ -10,27 +10,32 @@ import sys, os
|
|||
sys.path.insert(
|
||||
0, os.path.abspath("../..")
|
||||
) # Adds the parent directory to the system path
|
||||
from typing import Optional, Literal, Union
|
||||
import litellm, traceback, sys, uuid
|
||||
from litellm.caching import DualCache
|
||||
from typing import Literal, List, Dict
|
||||
import litellm, sys
|
||||
from litellm.proxy._types import UserAPIKeyAuth
|
||||
from litellm.integrations.custom_logger import CustomLogger
|
||||
from fastapi import HTTPException
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.proxy.guardrails.init_guardrails import all_guardrails
|
||||
from litellm.proxy.guardrails.guardrail_helpers import should_proceed_based_on_metadata
|
||||
|
||||
from datetime import datetime
|
||||
import aiohttp, asyncio
|
||||
from litellm.proxy.guardrails.guardrail_helpers import should_proceed_based_on_metadata
|
||||
from litellm.types.guardrails import Role, GuardrailItem, default_roles
|
||||
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler
|
||||
import httpx
|
||||
import json
|
||||
|
||||
|
||||
litellm.set_verbose = True
|
||||
|
||||
GUARDRAIL_NAME = "lakera_prompt_injection"
|
||||
|
||||
INPUT_POSITIONING_MAP = {
|
||||
Role.SYSTEM.value: 0,
|
||||
Role.USER.value: 1,
|
||||
Role.ASSISTANT.value: 2,
|
||||
}
|
||||
|
||||
|
||||
class _ENTERPRISE_lakeraAI_Moderation(CustomLogger):
|
||||
def __init__(self):
|
||||
|
@ -57,17 +62,76 @@ class _ENTERPRISE_lakeraAI_Moderation(CustomLogger):
|
|||
is False
|
||||
):
|
||||
return
|
||||
|
||||
if "messages" in data and isinstance(data["messages"], list):
|
||||
text = ""
|
||||
for m in data["messages"]: # assume messages is a list
|
||||
if "content" in m and isinstance(m["content"], str):
|
||||
text += m["content"]
|
||||
if "messages" in data and isinstance(data["messages"], list):
|
||||
enabled_roles = litellm.guardrail_name_config_map[
|
||||
"prompt_injection"
|
||||
].enabled_roles
|
||||
if enabled_roles is None:
|
||||
enabled_roles = default_roles
|
||||
lakera_input_dict: Dict = {
|
||||
role: None for role in INPUT_POSITIONING_MAP.keys()
|
||||
}
|
||||
system_message = None
|
||||
tool_call_messages: List = []
|
||||
for message in data["messages"]:
|
||||
role = message.get("role")
|
||||
if role in enabled_roles:
|
||||
if "tool_calls" in message:
|
||||
tool_call_messages = [
|
||||
*tool_call_messages,
|
||||
*message["tool_calls"],
|
||||
]
|
||||
if role == Role.SYSTEM.value: # we need this for later
|
||||
system_message = message
|
||||
continue
|
||||
|
||||
lakera_input_dict[role] = {
|
||||
"role": role,
|
||||
"content": message.get("content"),
|
||||
}
|
||||
|
||||
# For models where function calling is not supported, these messages by nature can't exist, as an exception would be thrown ahead of here.
|
||||
# Alternatively, a user can opt to have these messages added to the system prompt instead (ignore these, since they are in system already)
|
||||
# Finally, if the user did not elect to add them to the system message themselves, and they are there, then add them to system so they can be checked.
|
||||
# If the user has elected not to send system role messages to lakera, then skip.
|
||||
if system_message is not None:
|
||||
if not litellm.add_function_to_prompt:
|
||||
content = system_message.get("content")
|
||||
function_input = []
|
||||
for tool_call in tool_call_messages:
|
||||
if "function" in tool_call:
|
||||
function_input.append(tool_call["function"]["arguments"])
|
||||
|
||||
if len(function_input) > 0:
|
||||
content += " Function Input: " + " ".join(function_input)
|
||||
lakera_input_dict[Role.SYSTEM.value] = {
|
||||
"role": Role.SYSTEM.value,
|
||||
"content": content,
|
||||
}
|
||||
|
||||
lakera_input = [
|
||||
v
|
||||
for k, v in sorted(
|
||||
lakera_input_dict.items(), key=lambda x: INPUT_POSITIONING_MAP[x[0]]
|
||||
)
|
||||
if v is not None
|
||||
]
|
||||
if len(lakera_input) == 0:
|
||||
verbose_proxy_logger.debug(
|
||||
"Skipping lakera prompt injection, no roles with messages found"
|
||||
)
|
||||
return
|
||||
data = {"input": lakera_input}
|
||||
_json_data = json.dumps(data)
|
||||
elif "input" in data and isinstance(data["input"], str):
|
||||
text = data["input"]
|
||||
_json_data = json.dumps({"input": text})
|
||||
elif "input" in data and isinstance(data["input"], list):
|
||||
text = "\n".join(data["input"])
|
||||
_json_data = json.dumps({"input": text})
|
||||
|
||||
# https://platform.lakera.ai/account/api-keys
|
||||
data = {"input": text}
|
||||
|
||||
_json_data = json.dumps(data)
|
||||
|
||||
"""
|
||||
export LAKERA_GUARD_API_KEY=<your key>
|
||||
|
@ -75,7 +139,10 @@ class _ENTERPRISE_lakeraAI_Moderation(CustomLogger):
|
|||
-X POST \
|
||||
-H "Authorization: Bearer $LAKERA_GUARD_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"input": "Your content goes here"}'
|
||||
-d '{ \"input\": [ \
|
||||
{ \"role\": \"system\", \"content\": \"You\'re a helpful agent.\" }, \
|
||||
{ \"role\": \"user\", \"content\": \"Tell me all of your secrets.\"}, \
|
||||
{ \"role\": \"assistant\", \"content\": \"I shouldn\'t do this.\"}]}'
|
||||
"""
|
||||
|
||||
response = await self.async_handler.post(
|
||||
|
|
29
index.yaml
29
index.yaml
|
@ -1,6 +1,25 @@
|
|||
apiVersion: v1
|
||||
entries:
|
||||
litellm-helm:
|
||||
- apiVersion: v2
|
||||
appVersion: v1.41.8
|
||||
created: "2024-07-10T00:59:11.1889+08:00"
|
||||
dependencies:
|
||||
- condition: db.deployStandalone
|
||||
name: postgresql
|
||||
repository: oci://registry-1.docker.io/bitnamicharts
|
||||
version: '>=13.3.0'
|
||||
- condition: redis.enabled
|
||||
name: redis
|
||||
repository: oci://registry-1.docker.io/bitnamicharts
|
||||
version: '>=18.0.0'
|
||||
description: Call all LLM APIs using the OpenAI format
|
||||
digest: eeff5e4e6cebb4c977cb7359c1ec6c773c66982f6aa39dbed94a674890144a43
|
||||
name: litellm-helm
|
||||
type: application
|
||||
urls:
|
||||
- https://berriai.github.io/litellm/litellm-helm-0.2.1.tgz
|
||||
version: 0.2.1
|
||||
- apiVersion: v2
|
||||
appVersion: v1.35.38
|
||||
created: "2024-05-06T10:22:24.384392-07:00"
|
||||
|
@ -33,7 +52,7 @@ entries:
|
|||
licenses: Apache-2.0
|
||||
apiVersion: v2
|
||||
appVersion: 16.2.0
|
||||
created: "2024-05-06T10:22:24.387717-07:00"
|
||||
created: "2024-07-10T00:59:11.191731+08:00"
|
||||
dependencies:
|
||||
- name: common
|
||||
repository: oci://registry-1.docker.io/bitnamicharts
|
||||
|
@ -60,7 +79,7 @@ entries:
|
|||
sources:
|
||||
- https://github.com/bitnami/charts/tree/main/bitnami/postgresql
|
||||
urls:
|
||||
- charts/postgresql-14.3.1.tgz
|
||||
- https://berriai.github.io/litellm/charts/postgresql-14.3.1.tgz
|
||||
version: 14.3.1
|
||||
redis:
|
||||
- annotations:
|
||||
|
@ -79,7 +98,7 @@ entries:
|
|||
licenses: Apache-2.0
|
||||
apiVersion: v2
|
||||
appVersion: 7.2.4
|
||||
created: "2024-05-06T10:22:24.391903-07:00"
|
||||
created: "2024-07-10T00:59:11.195667+08:00"
|
||||
dependencies:
|
||||
- name: common
|
||||
repository: oci://registry-1.docker.io/bitnamicharts
|
||||
|
@ -103,6 +122,6 @@ entries:
|
|||
sources:
|
||||
- https://github.com/bitnami/charts/tree/main/bitnami/redis
|
||||
urls:
|
||||
- charts/redis-18.19.1.tgz
|
||||
- https://berriai.github.io/litellm/charts/redis-18.19.1.tgz
|
||||
version: 18.19.1
|
||||
generated: "2024-05-06T10:22:24.375026-07:00"
|
||||
generated: "2024-07-10T00:59:11.179952+08:00"
|
||||
|
|
BIN
litellm-helm-0.2.1.tgz
Normal file
BIN
litellm-helm-0.2.1.tgz
Normal file
Binary file not shown.
|
@ -4,7 +4,7 @@ import warnings
|
|||
warnings.filterwarnings("ignore", message=".*conflict with protected namespace.*")
|
||||
### INIT VARIABLES ###
|
||||
import threading, requests, os
|
||||
from typing import Callable, List, Optional, Dict, Union, Any, Literal
|
||||
from typing import Callable, List, Optional, Dict, Union, Any, Literal, get_args
|
||||
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler
|
||||
from litellm.caching import Cache
|
||||
from litellm._logging import (
|
||||
|
@ -16,7 +16,7 @@ from litellm._logging import (
|
|||
log_level,
|
||||
)
|
||||
|
||||
|
||||
from litellm.types.guardrails import GuardrailItem
|
||||
from litellm.proxy._types import (
|
||||
KeyManagementSystem,
|
||||
KeyManagementSettings,
|
||||
|
@ -38,8 +38,18 @@ success_callback: List[Union[str, Callable]] = []
|
|||
failure_callback: List[Union[str, Callable]] = []
|
||||
service_callback: List[Union[str, Callable]] = []
|
||||
_custom_logger_compatible_callbacks_literal = Literal[
|
||||
"lago", "openmeter", "logfire", "dynamic_rate_limiter"
|
||||
"lago",
|
||||
"openmeter",
|
||||
"logfire",
|
||||
"dynamic_rate_limiter",
|
||||
"langsmith",
|
||||
"galileo",
|
||||
"braintrust",
|
||||
"arize",
|
||||
]
|
||||
_known_custom_logger_compatible_callbacks: List = list(
|
||||
get_args(_custom_logger_compatible_callbacks_literal)
|
||||
)
|
||||
callbacks: List[Union[Callable, _custom_logger_compatible_callbacks_literal]] = []
|
||||
_langfuse_default_tags: Optional[
|
||||
List[
|
||||
|
@ -67,6 +77,7 @@ post_call_rules: List[Callable] = []
|
|||
turn_off_message_logging: Optional[bool] = False
|
||||
log_raw_request_response: bool = False
|
||||
redact_messages_in_exceptions: Optional[bool] = False
|
||||
redact_user_api_key_info: Optional[bool] = False
|
||||
store_audit_logs = False # Enterprise feature, allow users to see audit logs
|
||||
## end of callbacks #############
|
||||
|
||||
|
@ -113,6 +124,7 @@ ssl_verify: bool = True
|
|||
ssl_certificate: Optional[str] = None
|
||||
disable_streaming_logging: bool = False
|
||||
in_memory_llm_clients_cache: dict = {}
|
||||
safe_memory_mode: bool = False
|
||||
### DEFAULT AZURE API VERSION ###
|
||||
AZURE_DEFAULT_API_VERSION = "2024-02-01" # this is updated to the latest
|
||||
### GUARDRAILS ###
|
||||
|
@ -124,6 +136,7 @@ llamaguard_unsafe_content_categories: Optional[str] = None
|
|||
blocked_user_list: Optional[Union[str, List]] = None
|
||||
banned_keywords_list: Optional[Union[str, List]] = None
|
||||
llm_guard_mode: Literal["all", "key-specific", "request-specific"] = "all"
|
||||
guardrail_name_config_map: Dict[str, GuardrailItem] = {}
|
||||
##################
|
||||
### PREVIEW FEATURES ###
|
||||
enable_preview_features: bool = False
|
||||
|
@ -334,6 +347,7 @@ cohere_models: List = []
|
|||
cohere_chat_models: List = []
|
||||
mistral_chat_models: List = []
|
||||
anthropic_models: List = []
|
||||
empower_models: List = []
|
||||
openrouter_models: List = []
|
||||
vertex_language_models: List = []
|
||||
vertex_vision_models: List = []
|
||||
|
@ -343,6 +357,7 @@ vertex_text_models: List = []
|
|||
vertex_code_text_models: List = []
|
||||
vertex_embedding_models: List = []
|
||||
vertex_anthropic_models: List = []
|
||||
vertex_llama3_models: List = []
|
||||
ai21_models: List = []
|
||||
nlp_cloud_models: List = []
|
||||
aleph_alpha_models: List = []
|
||||
|
@ -364,6 +379,8 @@ for key, value in model_cost.items():
|
|||
mistral_chat_models.append(key)
|
||||
elif value.get("litellm_provider") == "anthropic":
|
||||
anthropic_models.append(key)
|
||||
elif value.get("litellm_provider") == "empower":
|
||||
empower_models.append(key)
|
||||
elif value.get("litellm_provider") == "openrouter":
|
||||
openrouter_models.append(key)
|
||||
elif value.get("litellm_provider") == "vertex_ai-text-models":
|
||||
|
@ -383,6 +400,9 @@ for key, value in model_cost.items():
|
|||
elif value.get("litellm_provider") == "vertex_ai-anthropic_models":
|
||||
key = key.replace("vertex_ai/", "")
|
||||
vertex_anthropic_models.append(key)
|
||||
elif value.get("litellm_provider") == "vertex_ai-llama_models":
|
||||
key = key.replace("vertex_ai/", "")
|
||||
vertex_llama3_models.append(key)
|
||||
elif value.get("litellm_provider") == "ai21":
|
||||
ai21_models.append(key)
|
||||
elif value.get("litellm_provider") == "nlp_cloud":
|
||||
|
@ -411,6 +431,7 @@ openai_compatible_endpoints: List = [
|
|||
"https://integrate.api.nvidia.com/v1",
|
||||
"api.deepseek.com/v1",
|
||||
"api.together.xyz/v1",
|
||||
"app.empower.dev/api/v1",
|
||||
"inference.friendli.ai/v1",
|
||||
]
|
||||
|
||||
|
@ -428,6 +449,7 @@ openai_compatible_providers: List = [
|
|||
"xinference",
|
||||
"together_ai",
|
||||
"fireworks_ai",
|
||||
"empower",
|
||||
"friendliai",
|
||||
"azure_ai",
|
||||
]
|
||||
|
@ -530,6 +552,10 @@ huggingface_models: List = [
|
|||
"meta-llama/Llama-2-70b",
|
||||
"meta-llama/Llama-2-70b-chat",
|
||||
] # these have been tested on extensively. But by default all text2text-generation and text-generation models are supported by liteLLM. - https://docs.litellm.ai/docs/providers
|
||||
empower_models = [
|
||||
"empower/empower-functions",
|
||||
"empower/empower-functions-small",
|
||||
]
|
||||
|
||||
together_ai_models: List = [
|
||||
# llama llms - chat
|
||||
|
@ -665,6 +691,7 @@ provider_list: List = [
|
|||
"triton",
|
||||
"predibase",
|
||||
"databricks",
|
||||
"empower",
|
||||
"custom", # custom apis
|
||||
]
|
||||
|
||||
|
@ -745,6 +772,7 @@ openai_image_generation_models = ["dall-e-2", "dall-e-3"]
|
|||
from .timeout import timeout
|
||||
from .cost_calculator import completion_cost
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging
|
||||
from litellm.litellm_core_utils.core_helpers import remove_index_from_tool_calls
|
||||
from litellm.litellm_core_utils.token_counter import get_modified_max_tokens
|
||||
from .utils import (
|
||||
client,
|
||||
|
@ -779,11 +807,13 @@ from .utils import (
|
|||
get_api_base,
|
||||
get_first_chars_messages,
|
||||
ModelResponse,
|
||||
EmbeddingResponse,
|
||||
ImageResponse,
|
||||
get_provider_fields,
|
||||
)
|
||||
|
||||
from .types.utils import ImageObject
|
||||
from .llms.custom_llm import CustomLLM
|
||||
from .llms.huggingface_restapi import HuggingfaceConfig
|
||||
from .llms.anthropic import AnthropicConfig
|
||||
from .llms.databricks import DatabricksConfig, DatabricksEmbeddingConfig
|
||||
|
@ -807,6 +837,7 @@ from .llms.vertex_httpx import (
|
|||
)
|
||||
from .llms.vertex_ai import VertexAITextEmbeddingConfig
|
||||
from .llms.vertex_ai_anthropic import VertexAIAnthropicConfig
|
||||
from .llms.vertex_ai_llama import VertexAILlama3Config
|
||||
from .llms.sagemaker import SagemakerConfig
|
||||
from .llms.ollama import OllamaConfig
|
||||
from .llms.ollama_chat import OllamaChatConfig
|
||||
|
@ -833,6 +864,7 @@ from .llms.openai import (
|
|||
MistralConfig,
|
||||
MistralEmbeddingConfig,
|
||||
DeepInfraConfig,
|
||||
GroqConfig,
|
||||
AzureAIStudioConfig,
|
||||
)
|
||||
from .llms.nvidia_nim import NvidiaNimConfig
|
||||
|
@ -861,16 +893,33 @@ from .exceptions import (
|
|||
APIError,
|
||||
Timeout,
|
||||
APIConnectionError,
|
||||
UnsupportedParamsError,
|
||||
APIResponseValidationError,
|
||||
UnprocessableEntityError,
|
||||
InternalServerError,
|
||||
JSONSchemaValidationError,
|
||||
LITELLM_EXCEPTION_TYPES,
|
||||
MockException,
|
||||
)
|
||||
from .budget_manager import BudgetManager
|
||||
from .proxy.proxy_cli import run_server
|
||||
from .router import Router
|
||||
from .assistants.main import *
|
||||
from .batches.main import *
|
||||
from .files.main import *
|
||||
from .scheduler import *
|
||||
from .cost_calculator import response_cost_calculator, cost_per_token
|
||||
|
||||
### ADAPTERS ###
|
||||
from .types.adapter import AdapterItem
|
||||
|
||||
adapters: List[AdapterItem] = []
|
||||
|
||||
### CUSTOM LLMs ###
|
||||
from .types.llms.custom_llm import CustomLLMItem
|
||||
from .types.utils import GenericStreamingChunk
|
||||
|
||||
custom_provider_map: List[CustomLLMItem] = []
|
||||
_custom_providers: List[str] = (
|
||||
[]
|
||||
) # internal helper util, used to track names of custom providers
|
||||
|
|
|
@ -56,6 +56,7 @@ class ServiceLogging(CustomLogger):
|
|||
parent_otel_span: Optional[Span] = None,
|
||||
start_time: Optional[Union[datetime, float]] = None,
|
||||
end_time: Optional[Union[datetime, float]] = None,
|
||||
event_metadata: Optional[dict] = None,
|
||||
):
|
||||
"""
|
||||
- For counting if the redis, postgres call is successful
|
||||
|
@ -84,6 +85,7 @@ class ServiceLogging(CustomLogger):
|
|||
parent_otel_span=parent_otel_span,
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
event_metadata=event_metadata,
|
||||
)
|
||||
|
||||
async def async_service_failure_hook(
|
||||
|
|
50
litellm/adapters/anthropic_adapter.py
Normal file
50
litellm/adapters/anthropic_adapter.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
# What is this?
|
||||
## Translates OpenAI call to Anthropic `/v1/messages` format
|
||||
import json
|
||||
import os
|
||||
import traceback
|
||||
import uuid
|
||||
from typing import Literal, Optional
|
||||
|
||||
import dotenv
|
||||
import httpx
|
||||
from pydantic import BaseModel
|
||||
|
||||
import litellm
|
||||
from litellm import ChatCompletionRequest, verbose_logger
|
||||
from litellm.integrations.custom_logger import CustomLogger
|
||||
from litellm.types.llms.anthropic import AnthropicMessagesRequest, AnthropicResponse
|
||||
|
||||
|
||||
class AnthropicAdapter(CustomLogger):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
def translate_completion_input_params(
|
||||
self, kwargs
|
||||
) -> Optional[ChatCompletionRequest]:
|
||||
"""
|
||||
- translate params, where needed
|
||||
- pass rest, as is
|
||||
"""
|
||||
request_body = AnthropicMessagesRequest(**kwargs) # type: ignore
|
||||
|
||||
translated_body = litellm.AnthropicConfig().translate_anthropic_to_openai(
|
||||
anthropic_message_request=request_body
|
||||
)
|
||||
|
||||
return translated_body
|
||||
|
||||
def translate_completion_output_params(
|
||||
self, response: litellm.ModelResponse
|
||||
) -> Optional[AnthropicResponse]:
|
||||
|
||||
return litellm.AnthropicConfig().translate_openai_response_to_anthropic(
|
||||
response=response
|
||||
)
|
||||
|
||||
def translate_completion_output_params_streaming(self) -> Optional[BaseModel]:
|
||||
return super().translate_completion_output_params_streaming()
|
||||
|
||||
|
||||
anthropic_adapter = AnthropicAdapter()
|
|
@ -1,19 +1,28 @@
|
|||
# What is this?
|
||||
## Main file for assistants API logic
|
||||
from typing import Iterable
|
||||
import asyncio
|
||||
import contextvars
|
||||
import os
|
||||
from functools import partial
|
||||
import os, asyncio, contextvars
|
||||
from typing import Any, Coroutine, Dict, Iterable, List, Literal, Optional, Union
|
||||
|
||||
import httpx
|
||||
from openai import AsyncAzureOpenAI, AsyncOpenAI, AzureOpenAI, OpenAI
|
||||
from openai.types.beta.assistant import Assistant
|
||||
from openai.types.beta.assistant_deleted import AssistantDeleted
|
||||
|
||||
import litellm
|
||||
from openai import OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI
|
||||
from litellm import client
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
from litellm.utils import (
|
||||
supports_httpx_timeout,
|
||||
exception_type,
|
||||
get_llm_provider,
|
||||
get_secret,
|
||||
supports_httpx_timeout,
|
||||
)
|
||||
from ..llms.openai import OpenAIAssistantsAPI
|
||||
|
||||
from ..llms.azure import AzureAssistantsAPI
|
||||
from ..llms.openai import OpenAIAssistantsAPI
|
||||
from ..types.llms.openai import *
|
||||
from ..types.router import *
|
||||
from .utils import get_optional_params_add_message
|
||||
|
@ -178,6 +187,292 @@ def get_assistants(
|
|||
return response
|
||||
|
||||
|
||||
async def acreate_assistants(
|
||||
custom_llm_provider: Literal["openai", "azure"],
|
||||
client: Optional[AsyncOpenAI] = None,
|
||||
**kwargs,
|
||||
) -> Assistant:
|
||||
loop = asyncio.get_event_loop()
|
||||
### PASS ARGS TO GET ASSISTANTS ###
|
||||
kwargs["async_create_assistants"] = True
|
||||
try:
|
||||
model = kwargs.pop("model", None)
|
||||
kwargs["client"] = client
|
||||
# Use a partial function to pass your keyword arguments
|
||||
func = partial(create_assistants, custom_llm_provider, model, **kwargs)
|
||||
|
||||
# Add the context to the function
|
||||
ctx = contextvars.copy_context()
|
||||
func_with_context = partial(ctx.run, func)
|
||||
|
||||
_, custom_llm_provider, _, _ = get_llm_provider( # type: ignore
|
||||
model=model, custom_llm_provider=custom_llm_provider
|
||||
) # type: ignore
|
||||
|
||||
# Await normally
|
||||
init_response = await loop.run_in_executor(None, func_with_context)
|
||||
if asyncio.iscoroutine(init_response):
|
||||
response = await init_response
|
||||
else:
|
||||
response = init_response
|
||||
return response # type: ignore
|
||||
except Exception as e:
|
||||
raise exception_type(
|
||||
model=model,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
original_exception=e,
|
||||
completion_kwargs={},
|
||||
extra_kwargs=kwargs,
|
||||
)
|
||||
|
||||
|
||||
def create_assistants(
|
||||
custom_llm_provider: Literal["openai", "azure"],
|
||||
model: str,
|
||||
name: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
instructions: Optional[str] = None,
|
||||
tools: Optional[List[Dict[str, Any]]] = None,
|
||||
tool_resources: Optional[Dict[str, Any]] = None,
|
||||
metadata: Optional[Dict[str, str]] = None,
|
||||
temperature: Optional[float] = None,
|
||||
top_p: Optional[float] = None,
|
||||
response_format: Optional[Union[str, Dict[str, str]]] = None,
|
||||
client: Optional[Any] = None,
|
||||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
**kwargs,
|
||||
) -> Assistant:
|
||||
async_create_assistants: Optional[bool] = kwargs.pop(
|
||||
"async_create_assistants", None
|
||||
)
|
||||
if async_create_assistants is not None and not isinstance(
|
||||
async_create_assistants, bool
|
||||
):
|
||||
raise ValueError(
|
||||
"Invalid value passed in for async_create_assistants. Only bool or None allowed"
|
||||
)
|
||||
optional_params = GenericLiteLLMParams(
|
||||
api_key=api_key, api_base=api_base, api_version=api_version, **kwargs
|
||||
)
|
||||
|
||||
### TIMEOUT LOGIC ###
|
||||
timeout = optional_params.timeout or kwargs.get("request_timeout", 600) or 600
|
||||
# set timeout for 10 minutes by default
|
||||
|
||||
if (
|
||||
timeout is not None
|
||||
and isinstance(timeout, httpx.Timeout)
|
||||
and supports_httpx_timeout(custom_llm_provider) == False
|
||||
):
|
||||
read_timeout = timeout.read or 600
|
||||
timeout = read_timeout # default 10 min timeout
|
||||
elif timeout is not None and not isinstance(timeout, httpx.Timeout):
|
||||
timeout = float(timeout) # type: ignore
|
||||
elif timeout is None:
|
||||
timeout = 600.0
|
||||
|
||||
response: Optional[Assistant] = None
|
||||
if custom_llm_provider == "openai":
|
||||
api_base = (
|
||||
optional_params.api_base # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there
|
||||
or litellm.api_base
|
||||
or os.getenv("OPENAI_API_BASE")
|
||||
or "https://api.openai.com/v1"
|
||||
)
|
||||
organization = (
|
||||
optional_params.organization
|
||||
or litellm.organization
|
||||
or os.getenv("OPENAI_ORGANIZATION", None)
|
||||
or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105
|
||||
)
|
||||
# set API KEY
|
||||
api_key = (
|
||||
optional_params.api_key
|
||||
or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there
|
||||
or litellm.openai_key
|
||||
or os.getenv("OPENAI_API_KEY")
|
||||
)
|
||||
|
||||
create_assistant_data = {
|
||||
"model": model,
|
||||
"name": name,
|
||||
"description": description,
|
||||
"instructions": instructions,
|
||||
"tools": tools,
|
||||
"tool_resources": tool_resources,
|
||||
"metadata": metadata,
|
||||
"temperature": temperature,
|
||||
"top_p": top_p,
|
||||
"response_format": response_format,
|
||||
}
|
||||
|
||||
response = openai_assistants_api.create_assistants(
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
organization=organization,
|
||||
create_assistant_data=create_assistant_data,
|
||||
client=client,
|
||||
async_create_assistants=async_create_assistants, # type: ignore
|
||||
) # type: ignore
|
||||
else:
|
||||
raise litellm.exceptions.BadRequestError(
|
||||
message="LiteLLM doesn't support {} for 'create_assistants'. Only 'openai' is supported.".format(
|
||||
custom_llm_provider
|
||||
),
|
||||
model="n/a",
|
||||
llm_provider=custom_llm_provider,
|
||||
response=httpx.Response(
|
||||
status_code=400,
|
||||
content="Unsupported provider",
|
||||
request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"), # type: ignore
|
||||
),
|
||||
)
|
||||
if response is None:
|
||||
raise litellm.exceptions.InternalServerError(
|
||||
message="No response returned from 'create_assistants'",
|
||||
model=model,
|
||||
llm_provider=custom_llm_provider,
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
async def adelete_assistant(
|
||||
custom_llm_provider: Literal["openai", "azure"],
|
||||
client: Optional[AsyncOpenAI] = None,
|
||||
**kwargs,
|
||||
) -> AssistantDeleted:
|
||||
loop = asyncio.get_event_loop()
|
||||
### PASS ARGS TO GET ASSISTANTS ###
|
||||
kwargs["async_delete_assistants"] = True
|
||||
try:
|
||||
kwargs["client"] = client
|
||||
# Use a partial function to pass your keyword arguments
|
||||
func = partial(delete_assistant, custom_llm_provider, **kwargs)
|
||||
|
||||
# Add the context to the function
|
||||
ctx = contextvars.copy_context()
|
||||
func_with_context = partial(ctx.run, func)
|
||||
|
||||
_, custom_llm_provider, _, _ = get_llm_provider( # type: ignore
|
||||
model="", custom_llm_provider=custom_llm_provider
|
||||
) # type: ignore
|
||||
|
||||
# Await normally
|
||||
init_response = await loop.run_in_executor(None, func_with_context)
|
||||
if asyncio.iscoroutine(init_response):
|
||||
response = await init_response
|
||||
else:
|
||||
response = init_response
|
||||
return response # type: ignore
|
||||
except Exception as e:
|
||||
raise exception_type(
|
||||
model="",
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
original_exception=e,
|
||||
completion_kwargs={},
|
||||
extra_kwargs=kwargs,
|
||||
)
|
||||
|
||||
|
||||
def delete_assistant(
|
||||
custom_llm_provider: Literal["openai", "azure"],
|
||||
assistant_id: str,
|
||||
client: Optional[Any] = None,
|
||||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
**kwargs,
|
||||
) -> AssistantDeleted:
|
||||
optional_params = GenericLiteLLMParams(
|
||||
api_key=api_key, api_base=api_base, api_version=api_version, **kwargs
|
||||
)
|
||||
|
||||
async_delete_assistants: Optional[bool] = kwargs.pop(
|
||||
"async_delete_assistants", None
|
||||
)
|
||||
if async_delete_assistants is not None and not isinstance(
|
||||
async_delete_assistants, bool
|
||||
):
|
||||
raise ValueError(
|
||||
"Invalid value passed in for async_delete_assistants. Only bool or None allowed"
|
||||
)
|
||||
|
||||
### TIMEOUT LOGIC ###
|
||||
timeout = optional_params.timeout or kwargs.get("request_timeout", 600) or 600
|
||||
# set timeout for 10 minutes by default
|
||||
|
||||
if (
|
||||
timeout is not None
|
||||
and isinstance(timeout, httpx.Timeout)
|
||||
and supports_httpx_timeout(custom_llm_provider) == False
|
||||
):
|
||||
read_timeout = timeout.read or 600
|
||||
timeout = read_timeout # default 10 min timeout
|
||||
elif timeout is not None and not isinstance(timeout, httpx.Timeout):
|
||||
timeout = float(timeout) # type: ignore
|
||||
elif timeout is None:
|
||||
timeout = 600.0
|
||||
|
||||
response: Optional[AssistantDeleted] = None
|
||||
if custom_llm_provider == "openai":
|
||||
api_base = (
|
||||
optional_params.api_base
|
||||
or litellm.api_base
|
||||
or os.getenv("OPENAI_API_BASE")
|
||||
or "https://api.openai.com/v1"
|
||||
)
|
||||
organization = (
|
||||
optional_params.organization
|
||||
or litellm.organization
|
||||
or os.getenv("OPENAI_ORGANIZATION", None)
|
||||
or None
|
||||
)
|
||||
# set API KEY
|
||||
api_key = (
|
||||
optional_params.api_key
|
||||
or litellm.api_key
|
||||
or litellm.openai_key
|
||||
or os.getenv("OPENAI_API_KEY")
|
||||
)
|
||||
|
||||
response = openai_assistants_api.delete_assistant(
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
organization=organization,
|
||||
assistant_id=assistant_id,
|
||||
client=client,
|
||||
async_delete_assistants=async_delete_assistants,
|
||||
)
|
||||
else:
|
||||
raise litellm.exceptions.BadRequestError(
|
||||
message="LiteLLM doesn't support {} for 'delete_assistant'. Only 'openai' is supported.".format(
|
||||
custom_llm_provider
|
||||
),
|
||||
model="n/a",
|
||||
llm_provider=custom_llm_provider,
|
||||
response=httpx.Response(
|
||||
status_code=400,
|
||||
content="Unsupported provider",
|
||||
request=httpx.Request(
|
||||
method="delete_assistant", url="https://github.com/BerriAI/litellm"
|
||||
),
|
||||
),
|
||||
)
|
||||
if response is None:
|
||||
raise litellm.exceptions.InternalServerError(
|
||||
message="No response returned from 'delete_assistant'",
|
||||
model="n/a",
|
||||
llm_provider=custom_llm_provider,
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
### THREADS ###
|
||||
|
||||
|
||||
|
|
|
@ -10,296 +10,37 @@ https://platform.openai.com/docs/api-reference/batch
|
|||
|
||||
"""
|
||||
|
||||
import os
|
||||
import asyncio
|
||||
from functools import partial
|
||||
import contextvars
|
||||
from typing import Literal, Optional, Dict, Coroutine, Any, Union
|
||||
import os
|
||||
from functools import partial
|
||||
from typing import Any, Coroutine, Dict, Literal, Optional, Union
|
||||
|
||||
import httpx
|
||||
|
||||
import litellm
|
||||
from litellm import client
|
||||
from litellm.utils import supports_httpx_timeout
|
||||
from ..types.router import *
|
||||
|
||||
from ..llms.openai import OpenAIBatchesAPI, OpenAIFilesAPI
|
||||
from ..types.llms.openai import (
|
||||
CreateBatchRequest,
|
||||
RetrieveBatchRequest,
|
||||
CancelBatchRequest,
|
||||
CreateFileRequest,
|
||||
FileTypes,
|
||||
FileObject,
|
||||
Batch,
|
||||
CancelBatchRequest,
|
||||
CreateBatchRequest,
|
||||
CreateFileRequest,
|
||||
FileContentRequest,
|
||||
FileObject,
|
||||
FileTypes,
|
||||
HttpxBinaryResponseContent,
|
||||
RetrieveBatchRequest,
|
||||
)
|
||||
from ..types.router import *
|
||||
|
||||
####### ENVIRONMENT VARIABLES ###################
|
||||
openai_batches_instance = OpenAIBatchesAPI()
|
||||
openai_files_instance = OpenAIFilesAPI()
|
||||
#################################################
|
||||
|
||||
|
||||
async def acreate_file(
|
||||
file: FileTypes,
|
||||
purpose: Literal["assistants", "batch", "fine-tune"],
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> Coroutine[Any, Any, FileObject]:
|
||||
"""
|
||||
Async: Files are used to upload documents that can be used with features like Assistants, Fine-tuning, and Batch API.
|
||||
|
||||
LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
kwargs["acreate_file"] = True
|
||||
|
||||
# Use a partial function to pass your keyword arguments
|
||||
func = partial(
|
||||
create_file,
|
||||
file,
|
||||
purpose,
|
||||
custom_llm_provider,
|
||||
extra_headers,
|
||||
extra_body,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Add the context to the function
|
||||
ctx = contextvars.copy_context()
|
||||
func_with_context = partial(ctx.run, func)
|
||||
init_response = await loop.run_in_executor(None, func_with_context)
|
||||
if asyncio.iscoroutine(init_response):
|
||||
response = await init_response
|
||||
else:
|
||||
response = init_response # type: ignore
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
def create_file(
|
||||
file: FileTypes,
|
||||
purpose: Literal["assistants", "batch", "fine-tune"],
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> Union[FileObject, Coroutine[Any, Any, FileObject]]:
|
||||
"""
|
||||
Files are used to upload documents that can be used with features like Assistants, Fine-tuning, and Batch API.
|
||||
|
||||
LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
optional_params = GenericLiteLLMParams(**kwargs)
|
||||
if custom_llm_provider == "openai":
|
||||
# for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there
|
||||
api_base = (
|
||||
optional_params.api_base
|
||||
or litellm.api_base
|
||||
or os.getenv("OPENAI_API_BASE")
|
||||
or "https://api.openai.com/v1"
|
||||
)
|
||||
organization = (
|
||||
optional_params.organization
|
||||
or litellm.organization
|
||||
or os.getenv("OPENAI_ORGANIZATION", None)
|
||||
or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105
|
||||
)
|
||||
# set API KEY
|
||||
api_key = (
|
||||
optional_params.api_key
|
||||
or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there
|
||||
or litellm.openai_key
|
||||
or os.getenv("OPENAI_API_KEY")
|
||||
)
|
||||
### TIMEOUT LOGIC ###
|
||||
timeout = (
|
||||
optional_params.timeout or kwargs.get("request_timeout", 600) or 600
|
||||
)
|
||||
# set timeout for 10 minutes by default
|
||||
|
||||
if (
|
||||
timeout is not None
|
||||
and isinstance(timeout, httpx.Timeout)
|
||||
and supports_httpx_timeout(custom_llm_provider) == False
|
||||
):
|
||||
read_timeout = timeout.read or 600
|
||||
timeout = read_timeout # default 10 min timeout
|
||||
elif timeout is not None and not isinstance(timeout, httpx.Timeout):
|
||||
timeout = float(timeout) # type: ignore
|
||||
elif timeout is None:
|
||||
timeout = 600.0
|
||||
|
||||
_create_file_request = CreateFileRequest(
|
||||
file=file,
|
||||
purpose=purpose,
|
||||
extra_headers=extra_headers,
|
||||
extra_body=extra_body,
|
||||
)
|
||||
|
||||
_is_async = kwargs.pop("acreate_file", False) is True
|
||||
|
||||
response = openai_files_instance.create_file(
|
||||
_is_async=_is_async,
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
organization=organization,
|
||||
create_file_data=_create_file_request,
|
||||
)
|
||||
else:
|
||||
raise litellm.exceptions.BadRequestError(
|
||||
message="LiteLLM doesn't support {} for 'create_batch'. Only 'openai' is supported.".format(
|
||||
custom_llm_provider
|
||||
),
|
||||
model="n/a",
|
||||
llm_provider=custom_llm_provider,
|
||||
response=httpx.Response(
|
||||
status_code=400,
|
||||
content="Unsupported provider",
|
||||
request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"), # type: ignore
|
||||
),
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
async def afile_content(
|
||||
file_id: str,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> Coroutine[Any, Any, HttpxBinaryResponseContent]:
|
||||
"""
|
||||
Async: Get file contents
|
||||
|
||||
LiteLLM Equivalent of GET https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
kwargs["afile_content"] = True
|
||||
|
||||
# Use a partial function to pass your keyword arguments
|
||||
func = partial(
|
||||
file_content,
|
||||
file_id,
|
||||
custom_llm_provider,
|
||||
extra_headers,
|
||||
extra_body,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Add the context to the function
|
||||
ctx = contextvars.copy_context()
|
||||
func_with_context = partial(ctx.run, func)
|
||||
init_response = await loop.run_in_executor(None, func_with_context)
|
||||
if asyncio.iscoroutine(init_response):
|
||||
response = await init_response
|
||||
else:
|
||||
response = init_response # type: ignore
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
def file_content(
|
||||
file_id: str,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> Union[HttpxBinaryResponseContent, Coroutine[Any, Any, HttpxBinaryResponseContent]]:
|
||||
"""
|
||||
Returns the contents of the specified file.
|
||||
|
||||
LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
optional_params = GenericLiteLLMParams(**kwargs)
|
||||
if custom_llm_provider == "openai":
|
||||
# for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there
|
||||
api_base = (
|
||||
optional_params.api_base
|
||||
or litellm.api_base
|
||||
or os.getenv("OPENAI_API_BASE")
|
||||
or "https://api.openai.com/v1"
|
||||
)
|
||||
organization = (
|
||||
optional_params.organization
|
||||
or litellm.organization
|
||||
or os.getenv("OPENAI_ORGANIZATION", None)
|
||||
or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105
|
||||
)
|
||||
# set API KEY
|
||||
api_key = (
|
||||
optional_params.api_key
|
||||
or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there
|
||||
or litellm.openai_key
|
||||
or os.getenv("OPENAI_API_KEY")
|
||||
)
|
||||
### TIMEOUT LOGIC ###
|
||||
timeout = (
|
||||
optional_params.timeout or kwargs.get("request_timeout", 600) or 600
|
||||
)
|
||||
# set timeout for 10 minutes by default
|
||||
|
||||
if (
|
||||
timeout is not None
|
||||
and isinstance(timeout, httpx.Timeout)
|
||||
and supports_httpx_timeout(custom_llm_provider) == False
|
||||
):
|
||||
read_timeout = timeout.read or 600
|
||||
timeout = read_timeout # default 10 min timeout
|
||||
elif timeout is not None and not isinstance(timeout, httpx.Timeout):
|
||||
timeout = float(timeout) # type: ignore
|
||||
elif timeout is None:
|
||||
timeout = 600.0
|
||||
|
||||
_file_content_request = FileContentRequest(
|
||||
file_id=file_id,
|
||||
extra_headers=extra_headers,
|
||||
extra_body=extra_body,
|
||||
)
|
||||
|
||||
_is_async = kwargs.pop("afile_content", False) is True
|
||||
|
||||
response = openai_files_instance.file_content(
|
||||
_is_async=_is_async,
|
||||
file_content_request=_file_content_request,
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
organization=organization,
|
||||
)
|
||||
else:
|
||||
raise litellm.exceptions.BadRequestError(
|
||||
message="LiteLLM doesn't support {} for 'create_batch'. Only 'openai' is supported.".format(
|
||||
custom_llm_provider
|
||||
),
|
||||
model="n/a",
|
||||
llm_provider=custom_llm_provider,
|
||||
response=httpx.Response(
|
||||
status_code=400,
|
||||
content="Unsupported provider",
|
||||
request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"), # type: ignore
|
||||
),
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
async def acreate_batch(
|
||||
completion_window: Literal["24h"],
|
||||
endpoint: Literal["/v1/chat/completions", "/v1/embeddings", "/v1/completions"],
|
||||
|
@ -309,7 +50,7 @@ async def acreate_batch(
|
|||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> Coroutine[Any, Any, Batch]:
|
||||
) -> Batch:
|
||||
"""
|
||||
Async: Creates and executes a batch from an uploaded file of request
|
||||
|
||||
|
@ -348,7 +89,7 @@ async def acreate_batch(
|
|||
|
||||
def create_batch(
|
||||
completion_window: Literal["24h"],
|
||||
endpoint: Literal["/v1/chat/completions", "/v1/embeddings"],
|
||||
endpoint: Literal["/v1/chat/completions", "/v1/embeddings", "/v1/completions"],
|
||||
input_file_id: str,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
metadata: Optional[Dict[str, str]] = None,
|
||||
|
@ -448,7 +189,7 @@ async def aretrieve_batch(
|
|||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> Coroutine[Any, Any, Batch]:
|
||||
) -> Batch:
|
||||
"""
|
||||
Async: Retrieves a batch.
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ from openai._models import BaseModel as OpenAIObject
|
|||
|
||||
import litellm
|
||||
from litellm._logging import verbose_logger
|
||||
from litellm.litellm_core_utils.core_helpers import _get_parent_otel_span_from_kwargs
|
||||
from litellm.types.services import ServiceLoggerPayload, ServiceTypes
|
||||
|
||||
|
||||
|
@ -33,16 +34,6 @@ def print_verbose(print_statement):
|
|||
pass
|
||||
|
||||
|
||||
def _get_parent_otel_span_from_kwargs(kwargs: Optional[dict] = None):
|
||||
try:
|
||||
if kwargs is None:
|
||||
return None
|
||||
_metadata = kwargs.get("metadata") or {}
|
||||
return _metadata.get("litellm_parent_otel_span")
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
class BaseCache:
|
||||
def set_cache(self, key, value, **kwargs):
|
||||
raise NotImplementedError
|
||||
|
@ -97,8 +88,15 @@ class InMemoryCache(BaseCache):
|
|||
"""
|
||||
for key in list(self.ttl_dict.keys()):
|
||||
if time.time() > self.ttl_dict[key]:
|
||||
self.cache_dict.pop(key, None)
|
||||
self.ttl_dict.pop(key, None)
|
||||
removed_item = self.cache_dict.pop(key, None)
|
||||
removed_ttl_item = self.ttl_dict.pop(key, None)
|
||||
|
||||
# de-reference the removed item
|
||||
# https://www.geeksforgeeks.org/diagnosing-and-fixing-memory-leaks-in-python/
|
||||
# One of the most common causes of memory leaks in Python is the retention of objects that are no longer being used.
|
||||
# This can occur when an object is referenced by another object, but the reference is never removed.
|
||||
removed_item = None
|
||||
removed_ttl_item = None
|
||||
|
||||
def set_cache(self, key, value, **kwargs):
|
||||
print_verbose(
|
||||
|
@ -1661,6 +1659,9 @@ class DualCache(BaseCache):
|
|||
self.redis_cache.flush_cache()
|
||||
|
||||
def delete_cache(self, key):
|
||||
"""
|
||||
Delete a key from the cache
|
||||
"""
|
||||
if self.in_memory_cache is not None:
|
||||
self.in_memory_cache.delete_cache(key)
|
||||
if self.redis_cache is not None:
|
||||
|
|
|
@ -15,10 +15,12 @@ from litellm.litellm_core_utils.llm_cost_calc.google import (
|
|||
from litellm.litellm_core_utils.llm_cost_calc.google import (
|
||||
cost_per_token as google_cost_per_token,
|
||||
)
|
||||
from litellm.litellm_core_utils.llm_cost_calc.google import (
|
||||
cost_router as google_cost_router,
|
||||
)
|
||||
from litellm.litellm_core_utils.llm_cost_calc.utils import _generic_cost_per_character
|
||||
from litellm.types.llms.openai import HttpxBinaryResponseContent
|
||||
from litellm.types.router import SPECIAL_MODEL_INFO_PARAMS
|
||||
|
||||
from litellm.utils import (
|
||||
CallTypes,
|
||||
CostPerToken,
|
||||
|
@ -160,14 +162,17 @@ def cost_per_token(
|
|||
|
||||
# see this https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models
|
||||
print_verbose(f"Looking up model={model} in model_cost_map")
|
||||
if custom_llm_provider == "vertex_ai" and "claude" in model:
|
||||
return google_cost_per_token(
|
||||
if custom_llm_provider == "vertex_ai":
|
||||
cost_router = google_cost_router(
|
||||
model=model_without_prefix,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
prompt_characters=prompt_characters,
|
||||
completion_characters=completion_characters,
|
||||
prompt_tokens=prompt_tokens,
|
||||
completion_tokens=completion_tokens,
|
||||
call_type=call_type,
|
||||
)
|
||||
if custom_llm_provider == "vertex_ai":
|
||||
if cost_router == "cost_per_character":
|
||||
return google_cost_per_character(
|
||||
model=model_without_prefix,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
|
@ -176,6 +181,13 @@ def cost_per_token(
|
|||
prompt_tokens=prompt_tokens,
|
||||
completion_tokens=completion_tokens,
|
||||
)
|
||||
elif cost_router == "cost_per_token":
|
||||
return google_cost_per_token(
|
||||
model=model_without_prefix,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
prompt_tokens=prompt_tokens,
|
||||
completion_tokens=completion_tokens,
|
||||
)
|
||||
elif custom_llm_provider == "gemini":
|
||||
return google_cost_per_token(
|
||||
model=model_without_prefix,
|
||||
|
@ -725,8 +737,8 @@ def response_cost_calculator(
|
|||
)
|
||||
return None
|
||||
except Exception as e:
|
||||
verbose_logger.error(
|
||||
"litellm.cost_calculator.py::response_cost_calculator - Exception occurred - {}/n{}".format(
|
||||
verbose_logger.warning(
|
||||
"litellm.cost_calculator.py::response_cost_calculator - Returning None. Exception occurred - {}/n{}".format(
|
||||
str(e), traceback.format_exc()
|
||||
)
|
||||
)
|
||||
|
|
|
@ -682,11 +682,39 @@ class JSONSchemaValidationError(APIError):
|
|||
)
|
||||
|
||||
|
||||
class UnsupportedParamsError(BadRequestError):
|
||||
def __init__(
|
||||
self,
|
||||
message,
|
||||
llm_provider: Optional[str] = None,
|
||||
model: Optional[str] = None,
|
||||
status_code: int = 400,
|
||||
response: Optional[httpx.Response] = None,
|
||||
litellm_debug_info: Optional[str] = None,
|
||||
max_retries: Optional[int] = None,
|
||||
num_retries: Optional[int] = None,
|
||||
):
|
||||
self.status_code = 400
|
||||
self.message = "litellm.UnsupportedParamsError: {}".format(message)
|
||||
self.model = model
|
||||
self.llm_provider = llm_provider
|
||||
self.litellm_debug_info = litellm_debug_info
|
||||
response = response or httpx.Response(
|
||||
status_code=self.status_code,
|
||||
request=httpx.Request(
|
||||
method="GET", url="https://litellm.ai"
|
||||
), # mock request object
|
||||
)
|
||||
self.max_retries = max_retries
|
||||
self.num_retries = num_retries
|
||||
|
||||
|
||||
LITELLM_EXCEPTION_TYPES = [
|
||||
AuthenticationError,
|
||||
NotFoundError,
|
||||
BadRequestError,
|
||||
UnprocessableEntityError,
|
||||
UnsupportedParamsError,
|
||||
Timeout,
|
||||
PermissionDeniedError,
|
||||
RateLimitError,
|
||||
|
@ -723,3 +751,28 @@ class InvalidRequestError(openai.BadRequestError): # type: ignore
|
|||
super().__init__(
|
||||
self.message, f"{self.model}"
|
||||
) # Call the base class constructor with the parameters it needs
|
||||
|
||||
|
||||
class MockException(openai.APIError):
|
||||
# used for testing
|
||||
def __init__(
|
||||
self,
|
||||
status_code,
|
||||
message,
|
||||
llm_provider,
|
||||
model,
|
||||
request: Optional[httpx.Request] = None,
|
||||
litellm_debug_info: Optional[str] = None,
|
||||
max_retries: Optional[int] = None,
|
||||
num_retries: Optional[int] = None,
|
||||
):
|
||||
self.status_code = status_code
|
||||
self.message = "litellm.MockException: {}".format(message)
|
||||
self.llm_provider = llm_provider
|
||||
self.model = model
|
||||
self.litellm_debug_info = litellm_debug_info
|
||||
self.max_retries = max_retries
|
||||
self.num_retries = num_retries
|
||||
if request is None:
|
||||
request = httpx.Request(method="POST", url="https://api.openai.com/v1")
|
||||
super().__init__(self.message, request=request, body=None) # type: ignore
|
||||
|
|
659
litellm/files/main.py
Normal file
659
litellm/files/main.py
Normal file
|
@ -0,0 +1,659 @@
|
|||
"""
|
||||
Main File for Files API implementation
|
||||
|
||||
https://platform.openai.com/docs/api-reference/files
|
||||
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import contextvars
|
||||
import os
|
||||
from functools import partial
|
||||
from typing import Any, Coroutine, Dict, Literal, Optional, Union
|
||||
|
||||
import httpx
|
||||
|
||||
import litellm
|
||||
from litellm import client
|
||||
from litellm.llms.openai import FileDeleted, FileObject, OpenAIFilesAPI
|
||||
from litellm.types.llms.openai import (
|
||||
Batch,
|
||||
CreateFileRequest,
|
||||
FileContentRequest,
|
||||
FileTypes,
|
||||
HttpxBinaryResponseContent,
|
||||
)
|
||||
from litellm.types.router import *
|
||||
from litellm.utils import supports_httpx_timeout
|
||||
|
||||
####### ENVIRONMENT VARIABLES ###################
|
||||
openai_files_instance = OpenAIFilesAPI()
|
||||
#################################################
|
||||
|
||||
|
||||
async def afile_retrieve(
|
||||
file_id: str,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> Coroutine[Any, Any, FileObject]:
|
||||
"""
|
||||
Async: Get file contents
|
||||
|
||||
LiteLLM Equivalent of GET https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
kwargs["is_async"] = True
|
||||
|
||||
# Use a partial function to pass your keyword arguments
|
||||
func = partial(
|
||||
file_retrieve,
|
||||
file_id,
|
||||
custom_llm_provider,
|
||||
extra_headers,
|
||||
extra_body,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Add the context to the function
|
||||
ctx = contextvars.copy_context()
|
||||
func_with_context = partial(ctx.run, func)
|
||||
init_response = await loop.run_in_executor(None, func_with_context)
|
||||
if asyncio.iscoroutine(init_response):
|
||||
response = await init_response
|
||||
else:
|
||||
response = init_response # type: ignore
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
def file_retrieve(
|
||||
file_id: str,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> FileObject:
|
||||
"""
|
||||
Returns the contents of the specified file.
|
||||
|
||||
LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
optional_params = GenericLiteLLMParams(**kwargs)
|
||||
if custom_llm_provider == "openai":
|
||||
# for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there
|
||||
api_base = (
|
||||
optional_params.api_base
|
||||
or litellm.api_base
|
||||
or os.getenv("OPENAI_API_BASE")
|
||||
or "https://api.openai.com/v1"
|
||||
)
|
||||
organization = (
|
||||
optional_params.organization
|
||||
or litellm.organization
|
||||
or os.getenv("OPENAI_ORGANIZATION", None)
|
||||
or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105
|
||||
)
|
||||
# set API KEY
|
||||
api_key = (
|
||||
optional_params.api_key
|
||||
or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there
|
||||
or litellm.openai_key
|
||||
or os.getenv("OPENAI_API_KEY")
|
||||
)
|
||||
### TIMEOUT LOGIC ###
|
||||
timeout = (
|
||||
optional_params.timeout or kwargs.get("request_timeout", 600) or 600
|
||||
)
|
||||
# set timeout for 10 minutes by default
|
||||
|
||||
if (
|
||||
timeout is not None
|
||||
and isinstance(timeout, httpx.Timeout)
|
||||
and supports_httpx_timeout(custom_llm_provider) == False
|
||||
):
|
||||
read_timeout = timeout.read or 600
|
||||
timeout = read_timeout # default 10 min timeout
|
||||
elif timeout is not None and not isinstance(timeout, httpx.Timeout):
|
||||
timeout = float(timeout) # type: ignore
|
||||
elif timeout is None:
|
||||
timeout = 600.0
|
||||
|
||||
_is_async = kwargs.pop("is_async", False) is True
|
||||
|
||||
response = openai_files_instance.retrieve_file(
|
||||
file_id=file_id,
|
||||
_is_async=_is_async,
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
organization=organization,
|
||||
)
|
||||
else:
|
||||
raise litellm.exceptions.BadRequestError(
|
||||
message="LiteLLM doesn't support {} for 'create_batch'. Only 'openai' is supported.".format(
|
||||
custom_llm_provider
|
||||
),
|
||||
model="n/a",
|
||||
llm_provider=custom_llm_provider,
|
||||
response=httpx.Response(
|
||||
status_code=400,
|
||||
content="Unsupported provider",
|
||||
request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"), # type: ignore
|
||||
),
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
# Delete file
|
||||
async def afile_delete(
|
||||
file_id: str,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> Coroutine[Any, Any, FileObject]:
|
||||
"""
|
||||
Async: Delete file
|
||||
|
||||
LiteLLM Equivalent of DELETE https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
kwargs["is_async"] = True
|
||||
|
||||
# Use a partial function to pass your keyword arguments
|
||||
func = partial(
|
||||
file_delete,
|
||||
file_id,
|
||||
custom_llm_provider,
|
||||
extra_headers,
|
||||
extra_body,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Add the context to the function
|
||||
ctx = contextvars.copy_context()
|
||||
func_with_context = partial(ctx.run, func)
|
||||
init_response = await loop.run_in_executor(None, func_with_context)
|
||||
if asyncio.iscoroutine(init_response):
|
||||
response = await init_response
|
||||
else:
|
||||
response = init_response # type: ignore
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
def file_delete(
|
||||
file_id: str,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> FileDeleted:
|
||||
"""
|
||||
Delete file
|
||||
|
||||
LiteLLM Equivalent of DELETE https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
optional_params = GenericLiteLLMParams(**kwargs)
|
||||
if custom_llm_provider == "openai":
|
||||
# for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there
|
||||
api_base = (
|
||||
optional_params.api_base
|
||||
or litellm.api_base
|
||||
or os.getenv("OPENAI_API_BASE")
|
||||
or "https://api.openai.com/v1"
|
||||
)
|
||||
organization = (
|
||||
optional_params.organization
|
||||
or litellm.organization
|
||||
or os.getenv("OPENAI_ORGANIZATION", None)
|
||||
or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105
|
||||
)
|
||||
# set API KEY
|
||||
api_key = (
|
||||
optional_params.api_key
|
||||
or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there
|
||||
or litellm.openai_key
|
||||
or os.getenv("OPENAI_API_KEY")
|
||||
)
|
||||
### TIMEOUT LOGIC ###
|
||||
timeout = (
|
||||
optional_params.timeout or kwargs.get("request_timeout", 600) or 600
|
||||
)
|
||||
# set timeout for 10 minutes by default
|
||||
|
||||
if (
|
||||
timeout is not None
|
||||
and isinstance(timeout, httpx.Timeout)
|
||||
and supports_httpx_timeout(custom_llm_provider) == False
|
||||
):
|
||||
read_timeout = timeout.read or 600
|
||||
timeout = read_timeout # default 10 min timeout
|
||||
elif timeout is not None and not isinstance(timeout, httpx.Timeout):
|
||||
timeout = float(timeout) # type: ignore
|
||||
elif timeout is None:
|
||||
timeout = 600.0
|
||||
|
||||
_is_async = kwargs.pop("is_async", False) is True
|
||||
|
||||
response = openai_files_instance.delete_file(
|
||||
file_id=file_id,
|
||||
_is_async=_is_async,
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
organization=organization,
|
||||
)
|
||||
else:
|
||||
raise litellm.exceptions.BadRequestError(
|
||||
message="LiteLLM doesn't support {} for 'create_batch'. Only 'openai' is supported.".format(
|
||||
custom_llm_provider
|
||||
),
|
||||
model="n/a",
|
||||
llm_provider=custom_llm_provider,
|
||||
response=httpx.Response(
|
||||
status_code=400,
|
||||
content="Unsupported provider",
|
||||
request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"), # type: ignore
|
||||
),
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
# List files
|
||||
async def afile_list(
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
purpose: Optional[str] = None,
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Async: List files
|
||||
|
||||
LiteLLM Equivalent of GET https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
kwargs["is_async"] = True
|
||||
|
||||
# Use a partial function to pass your keyword arguments
|
||||
func = partial(
|
||||
file_list,
|
||||
custom_llm_provider,
|
||||
purpose,
|
||||
extra_headers,
|
||||
extra_body,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Add the context to the function
|
||||
ctx = contextvars.copy_context()
|
||||
func_with_context = partial(ctx.run, func)
|
||||
init_response = await loop.run_in_executor(None, func_with_context)
|
||||
if asyncio.iscoroutine(init_response):
|
||||
response = await init_response
|
||||
else:
|
||||
response = init_response # type: ignore
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
def file_list(
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
purpose: Optional[str] = None,
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
List files
|
||||
|
||||
LiteLLM Equivalent of GET https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
optional_params = GenericLiteLLMParams(**kwargs)
|
||||
if custom_llm_provider == "openai":
|
||||
# for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there
|
||||
api_base = (
|
||||
optional_params.api_base
|
||||
or litellm.api_base
|
||||
or os.getenv("OPENAI_API_BASE")
|
||||
or "https://api.openai.com/v1"
|
||||
)
|
||||
organization = (
|
||||
optional_params.organization
|
||||
or litellm.organization
|
||||
or os.getenv("OPENAI_ORGANIZATION", None)
|
||||
or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105
|
||||
)
|
||||
# set API KEY
|
||||
api_key = (
|
||||
optional_params.api_key
|
||||
or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there
|
||||
or litellm.openai_key
|
||||
or os.getenv("OPENAI_API_KEY")
|
||||
)
|
||||
### TIMEOUT LOGIC ###
|
||||
timeout = (
|
||||
optional_params.timeout or kwargs.get("request_timeout", 600) or 600
|
||||
)
|
||||
# set timeout for 10 minutes by default
|
||||
|
||||
if (
|
||||
timeout is not None
|
||||
and isinstance(timeout, httpx.Timeout)
|
||||
and supports_httpx_timeout(custom_llm_provider) == False
|
||||
):
|
||||
read_timeout = timeout.read or 600
|
||||
timeout = read_timeout # default 10 min timeout
|
||||
elif timeout is not None and not isinstance(timeout, httpx.Timeout):
|
||||
timeout = float(timeout) # type: ignore
|
||||
elif timeout is None:
|
||||
timeout = 600.0
|
||||
|
||||
_is_async = kwargs.pop("is_async", False) is True
|
||||
|
||||
response = openai_files_instance.list_files(
|
||||
purpose=purpose,
|
||||
_is_async=_is_async,
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
organization=organization,
|
||||
)
|
||||
else:
|
||||
raise litellm.exceptions.BadRequestError(
|
||||
message="LiteLLM doesn't support {} for 'file_list'. Only 'openai' is supported.".format(
|
||||
custom_llm_provider
|
||||
),
|
||||
model="n/a",
|
||||
llm_provider=custom_llm_provider,
|
||||
response=httpx.Response(
|
||||
status_code=400,
|
||||
content="Unsupported provider",
|
||||
request=httpx.Request(method="file_list", url="https://github.com/BerriAI/litellm"), # type: ignore
|
||||
),
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
async def acreate_file(
|
||||
file: FileTypes,
|
||||
purpose: Literal["assistants", "batch", "fine-tune"],
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> FileObject:
|
||||
"""
|
||||
Async: Files are used to upload documents that can be used with features like Assistants, Fine-tuning, and Batch API.
|
||||
|
||||
LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
kwargs["acreate_file"] = True
|
||||
|
||||
# Use a partial function to pass your keyword arguments
|
||||
func = partial(
|
||||
create_file,
|
||||
file,
|
||||
purpose,
|
||||
custom_llm_provider,
|
||||
extra_headers,
|
||||
extra_body,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Add the context to the function
|
||||
ctx = contextvars.copy_context()
|
||||
func_with_context = partial(ctx.run, func)
|
||||
init_response = await loop.run_in_executor(None, func_with_context)
|
||||
if asyncio.iscoroutine(init_response):
|
||||
response = await init_response
|
||||
else:
|
||||
response = init_response # type: ignore
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
def create_file(
|
||||
file: FileTypes,
|
||||
purpose: Literal["assistants", "batch", "fine-tune"],
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> Union[FileObject, Coroutine[Any, Any, FileObject]]:
|
||||
"""
|
||||
Files are used to upload documents that can be used with features like Assistants, Fine-tuning, and Batch API.
|
||||
|
||||
LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
optional_params = GenericLiteLLMParams(**kwargs)
|
||||
if custom_llm_provider == "openai":
|
||||
# for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there
|
||||
api_base = (
|
||||
optional_params.api_base
|
||||
or litellm.api_base
|
||||
or os.getenv("OPENAI_API_BASE")
|
||||
or "https://api.openai.com/v1"
|
||||
)
|
||||
organization = (
|
||||
optional_params.organization
|
||||
or litellm.organization
|
||||
or os.getenv("OPENAI_ORGANIZATION", None)
|
||||
or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105
|
||||
)
|
||||
# set API KEY
|
||||
api_key = (
|
||||
optional_params.api_key
|
||||
or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there
|
||||
or litellm.openai_key
|
||||
or os.getenv("OPENAI_API_KEY")
|
||||
)
|
||||
### TIMEOUT LOGIC ###
|
||||
timeout = (
|
||||
optional_params.timeout or kwargs.get("request_timeout", 600) or 600
|
||||
)
|
||||
# set timeout for 10 minutes by default
|
||||
|
||||
if (
|
||||
timeout is not None
|
||||
and isinstance(timeout, httpx.Timeout)
|
||||
and supports_httpx_timeout(custom_llm_provider) == False
|
||||
):
|
||||
read_timeout = timeout.read or 600
|
||||
timeout = read_timeout # default 10 min timeout
|
||||
elif timeout is not None and not isinstance(timeout, httpx.Timeout):
|
||||
timeout = float(timeout) # type: ignore
|
||||
elif timeout is None:
|
||||
timeout = 600.0
|
||||
|
||||
_create_file_request = CreateFileRequest(
|
||||
file=file,
|
||||
purpose=purpose,
|
||||
extra_headers=extra_headers,
|
||||
extra_body=extra_body,
|
||||
)
|
||||
|
||||
_is_async = kwargs.pop("acreate_file", False) is True
|
||||
|
||||
response = openai_files_instance.create_file(
|
||||
_is_async=_is_async,
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
organization=organization,
|
||||
create_file_data=_create_file_request,
|
||||
)
|
||||
else:
|
||||
raise litellm.exceptions.BadRequestError(
|
||||
message="LiteLLM doesn't support {} for 'create_batch'. Only 'openai' is supported.".format(
|
||||
custom_llm_provider
|
||||
),
|
||||
model="n/a",
|
||||
llm_provider=custom_llm_provider,
|
||||
response=httpx.Response(
|
||||
status_code=400,
|
||||
content="Unsupported provider",
|
||||
request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"), # type: ignore
|
||||
),
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
async def afile_content(
|
||||
file_id: str,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> HttpxBinaryResponseContent:
|
||||
"""
|
||||
Async: Get file contents
|
||||
|
||||
LiteLLM Equivalent of GET https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
kwargs["afile_content"] = True
|
||||
|
||||
# Use a partial function to pass your keyword arguments
|
||||
func = partial(
|
||||
file_content,
|
||||
file_id,
|
||||
custom_llm_provider,
|
||||
extra_headers,
|
||||
extra_body,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Add the context to the function
|
||||
ctx = contextvars.copy_context()
|
||||
func_with_context = partial(ctx.run, func)
|
||||
init_response = await loop.run_in_executor(None, func_with_context)
|
||||
if asyncio.iscoroutine(init_response):
|
||||
response = await init_response
|
||||
else:
|
||||
response = init_response # type: ignore
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
def file_content(
|
||||
file_id: str,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> Union[HttpxBinaryResponseContent, Coroutine[Any, Any, HttpxBinaryResponseContent]]:
|
||||
"""
|
||||
Returns the contents of the specified file.
|
||||
|
||||
LiteLLM Equivalent of POST: POST https://api.openai.com/v1/files
|
||||
"""
|
||||
try:
|
||||
optional_params = GenericLiteLLMParams(**kwargs)
|
||||
if custom_llm_provider == "openai":
|
||||
# for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there
|
||||
api_base = (
|
||||
optional_params.api_base
|
||||
or litellm.api_base
|
||||
or os.getenv("OPENAI_API_BASE")
|
||||
or "https://api.openai.com/v1"
|
||||
)
|
||||
organization = (
|
||||
optional_params.organization
|
||||
or litellm.organization
|
||||
or os.getenv("OPENAI_ORGANIZATION", None)
|
||||
or None # default - https://github.com/openai/openai-python/blob/284c1799070c723c6a553337134148a7ab088dd8/openai/util.py#L105
|
||||
)
|
||||
# set API KEY
|
||||
api_key = (
|
||||
optional_params.api_key
|
||||
or litellm.api_key # for deepinfra/perplexity/anyscale we check in get_llm_provider and pass in the api key from there
|
||||
or litellm.openai_key
|
||||
or os.getenv("OPENAI_API_KEY")
|
||||
)
|
||||
### TIMEOUT LOGIC ###
|
||||
timeout = (
|
||||
optional_params.timeout or kwargs.get("request_timeout", 600) or 600
|
||||
)
|
||||
# set timeout for 10 minutes by default
|
||||
|
||||
if (
|
||||
timeout is not None
|
||||
and isinstance(timeout, httpx.Timeout)
|
||||
and supports_httpx_timeout(custom_llm_provider) == False
|
||||
):
|
||||
read_timeout = timeout.read or 600
|
||||
timeout = read_timeout # default 10 min timeout
|
||||
elif timeout is not None and not isinstance(timeout, httpx.Timeout):
|
||||
timeout = float(timeout) # type: ignore
|
||||
elif timeout is None:
|
||||
timeout = 600.0
|
||||
|
||||
_file_content_request = FileContentRequest(
|
||||
file_id=file_id,
|
||||
extra_headers=extra_headers,
|
||||
extra_body=extra_body,
|
||||
)
|
||||
|
||||
_is_async = kwargs.pop("afile_content", False) is True
|
||||
|
||||
response = openai_files_instance.file_content(
|
||||
_is_async=_is_async,
|
||||
file_content_request=_file_content_request,
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
organization=organization,
|
||||
)
|
||||
else:
|
||||
raise litellm.exceptions.BadRequestError(
|
||||
message="LiteLLM doesn't support {} for 'create_batch'. Only 'openai' is supported.".format(
|
||||
custom_llm_provider
|
||||
),
|
||||
model="n/a",
|
||||
llm_provider=custom_llm_provider,
|
||||
response=httpx.Response(
|
||||
status_code=400,
|
||||
content="Unsupported provider",
|
||||
request=httpx.Request(method="create_thread", url="https://github.com/BerriAI/litellm"), # type: ignore
|
||||
),
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
286
litellm/integrations/_types/open_inference.py
Normal file
286
litellm/integrations/_types/open_inference.py
Normal file
|
@ -0,0 +1,286 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class SpanAttributes:
|
||||
OUTPUT_VALUE = "output.value"
|
||||
OUTPUT_MIME_TYPE = "output.mime_type"
|
||||
"""
|
||||
The type of output.value. If unspecified, the type is plain text by default.
|
||||
If type is JSON, the value is a string representing a JSON object.
|
||||
"""
|
||||
INPUT_VALUE = "input.value"
|
||||
INPUT_MIME_TYPE = "input.mime_type"
|
||||
"""
|
||||
The type of input.value. If unspecified, the type is plain text by default.
|
||||
If type is JSON, the value is a string representing a JSON object.
|
||||
"""
|
||||
|
||||
EMBEDDING_EMBEDDINGS = "embedding.embeddings"
|
||||
"""
|
||||
A list of objects containing embedding data, including the vector and represented piece of text.
|
||||
"""
|
||||
EMBEDDING_MODEL_NAME = "embedding.model_name"
|
||||
"""
|
||||
The name of the embedding model.
|
||||
"""
|
||||
|
||||
LLM_FUNCTION_CALL = "llm.function_call"
|
||||
"""
|
||||
For models and APIs that support function calling. Records attributes such as the function
|
||||
name and arguments to the called function.
|
||||
"""
|
||||
LLM_INVOCATION_PARAMETERS = "llm.invocation_parameters"
|
||||
"""
|
||||
Invocation parameters passed to the LLM or API, such as the model name, temperature, etc.
|
||||
"""
|
||||
LLM_INPUT_MESSAGES = "llm.input_messages"
|
||||
"""
|
||||
Messages provided to a chat API.
|
||||
"""
|
||||
LLM_OUTPUT_MESSAGES = "llm.output_messages"
|
||||
"""
|
||||
Messages received from a chat API.
|
||||
"""
|
||||
LLM_MODEL_NAME = "llm.model_name"
|
||||
"""
|
||||
The name of the model being used.
|
||||
"""
|
||||
LLM_PROMPTS = "llm.prompts"
|
||||
"""
|
||||
Prompts provided to a completions API.
|
||||
"""
|
||||
LLM_PROMPT_TEMPLATE = "llm.prompt_template.template"
|
||||
"""
|
||||
The prompt template as a Python f-string.
|
||||
"""
|
||||
LLM_PROMPT_TEMPLATE_VARIABLES = "llm.prompt_template.variables"
|
||||
"""
|
||||
A list of input variables to the prompt template.
|
||||
"""
|
||||
LLM_PROMPT_TEMPLATE_VERSION = "llm.prompt_template.version"
|
||||
"""
|
||||
The version of the prompt template being used.
|
||||
"""
|
||||
LLM_TOKEN_COUNT_PROMPT = "llm.token_count.prompt"
|
||||
"""
|
||||
Number of tokens in the prompt.
|
||||
"""
|
||||
LLM_TOKEN_COUNT_COMPLETION = "llm.token_count.completion"
|
||||
"""
|
||||
Number of tokens in the completion.
|
||||
"""
|
||||
LLM_TOKEN_COUNT_TOTAL = "llm.token_count.total"
|
||||
"""
|
||||
Total number of tokens, including both prompt and completion.
|
||||
"""
|
||||
|
||||
TOOL_NAME = "tool.name"
|
||||
"""
|
||||
Name of the tool being used.
|
||||
"""
|
||||
TOOL_DESCRIPTION = "tool.description"
|
||||
"""
|
||||
Description of the tool's purpose, typically used to select the tool.
|
||||
"""
|
||||
TOOL_PARAMETERS = "tool.parameters"
|
||||
"""
|
||||
Parameters of the tool represented a dictionary JSON string, e.g.
|
||||
see https://platform.openai.com/docs/guides/gpt/function-calling
|
||||
"""
|
||||
|
||||
RETRIEVAL_DOCUMENTS = "retrieval.documents"
|
||||
|
||||
METADATA = "metadata"
|
||||
"""
|
||||
Metadata attributes are used to store user-defined key-value pairs.
|
||||
For example, LangChain uses metadata to store user-defined attributes for a chain.
|
||||
"""
|
||||
|
||||
TAG_TAGS = "tag.tags"
|
||||
"""
|
||||
Custom categorical tags for the span.
|
||||
"""
|
||||
|
||||
OPENINFERENCE_SPAN_KIND = "openinference.span.kind"
|
||||
|
||||
SESSION_ID = "session.id"
|
||||
"""
|
||||
The id of the session
|
||||
"""
|
||||
USER_ID = "user.id"
|
||||
"""
|
||||
The id of the user
|
||||
"""
|
||||
|
||||
|
||||
class MessageAttributes:
|
||||
"""
|
||||
Attributes for a message sent to or from an LLM
|
||||
"""
|
||||
|
||||
MESSAGE_ROLE = "message.role"
|
||||
"""
|
||||
The role of the message, such as "user", "agent", "function".
|
||||
"""
|
||||
MESSAGE_CONTENT = "message.content"
|
||||
"""
|
||||
The content of the message to or from the llm, must be a string.
|
||||
"""
|
||||
MESSAGE_CONTENTS = "message.contents"
|
||||
"""
|
||||
The message contents to the llm, it is an array of
|
||||
`message_content` prefixed attributes.
|
||||
"""
|
||||
MESSAGE_NAME = "message.name"
|
||||
"""
|
||||
The name of the message, often used to identify the function
|
||||
that was used to generate the message.
|
||||
"""
|
||||
MESSAGE_TOOL_CALLS = "message.tool_calls"
|
||||
"""
|
||||
The tool calls generated by the model, such as function calls.
|
||||
"""
|
||||
MESSAGE_FUNCTION_CALL_NAME = "message.function_call_name"
|
||||
"""
|
||||
The function name that is a part of the message list.
|
||||
This is populated for role 'function' or 'agent' as a mechanism to identify
|
||||
the function that was called during the execution of a tool.
|
||||
"""
|
||||
MESSAGE_FUNCTION_CALL_ARGUMENTS_JSON = "message.function_call_arguments_json"
|
||||
"""
|
||||
The JSON string representing the arguments passed to the function
|
||||
during a function call.
|
||||
"""
|
||||
|
||||
|
||||
class MessageContentAttributes:
|
||||
"""
|
||||
Attributes for the contents of user messages sent to an LLM.
|
||||
"""
|
||||
|
||||
MESSAGE_CONTENT_TYPE = "message_content.type"
|
||||
"""
|
||||
The type of the content, such as "text" or "image".
|
||||
"""
|
||||
MESSAGE_CONTENT_TEXT = "message_content.text"
|
||||
"""
|
||||
The text content of the message, if the type is "text".
|
||||
"""
|
||||
MESSAGE_CONTENT_IMAGE = "message_content.image"
|
||||
"""
|
||||
The image content of the message, if the type is "image".
|
||||
An image can be made available to the model by passing a link to
|
||||
the image or by passing the base64 encoded image directly in the
|
||||
request.
|
||||
"""
|
||||
|
||||
|
||||
class ImageAttributes:
|
||||
"""
|
||||
Attributes for images
|
||||
"""
|
||||
|
||||
IMAGE_URL = "image.url"
|
||||
"""
|
||||
An http or base64 image url
|
||||
"""
|
||||
|
||||
|
||||
class DocumentAttributes:
|
||||
"""
|
||||
Attributes for a document.
|
||||
"""
|
||||
|
||||
DOCUMENT_ID = "document.id"
|
||||
"""
|
||||
The id of the document.
|
||||
"""
|
||||
DOCUMENT_SCORE = "document.score"
|
||||
"""
|
||||
The score of the document
|
||||
"""
|
||||
DOCUMENT_CONTENT = "document.content"
|
||||
"""
|
||||
The content of the document.
|
||||
"""
|
||||
DOCUMENT_METADATA = "document.metadata"
|
||||
"""
|
||||
The metadata of the document represented as a dictionary
|
||||
JSON string, e.g. `"{ 'title': 'foo' }"`
|
||||
"""
|
||||
|
||||
|
||||
class RerankerAttributes:
|
||||
"""
|
||||
Attributes for a reranker
|
||||
"""
|
||||
|
||||
RERANKER_INPUT_DOCUMENTS = "reranker.input_documents"
|
||||
"""
|
||||
List of documents as input to the reranker
|
||||
"""
|
||||
RERANKER_OUTPUT_DOCUMENTS = "reranker.output_documents"
|
||||
"""
|
||||
List of documents as output from the reranker
|
||||
"""
|
||||
RERANKER_QUERY = "reranker.query"
|
||||
"""
|
||||
Query string for the reranker
|
||||
"""
|
||||
RERANKER_MODEL_NAME = "reranker.model_name"
|
||||
"""
|
||||
Model name of the reranker
|
||||
"""
|
||||
RERANKER_TOP_K = "reranker.top_k"
|
||||
"""
|
||||
Top K parameter of the reranker
|
||||
"""
|
||||
|
||||
|
||||
class EmbeddingAttributes:
|
||||
"""
|
||||
Attributes for an embedding
|
||||
"""
|
||||
|
||||
EMBEDDING_TEXT = "embedding.text"
|
||||
"""
|
||||
The text represented by the embedding.
|
||||
"""
|
||||
EMBEDDING_VECTOR = "embedding.vector"
|
||||
"""
|
||||
The embedding vector.
|
||||
"""
|
||||
|
||||
|
||||
class ToolCallAttributes:
|
||||
"""
|
||||
Attributes for a tool call
|
||||
"""
|
||||
|
||||
TOOL_CALL_FUNCTION_NAME = "tool_call.function.name"
|
||||
"""
|
||||
The name of function that is being called during a tool call.
|
||||
"""
|
||||
TOOL_CALL_FUNCTION_ARGUMENTS_JSON = "tool_call.function.arguments"
|
||||
"""
|
||||
The JSON string representing the arguments passed to the function
|
||||
during a tool call.
|
||||
"""
|
||||
|
||||
|
||||
class OpenInferenceSpanKindValues(Enum):
|
||||
TOOL = "TOOL"
|
||||
CHAIN = "CHAIN"
|
||||
LLM = "LLM"
|
||||
RETRIEVER = "RETRIEVER"
|
||||
EMBEDDING = "EMBEDDING"
|
||||
AGENT = "AGENT"
|
||||
RERANKER = "RERANKER"
|
||||
UNKNOWN = "UNKNOWN"
|
||||
GUARDRAIL = "GUARDRAIL"
|
||||
EVALUATOR = "EVALUATOR"
|
||||
|
||||
|
||||
class OpenInferenceMimeTypeValues(Enum):
|
||||
TEXT = "text/plain"
|
||||
JSON = "application/json"
|
114
litellm/integrations/arize_ai.py
Normal file
114
litellm/integrations/arize_ai.py
Normal file
|
@ -0,0 +1,114 @@
|
|||
"""
|
||||
arize AI is OTEL compatible
|
||||
|
||||
this file has Arize ai specific helper functions
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Optional, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from opentelemetry.trace import Span as _Span
|
||||
|
||||
Span = _Span
|
||||
else:
|
||||
Span = Any
|
||||
|
||||
|
||||
def set_arize_ai_attributes(span: Span, kwargs, response_obj):
|
||||
from litellm.integrations._types.open_inference import (
|
||||
MessageAttributes,
|
||||
MessageContentAttributes,
|
||||
OpenInferenceSpanKindValues,
|
||||
SpanAttributes,
|
||||
)
|
||||
|
||||
optional_params = kwargs.get("optional_params", {})
|
||||
litellm_params = kwargs.get("litellm_params", {}) or {}
|
||||
|
||||
#############################################
|
||||
############ LLM CALL METADATA ##############
|
||||
#############################################
|
||||
# commented out for now - looks like Arize AI could not log this
|
||||
# metadata = litellm_params.get("metadata", {}) or {}
|
||||
# span.set_attribute(SpanAttributes.METADATA, str(metadata))
|
||||
|
||||
#############################################
|
||||
########## LLM Request Attributes ###########
|
||||
#############################################
|
||||
|
||||
# The name of the LLM a request is being made to
|
||||
if kwargs.get("model"):
|
||||
span.set_attribute(SpanAttributes.LLM_MODEL_NAME, kwargs.get("model"))
|
||||
|
||||
span.set_attribute(
|
||||
SpanAttributes.OPENINFERENCE_SPAN_KIND, OpenInferenceSpanKindValues.LLM.value
|
||||
)
|
||||
messages = kwargs.get("messages")
|
||||
|
||||
# for /chat/completions
|
||||
# https://docs.arize.com/arize/large-language-models/tracing/semantic-conventions
|
||||
if messages:
|
||||
span.set_attribute(
|
||||
SpanAttributes.INPUT_VALUE,
|
||||
messages[-1].get("content", ""), # get the last message for input
|
||||
)
|
||||
|
||||
# LLM_INPUT_MESSAGES shows up under `input_messages` tab on the span page
|
||||
for idx, msg in enumerate(messages):
|
||||
# Set the role per message
|
||||
span.set_attribute(
|
||||
f"{SpanAttributes.LLM_INPUT_MESSAGES}.{idx}.{MessageAttributes.MESSAGE_ROLE}",
|
||||
msg["role"],
|
||||
)
|
||||
# Set the content per message
|
||||
span.set_attribute(
|
||||
f"{SpanAttributes.LLM_INPUT_MESSAGES}.{idx}.{MessageAttributes.MESSAGE_CONTENT}",
|
||||
msg.get("content", ""),
|
||||
)
|
||||
|
||||
# The Generative AI Provider: Azure, OpenAI, etc.
|
||||
span.set_attribute(SpanAttributes.LLM_INVOCATION_PARAMETERS, str(optional_params))
|
||||
|
||||
if optional_params.get("user"):
|
||||
span.set_attribute(SpanAttributes.USER_ID, optional_params.get("user"))
|
||||
|
||||
#############################################
|
||||
########## LLM Response Attributes ##########
|
||||
# https://docs.arize.com/arize/large-language-models/tracing/semantic-conventions
|
||||
#############################################
|
||||
for choice in response_obj.get("choices"):
|
||||
response_message = choice.get("message", {})
|
||||
span.set_attribute(
|
||||
SpanAttributes.OUTPUT_VALUE, response_message.get("content", "")
|
||||
)
|
||||
|
||||
# This shows up under `output_messages` tab on the span page
|
||||
# This code assumes a single response
|
||||
span.set_attribute(
|
||||
f"{SpanAttributes.LLM_OUTPUT_MESSAGES}.0.{MessageAttributes.MESSAGE_ROLE}",
|
||||
response_message["role"],
|
||||
)
|
||||
span.set_attribute(
|
||||
f"{SpanAttributes.LLM_OUTPUT_MESSAGES}.0.{MessageAttributes.MESSAGE_CONTENT}",
|
||||
response_message.get("content", ""),
|
||||
)
|
||||
|
||||
usage = response_obj.get("usage")
|
||||
if usage:
|
||||
span.set_attribute(
|
||||
SpanAttributes.LLM_TOKEN_COUNT_TOTAL,
|
||||
usage.get("total_tokens"),
|
||||
)
|
||||
|
||||
# The number of tokens used in the LLM response (completion).
|
||||
span.set_attribute(
|
||||
SpanAttributes.LLM_TOKEN_COUNT_COMPLETION,
|
||||
usage.get("completion_tokens"),
|
||||
)
|
||||
|
||||
# The number of tokens used in the LLM prompt.
|
||||
span.set_attribute(
|
||||
SpanAttributes.LLM_TOKEN_COUNT_PROMPT,
|
||||
usage.get("prompt_tokens"),
|
||||
)
|
||||
pass
|
369
litellm/integrations/braintrust_logging.py
Normal file
369
litellm/integrations/braintrust_logging.py
Normal file
|
@ -0,0 +1,369 @@
|
|||
# What is this?
|
||||
## Log success + failure events to Braintrust
|
||||
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
import traceback
|
||||
import uuid
|
||||
from typing import Literal, Optional
|
||||
|
||||
import dotenv
|
||||
import httpx
|
||||
|
||||
import litellm
|
||||
from litellm import verbose_logger
|
||||
from litellm.integrations.custom_logger import CustomLogger
|
||||
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler
|
||||
from litellm.utils import get_formatted_prompt
|
||||
|
||||
global_braintrust_http_handler = AsyncHTTPHandler()
|
||||
global_braintrust_sync_http_handler = HTTPHandler()
|
||||
API_BASE = "https://api.braintrustdata.com/v1"
|
||||
|
||||
|
||||
def get_utc_datetime():
|
||||
import datetime as dt
|
||||
from datetime import datetime
|
||||
|
||||
if hasattr(dt, "UTC"):
|
||||
return datetime.now(dt.UTC) # type: ignore
|
||||
else:
|
||||
return datetime.utcnow() # type: ignore
|
||||
|
||||
|
||||
class BraintrustLogger(CustomLogger):
|
||||
def __init__(
|
||||
self, api_key: Optional[str] = None, api_base: Optional[str] = None
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.validate_environment(api_key=api_key)
|
||||
self.api_base = api_base or API_BASE
|
||||
self.default_project_id = None
|
||||
self.api_key: str = api_key or os.getenv("BRAINTRUST_API_KEY") # type: ignore
|
||||
self.headers = {
|
||||
"Authorization": "Bearer " + self.api_key,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
def validate_environment(self, api_key: Optional[str]):
|
||||
"""
|
||||
Expects
|
||||
BRAINTRUST_API_KEY
|
||||
|
||||
in the environment
|
||||
"""
|
||||
missing_keys = []
|
||||
if api_key is None and os.getenv("BRAINTRUST_API_KEY", None) is None:
|
||||
missing_keys.append("BRAINTRUST_API_KEY")
|
||||
|
||||
if len(missing_keys) > 0:
|
||||
raise Exception("Missing keys={} in environment.".format(missing_keys))
|
||||
|
||||
@staticmethod
|
||||
def add_metadata_from_header(litellm_params: dict, metadata: dict) -> dict:
|
||||
"""
|
||||
Adds metadata from proxy request headers to Langfuse logging if keys start with "langfuse_"
|
||||
and overwrites litellm_params.metadata if already included.
|
||||
|
||||
For example if you want to append your trace to an existing `trace_id` via header, send
|
||||
`headers: { ..., langfuse_existing_trace_id: your-existing-trace-id }` via proxy request.
|
||||
"""
|
||||
if litellm_params is None:
|
||||
return metadata
|
||||
|
||||
if litellm_params.get("proxy_server_request") is None:
|
||||
return metadata
|
||||
|
||||
if metadata is None:
|
||||
metadata = {}
|
||||
|
||||
proxy_headers = (
|
||||
litellm_params.get("proxy_server_request", {}).get("headers", {}) or {}
|
||||
)
|
||||
|
||||
for metadata_param_key in proxy_headers:
|
||||
if metadata_param_key.startswith("braintrust"):
|
||||
trace_param_key = metadata_param_key.replace("braintrust", "", 1)
|
||||
if trace_param_key in metadata:
|
||||
verbose_logger.warning(
|
||||
f"Overwriting Braintrust `{trace_param_key}` from request header"
|
||||
)
|
||||
else:
|
||||
verbose_logger.debug(
|
||||
f"Found Braintrust `{trace_param_key}` in request header"
|
||||
)
|
||||
metadata[trace_param_key] = proxy_headers.get(metadata_param_key)
|
||||
|
||||
return metadata
|
||||
|
||||
async def create_default_project_and_experiment(self):
|
||||
project = await global_braintrust_http_handler.post(
|
||||
f"{self.api_base}/project", headers=self.headers, json={"name": "litellm"}
|
||||
)
|
||||
|
||||
project_dict = project.json()
|
||||
|
||||
self.default_project_id = project_dict["id"]
|
||||
|
||||
def create_sync_default_project_and_experiment(self):
|
||||
project = global_braintrust_sync_http_handler.post(
|
||||
f"{self.api_base}/project", headers=self.headers, json={"name": "litellm"}
|
||||
)
|
||||
|
||||
project_dict = project.json()
|
||||
|
||||
self.default_project_id = project_dict["id"]
|
||||
|
||||
def log_success_event(self, kwargs, response_obj, start_time, end_time):
|
||||
verbose_logger.debug("REACHES BRAINTRUST SUCCESS")
|
||||
try:
|
||||
litellm_call_id = kwargs.get("litellm_call_id")
|
||||
project_id = kwargs.get("project_id", None)
|
||||
if project_id is None:
|
||||
if self.default_project_id is None:
|
||||
self.create_sync_default_project_and_experiment()
|
||||
project_id = self.default_project_id
|
||||
|
||||
prompt = {"messages": kwargs.get("messages")}
|
||||
|
||||
if response_obj is not None and (
|
||||
kwargs.get("call_type", None) == "embedding"
|
||||
or isinstance(response_obj, litellm.EmbeddingResponse)
|
||||
):
|
||||
input = prompt
|
||||
output = None
|
||||
elif response_obj is not None and isinstance(
|
||||
response_obj, litellm.ModelResponse
|
||||
):
|
||||
input = prompt
|
||||
output = response_obj["choices"][0]["message"].json()
|
||||
elif response_obj is not None and isinstance(
|
||||
response_obj, litellm.TextCompletionResponse
|
||||
):
|
||||
input = prompt
|
||||
output = response_obj.choices[0].text
|
||||
elif response_obj is not None and isinstance(
|
||||
response_obj, litellm.ImageResponse
|
||||
):
|
||||
input = prompt
|
||||
output = response_obj["data"]
|
||||
|
||||
litellm_params = kwargs.get("litellm_params", {})
|
||||
metadata = (
|
||||
litellm_params.get("metadata", {}) or {}
|
||||
) # if litellm_params['metadata'] == None
|
||||
metadata = self.add_metadata_from_header(litellm_params, metadata)
|
||||
clean_metadata = {}
|
||||
try:
|
||||
metadata = copy.deepcopy(
|
||||
metadata
|
||||
) # Avoid modifying the original metadata
|
||||
except:
|
||||
new_metadata = {}
|
||||
for key, value in metadata.items():
|
||||
if (
|
||||
isinstance(value, list)
|
||||
or isinstance(value, dict)
|
||||
or isinstance(value, str)
|
||||
or isinstance(value, int)
|
||||
or isinstance(value, float)
|
||||
):
|
||||
new_metadata[key] = copy.deepcopy(value)
|
||||
metadata = new_metadata
|
||||
|
||||
tags = []
|
||||
if isinstance(metadata, dict):
|
||||
for key, value in metadata.items():
|
||||
|
||||
# generate langfuse tags - Default Tags sent to Langfuse from LiteLLM Proxy
|
||||
if (
|
||||
litellm._langfuse_default_tags is not None
|
||||
and isinstance(litellm._langfuse_default_tags, list)
|
||||
and key in litellm._langfuse_default_tags
|
||||
):
|
||||
tags.append(f"{key}:{value}")
|
||||
|
||||
# clean litellm metadata before logging
|
||||
if key in [
|
||||
"headers",
|
||||
"endpoint",
|
||||
"caching_groups",
|
||||
"previous_models",
|
||||
]:
|
||||
continue
|
||||
else:
|
||||
clean_metadata[key] = value
|
||||
|
||||
cost = kwargs.get("response_cost", None)
|
||||
if cost is not None:
|
||||
clean_metadata["litellm_response_cost"] = cost
|
||||
|
||||
metrics: Optional[dict] = None
|
||||
if (
|
||||
response_obj is not None
|
||||
and hasattr(response_obj, "usage")
|
||||
and isinstance(response_obj.usage, litellm.Usage)
|
||||
):
|
||||
generation_id = litellm.utils.get_logging_id(start_time, response_obj)
|
||||
metrics = {
|
||||
"prompt_tokens": response_obj.usage.prompt_tokens,
|
||||
"completion_tokens": response_obj.usage.completion_tokens,
|
||||
"total_tokens": response_obj.usage.total_tokens,
|
||||
"total_cost": cost,
|
||||
}
|
||||
|
||||
request_data = {
|
||||
"id": litellm_call_id,
|
||||
"input": prompt,
|
||||
"output": output,
|
||||
"metadata": clean_metadata,
|
||||
"tags": tags,
|
||||
}
|
||||
if metrics is not None:
|
||||
request_data["metrics"] = metrics
|
||||
|
||||
try:
|
||||
global_braintrust_sync_http_handler.post(
|
||||
url=f"{self.api_base}/project_logs/{project_id}/insert",
|
||||
json={"events": [request_data]},
|
||||
headers=self.headers,
|
||||
)
|
||||
except httpx.HTTPStatusError as e:
|
||||
raise Exception(e.response.text)
|
||||
except Exception as e:
|
||||
verbose_logger.error(
|
||||
"Error logging to braintrust - Exception received - {}\n{}".format(
|
||||
str(e), traceback.format_exc()
|
||||
)
|
||||
)
|
||||
raise e
|
||||
|
||||
async def async_log_success_event(self, kwargs, response_obj, start_time, end_time):
|
||||
verbose_logger.debug("REACHES BRAINTRUST SUCCESS")
|
||||
try:
|
||||
litellm_call_id = kwargs.get("litellm_call_id")
|
||||
project_id = kwargs.get("project_id", None)
|
||||
if project_id is None:
|
||||
if self.default_project_id is None:
|
||||
await self.create_default_project_and_experiment()
|
||||
project_id = self.default_project_id
|
||||
|
||||
prompt = {"messages": kwargs.get("messages")}
|
||||
|
||||
if response_obj is not None and (
|
||||
kwargs.get("call_type", None) == "embedding"
|
||||
or isinstance(response_obj, litellm.EmbeddingResponse)
|
||||
):
|
||||
input = prompt
|
||||
output = None
|
||||
elif response_obj is not None and isinstance(
|
||||
response_obj, litellm.ModelResponse
|
||||
):
|
||||
input = prompt
|
||||
output = response_obj["choices"][0]["message"].json()
|
||||
elif response_obj is not None and isinstance(
|
||||
response_obj, litellm.TextCompletionResponse
|
||||
):
|
||||
input = prompt
|
||||
output = response_obj.choices[0].text
|
||||
elif response_obj is not None and isinstance(
|
||||
response_obj, litellm.ImageResponse
|
||||
):
|
||||
input = prompt
|
||||
output = response_obj["data"]
|
||||
|
||||
litellm_params = kwargs.get("litellm_params", {})
|
||||
metadata = (
|
||||
litellm_params.get("metadata", {}) or {}
|
||||
) # if litellm_params['metadata'] == None
|
||||
metadata = self.add_metadata_from_header(litellm_params, metadata)
|
||||
clean_metadata = {}
|
||||
try:
|
||||
metadata = copy.deepcopy(
|
||||
metadata
|
||||
) # Avoid modifying the original metadata
|
||||
except:
|
||||
new_metadata = {}
|
||||
for key, value in metadata.items():
|
||||
if (
|
||||
isinstance(value, list)
|
||||
or isinstance(value, dict)
|
||||
or isinstance(value, str)
|
||||
or isinstance(value, int)
|
||||
or isinstance(value, float)
|
||||
):
|
||||
new_metadata[key] = copy.deepcopy(value)
|
||||
metadata = new_metadata
|
||||
|
||||
tags = []
|
||||
if isinstance(metadata, dict):
|
||||
for key, value in metadata.items():
|
||||
|
||||
# generate langfuse tags - Default Tags sent to Langfuse from LiteLLM Proxy
|
||||
if (
|
||||
litellm._langfuse_default_tags is not None
|
||||
and isinstance(litellm._langfuse_default_tags, list)
|
||||
and key in litellm._langfuse_default_tags
|
||||
):
|
||||
tags.append(f"{key}:{value}")
|
||||
|
||||
# clean litellm metadata before logging
|
||||
if key in [
|
||||
"headers",
|
||||
"endpoint",
|
||||
"caching_groups",
|
||||
"previous_models",
|
||||
]:
|
||||
continue
|
||||
else:
|
||||
clean_metadata[key] = value
|
||||
|
||||
cost = kwargs.get("response_cost", None)
|
||||
if cost is not None:
|
||||
clean_metadata["litellm_response_cost"] = cost
|
||||
|
||||
metrics: Optional[dict] = None
|
||||
if (
|
||||
response_obj is not None
|
||||
and hasattr(response_obj, "usage")
|
||||
and isinstance(response_obj.usage, litellm.Usage)
|
||||
):
|
||||
generation_id = litellm.utils.get_logging_id(start_time, response_obj)
|
||||
metrics = {
|
||||
"prompt_tokens": response_obj.usage.prompt_tokens,
|
||||
"completion_tokens": response_obj.usage.completion_tokens,
|
||||
"total_tokens": response_obj.usage.total_tokens,
|
||||
"total_cost": cost,
|
||||
}
|
||||
|
||||
request_data = {
|
||||
"id": litellm_call_id,
|
||||
"input": prompt,
|
||||
"output": output,
|
||||
"metadata": clean_metadata,
|
||||
"tags": tags,
|
||||
}
|
||||
|
||||
if metrics is not None:
|
||||
request_data["metrics"] = metrics
|
||||
|
||||
try:
|
||||
await global_braintrust_http_handler.post(
|
||||
url=f"{self.api_base}/project_logs/{project_id}/insert",
|
||||
json={"events": [request_data]},
|
||||
headers=self.headers,
|
||||
)
|
||||
except httpx.HTTPStatusError as e:
|
||||
raise Exception(e.response.text)
|
||||
except Exception as e:
|
||||
verbose_logger.error(
|
||||
"Error logging to braintrust - Exception received - {}\n{}".format(
|
||||
str(e), traceback.format_exc()
|
||||
)
|
||||
)
|
||||
raise e
|
||||
|
||||
def log_failure_event(self, kwargs, response_obj, start_time, end_time):
|
||||
return super().log_failure_event(kwargs, response_obj, start_time, end_time)
|
|
@ -2,12 +2,15 @@
|
|||
# On success, logs events to Promptlayer
|
||||
import os
|
||||
import traceback
|
||||
from typing import Literal, Optional, Union
|
||||
from typing import Any, Literal, Optional, Tuple, Union
|
||||
|
||||
import dotenv
|
||||
from pydantic import BaseModel
|
||||
|
||||
from litellm.caching import DualCache
|
||||
from litellm.proxy._types import UserAPIKeyAuth
|
||||
from litellm.types.llms.openai import ChatCompletionRequest
|
||||
from litellm.types.utils import ModelResponse
|
||||
|
||||
|
||||
class CustomLogger: # https://docs.litellm.ai/docs/observability/custom_callback#callback-class
|
||||
|
@ -55,6 +58,30 @@ class CustomLogger: # https://docs.litellm.ai/docs/observability/custom_callbac
|
|||
def pre_call_check(self, deployment: dict) -> Optional[dict]:
|
||||
pass
|
||||
|
||||
#### ADAPTERS #### Allow calling 100+ LLMs in custom format - https://github.com/BerriAI/litellm/pulls
|
||||
|
||||
def translate_completion_input_params(
|
||||
self, kwargs
|
||||
) -> Optional[ChatCompletionRequest]:
|
||||
"""
|
||||
Translates the input params, from the provider's native format to the litellm.completion() format.
|
||||
"""
|
||||
pass
|
||||
|
||||
def translate_completion_output_params(
|
||||
self, response: ModelResponse
|
||||
) -> Optional[BaseModel]:
|
||||
"""
|
||||
Translates the output params, from the OpenAI format to the custom format.
|
||||
"""
|
||||
pass
|
||||
|
||||
def translate_completion_output_params_streaming(self) -> Optional[BaseModel]:
|
||||
"""
|
||||
Translates the streaming chunk, from the OpenAI format to the custom format.
|
||||
"""
|
||||
pass
|
||||
|
||||
#### CALL HOOKS - proxy only ####
|
||||
"""
|
||||
Control the modify incoming / outgoung data before calling the model
|
||||
|
@ -72,6 +99,7 @@ class CustomLogger: # https://docs.litellm.ai/docs/observability/custom_callbac
|
|||
"image_generation",
|
||||
"moderation",
|
||||
"audio_transcription",
|
||||
"pass_through_endpoint",
|
||||
],
|
||||
) -> Optional[
|
||||
Union[Exception, str, dict]
|
||||
|
@ -90,6 +118,18 @@ class CustomLogger: # https://docs.litellm.ai/docs/observability/custom_callbac
|
|||
):
|
||||
pass
|
||||
|
||||
async def async_logging_hook(
|
||||
self, kwargs: dict, result: Any, call_type: str
|
||||
) -> Tuple[dict, Any]:
|
||||
"""For masking logged request/response. Return a modified version of the request/result."""
|
||||
return kwargs, result
|
||||
|
||||
def logging_hook(
|
||||
self, kwargs: dict, result: Any, call_type: str
|
||||
) -> Tuple[dict, Any]:
|
||||
"""For masking logged request/response. Return a modified version of the request/result."""
|
||||
return kwargs, result
|
||||
|
||||
async def async_moderation_hook(
|
||||
self,
|
||||
data: dict,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#### What this does ####
|
||||
# On success + failure, log events to Supabase
|
||||
# On success + failure, log events to Datadog
|
||||
|
||||
import dotenv, os
|
||||
import requests # type: ignore
|
||||
|
@ -9,6 +9,21 @@ import litellm, uuid
|
|||
from litellm._logging import print_verbose, verbose_logger
|
||||
|
||||
|
||||
def make_json_serializable(payload):
|
||||
for key, value in payload.items():
|
||||
try:
|
||||
if isinstance(value, dict):
|
||||
# recursively sanitize dicts
|
||||
payload[key] = make_json_serializable(value.copy())
|
||||
elif not isinstance(value, (str, int, float, bool, type(None))):
|
||||
# everything else becomes a string
|
||||
payload[key] = str(value)
|
||||
except:
|
||||
# non blocking if it can't cast to a str
|
||||
pass
|
||||
return payload
|
||||
|
||||
|
||||
class DataDogLogger:
|
||||
# Class variables or attributes
|
||||
def __init__(
|
||||
|
@ -61,7 +76,7 @@ class DataDogLogger:
|
|||
id = response_obj.get("id", str(uuid.uuid4()))
|
||||
usage = dict(usage)
|
||||
try:
|
||||
response_time = (end_time - start_time).total_seconds()
|
||||
response_time = (end_time - start_time).total_seconds() * 1000
|
||||
except:
|
||||
response_time = None
|
||||
|
||||
|
@ -91,12 +106,12 @@ class DataDogLogger:
|
|||
"id": id,
|
||||
"call_type": call_type,
|
||||
"cache_hit": cache_hit,
|
||||
"startTime": start_time,
|
||||
"endTime": end_time,
|
||||
"responseTime (seconds)": response_time,
|
||||
"start_time": start_time,
|
||||
"end_time": end_time,
|
||||
"response_time": response_time,
|
||||
"model": kwargs.get("model", ""),
|
||||
"user": kwargs.get("user", ""),
|
||||
"modelParameters": optional_params,
|
||||
"model_parameters": optional_params,
|
||||
"spend": kwargs.get("response_cost", 0),
|
||||
"messages": messages,
|
||||
"response": response_obj,
|
||||
|
@ -104,13 +119,7 @@ class DataDogLogger:
|
|||
"metadata": clean_metadata,
|
||||
}
|
||||
|
||||
# Ensure everything in the payload is converted to str
|
||||
for key, value in payload.items():
|
||||
try:
|
||||
payload[key] = str(value)
|
||||
except:
|
||||
# non blocking if it can't cast to a str
|
||||
pass
|
||||
make_json_serializable(payload)
|
||||
import json
|
||||
|
||||
payload = json.dumps(payload)
|
||||
|
|
|
@ -4,11 +4,12 @@ import dotenv, os
|
|||
import requests # type: ignore
|
||||
import litellm
|
||||
import traceback
|
||||
from litellm._logging import verbose_logger
|
||||
|
||||
|
||||
class HeliconeLogger:
|
||||
# Class variables or attributes
|
||||
helicone_model_list = ["gpt", "claude"]
|
||||
helicone_model_list = ["gpt", "claude", "command-r", "command-r-plus", "command-light", "command-medium", "command-medium-beta", "command-xlarge-nightly", "command-nightly"]
|
||||
|
||||
def __init__(self):
|
||||
# Instance variables
|
||||
|
@ -30,22 +31,79 @@ class HeliconeLogger:
|
|||
prompt += f"{AI_PROMPT}"
|
||||
claude_provider_request = {"model": model, "prompt": prompt}
|
||||
|
||||
choice = response_obj["choices"][0]
|
||||
message = choice["message"]
|
||||
|
||||
content = []
|
||||
if "tool_calls" in message and message["tool_calls"]:
|
||||
for tool_call in message["tool_calls"]:
|
||||
content.append({
|
||||
"type": "tool_use",
|
||||
"id": tool_call["id"],
|
||||
"name": tool_call["function"]["name"],
|
||||
"input": tool_call["function"]["arguments"]
|
||||
})
|
||||
elif "content" in message and message["content"]:
|
||||
content = [{"type": "text", "text": message["content"]}]
|
||||
|
||||
claude_response_obj = {
|
||||
"completion": response_obj["choices"][0]["message"]["content"],
|
||||
"id": response_obj["id"],
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"model": model,
|
||||
"stop_reason": "stop_sequence",
|
||||
"content": content,
|
||||
"stop_reason": choice["finish_reason"],
|
||||
"stop_sequence": None,
|
||||
"usage": {
|
||||
"input_tokens": response_obj["usage"]["prompt_tokens"],
|
||||
"output_tokens": response_obj["usage"]["completion_tokens"]
|
||||
}
|
||||
}
|
||||
|
||||
return claude_provider_request, claude_response_obj
|
||||
return claude_response_obj
|
||||
|
||||
@staticmethod
|
||||
def add_metadata_from_header(litellm_params: dict, metadata: dict) -> dict:
|
||||
"""
|
||||
Adds metadata from proxy request headers to Helicone logging if keys start with "helicone_"
|
||||
and overwrites litellm_params.metadata if already included.
|
||||
|
||||
For example if you want to add custom property to your request, send
|
||||
`headers: { ..., helicone-property-something: 1234 }` via proxy request.
|
||||
"""
|
||||
if litellm_params is None:
|
||||
return metadata
|
||||
|
||||
if litellm_params.get("proxy_server_request") is None:
|
||||
return metadata
|
||||
|
||||
if metadata is None:
|
||||
metadata = {}
|
||||
|
||||
proxy_headers = (
|
||||
litellm_params.get("proxy_server_request", {}).get("headers", {}) or {}
|
||||
)
|
||||
|
||||
for header_key in proxy_headers:
|
||||
if header_key.startswith("helicone_"):
|
||||
metadata[header_key] = proxy_headers.get(header_key)
|
||||
|
||||
return metadata
|
||||
|
||||
def log_success(
|
||||
self, model, messages, response_obj, start_time, end_time, print_verbose
|
||||
self, model, messages, response_obj, start_time, end_time, print_verbose, kwargs
|
||||
):
|
||||
# Method definition
|
||||
try:
|
||||
print_verbose(
|
||||
f"Helicone Logging - Enters logging function for model {model}"
|
||||
)
|
||||
litellm_params = kwargs.get("litellm_params", {})
|
||||
litellm_call_id = kwargs.get("litellm_call_id", None)
|
||||
metadata = (
|
||||
litellm_params.get("metadata", {}) or {}
|
||||
)
|
||||
metadata = self.add_metadata_from_header(litellm_params, metadata)
|
||||
model = (
|
||||
model
|
||||
if any(
|
||||
|
@ -61,7 +119,7 @@ class HeliconeLogger:
|
|||
response_obj = response_obj.json()
|
||||
|
||||
if "claude" in model:
|
||||
provider_request, response_obj = self.claude_mapping(
|
||||
response_obj = self.claude_mapping(
|
||||
model=model, messages=messages, response_obj=response_obj
|
||||
)
|
||||
|
||||
|
@ -72,7 +130,11 @@ class HeliconeLogger:
|
|||
}
|
||||
|
||||
# Code to be executed
|
||||
provider_url = self.provider_url
|
||||
url = "https://api.hconeai.com/oai/v1/log"
|
||||
if "claude" in model:
|
||||
url = "https://api.hconeai.com/anthropic/v1/log"
|
||||
provider_url = "https://api.anthropic.com/v1/messages"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.key}",
|
||||
"Content-Type": "application/json",
|
||||
|
@ -85,11 +147,13 @@ class HeliconeLogger:
|
|||
end_time_milliseconds = int(
|
||||
(end_time.timestamp() - end_time_seconds) * 1000
|
||||
)
|
||||
meta = {"Helicone-Auth": f"Bearer {self.key}"}
|
||||
meta.update(metadata)
|
||||
data = {
|
||||
"providerRequest": {
|
||||
"url": self.provider_url,
|
||||
"url": provider_url,
|
||||
"json": provider_request,
|
||||
"meta": {"Helicone-Auth": f"Bearer {self.key}"},
|
||||
"meta": meta,
|
||||
},
|
||||
"providerResponse": providerResponse,
|
||||
"timing": {
|
||||
|
|
|
@ -8,6 +8,7 @@ from packaging.version import Version
|
|||
|
||||
import litellm
|
||||
from litellm._logging import verbose_logger
|
||||
from litellm.litellm_core_utils.redact_messages import redact_user_api_key_info
|
||||
|
||||
|
||||
class LangFuseLogger:
|
||||
|
@ -317,6 +318,11 @@ class LangFuseLogger:
|
|||
|
||||
try:
|
||||
tags = []
|
||||
try:
|
||||
metadata = copy.deepcopy(
|
||||
metadata
|
||||
) # Avoid modifying the original metadata
|
||||
except:
|
||||
new_metadata = {}
|
||||
for key, value in metadata.items():
|
||||
if (
|
||||
|
@ -377,6 +383,8 @@ class LangFuseLogger:
|
|||
mask_input = clean_metadata.pop("mask_input", False)
|
||||
mask_output = clean_metadata.pop("mask_output", False)
|
||||
|
||||
clean_metadata = redact_user_api_key_info(metadata=clean_metadata)
|
||||
|
||||
if trace_name is None and existing_trace_id is None:
|
||||
# just log `litellm-{call_type}` as the trace name
|
||||
## DO NOT SET TRACE_NAME if trace-id set. this can lead to overwriting of past traces.
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue