mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
commit
94edbd1b35
279 changed files with 10063 additions and 1043 deletions
|
|
@ -144,11 +144,12 @@ jobs:
|
|||
name: Linting Testing
|
||||
command: |
|
||||
cd litellm
|
||||
pip install "cryptography<40.0.0"
|
||||
pip install "cryptography>=43.0.1"
|
||||
python -m pip install types-requests types-setuptools types-redis types-PyYAML
|
||||
if ! python -m mypy . \
|
||||
--config-file mypy.ini \
|
||||
--ignore-missing-imports; then
|
||||
--ignore-missing-imports \
|
||||
--no-incremental; then
|
||||
echo "mypy detected errors"
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
|||
33
README.md
33
README.md
|
|
@ -350,13 +350,21 @@ curl 'http://0.0.0.0:4000/key/generate' \
|
|||
|
||||
[**Read the Docs**](https://docs.litellm.ai/docs/)
|
||||
|
||||
## Contributing
|
||||
## Run in Developer mode
|
||||
### Services
|
||||
1. Setup .env file in root
|
||||
2. Run dependant services `docker-compose up db prometheus`
|
||||
|
||||
Interested in contributing? Contributions to LiteLLM Python SDK, Proxy Server, and LLM integrations are both accepted and highly encouraged!
|
||||
### Backend
|
||||
1. (In root) create virtual environment `python -m venv .venv`
|
||||
2. Activate virtual environment `source .venv/bin/activate`
|
||||
3. Install dependencies `pip install -e ".[all]"`
|
||||
4. Start proxy backend `python litellm/proxy_cli.py`
|
||||
|
||||
**Quick start:** `git clone` → `make install-dev` → `make format` → `make lint` → `make test-unit`
|
||||
|
||||
See our comprehensive [Contributing Guide (CONTRIBUTING.md)](CONTRIBUTING.md) for detailed instructions.
|
||||
### Frontend
|
||||
1. Navigate to `ui/litellm-dashboard`
|
||||
2. Install dependencies `npm install`
|
||||
3. Run `npm run dev` to start the dashboard
|
||||
|
||||
# Enterprise
|
||||
For companies that need better security, user management and professional support
|
||||
|
|
@ -434,18 +442,3 @@ All these checks must pass before your PR can be merged.
|
|||
</a>
|
||||
|
||||
|
||||
## Run in Developer mode
|
||||
### Services
|
||||
1. Setup .env file in root
|
||||
2. Run dependant services `docker-compose up db prometheus`
|
||||
|
||||
### Backend
|
||||
1. (In root) create virtual environment `python -m venv .venv`
|
||||
2. Activate virtual environment `source .venv/bin/activate`
|
||||
3. Install dependencies `pip install -e ".[all]"`
|
||||
4. Start proxy backend `python3 /path/to/litellm/proxy_cli.py`
|
||||
|
||||
### Frontend
|
||||
1. Navigate to `ui/litellm-dashboard`
|
||||
2. Install dependencies `npm install`
|
||||
3. Run `npm run dev` to start the dashboard
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import os
|
|||
import litellm
|
||||
from litellm import Router
|
||||
from dotenv import load_dotenv
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ sys.path.insert(
|
|||
import litellm
|
||||
from litellm import Router
|
||||
from dotenv import load_dotenv
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ sys.path.insert(
|
|||
import litellm
|
||||
from litellm import Router
|
||||
from dotenv import load_dotenv
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ response = completion(
|
|||
|
||||
print(response.usage)
|
||||
```
|
||||
> **Note:** LiteLLM supports endpoint bridging—if a model does not natively support a requested endpoint, LiteLLM will automatically route the call to the correct supported endpoint (such as bridging `/chat/completions` to `/responses` or vice versa) based on the model's `mode`set in `model_prices_and_context_window`.
|
||||
|
||||
## Streaming Usage
|
||||
|
||||
|
|
|
|||
294
docs/my-website/docs/completion/web_fetch.md
Normal file
294
docs/my-website/docs/completion/web_fetch.md
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Web Fetch
|
||||
|
||||
The web fetch tool allows LLMs to retrieve full content from specified web pages and PDF documents. This enables AI models to access real-time information from the internet and incorporate web content into their responses.
|
||||
|
||||
## Web Fetch vs Web Search
|
||||
|
||||
**Web Fetch** retrieves the full content from specific web pages that you provide URLs for, while **Web Search** performs internet searches to find relevant information based on your queries.
|
||||
|
||||
| Feature | Web Fetch | Web Search |
|
||||
|---------|-----------|------------|
|
||||
| **Purpose** | Retrieve content from specific URLs | Search the internet for information |
|
||||
| **Input** | You provide exact URLs to fetch | You provide search queries/questions |
|
||||
| **Output** | Full page content from specified URLs | Search results with relevant information |
|
||||
| **Use Cases** | - Analyzing specific articles<br/>- Comparing content from known websites<br/>- Extracting data from particular pages | - Finding current news/events<br/>- Researching topics<br/>- Getting real-time information |
|
||||
|
||||
|
||||
**Example Web Fetch**: "Fetch the content from https://example.com/pricing and summarize it"
|
||||
**Example Web Search**: "What are the latest AI developments this week?"
|
||||
|
||||
**Supported Providers:**
|
||||
- Anthropic API (`anthropic/`)
|
||||
|
||||
**Supported Tool Types:**
|
||||
- `web_fetch_20250910` - Web content retrieval tool with usage limits, domain filtering, and citation support
|
||||
|
||||
|
||||
## Quick Start
|
||||
|
||||
### LiteLLM Python SDK
|
||||
|
||||
```python
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
os.environ["ANTHROPIC_API_KEY"] = "your-api-key"
|
||||
|
||||
# Web fetch tool
|
||||
tools = [
|
||||
{
|
||||
"type": "web_fetch_20250910",
|
||||
"name": "web_fetch",
|
||||
"max_uses": 5,
|
||||
}
|
||||
]
|
||||
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Please analyze the content at https://example.com/article and summarize the main points"
|
||||
}
|
||||
]
|
||||
|
||||
response = completion(
|
||||
model="anthropic/claude-3-5-sonnet-latest",
|
||||
messages=messages,
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
### LiteLLM Proxy
|
||||
|
||||
1. Define web fetch models on config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: claude-3-5-sonnet-latest # Anthropic claude-3-5-sonnet-latest
|
||||
litellm_params:
|
||||
model: anthropic/claude-3-5-sonnet-latest
|
||||
api_key: os.environ/ANTHROPIC_API_KEY
|
||||
```
|
||||
|
||||
2. Run proxy server
|
||||
|
||||
```bash
|
||||
litellm --config config.yaml
|
||||
```
|
||||
|
||||
3. Test it using the OpenAI Python SDK
|
||||
|
||||
```python
|
||||
import os
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key="sk-1234", # your litellm proxy api key
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="claude-3-5-sonnet-latest",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Please fetch and analyze the content from https://news.ycombinator.com and tell me about the top stories"
|
||||
}
|
||||
],
|
||||
tools=[
|
||||
{
|
||||
"type": "web_fetch_20250910",
|
||||
"name": "web_fetch",
|
||||
"max_uses": 5,
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
## Supported Models
|
||||
|
||||
Web fetch is available on the following Anthropic API models:
|
||||
|
||||
- `claude-opus-4-1-20250805` (Claude Opus 4.1)
|
||||
- `claude-opus-4-20250514` (Claude Opus 4)
|
||||
- `claude-sonnet-4-20250514` (Claude Sonnet 4)
|
||||
- `claude-3-7-sonnet-20250219` (Claude Sonnet 3.7)
|
||||
- `claude-3-5-sonnet-latest` (Claude Sonnet 3.5 v2 - deprecated)
|
||||
- `claude-3-5-haiku-latest` (Claude Haiku 3.5)
|
||||
|
||||
:::note
|
||||
The web fetch tool currently does not support websites dynamically rendered via JavaScript.
|
||||
:::
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Web Content Retrieval
|
||||
|
||||
```python
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
os.environ["ANTHROPIC_API_KEY"] = "your-api-key"
|
||||
|
||||
tools = [
|
||||
{
|
||||
"type": "web_fetch_20250910",
|
||||
"name": "web_fetch",
|
||||
"max_uses": 3,
|
||||
}
|
||||
]
|
||||
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Fetch the latest news from https://techcrunch.com and summarize the top 3 articles"
|
||||
}
|
||||
]
|
||||
|
||||
response = completion(
|
||||
model="anthropic/claude-3-5-sonnet-latest",
|
||||
messages=messages,
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
### Research and Analysis
|
||||
|
||||
```python
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
os.environ["ANTHROPIC_API_KEY"] = "your-api-key"
|
||||
|
||||
tools = [
|
||||
{
|
||||
"type": "web_fetch_20250910",
|
||||
"name": "web_fetch",
|
||||
"max_uses": 10,
|
||||
}
|
||||
]
|
||||
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Research the latest developments in AI by fetching content from multiple tech news websites and provide a comprehensive analysis"
|
||||
}
|
||||
]
|
||||
|
||||
response = completion(
|
||||
model="anthropic/claude-3-5-sonnet-latest",
|
||||
messages=messages,
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
### Content Comparison
|
||||
|
||||
```python
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
os.environ["ANTHROPIC_API_KEY"] = "your-api-key"
|
||||
|
||||
tools = [
|
||||
{
|
||||
"type": "web_fetch_20250910",
|
||||
"name": "web_fetch",
|
||||
"max_uses": 5,
|
||||
}
|
||||
]
|
||||
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Compare the pricing information from https://openai.com/pricing and https://anthropic.com/pricing and create a comparison table"
|
||||
}
|
||||
]
|
||||
|
||||
response = completion(
|
||||
model="anthropic/claude-3-5-sonnet-latest",
|
||||
messages=messages,
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
## Advanced Usage with Multiple Tools
|
||||
|
||||
You can combine web fetch with other tools like computer use or text editor:
|
||||
|
||||
```python
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
os.environ["ANTHROPIC_API_KEY"] = "your-api-key"
|
||||
|
||||
tools = [
|
||||
{
|
||||
"type": "web_fetch_20250910",
|
||||
"name": "web_fetch",
|
||||
"max_uses": 5,
|
||||
},
|
||||
{
|
||||
"type": "text_editor_20250124",
|
||||
"name": "str_replace_editor"
|
||||
}
|
||||
]
|
||||
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Fetch the latest AI research papers from arXiv, analyze them, and create a detailed report file with your findings"
|
||||
}
|
||||
]
|
||||
|
||||
response = completion(
|
||||
model="anthropic/claude-3-5-sonnet-latest",
|
||||
messages=messages,
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
## Spec
|
||||
|
||||
### Web Fetch Tool (`web_fetch_20250910`)
|
||||
|
||||
The web fetch tool supports the following parameters:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "web_fetch_20250910",
|
||||
"name": "web_fetch",
|
||||
|
||||
// Optional: Limit the number of fetches per request
|
||||
"max_uses": 10,
|
||||
|
||||
// Optional: Only fetch from these domains
|
||||
"allowed_domains": ["example.com", "docs.example.com"],
|
||||
|
||||
// Optional: Never fetch from these domains
|
||||
"blocked_domains": ["private.example.com"],
|
||||
|
||||
// Optional: Enable citations for fetched content
|
||||
"citations": {
|
||||
"enabled": true
|
||||
},
|
||||
|
||||
// Optional: Maximum content length in tokens
|
||||
"max_content_tokens": 100000
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Using Web Search
|
||||
# Web Search
|
||||
|
||||
Use web search with litellm
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
import Image from '@theme/IdealImage';
|
||||
|
||||
# Enterprise
|
||||
|
||||
:::info
|
||||
✨ SSO is free for up to 5 users. After that, an enterprise license is required. [Get Started with Enterprise here](https://www.litellm.ai/enterprise)
|
||||
:::
|
||||
|
||||
For companies that need SSO, user management and professional support for LiteLLM Proxy
|
||||
|
||||
:::info
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ This is an Enterprise only endpoint [Get Started with Enterprise here](https://c
|
|||
| Feature | Supported | Notes |
|
||||
|-------|-------|-------|
|
||||
| Supported Providers | OpenAI, Azure OpenAI, Vertex AI | - |
|
||||
|
||||
#### ⚡️See an exhaustive list of supported models and providers at [models.litellm.ai](https://models.litellm.ai/)
|
||||
| Cost Tracking | 🟡 | [Let us know if you need this](https://github.com/BerriAI/litellm/issues) |
|
||||
| Logging | ✅ | Works across all logging integrations |
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ Next Steps 👉 [Call all supported models - e.g. Claude-2, Llama2-70b, etc.](./
|
|||
More details 👉
|
||||
|
||||
- [Completion() function details](./completion/)
|
||||
- [All supported models / providers on LiteLLM](./providers/)
|
||||
- [Overview of supported models / providers on LiteLLM](./providers/)
|
||||
- [Search all models / providers](https://models.litellm.ai/)
|
||||
- [Build your own OpenAI proxy](https://github.com/BerriAI/liteLLM-proxy/tree/main)
|
||||
|
||||
## streaming
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ LiteLLM provides image editing functionality that maps to OpenAI's `/images/edit
|
|||
| Supported LiteLLM Proxy Versions | 1.71.1+ | |
|
||||
| Supported LLM providers | **OpenAI** | Currently only `openai` is supported |
|
||||
|
||||
#### ⚡️See all supported models and providers at [models.litellm.ai](https://models.litellm.ai/)
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### LiteLLM Python SDK
|
||||
|
|
|
|||
|
|
@ -279,6 +279,8 @@ print(f"response: {response}")
|
|||
|
||||
## Supported Providers
|
||||
|
||||
#### ⚡️See all supported models and providers at [models.litellm.ai](https://models.litellm.ai/)
|
||||
|
||||
| Provider | Documentation Link |
|
||||
|----------|-------------------|
|
||||
| OpenAI | [OpenAI Image Generation →](./providers/openai) |
|
||||
|
|
|
|||
|
|
@ -524,6 +524,15 @@ try:
|
|||
except OpenAIError as e:
|
||||
print(e)
|
||||
```
|
||||
### See How LiteLLM Transforms Your Requests
|
||||
|
||||
Want to understand how LiteLLM parses and normalizes your LLM API requests? Use the `/utils/transform_request` endpoint to see exactly how your request is transformed internally.
|
||||
|
||||
You can try it out now directly on our Demo App!
|
||||
Go to the [LiteLLM API docs for transform_request](https://litellm-api.up.railway.app/#/llm%20utils/transform_request_utils_transform_request_post)
|
||||
|
||||
LiteLLM will show you the normalized, provider-agnostic version of your request. This is useful for debugging, learning, and understanding how LiteLLM handles different providers and options.
|
||||
|
||||
|
||||
### Logging Observability - Log LLM Input/Output ([Docs](https://docs.litellm.ai/docs/observability/callbacks))
|
||||
LiteLLM exposes pre defined callbacks to send data to Lunary, MLflow, Langfuse, Helicone, Promptlayer, Traceloop, Slack
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@ Tutorial on how to get to 1K+ RPS with LiteLLM Proxy on locust
|
|||
|
||||
**Use this config for testing:**
|
||||
|
||||
**Note:** we're currently migrating to aiohttp which has 10x higher throughput. We recommend using the `aiohttp_openai/` provider for load testing.
|
||||
**Note:** we're currently migrating to aiohttp which has 10x higher throughput. We recommend using the `openai/` provider for load testing.
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: "fake-openai-endpoint"
|
||||
litellm_params:
|
||||
model: aiohttp_openai/any
|
||||
model: openai/any
|
||||
api_base: https://your-fake-openai-endpoint.com/chat/completions
|
||||
api_key: "test"
|
||||
```
|
||||
|
|
@ -58,7 +58,7 @@ litellm provides a hosted `fake-openai-endpoint` you can load test against
|
|||
model_list:
|
||||
- model_name: fake-openai-endpoint
|
||||
litellm_params:
|
||||
model: aiohttp_openai/fake
|
||||
model: openai/fake
|
||||
api_key: fake-key
|
||||
api_base: https://exampleopenaiendpoint-production.up.railway.app/
|
||||
|
||||
|
|
|
|||
|
|
@ -130,6 +130,8 @@ Here's the exact json output and type you can expect from all moderation calls:
|
|||
|
||||
## **Supported Providers**
|
||||
|
||||
#### ⚡️See all supported models and providers at [models.litellm.ai](https://models.litellm.ai/)
|
||||
|
||||
| Provider |
|
||||
|-------------|
|
||||
| OpenAI |
|
||||
|
|
|
|||
|
|
@ -5,13 +5,15 @@
|
|||
liteLLM provides `input_callbacks`, `success_callbacks` and `failure_callbacks`, making it easy for you to send data to a particular provider depending on the status of your responses.
|
||||
|
||||
:::tip
|
||||
**New to LiteLLM Callbacks?** Check out our comprehensive [Callback Management Guide](./callback_management.md) to understand when to use different callback hooks like `async_log_success_event` vs `async_post_call_success_hook`.
|
||||
**New to LiteLLM Callbacks?**
|
||||
|
||||
- For proxy/server logging and observability, see the [Proxy Logging Guide](https://docs.litellm.ai/docs/proxy/logging).
|
||||
- To write your own callback logic, see the [Custom Callbacks Guide](https://docs.litellm.ai/docs/observability/custom_callback).
|
||||
:::
|
||||
|
||||
liteLLM supports:
|
||||
|
||||
- [Custom Callback Functions](https://docs.litellm.ai/docs/observability/custom_callback)
|
||||
- [Callback Management Guide](./callback_management.md) - **Comprehensive guide for choosing the right hooks**
|
||||
### Supported Callback Integrations
|
||||
|
||||
- [Lunary](https://lunary.ai/docs)
|
||||
- [Langfuse](https://langfuse.com/docs)
|
||||
- [LangSmith](https://www.langchain.com/langsmith)
|
||||
|
|
@ -21,9 +23,20 @@ liteLLM supports:
|
|||
- [Sentry](https://docs.sentry.io/platforms/python/)
|
||||
- [PostHog](https://posthog.com/docs/libraries/python)
|
||||
- [Slack](https://slack.dev/bolt-python/concepts)
|
||||
- [Arize](https://docs.arize.com/)
|
||||
- [PromptLayer](https://docs.promptlayer.com/)
|
||||
|
||||
This is **not** an extensive list. Please check the dropdown for all logging integrations.
|
||||
|
||||
### Related Cookbooks
|
||||
Try out our cookbooks for code snippets and interactive demos:
|
||||
|
||||
- [Langfuse Callback Example (Colab)](https://colab.research.google.com/github/BerriAI/litellm/blob/main/cookbook/logging_observability/LiteLLM_Langfuse.ipynb)
|
||||
- [Lunary Callback Example (Colab)](https://colab.research.google.com/github/BerriAI/litellm/blob/main/cookbook/logging_observability/LiteLLM_Lunary.ipynb)
|
||||
- [Arize Callback Example (Colab)](https://colab.research.google.com/github/BerriAI/litellm/blob/main/cookbook/logging_observability/LiteLLM_Arize.ipynb)
|
||||
- [Proxy + Langfuse Callback Example (Colab)](https://colab.research.google.com/github/BerriAI/litellm/blob/main/cookbook/logging_observability/LiteLLM_Proxy_Langfuse.ipynb)
|
||||
- [PromptLayer Callback Example (Colab)](https://colab.research.google.com/github/BerriAI/litellm/blob/main/cookbook/LiteLLM_PromptLayer.ipynb)
|
||||
|
||||
### Quick Start
|
||||
|
||||
```python
|
||||
|
|
|
|||
|
|
@ -67,6 +67,23 @@ asyncio.run(completion())
|
|||
- `async_post_call_success_hook` - Access user data + modify responses
|
||||
- `async_pre_call_hook` - Modify requests before sending
|
||||
|
||||
### Example: Modifying the Response in async_post_call_success_hook
|
||||
|
||||
You can use `async_post_call_success_hook` to add custom headers or metadata to the response before it is returned to the client. For example:
|
||||
|
||||
```python
|
||||
async def async_post_call_success_hook(data, user_api_key_dict, response):
|
||||
# Add a custom header to the response
|
||||
additional_headers = getattr(response, "_hidden_params", {}).get("additional_headers", {}) or {}
|
||||
additional_headers["x-litellm-custom-header"] = "my-value"
|
||||
if not hasattr(response, "_hidden_params"):
|
||||
response._hidden_params = {}
|
||||
response._hidden_params["additional_headers"] = additional_headers
|
||||
return response
|
||||
```
|
||||
|
||||
This allows you to inject custom metadata or headers into the response for downstream consumers. You can use this pattern to pass information to clients, proxies, or observability tools.
|
||||
|
||||
## Callback Functions
|
||||
If you just want to log on a specific event (e.g. on input) - you can use callback functions.
|
||||
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@ These can be passed inside metadata with the `opik` key.
|
|||
- `project_name` - Name of the Opik project to send data to.
|
||||
- `current_span_data` - The current span data to be used for tracing.
|
||||
- `tags` - Tags to be used for tracing.
|
||||
- `thread_id` - The thread id to group together multiple related traces.
|
||||
|
||||
### Usage
|
||||
|
||||
|
|
@ -159,8 +160,10 @@ response = litellm.completion(
|
|||
messages=messages,
|
||||
metadata = {
|
||||
"opik": {
|
||||
"project_name": "your-opik-project-name",
|
||||
"current_span_data": get_current_span_data(),
|
||||
"tags": ["streaming-test"],
|
||||
"thread_id": "your-thread-id"
|
||||
},
|
||||
}
|
||||
)
|
||||
|
|
@ -174,7 +177,7 @@ curl -L -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
|
|||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-d '{
|
||||
"model": "gpt-3.5-turbo-testing",
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
|
|
@ -183,8 +186,10 @@ curl -L -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
|
|||
],
|
||||
"metadata": {
|
||||
"opik": {
|
||||
"project_name": "your-opik-project-name",
|
||||
"current_span_data": "...",
|
||||
"tags": ["streaming-test"],
|
||||
"thread_id": "your-thread-id"
|
||||
},
|
||||
}
|
||||
}'
|
||||
|
|
@ -195,12 +200,25 @@ curl -L -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
|
|||
|
||||
|
||||
|
||||
You can also pass the fields as part of the request header with a `opik_*` prefix:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```shell
|
||||
curl --location --request POST 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--header 'opik_project_name: your-opik-project-name' \
|
||||
--header 'opik_thread_id: your-thread-id' \
|
||||
--header 'opik_tags: ["streaming-test"]' \
|
||||
--data '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What's the weather like in Boston today?"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2340,6 +2340,39 @@ response = completion(
|
|||
|
||||
Make the bedrock completion call
|
||||
|
||||
---
|
||||
|
||||
### Required AWS IAM Policy for AssumeRole
|
||||
|
||||
To use `aws_role_name` (STS AssumeRole) with LiteLLM, your IAM user or role **must** have permission to call `sts:AssumeRole` on the target role. If you see an error like:
|
||||
|
||||
```
|
||||
An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:sts::...:assumed-role/litellm-ecs-task-role/... is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::...:role/Enterprise/BedrockCrossAccountConsumer
|
||||
```
|
||||
|
||||
This means the IAM identity running LiteLLM does **not** have permission to assume the target role. You must update your IAM policy to allow this action.
|
||||
|
||||
#### Example IAM Policy
|
||||
|
||||
Replace `<TARGET_ROLE_ARN>` with the ARN of the role you want to assume (e.g., `arn:aws:iam::123456789012:role/Enterprise/BedrockCrossAccountConsumer`).
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "sts:AssumeRole",
|
||||
"Resource": "<TARGET_ROLE_ARN>"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The target role itself must also trust the calling IAM identity (via its trust policy) for AssumeRole to succeed. See [AWS AssumeRole docs](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-api.html) for more details.
|
||||
|
||||
---
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
|
|
|
|||
|
|
@ -1199,6 +1199,10 @@ response = litellm.completion(
|
|||
| gemini-2.0-flash | `completion(model='gemini/gemini-2.0-flash', messages)` | `os.environ['GEMINI_API_KEY']` |
|
||||
| gemini-2.0-flash-exp | `completion(model='gemini/gemini-2.0-flash-exp', messages)` | `os.environ['GEMINI_API_KEY']` |
|
||||
| gemini-2.0-flash-lite-preview-02-05 | `completion(model='gemini/gemini-2.0-flash-lite-preview-02-05', messages)` | `os.environ['GEMINI_API_KEY']` |
|
||||
| gemini-2.5-flash-preview-09-2025 | `completion(model='gemini/gemini-2.5-flash-preview-09-2025', messages)` | `os.environ['GEMINI_API_KEY']` |
|
||||
| gemini-2.5-flash-lite-preview-09-2025 | `completion(model='gemini/gemini-2.5-flash-lite-preview-09-2025', messages)` | `os.environ['GEMINI_API_KEY']` |
|
||||
| gemini-flash-latest | `completion(model='gemini/gemini-flash-latest', messages)` | `os.environ['GEMINI_API_KEY']` |
|
||||
| gemini-flash-lite-latest | `completion(model='gemini/gemini-flash-lite-latest', messages)` | `os.environ['GEMINI_API_KEY']` |
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -196,6 +196,19 @@ model_list:
|
|||
vertex_location: "us-central1"
|
||||
vertex_credentials: "/path/to/service_account.json" # [OPTIONAL] Do this OR `!gcloud auth application-default login` - run this to add vertex credentials to your env
|
||||
```
|
||||
or
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gemini-pro
|
||||
litellm_params:
|
||||
model: vertex_ai/gemini-1.5-pro
|
||||
litellm_credential_name: vertex-global
|
||||
vertex_project: project-name-here
|
||||
vertex_location: global
|
||||
base_model: gemini
|
||||
model_info:
|
||||
provider: Vertex
|
||||
```
|
||||
|
||||
2. Start Proxy
|
||||
|
||||
|
|
@ -885,7 +898,7 @@ curl http://0.0.0.0:4000/chat/completions \
|
|||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Pre-requisites
|
||||
* `pip install google-cloud-aiplatform` (pre-installed on proxy docker image)
|
||||
|
|
@ -1284,6 +1297,10 @@ litellm.vertex_location = "us-central1 # Your Location
|
|||
| Model Name | Function Call |
|
||||
|------------------|--------------------------------------|
|
||||
| gemini-2.5-pro | `completion('gemini-2.5-pro', messages)`, `completion('vertex_ai/gemini-2.5-pro', messages)` |
|
||||
| gemini-2.5-flash-preview-09-2025 | `completion('gemini-2.5-flash-preview-09-2025', messages)`, `completion('vertex_ai/gemini-2.5-flash-preview-09-2025', messages)` |
|
||||
| gemini-2.5-flash-lite-preview-09-2025 | `completion('gemini-2.5-flash-lite-preview-09-2025', messages)`, `completion('vertex_ai/gemini-2.5-flash-lite-preview-09-2025', messages)` |
|
||||
| gemini-flash-latest | `completion('gemini-flash-latest', messages)`, `completion('vertex_ai/gemini-flash-latest', messages)` |
|
||||
| gemini-flash-lite-latest | `completion('gemini-flash-lite-latest', messages)`, `completion('vertex_ai/gemini-flash-lite-latest', messages)` |
|
||||
|
||||
## Fine-tuned Models
|
||||
|
||||
|
|
|
|||
|
|
@ -958,6 +958,19 @@ curl http://localhost:4000/v1/chat/completions \
|
|||
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Redis max_connections
|
||||
|
||||
You can set the `max_connections` parameter in your `cache_params` for Redis. This is passed directly to the Redis client and controls the maximum number of simultaneous connections in the pool. If you see errors like `No connection available`, try increasing this value:
|
||||
|
||||
```yaml
|
||||
litellm_settings:
|
||||
cache: true
|
||||
cache_params:
|
||||
type: redis
|
||||
max_connections: 100
|
||||
```
|
||||
|
||||
## Supported `cache_params` on proxy config.yaml
|
||||
|
||||
```yaml
|
||||
|
|
@ -966,6 +979,7 @@ cache_params:
|
|||
ttl: Optional[float]
|
||||
default_in_memory_ttl: Optional[float]
|
||||
default_in_redis_ttl: Optional[float]
|
||||
max_connections: Optional[Int]
|
||||
|
||||
# Type of cache (options: "local", "redis", "s3")
|
||||
type: s3
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ litellm_settings:
|
|||
port: 6379 # The port number for the Redis cache. Required if type is "redis".
|
||||
password: "your_password" # The password for the Redis cache. Required if type is "redis".
|
||||
namespace: "litellm.caching.caching" # namespace for redis cache
|
||||
max_connections: 100 # [OPTIONAL] Set Maximum number of Redis connections. Passed directly to redis-py.
|
||||
|
||||
# Optional - Redis Cluster Settings
|
||||
redis_startup_nodes: [{"host": "127.0.0.1", "port": "7001"}]
|
||||
|
|
@ -613,6 +614,8 @@ router_settings:
|
|||
| LITELLM_MIGRATION_DIR | Custom migrations directory for prisma migrations, used for baselining db in read-only file systems.
|
||||
| LITELLM_HOSTED_UI | URL of the hosted UI for LiteLLM
|
||||
| LITELM_ENVIRONMENT | Environment of LiteLLM Instance, used by logging services. Currently only used by DeepEval.
|
||||
| LITELLM_KEY_ROTATION_ENABLED | Enable auto-key rotation for LiteLLM (boolean). Default is false.
|
||||
| LITELLM_KEY_ROTATION_CHECK_INTERVAL_SECONDS | Interval in seconds for how often to run job that auto-rotates keys. Default is 86400 (24 hours).
|
||||
| LITELLM_LICENSE | License key for LiteLLM usage
|
||||
| LITELLM_LOCAL_MODEL_COST_MAP | Local configuration for model cost mapping in LiteLLM
|
||||
| LITELLM_LOG | Enable detailed logging for LiteLLM
|
||||
|
|
|
|||
|
|
@ -83,6 +83,24 @@ model_list:
|
|||
cache_read_input_token_cost: 0.0000006
|
||||
```
|
||||
|
||||
### Additional Cost Keys
|
||||
|
||||
There are other keys you can use to specify costs for different scenarios and modalities:
|
||||
|
||||
- `input_cost_per_token_above_200k_tokens` - Cost for input tokens when context exceeds 200k tokens
|
||||
- `output_cost_per_token_above_200k_tokens` - Cost for output tokens when context exceeds 200k tokens
|
||||
- `cache_creation_input_token_cost_above_200k_tokens` - Cache creation cost for large contexts
|
||||
- `cache_read_input_token_cost_above_200k_token` - Cache read cost for large contexts
|
||||
- `input_cost_per_image` - Cost per image in multimodal requests
|
||||
- `output_cost_per_reasoning_token` - Cost for reasoning tokens (e.g., OpenAI o1 models)
|
||||
- `input_cost_per_audio_token` - Cost for audio input tokens
|
||||
- `output_cost_per_audio_token` - Cost for audio output tokens
|
||||
- `input_cost_per_video_per_second` - Cost per second of video input
|
||||
- `input_cost_per_video_per_second_above_128k_tokens` - Video cost for large contexts
|
||||
- `input_cost_per_character` - Character-based pricing for some providers
|
||||
|
||||
These keys evolve based on how new models handle multimodality. The latest version can be found at [https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json](https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json).
|
||||
|
||||
## Set 'base_model' for Cost Tracking (e.g. Azure deployments)
|
||||
|
||||
**Problem**: Azure returns `gpt-4` in the response when `azure/gpt-4-1106-preview` is used. This leads to inaccurate cost tracking
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
# ✨ Event Hooks for SSO Login
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://www.litellm.ai/enterprise)
|
||||
|
||||
✨ SSO is free for up to 5 users. After that, an enterprise license is required. [Get Started with Enterprise here](https://www.litellm.ai/enterprise)
|
||||
:::
|
||||
|
||||
## Overview
|
||||
|
|
|
|||
|
|
@ -84,3 +84,29 @@ LiteLLM emits the following prometheus metrics to monitor the health/status of t
|
|||
| `litellm_in_memory_spend_update_queue_size` | In-memory aggregate spend values for keys, users, teams, team members, etc.| In-Memory |
|
||||
| `litellm_redis_spend_update_queue_size` | Redis aggregate spend values for keys, users, teams, etc. | Redis |
|
||||
|
||||
|
||||
## Troubleshooting: Redis Connection Errors
|
||||
|
||||
You may see errors like:
|
||||
|
||||
```
|
||||
LiteLLM Redis Caching: async async_increment() - Got exception from REDIS No connection available., Writing value=21
|
||||
LiteLLM Redis Caching: async set_cache_pipeline() - Got exception from REDIS No connection available., Writing value=None
|
||||
```
|
||||
|
||||
This means all available Redis connections are in use, and LiteLLM cannot obtain a new connection from the pool. This can happen under high load or with many concurrent proxy requests.
|
||||
|
||||
**Solution:**
|
||||
|
||||
- Increase the `max_connections` parameter in your Redis config section in `proxy_config.yaml` to allow more simultaneous connections. For example:
|
||||
|
||||
```yaml
|
||||
litellm_settings:
|
||||
cache: True
|
||||
cache_params:
|
||||
type: redis
|
||||
max_connections: 100 # Increase as needed for your traffic
|
||||
```
|
||||
|
||||
Adjust this value based on your expected concurrency and Redis server capacity.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import TabItem from '@theme/TabItem';
|
|||
|
||||
# Bedrock Guardrails
|
||||
|
||||
:::tip ⚡️
|
||||
If you haven't set up or authenticated your Bedrock provider yet, see the [Bedrock Provider Setup & Authentication Guide](../../providers/bedrock.md).
|
||||
:::
|
||||
|
||||
LiteLLM supports Bedrock guardrails via the [Bedrock ApplyGuardrail API](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ApplyGuardrail.html).
|
||||
|
||||
## Quick Start
|
||||
|
|
|
|||
|
|
@ -172,6 +172,9 @@ router_settings:
|
|||
redis_host: <your redis host>
|
||||
redis_password: <your redis password>
|
||||
redis_port: 1992
|
||||
cache_params:
|
||||
type: redis
|
||||
max_connections: 100 # maximum Redis connections in the pool; tune based on expected concurrency/load
|
||||
```
|
||||
|
||||
## Router settings on config - routing_strategy, model_group_alias
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ Found under `kwargs["standard_logging_object"]`. This is a standard payload, log
|
|||
| `trace_id` | `str` | Trace multiple LLM calls belonging to same overall request |
|
||||
| `call_type` | `str` | Type of call |
|
||||
| `response_cost` | `float` | Cost of the response in USD ($) |
|
||||
| `cost_breakdown` | `Optional[CostBreakdown]` | Detailed cost breakdown object |
|
||||
| `response_cost_failure_debug_info` | `StandardLoggingModelCostFailureDebugInformation` | Debug information if cost tracking fails |
|
||||
| `status` | `StandardLoggingPayloadStatus` | Status of the payload |
|
||||
| `total_tokens` | `int` | Total number of tokens |
|
||||
|
|
@ -39,6 +40,29 @@ Found under `kwargs["standard_logging_object"]`. This is a standard payload, log
|
|||
| `model_parameters` | `dict` | Model parameters |
|
||||
| `hidden_params` | `StandardLoggingHiddenParams` | Hidden parameters |
|
||||
|
||||
## Cost Breakdown
|
||||
|
||||
The `cost_breakdown` field provides detailed cost breakdown for completion requests as a `CostBreakdown` object containing:
|
||||
|
||||
- **`input_cost`**: Cost of input/prompt tokens including cache creation tokens
|
||||
- **`output_cost`**: Cost of output/completion tokens (including reasoning tokens if applicable)
|
||||
- **`tool_usage_cost`**: Cost of built-in tools usage (e.g., web search, code interpreter)
|
||||
- **`total_cost`**: Total cost of input + output + tool usage
|
||||
|
||||
**Note**: This field is populated for all call types. For non-completion calls, `input_cost` and `output_cost` may be 0.
|
||||
|
||||
The total cost relationship is: `response_cost = cost_breakdown.total_cost`
|
||||
|
||||
### CostBreakdown Type
|
||||
|
||||
```python
|
||||
class CostBreakdown(TypedDict, total=False):
|
||||
input_cost: float # Cost of input/prompt tokens in USD
|
||||
output_cost: float # Cost of output/completion tokens in USD (includes reasoning)
|
||||
tool_usage_cost: float # Cost of built-in tools usage in USD
|
||||
total_cost: float # Total cost in USD
|
||||
```
|
||||
|
||||
## StandardLoggingUserAPIKeyMetadata
|
||||
|
||||
| Field | Type | Description |
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import Image from '@theme/IdealImage';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
|
|
@ -6,6 +5,11 @@ import TabItem from '@theme/TabItem';
|
|||
|
||||
Store prompts as `.prompt` files in your repository and use them directly with LiteLLM. No external services required.
|
||||
|
||||
## Supported Integrations
|
||||
|
||||
- **File System**: Store `.prompt` files locally
|
||||
- **BitBucket**: Store `.prompt` files in BitBucket repositories with team-based access control
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Tabs>
|
||||
|
|
@ -41,6 +45,50 @@ response = litellm.completion(
|
|||
)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bitbucket" label="BITBUCKET">
|
||||
|
||||
**1. Create a .prompt file in BitBucket**
|
||||
|
||||
Create `prompts/hello.prompt` in your BitBucket repository:
|
||||
|
||||
```yaml
|
||||
---
|
||||
model: gpt-4
|
||||
temperature: 0.7
|
||||
---
|
||||
System: You are a helpful assistant.
|
||||
|
||||
User: {{user_message}}
|
||||
```
|
||||
|
||||
**2. Configure BitBucket access**
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
# Configure BitBucket access
|
||||
bitbucket_config = {
|
||||
"workspace": "your-workspace",
|
||||
"repository": "your-repo",
|
||||
"access_token": "your-access-token",
|
||||
"branch": "main"
|
||||
}
|
||||
|
||||
# Set global BitBucket configuration
|
||||
litellm.set_global_bitbucket_config(bitbucket_config)
|
||||
```
|
||||
|
||||
**3. Use with LiteLLM**
|
||||
|
||||
```python
|
||||
response = litellm.completion(
|
||||
model="bitbucket/gpt-4",
|
||||
prompt_id="hello",
|
||||
prompt_variables={"user_message": "What is the capital of France?"}
|
||||
)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
|
|
@ -70,6 +118,12 @@ model_list:
|
|||
|
||||
litellm_settings:
|
||||
global_prompt_directory: "./prompts"
|
||||
# Or use BitBucket for team-based prompt management
|
||||
global_bitbucket_config:
|
||||
workspace: "your-workspace"
|
||||
repository: "your-repo"
|
||||
access_token: "your-access-token"
|
||||
branch: "main"
|
||||
```
|
||||
|
||||
**3. Start the proxy**
|
||||
|
|
@ -142,21 +196,43 @@ User: {{user_message}}
|
|||
|
||||
### API Reference
|
||||
|
||||
For dotprompt integration, use these parameters:
|
||||
For prompt integrations, use these parameters:
|
||||
|
||||
**File System (dotprompt):**
|
||||
```
|
||||
model: dotprompt/<base_model> # required (e.g., dotprompt/gpt-4)
|
||||
prompt_id: str # required - the .prompt filename without extension
|
||||
prompt_variables: Optional[dict] # optional - variables for template rendering
|
||||
```
|
||||
|
||||
**Example API call:**
|
||||
**BitBucket:**
|
||||
```
|
||||
model: bitbucket/<base_model> # required (e.g., bitbucket/gpt-4)
|
||||
prompt_id: str # required - the .prompt filename without extension
|
||||
prompt_variables: Optional[dict] # optional - variables for template rendering
|
||||
bitbucket_config: Optional[dict] # optional - BitBucket configuration (if not set globally)
|
||||
```
|
||||
|
||||
**Example API calls:**
|
||||
|
||||
```python
|
||||
# File system integration
|
||||
response = litellm.completion(
|
||||
model="dotprompt/gpt-4",
|
||||
prompt_id="hello",
|
||||
prompt_variables={"user_message": "Hello world"},
|
||||
messages=[{"role": "user", "content": "This will be ignored"}]
|
||||
)
|
||||
|
||||
# BitBucket integration
|
||||
response = litellm.completion(
|
||||
model="bitbucket/gpt-4",
|
||||
prompt_id="hello",
|
||||
prompt_variables={"user_message": "Hello world"},
|
||||
bitbucket_config={
|
||||
"workspace": "your-workspace",
|
||||
"repository": "your-repo",
|
||||
"access_token": "your-token"
|
||||
}
|
||||
)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ export PROXY_LOGOUT_URL="https://www.google.com"
|
|||
<Image img={require('../../img/ui_logout.png')} style={{ width: '400px', height: 'auto' }} />
|
||||
|
||||
|
||||
### Set max budget for internal users
|
||||
### Set default max budget for internal users
|
||||
|
||||
Automatically apply budget per internal user when they sign up. By default the table will be checked every 10 minutes, for users to reset. To modify this, [see this](./users.md#reset-budgets)
|
||||
|
||||
|
|
@ -239,6 +239,10 @@ litellm_settings:
|
|||
|
||||
This sets a max budget of $10 USD for internal users when they sign up.
|
||||
|
||||
You can also manage these settings visually in the UI:
|
||||
|
||||
<Image img={require('../../img/default_user_settings_admin_ui.png')} style={{ width: '700px', height: 'auto' }} />
|
||||
|
||||
This budget only applies to personal keys created by that user - seen under `Default Team` on the UI.
|
||||
|
||||
<Image img={require('../../img/max_budget_for_internal_users.png')} style={{ width: '500px', height: 'auto' }} />
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ Email us @ krrish@berri.ai
|
|||
## Supported Models for LiteLLM Key
|
||||
These are the models that currently work with the "sk-litellm-.." keys.
|
||||
|
||||
For a complete list of models/providers that you can call with LiteLLM, [check out our provider list](./providers/)
|
||||
For a complete list of models/providers that you can call with LiteLLM, [check out our provider list](./providers/) or check out [models.litellm.ai](https://models.litellm.ai/)
|
||||
|
||||
* OpenAI models - [OpenAI docs](./providers/openai.md)
|
||||
* gpt-4
|
||||
|
|
|
|||
|
|
@ -109,6 +109,8 @@ curl http://0.0.0.0:4000/rerank \
|
|||
|
||||
## **Supported Providers**
|
||||
|
||||
#### ⚡️See all supported models and providers at [models.litellm.ai](https://models.litellm.ai/)
|
||||
|
||||
| Provider | Link to Usage |
|
||||
|-------------|--------------------|
|
||||
| Cohere (v1 + v2 clients) | [Usage](#quick-start) |
|
||||
|
|
|
|||
|
|
@ -3,8 +3,11 @@ import TabItem from '@theme/TabItem';
|
|||
|
||||
# /responses [Beta]
|
||||
|
||||
|
||||
LiteLLM provides a BETA endpoint in the spec of [OpenAI's `/responses` API](https://platform.openai.com/docs/api-reference/responses)
|
||||
|
||||
Requests to /chat/completions may be bridged here automatically when the provider lacks support for that endpoint. The model’s default `mode` determines how bridging works.(see `model_prices_and_context_window`)
|
||||
|
||||
| Feature | Supported | Notes |
|
||||
|---------|-----------|--------|
|
||||
| Cost Tracking | ✅ | Works with all supported models |
|
||||
|
|
@ -78,6 +81,43 @@ print(retrieved_response)
|
|||
# retrieved_response = await litellm.aget_responses(response_id=response_id)
|
||||
```
|
||||
|
||||
#### CANCEL a Response
|
||||
You can cancel an in-progress response (if supported by the provider):
|
||||
|
||||
```python showLineNumbers title="Cancel Response by ID"
|
||||
import litellm
|
||||
|
||||
# First, create a response
|
||||
response = litellm.responses(
|
||||
model="openai/o1-pro",
|
||||
input="Tell me a three sentence bedtime story about a unicorn.",
|
||||
max_output_tokens=100
|
||||
)
|
||||
|
||||
# Get the response ID
|
||||
response_id = response.id
|
||||
|
||||
# Cancel the response by ID
|
||||
cancel_response = litellm.cancel_responses(
|
||||
response_id=response_id
|
||||
)
|
||||
|
||||
print(cancel_response)
|
||||
|
||||
# For async usage
|
||||
# cancel_response = await litellm.acancel_responses(response_id=response_id)
|
||||
```
|
||||
|
||||
|
||||
**REST API:**
|
||||
```bash
|
||||
curl -X POST http://localhost:4000/v1/responses/response_id/cancel \
|
||||
-H "Authorization: Bearer sk-1234"
|
||||
```
|
||||
|
||||
This will attempt to cancel the in-progress response with the given ID.
|
||||
**Note:** Not all providers support response cancellation. If unsupported, an error will be raised.
|
||||
|
||||
#### DELETE a Response
|
||||
```python showLineNumbers title="Delete Response by ID"
|
||||
import litellm
|
||||
|
|
@ -795,9 +835,9 @@ curl http://localhost:4000/v1/responses \
|
|||
|
||||
|
||||
|
||||
## Session Management - Non-OpenAI Models
|
||||
## Session Management
|
||||
|
||||
LiteLLM Proxy supports session management for non-OpenAI models. This allows you to store and fetch conversation history (state) in LiteLLM Proxy.
|
||||
LiteLLM Proxy supports session management for all supported models. This allows you to store and fetch conversation history (state) in LiteLLM Proxy.
|
||||
|
||||
#### Usage
|
||||
|
||||
|
|
|
|||
BIN
docs/my-website/img/default_user_settings_admin_ui.png
Normal file
BIN
docs/my-website/img/default_user_settings_admin_ui.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 234 KiB |
14
docs/my-website/package-lock.json
generated
14
docs/my-website/package-lock.json
generated
|
|
@ -17120,9 +17120,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/prebuild-install/node_modules/tar-fs": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz",
|
||||
"integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
|
||||
"integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
|
|
@ -19295,9 +19296,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tar-fs": {
|
||||
"version": "3.0.10",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.10.tgz",
|
||||
"integrity": "sha512-C1SwlQGNLe/jPNqapK8epDsXME7CAJR5RL3GcE6KWx1d9OUByzoHVcbu1VPI8tevg9H8Alae0AApHHFGzrD5zA==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz",
|
||||
"integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^3.1.5"
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ This release allow you to group requests to LiteLLM proxy into a session. If you
|
|||
1. Added support for max_completion_tokens parameter [Get Started](https://docs.litellm.ai/docs/providers/sagemaker), [PR](https://github.com/BerriAI/litellm/pull/10300)
|
||||
- **Responses API**
|
||||
1. Added support for GET and DELETE operations - `/v1/responses/{response_id}` [Get Started](../../docs/response_api)
|
||||
2. Added session management support for non-OpenAI models [PR](https://github.com/BerriAI/litellm/pull/10321)
|
||||
2. Added session management support for all supported models [PR](https://github.com/BerriAI/litellm/pull/10321)
|
||||
3. Added routing affinity to maintain model consistency within sessions [Get Started](https://docs.litellm.ai/docs/response_api#load-balancing-with-routing-affinity), [PR](https://github.com/BerriAI/litellm/pull/10193)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -57,32 +57,31 @@ const sidebars = {
|
|||
type: "category",
|
||||
label: "Alerting & Monitoring",
|
||||
items: [
|
||||
"proxy/prometheus",
|
||||
"proxy/alerting",
|
||||
"proxy/pagerduty"
|
||||
].sort()
|
||||
"proxy/pagerduty",
|
||||
"proxy/prometheus"
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "[Beta] Prompt Management",
|
||||
items: [
|
||||
"proxy/prompt_management",
|
||||
"proxy/custom_prompt_management",
|
||||
"proxy/native_litellm_prompt",
|
||||
"proxy/custom_prompt_management"
|
||||
].sort()
|
||||
"proxy/prompt_management"
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "AI Tools (OpenWebUI, Claude Code, etc.)",
|
||||
items: [
|
||||
"integrations/letta",
|
||||
"tutorials/openweb_ui",
|
||||
"tutorials/openai_codex",
|
||||
"tutorials/litellm_gemini_cli",
|
||||
"tutorials/litellm_qwen_code_cli",
|
||||
"tutorials/github_copilot_integration",
|
||||
"tutorials/claude_responses_api",
|
||||
"tutorials/cost_tracking_coding",
|
||||
"tutorials/github_copilot_integration",
|
||||
"tutorials/litellm_gemini_cli",
|
||||
"tutorials/litellm_qwen_code_cli",
|
||||
"tutorials/openai_codex",
|
||||
"tutorials/openweb_ui"
|
||||
]
|
||||
},
|
||||
|
||||
|
|
@ -112,29 +111,115 @@ const sidebars = {
|
|||
label: "Setup & Deployment",
|
||||
items: [
|
||||
"proxy/quick_start",
|
||||
"proxy/user_onboarding",
|
||||
"proxy/deploy",
|
||||
"proxy/prod",
|
||||
"proxy/cli",
|
||||
"proxy/release_cycle",
|
||||
"proxy/model_management",
|
||||
"proxy/health",
|
||||
"proxy/debugging",
|
||||
"proxy/deploy",
|
||||
"proxy/health",
|
||||
"proxy/master_key_rotations",
|
||||
"proxy/model_management",
|
||||
"proxy/prod",
|
||||
"proxy/release_cycle",
|
||||
],
|
||||
},
|
||||
"proxy/demo",
|
||||
{
|
||||
type: "category",
|
||||
label: "Admin UI",
|
||||
items: [
|
||||
"proxy/admin_ui_sso",
|
||||
"proxy/custom_root_ui",
|
||||
"proxy/custom_sso",
|
||||
"proxy/model_hub",
|
||||
"proxy/public_teams",
|
||||
"proxy/self_serve",
|
||||
"proxy/ui",
|
||||
"proxy/ui/bulk_edit_users",
|
||||
"proxy/ui_credentials",
|
||||
"tutorials/scim_litellm",
|
||||
{
|
||||
type: "category",
|
||||
label: "UI Logs",
|
||||
items: [
|
||||
"proxy/ui_logs",
|
||||
"proxy/ui_logs_sessions"
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Architecture",
|
||||
items: ["proxy/architecture", "proxy/control_plane_and_data_plane", "proxy/db_info", "proxy/db_deadlocks", "router_architecture", "proxy/user_management_heirarchy", "proxy/jwt_auth_arch", "proxy/image_handling", "proxy/spend_logs_deletion"],
|
||||
items: [
|
||||
"proxy/architecture",
|
||||
"proxy/control_plane_and_data_plane",
|
||||
"proxy/db_deadlocks",
|
||||
"proxy/db_info",
|
||||
"proxy/image_handling",
|
||||
"proxy/jwt_auth_arch",
|
||||
"proxy/spend_logs_deletion",
|
||||
"proxy/user_management_heirarchy",
|
||||
"router_architecture"
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
label: "All Endpoints (Swagger)",
|
||||
href: "https://litellm-api.up.railway.app/",
|
||||
},
|
||||
"proxy/management_cli",
|
||||
"proxy/enterprise",
|
||||
"proxy/management_cli",
|
||||
{
|
||||
type: "category",
|
||||
label: "Authentication",
|
||||
items: [
|
||||
"proxy/virtual_keys",
|
||||
"proxy/token_auth",
|
||||
"proxy/service_accounts",
|
||||
"proxy/access_control",
|
||||
"proxy/cli_sso",
|
||||
"proxy/custom_auth",
|
||||
"proxy/ip_address",
|
||||
"proxy/email",
|
||||
"proxy/multiple_admins",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Budgets + Rate Limits",
|
||||
items: [
|
||||
"proxy/customers",
|
||||
"proxy/dynamic_rate_limit",
|
||||
"proxy/rate_limit_tiers",
|
||||
"proxy/team_budgets",
|
||||
"proxy/temporary_budget_increase",
|
||||
"proxy/users"
|
||||
],
|
||||
},
|
||||
"proxy/caching",
|
||||
{
|
||||
type: "category",
|
||||
label: "Create Custom Plugins",
|
||||
description: "Modify requests, responses, and more",
|
||||
items: [
|
||||
"proxy/call_hooks",
|
||||
"proxy/rules",
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
label: "Load Balancing, Routing, Fallbacks",
|
||||
href: "https://docs.litellm.ai/docs/routing-load-balancing",
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Logging, Alerting, Metrics",
|
||||
items: [
|
||||
"proxy/dynamic_logging",
|
||||
"proxy/logging",
|
||||
"proxy/logging_spec",
|
||||
"proxy/team_logging"
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Making LLM Requests",
|
||||
|
|
@ -147,19 +232,6 @@ const sidebars = {
|
|||
"proxy/model_discovery",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Authentication",
|
||||
items: [
|
||||
"proxy/virtual_keys",
|
||||
"proxy/token_auth",
|
||||
"proxy/service_accounts",
|
||||
"proxy/access_control",
|
||||
"proxy/ip_address",
|
||||
"proxy/email",
|
||||
"proxy/custom_auth",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Model Access",
|
||||
|
|
@ -168,73 +240,6 @@ const sidebars = {
|
|||
"proxy/team_model_add"
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Spend Tracking",
|
||||
items: ["proxy/cost_tracking", "proxy/custom_pricing", "proxy/billing",],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Budgets + Rate Limits",
|
||||
items: ["proxy/users", "proxy/temporary_budget_increase", "proxy/rate_limit_tiers", "proxy/team_budgets", "proxy/dynamic_rate_limit", "proxy/customers"],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Enterprise Features",
|
||||
items: [
|
||||
"proxy/enterprise",
|
||||
{
|
||||
type: "category",
|
||||
label: "Admin UI",
|
||||
items: [
|
||||
"proxy/ui",
|
||||
"proxy/admin_ui_sso",
|
||||
"proxy/custom_root_ui",
|
||||
"proxy/model_hub",
|
||||
"proxy/self_serve",
|
||||
"proxy/public_teams",
|
||||
"proxy/ui_credentials",
|
||||
"proxy/ui/bulk_edit_users",
|
||||
{
|
||||
type: "category",
|
||||
label: "UI Logs",
|
||||
items: [
|
||||
"proxy/ui_logs",
|
||||
"proxy/ui_logs_sessions"
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "SSO & Identity Management",
|
||||
items: [
|
||||
"proxy/cli_sso",
|
||||
"proxy/admin_ui_sso",
|
||||
"proxy/custom_sso",
|
||||
"tutorials/scim_litellm",
|
||||
"tutorials/msft_sso",
|
||||
"proxy/multiple_admins",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
label: "Load Balancing, Routing, Fallbacks",
|
||||
href: "https://docs.litellm.ai/docs/routing-load-balancing",
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Logging, Alerting, Metrics",
|
||||
items: [
|
||||
"proxy/logging",
|
||||
"proxy/logging_spec",
|
||||
"proxy/team_logging",
|
||||
"proxy/dynamic_logging"
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
type: "category",
|
||||
label: "Secret Managers",
|
||||
|
|
@ -245,14 +250,13 @@ const sidebars = {
|
|||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Create Custom Plugins",
|
||||
description: "Modify requests, responses, and more",
|
||||
label: "Spend Tracking",
|
||||
items: [
|
||||
"proxy/call_hooks",
|
||||
"proxy/rules",
|
||||
]
|
||||
"proxy/billing",
|
||||
"proxy/cost_tracking",
|
||||
"proxy/custom_pricing"
|
||||
],
|
||||
},
|
||||
"proxy/caching",
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -266,13 +270,11 @@ const sidebars = {
|
|||
slug: "/supported_endpoints",
|
||||
},
|
||||
items: [
|
||||
"anthropic_unified",
|
||||
"apply_guardrail",
|
||||
"assistants",
|
||||
{
|
||||
type: "category",
|
||||
label: "/audio",
|
||||
"items": [
|
||||
items: [
|
||||
"audio_transcription",
|
||||
"text_to_speech",
|
||||
]
|
||||
|
|
@ -301,6 +303,7 @@ const sidebars = {
|
|||
"completion/http_handler_config",
|
||||
],
|
||||
},
|
||||
"text_completion",
|
||||
"embedding/supported_embedding",
|
||||
{
|
||||
type: "category",
|
||||
|
|
@ -318,13 +321,14 @@ const sidebars = {
|
|||
"proxy/managed_finetuning",
|
||||
]
|
||||
},
|
||||
"generateContent",
|
||||
"generateContent",
|
||||
"apply_guardrail",
|
||||
{
|
||||
type: "category",
|
||||
label: "/images",
|
||||
items: [
|
||||
"image_generation",
|
||||
"image_edits",
|
||||
"image_generation",
|
||||
"image_variations",
|
||||
]
|
||||
},
|
||||
|
|
@ -335,23 +339,23 @@ const sidebars = {
|
|||
label: "Pass-through Endpoints (Anthropic SDK, etc.)",
|
||||
items: [
|
||||
"pass_through/intro",
|
||||
"pass_through/vertex_ai",
|
||||
"pass_through/google_ai_studio",
|
||||
"pass_through/anthropic_completion",
|
||||
"pass_through/assembly_ai",
|
||||
"pass_through/bedrock",
|
||||
"pass_through/cohere",
|
||||
"pass_through/vllm",
|
||||
"pass_through/google_ai_studio",
|
||||
"pass_through/langfuse",
|
||||
"pass_through/mistral",
|
||||
"pass_through/openai_passthrough",
|
||||
"pass_through/anthropic_completion",
|
||||
"pass_through/bedrock",
|
||||
"pass_through/assembly_ai",
|
||||
"pass_through/langfuse",
|
||||
"proxy/pass_through",
|
||||
],
|
||||
"pass_through/vertex_ai",
|
||||
"pass_through/vllm",
|
||||
"proxy/pass_through"
|
||||
]
|
||||
},
|
||||
"realtime",
|
||||
"rerank",
|
||||
"response_api",
|
||||
"text_completion",
|
||||
"anthropic_unified",
|
||||
{
|
||||
type: "category",
|
||||
label: "/vector_stores",
|
||||
|
|
@ -398,7 +402,6 @@ const sidebars = {
|
|||
items: [
|
||||
"providers/azure_ai",
|
||||
"providers/azure_ai_img",
|
||||
"providers/azure_ai_img_edit",
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -515,33 +518,39 @@ const sidebars = {
|
|||
type: "category",
|
||||
label: "Guides",
|
||||
items: [
|
||||
"exception_mapping",
|
||||
{
|
||||
type: "category",
|
||||
label: "Tools",
|
||||
items: [
|
||||
"completion/computer_use",
|
||||
"completion/web_search",
|
||||
"completion/web_fetch",
|
||||
"completion/function_call",
|
||||
]
|
||||
},
|
||||
"completion/audio",
|
||||
"completion/document_understanding",
|
||||
"completion/drop_params",
|
||||
"completion/image_generation_chat",
|
||||
"completion/json_mode",
|
||||
"completion/knowledgebase",
|
||||
"completion/message_trimming",
|
||||
"completion/model_alias",
|
||||
"completion/mock_requests",
|
||||
"completion/predict_outputs",
|
||||
"completion/prefix",
|
||||
"completion/prompt_caching",
|
||||
"completion/prompt_formatting",
|
||||
"completion/reliable_completions",
|
||||
"completion/stream",
|
||||
"completion/provider_specific_params",
|
||||
"completion/vision",
|
||||
"exception_mapping",
|
||||
"completion/batching",
|
||||
"guides/finetuned_models",
|
||||
"guides/security_settings",
|
||||
"completion/audio",
|
||||
"completion/image_generation_chat",
|
||||
"completion/web_search",
|
||||
"completion/document_understanding",
|
||||
"completion/vision",
|
||||
"completion/json_mode",
|
||||
"reasoning_content",
|
||||
"completion/computer_use",
|
||||
"completion/prompt_caching",
|
||||
"completion/predict_outputs",
|
||||
"completion/knowledgebase",
|
||||
"completion/prefix",
|
||||
"completion/drop_params",
|
||||
"completion/prompt_formatting",
|
||||
"completion/stream",
|
||||
"completion/message_trimming",
|
||||
"completion/function_call",
|
||||
"completion/model_alias",
|
||||
"completion/batching",
|
||||
"completion/mock_requests",
|
||||
"completion/reliable_completions",
|
||||
"proxy/veo_video_generation",
|
||||
|
||||
"reasoning_content"
|
||||
]
|
||||
},
|
||||
|
||||
|
|
@ -554,26 +563,35 @@ const sidebars = {
|
|||
description: "Learn how to load balance, route, and set fallbacks for your LLM requests",
|
||||
slug: "/routing-load-balancing",
|
||||
},
|
||||
items: ["routing", "scheduler", "proxy/load_balancing", "proxy/reliability", "proxy/timeout", "proxy/auto_routing", "proxy/tag_routing", "proxy/provider_budget_routing", "wildcard_routing"],
|
||||
items: [
|
||||
"routing",
|
||||
"scheduler",
|
||||
"proxy/auto_routing",
|
||||
"proxy/load_balancing",
|
||||
"proxy/provider_budget_routing",
|
||||
"proxy/reliability",
|
||||
"proxy/tag_routing",
|
||||
"proxy/timeout",
|
||||
"wildcard_routing"
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "LiteLLM Python SDK",
|
||||
items: [
|
||||
"set_keys",
|
||||
"completion/token_usage",
|
||||
"sdk/headers",
|
||||
"sdk_custom_pricing",
|
||||
"embedding/async_embedding",
|
||||
"embedding/moderation",
|
||||
"budget_manager",
|
||||
"caching/all_caches",
|
||||
"completion/token_usage",
|
||||
"embedding/async_embedding",
|
||||
"embedding/moderation",
|
||||
"migration",
|
||||
"sdk_custom_pricing",
|
||||
{
|
||||
type: "category",
|
||||
label: "LangChain, LlamaIndex, Instructor Integration",
|
||||
items: ["langchain/langchain", "tutorials/instructor"],
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1699,7 +1699,7 @@ This release allow you to group requests to LiteLLM proxy into a session. If you
|
|||
1. Added support for max\_completion\_tokens parameter [Get Started](https://docs.litellm.ai/docs/providers/sagemaker), [PR](https://github.com/BerriAI/litellm/pull/10300)
|
||||
- **Responses API**
|
||||
1. Added support for GET and DELETE operations - `/v1/responses/{response_id}` [Get Started](https://docs.litellm.ai/docs/response_api)
|
||||
2. Added session management support for non-OpenAI models [PR](https://github.com/BerriAI/litellm/pull/10321)
|
||||
2. Added session management support for all supported models [PR](https://github.com/BerriAI/litellm/pull/10321)
|
||||
3. Added routing affinity to maintain model consistency within sessions [Get Started](https://docs.litellm.ai/docs/response_api#load-balancing-with-routing-affinity), [PR](https://github.com/BerriAI/litellm/pull/10193)
|
||||
|
||||
## Spend Tracking Improvements [](https://docs.litellm.ai/release_notes\#spend-tracking-improvements "Direct link to Spend Tracking Improvements")
|
||||
|
|
@ -7736,7 +7736,7 @@ This release allow you to group requests to LiteLLM proxy into a session. If you
|
|||
1. Added support for max\_completion\_tokens parameter [Get Started](https://docs.litellm.ai/docs/providers/sagemaker), [PR](https://github.com/BerriAI/litellm/pull/10300)
|
||||
- **Responses API**
|
||||
1. Added support for GET and DELETE operations - `/v1/responses/{response_id}` [Get Started](https://docs.litellm.ai/docs/response_api)
|
||||
2. Added session management support for non-OpenAI models [PR](https://github.com/BerriAI/litellm/pull/10321)
|
||||
2. Added session management support for all supported models [PR](https://github.com/BerriAI/litellm/pull/10321)
|
||||
3. Added routing affinity to maintain model consistency within sessions [Get Started](https://docs.litellm.ai/docs/response_api#load-balancing-with-routing-affinity), [PR](https://github.com/BerriAI/litellm/pull/10193)
|
||||
|
||||
## Spend Tracking Improvements [](https://docs.litellm.ai/release_notes/tags/responses-api\#spend-tracking-improvements "Direct link to Spend Tracking Improvements")
|
||||
|
|
@ -8295,7 +8295,7 @@ This release allow you to group requests to LiteLLM proxy into a session. If you
|
|||
1. Added support for max\_completion\_tokens parameter [Get Started](https://docs.litellm.ai/docs/providers/sagemaker), [PR](https://github.com/BerriAI/litellm/pull/10300)
|
||||
- **Responses API**
|
||||
1. Added support for GET and DELETE operations - `/v1/responses/{response_id}` [Get Started](https://docs.litellm.ai/docs/response_api)
|
||||
2. Added session management support for non-OpenAI models [PR](https://github.com/BerriAI/litellm/pull/10321)
|
||||
2. Added session management support for all supported models [PR](https://github.com/BerriAI/litellm/pull/10321)
|
||||
3. Added routing affinity to maintain model consistency within sessions [Get Started](https://docs.litellm.ai/docs/response_api#load-balancing-with-routing-affinity), [PR](https://github.com/BerriAI/litellm/pull/10193)
|
||||
|
||||
## Spend Tracking Improvements [](https://docs.litellm.ai/release_notes/tags/security\#spend-tracking-improvements "Direct link to Spend Tracking Improvements")
|
||||
|
|
@ -8821,7 +8821,7 @@ This release allow you to group requests to LiteLLM proxy into a session. If you
|
|||
1. Added support for max\_completion\_tokens parameter [Get Started](https://docs.litellm.ai/docs/providers/sagemaker), [PR](https://github.com/BerriAI/litellm/pull/10300)
|
||||
- **Responses API**
|
||||
1. Added support for GET and DELETE operations - `/v1/responses/{response_id}` [Get Started](https://docs.litellm.ai/docs/response_api)
|
||||
2. Added session management support for non-OpenAI models [PR](https://github.com/BerriAI/litellm/pull/10321)
|
||||
2. Added session management support for all supported models [PR](https://github.com/BerriAI/litellm/pull/10321)
|
||||
3. Added routing affinity to maintain model consistency within sessions [Get Started](https://docs.litellm.ai/docs/response_api#load-balancing-with-routing-affinity), [PR](https://github.com/BerriAI/litellm/pull/10193)
|
||||
|
||||
## Spend Tracking Improvements [](https://docs.litellm.ai/release_notes/tags/session-management\#spend-tracking-improvements "Direct link to Spend Tracking Improvements")
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Callback to log events to a Generic API Endpoint
|
|||
import asyncio
|
||||
import os
|
||||
import traceback
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
import litellm
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Polls LiteLLM_ManagedObjectTable to check if the batch job is complete, and if the cost has been tracked.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Optional, cast
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import asyncio
|
||||
import base64
|
||||
import json
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union, cast
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
|
|
|||
BIN
litellm-proxy-extras/dist/litellm_proxy_extras-0.2.20-py3-none-any.whl
vendored
Normal file
BIN
litellm-proxy-extras/dist/litellm_proxy_extras-0.2.20-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
litellm-proxy-extras/dist/litellm_proxy_extras-0.2.20.tar.gz
vendored
Normal file
BIN
litellm-proxy-extras/dist/litellm_proxy_extras-0.2.20.tar.gz
vendored
Normal file
Binary file not shown.
|
|
@ -221,6 +221,10 @@ model LiteLLM_VerificationToken {
|
|||
created_by String?
|
||||
updated_at DateTime? @default(now()) @updatedAt @map("updated_at")
|
||||
updated_by String?
|
||||
rotation_count Int? @default(0) // Number of times key has been rotated
|
||||
auto_rotate Boolean? @default(false) // Whether this key should be auto-rotated
|
||||
rotation_interval String? // How often to rotate (e.g., "30d", "90d")
|
||||
last_rotation_at DateTime? // When this key was last rotated
|
||||
litellm_budget_table LiteLLM_BudgetTable? @relation(fields: [budget_id], references: [budget_id])
|
||||
litellm_organization_table LiteLLM_OrganizationTable? @relation(fields: [organization_id], references: [organization_id])
|
||||
object_permission LiteLLM_ObjectPermissionTable? @relation(fields: [object_permission_id], references: [object_permission_id])
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "litellm-proxy-extras"
|
||||
version = "0.2.19"
|
||||
version = "0.2.20"
|
||||
description = "Additional files for the LiteLLM Proxy. Reduces the size of the main litellm package."
|
||||
authors = ["BerriAI"]
|
||||
readme = "README.md"
|
||||
|
|
@ -22,7 +22,7 @@ requires = ["poetry-core"]
|
|||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.commitizen]
|
||||
version = "0.2.19"
|
||||
version = "0.2.20"
|
||||
version_files = [
|
||||
"pyproject.toml:version",
|
||||
"../requirements.txt:litellm-proxy-extras==",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from typing import (
|
|||
TYPE_CHECKING,
|
||||
)
|
||||
from litellm.types.integrations.datadog_llm_obs import DatadogLLMObsInitParams
|
||||
from litellm.types.integrations.datadog import DatadogInitParams
|
||||
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler
|
||||
from litellm.caching.caching import Cache, DualCache, RedisCache, InMemoryCache
|
||||
from litellm.caching.llm_caching_handler import LLMClientCache
|
||||
|
|
@ -150,6 +151,7 @@ _custom_logger_compatible_callbacks_literal = Literal[
|
|||
"aws_sqs",
|
||||
"vector_store_pre_call_hook",
|
||||
"dotprompt",
|
||||
"bitbucket",
|
||||
"cloudzero",
|
||||
"posthog",
|
||||
]
|
||||
|
|
@ -343,6 +345,7 @@ suppress_debug_info = False
|
|||
dynamodb_table_name: Optional[str] = None
|
||||
s3_callback_params: Optional[Dict] = None
|
||||
datadog_llm_observability_params: Optional[Union[DatadogLLMObsInitParams, Dict]] = None
|
||||
datadog_params: Optional[Union[DatadogInitParams, Dict]] = None
|
||||
aws_sqs_callback_params: Optional[Dict] = None
|
||||
generic_logger_headers: Optional[Dict] = None
|
||||
default_key_generate_params: Optional[Dict] = None
|
||||
|
|
@ -1350,3 +1353,11 @@ from litellm.litellm_core_utils.cli_token_utils import get_litellm_gateway_api_k
|
|||
|
||||
### PASSTHROUGH ###
|
||||
from .passthrough import allm_passthrough_route, llm_passthrough_route
|
||||
|
||||
### GLOBAL CONFIG ###
|
||||
global_bitbucket_config: Optional[Dict[str, Any]] = None
|
||||
|
||||
def set_global_bitbucket_config(config: Dict[str, Any]) -> None:
|
||||
"""Set global BitBucket configuration for prompt management."""
|
||||
global global_bitbucket_config
|
||||
global_bitbucket_config = config
|
||||
|
|
|
|||
|
|
@ -1,17 +1,10 @@
|
|||
"""
|
||||
Internal unified UUID helper.
|
||||
|
||||
Tries to use fastuuid (performance) and falls back to stdlib uuid if unavailable.
|
||||
Always uses fastuuid for performance.
|
||||
"""
|
||||
|
||||
FASTUUID_AVAILABLE = False
|
||||
|
||||
try:
|
||||
import fastuuid as _uuid # type: ignore
|
||||
|
||||
FASTUUID_AVAILABLE = True
|
||||
except Exception: # pragma: no cover - fallback path
|
||||
import uuid as _uuid # type: ignore
|
||||
import fastuuid as _uuid # type: ignore
|
||||
|
||||
|
||||
# Expose a module-like alias so callers can use: uuid.uuid4()
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class InMemoryCache(BaseCache):
|
|||
max_size_in_memory [int]: Maximum number of items in cache. done to prevent memory leaks. Use 200 items as a default
|
||||
"""
|
||||
self.max_size_in_memory = (
|
||||
max_size_in_memory or 200
|
||||
max_size_in_memory if max_size_in_memory is not None else 200
|
||||
) # set an upper bound of 200 items in-memory
|
||||
self.default_ttl = default_ttl or 600
|
||||
self.max_size_per_item = (
|
||||
|
|
@ -103,20 +103,32 @@ class InMemoryCache(BaseCache):
|
|||
def evict_cache(self):
|
||||
"""
|
||||
Eviction policy:
|
||||
- check if any items in ttl_dict are expired -> remove them from ttl_dict and cache_dict
|
||||
1. First, remove expired items from ttl_dict and cache_dict
|
||||
2. If cache is still at or above max_size_in_memory, evict items with earliest expiration times
|
||||
|
||||
|
||||
This guarantees the following:
|
||||
- 1. When item ttl not set: At minimumm each item will remain in memory for 5 minutes
|
||||
- 2. When ttl is set: the item will remain in memory for at least that amount of time
|
||||
- 1. When item ttl not set: At minimum each item will remain in memory for the default ttl
|
||||
- 2. When ttl is set: the item will remain in memory for at least that amount of time, unless cache size requires eviction
|
||||
- 3. the size of in-memory cache is bounded
|
||||
|
||||
"""
|
||||
current_time = time.time()
|
||||
|
||||
# Step 1: Remove expired items
|
||||
expired_keys = [key for key, ttl in self.ttl_dict.items() if current_time > ttl]
|
||||
for key in expired_keys:
|
||||
self._remove_key(key)
|
||||
|
||||
# Step 2: If cache is still full, evict items with earliest expiration times
|
||||
if len(self.cache_dict) >= self.max_size_in_memory:
|
||||
# Sort by expiration time (earliest first) and evict until we're under the limit
|
||||
items_by_expiration = sorted(self.ttl_dict.items(), key=lambda x: x[1])
|
||||
keys_to_evict = items_by_expiration[:len(self.cache_dict) - self.max_size_in_memory + 1]
|
||||
|
||||
for key, _ in keys_to_evict:
|
||||
self._remove_key(key)
|
||||
|
||||
# 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.
|
||||
|
|
@ -135,6 +147,10 @@ class InMemoryCache(BaseCache):
|
|||
return False
|
||||
|
||||
def set_cache(self, key, value, **kwargs):
|
||||
# Handle the edge case where max_size_in_memory is 0
|
||||
if self.max_size_in_memory == 0:
|
||||
return # Don't cache anything if max size is 0
|
||||
|
||||
if len(self.cache_dict) >= self.max_size_in_memory:
|
||||
# only evict when cache is full
|
||||
self.evict_cache()
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ class QdrantSemanticCache(BaseCache):
|
|||
|
||||
def set_cache(self, key, value, **kwargs):
|
||||
print_verbose(f"qdrant semantic-cache set_cache, kwargs: {kwargs}")
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
|
||||
# get the prompt
|
||||
messages = kwargs["messages"]
|
||||
|
|
@ -279,7 +279,7 @@ class QdrantSemanticCache(BaseCache):
|
|||
pass
|
||||
|
||||
async def async_set_cache(self, key, value, **kwargs):
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
|
||||
from litellm.proxy.proxy_server import llm_model_list, llm_router
|
||||
|
||||
|
|
|
|||
|
|
@ -812,6 +812,11 @@ BEDROCK_EMBEDDING_PROVIDERS_LITERAL = Literal[
|
|||
]
|
||||
|
||||
BEDROCK_CONVERSE_MODELS = [
|
||||
"qwen.qwen3-coder-480b-a35b-v1:0",
|
||||
"qwen.qwen3-235b-a22b-2507-v1:0",
|
||||
"qwen.qwen3-coder-30b-a3b-v1:0",
|
||||
"qwen.qwen3-32b-v1:0",
|
||||
"deepseek.v3-v1:0",
|
||||
"openai.gpt-oss-20b-1:0",
|
||||
"openai.gpt-oss-120b-1:0",
|
||||
"anthropic.claude-opus-4-1-20250805-v1:0",
|
||||
|
|
@ -984,7 +989,11 @@ HEALTH_CHECK_TIMEOUT_SECONDS = int(
|
|||
) # 60 seconds
|
||||
LITTELM_INTERNAL_HEALTH_SERVICE_ACCOUNT_NAME = "litellm-internal-health-check"
|
||||
LITTELM_CLI_SERVICE_ACCOUNT_NAME = "litellm-cli"
|
||||
LITELLM_INTERNAL_JOBS_SERVICE_ACCOUNT_NAME = "litellm_internal_jobs"
|
||||
|
||||
# Key Rotation Constants
|
||||
LITELLM_KEY_ROTATION_ENABLED = os.getenv("LITELLM_KEY_ROTATION_ENABLED", "false")
|
||||
LITELLM_KEY_ROTATION_CHECK_INTERVAL_SECONDS = int(os.getenv("LITELLM_KEY_ROTATION_CHECK_INTERVAL_SECONDS", 86400)) # 24 hours default
|
||||
UI_SESSION_TOKEN_TEAM_ID = "litellm-dashboard"
|
||||
LITELLM_PROXY_ADMIN_NAME = "default_user_id"
|
||||
|
||||
|
|
|
|||
|
|
@ -584,6 +584,42 @@ def _infer_call_type(
|
|||
return call_type
|
||||
|
||||
|
||||
def _store_cost_breakdown_in_logging_obj(
|
||||
litellm_logging_obj: Optional[LitellmLoggingObject],
|
||||
prompt_tokens_cost_usd_dollar: float,
|
||||
completion_tokens_cost_usd_dollar: float,
|
||||
cost_for_built_in_tools_cost_usd_dollar: float,
|
||||
total_cost_usd_dollar: float,
|
||||
) -> None:
|
||||
"""
|
||||
Helper function to store cost breakdown in the logging object.
|
||||
|
||||
Args:
|
||||
litellm_logging_obj: The logging object to store breakdown in
|
||||
call_type: Type of call (completion, etc.)
|
||||
prompt_tokens_cost_usd_dollar: Cost of input tokens
|
||||
completion_tokens_cost_usd_dollar: Cost of completion tokens (includes reasoning if applicable)
|
||||
cost_for_built_in_tools_cost_usd_dollar: Cost of built-in tools
|
||||
total_cost_usd_dollar: Total cost of request
|
||||
"""
|
||||
if (litellm_logging_obj is None):
|
||||
return
|
||||
|
||||
try:
|
||||
# Store the cost breakdown - reasoning cost is 0 since it's already included in completion cost
|
||||
litellm_logging_obj.set_cost_breakdown(
|
||||
input_cost=prompt_tokens_cost_usd_dollar,
|
||||
output_cost=completion_tokens_cost_usd_dollar,
|
||||
total_cost=total_cost_usd_dollar,
|
||||
cost_for_built_in_tools_cost_usd_dollar=cost_for_built_in_tools_cost_usd_dollar
|
||||
)
|
||||
|
||||
except Exception as breakdown_error:
|
||||
verbose_logger.debug(f"Error storing cost breakdown: {str(breakdown_error)}")
|
||||
# Don't fail the main cost calculation if breakdown storage fails
|
||||
pass
|
||||
|
||||
|
||||
def completion_cost( # noqa: PLR0915
|
||||
completion_response=None,
|
||||
model: Optional[str] = None,
|
||||
|
|
@ -923,7 +959,7 @@ def completion_cost( # noqa: PLR0915
|
|||
_final_cost = (
|
||||
prompt_tokens_cost_usd_dollar + completion_tokens_cost_usd_dollar
|
||||
)
|
||||
_final_cost += (
|
||||
cost_for_built_in_tools = (
|
||||
StandardBuiltInToolCostTracking.get_cost_for_built_in_tools(
|
||||
model=model,
|
||||
response_object=completion_response,
|
||||
|
|
@ -932,6 +968,17 @@ def completion_cost( # noqa: PLR0915
|
|||
custom_llm_provider=custom_llm_provider,
|
||||
)
|
||||
)
|
||||
_final_cost += cost_for_built_in_tools
|
||||
|
||||
# Store cost breakdown in logging object if available
|
||||
_store_cost_breakdown_in_logging_obj(
|
||||
litellm_logging_obj=litellm_logging_obj,
|
||||
prompt_tokens_cost_usd_dollar=prompt_tokens_cost_usd_dollar,
|
||||
completion_tokens_cost_usd_dollar=completion_tokens_cost_usd_dollar,
|
||||
cost_for_built_in_tools_cost_usd_dollar=cost_for_built_in_tools,
|
||||
total_cost_usd_dollar=_final_cost
|
||||
)
|
||||
|
||||
return _final_cost
|
||||
except Exception as e:
|
||||
verbose_logger.debug(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import asyncio
|
|||
import json
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Optional
|
||||
|
||||
|
|
|
|||
317
litellm/integrations/bitbucket/README.md
Normal file
317
litellm/integrations/bitbucket/README.md
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
# LiteLLM BitBucket Prompt Management
|
||||
|
||||
A powerful prompt management system for LiteLLM that fetches `.prompt` files from BitBucket repositories. This enables team-based prompt management with BitBucket's built-in access control and version control capabilities.
|
||||
|
||||
## Features
|
||||
|
||||
- **🏢 Team-based access control**: Leverage BitBucket's workspace and repository permissions
|
||||
- **📁 Repository-based prompt storage**: Store prompts in BitBucket repositories
|
||||
- **🔐 Multiple authentication methods**: Support for access tokens and basic auth
|
||||
- **🎯 YAML frontmatter**: Define model, parameters, and schemas in file headers
|
||||
- **🔧 Handlebars templating**: Use `{{variable}}` syntax with Jinja2 backend
|
||||
- **✅ Input validation**: Automatic validation against defined schemas
|
||||
- **🔗 LiteLLM integration**: Works seamlessly with `litellm.completion()`
|
||||
- **💬 Smart message parsing**: Converts prompts to proper chat messages
|
||||
- **⚙️ Parameter extraction**: Automatically applies model settings from prompts
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Set up BitBucket Repository
|
||||
|
||||
Create a repository in your BitBucket workspace and add `.prompt` files:
|
||||
|
||||
```
|
||||
your-repo/
|
||||
├── prompts/
|
||||
│ ├── chat_assistant.prompt
|
||||
│ ├── code_reviewer.prompt
|
||||
│ └── data_analyst.prompt
|
||||
```
|
||||
|
||||
### 2. Create a `.prompt` file
|
||||
|
||||
Create a file called `prompts/chat_assistant.prompt`:
|
||||
|
||||
```yaml
|
||||
---
|
||||
model: gpt-4
|
||||
temperature: 0.7
|
||||
max_tokens: 150
|
||||
input:
|
||||
schema:
|
||||
user_message: string
|
||||
system_context?: string
|
||||
---
|
||||
|
||||
{% if system_context %}System: {{system_context}}
|
||||
|
||||
{% endif %}User: {{user_message}}
|
||||
```
|
||||
|
||||
### 3. Configure BitBucket Access
|
||||
|
||||
#### Option A: Access Token (Recommended)
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
# Configure BitBucket access
|
||||
bitbucket_config = {
|
||||
"workspace": "your-workspace",
|
||||
"repository": "your-repo",
|
||||
"access_token": "your-access-token",
|
||||
"branch": "main" # optional, defaults to main
|
||||
}
|
||||
|
||||
# Set global BitBucket configuration
|
||||
litellm.set_global_bitbucket_config(bitbucket_config)
|
||||
```
|
||||
|
||||
#### Option B: Basic Authentication
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
# Configure BitBucket access with basic auth
|
||||
bitbucket_config = {
|
||||
"workspace": "your-workspace",
|
||||
"repository": "your-repo",
|
||||
"username": "your-username",
|
||||
"access_token": "your-app-password", # Use app password for basic auth
|
||||
"auth_method": "basic",
|
||||
"branch": "main"
|
||||
}
|
||||
|
||||
litellm.set_global_bitbucket_config(bitbucket_config)
|
||||
```
|
||||
|
||||
### 4. Use with LiteLLM
|
||||
|
||||
```python
|
||||
# Use with completion - the model prefix 'bitbucket/' tells LiteLLM to use BitBucket prompt management
|
||||
response = litellm.completion(
|
||||
model="bitbucket/gpt-4", # The actual model comes from the .prompt file
|
||||
prompt_id="prompts/chat_assistant", # Location of the prompt file
|
||||
prompt_variables={
|
||||
"user_message": "What is machine learning?",
|
||||
"system_context": "You are a helpful AI tutor."
|
||||
},
|
||||
# Any additional messages will be appended after the prompt
|
||||
messages=[{"role": "user", "content": "Please explain it simply."}]
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
## Proxy Server Configuration
|
||||
|
||||
### 1. Create a `.prompt` file
|
||||
|
||||
Create `prompts/hello.prompt`:
|
||||
|
||||
```yaml
|
||||
---
|
||||
model: gpt-4
|
||||
temperature: 0.7
|
||||
---
|
||||
System: You are a helpful assistant.
|
||||
|
||||
User: {{user_message}}
|
||||
```
|
||||
|
||||
### 2. Setup config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: my-bitbucket-model
|
||||
litellm_params:
|
||||
model: bitbucket/gpt-4
|
||||
prompt_id: "prompts/hello"
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
|
||||
litellm_settings:
|
||||
global_bitbucket_config:
|
||||
workspace: "your-workspace"
|
||||
repository: "your-repo"
|
||||
access_token: "your-access-token"
|
||||
branch: "main"
|
||||
```
|
||||
|
||||
### 3. Start the proxy
|
||||
|
||||
```bash
|
||||
litellm --config config.yaml --detailed_debug
|
||||
```
|
||||
|
||||
### 4. Test it!
|
||||
|
||||
```bash
|
||||
curl -L -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-d '{
|
||||
"model": "my-bitbucket-model",
|
||||
"messages": [{"role": "user", "content": "IGNORED"}],
|
||||
"prompt_variables": {
|
||||
"user_message": "What is the capital of France?"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## Prompt File Format
|
||||
|
||||
### Basic Structure
|
||||
|
||||
```yaml
|
||||
---
|
||||
# Model configuration
|
||||
model: gpt-4
|
||||
temperature: 0.7
|
||||
max_tokens: 500
|
||||
|
||||
# Input schema (optional)
|
||||
input:
|
||||
schema:
|
||||
user_message: string
|
||||
system_context?: string
|
||||
---
|
||||
|
||||
System: You are a helpful {{role}} assistant.
|
||||
|
||||
User: {{user_message}}
|
||||
```
|
||||
|
||||
### Advanced Features
|
||||
|
||||
**Multi-role conversations:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
model: gpt-4
|
||||
temperature: 0.3
|
||||
---
|
||||
System: You are a helpful coding assistant.
|
||||
|
||||
User: {{user_question}}
|
||||
```
|
||||
|
||||
**Dynamic model selection:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
model: "{{preferred_model}}" # Model can be a variable
|
||||
temperature: 0.7
|
||||
---
|
||||
System: You are a helpful assistant specialized in {{domain}}.
|
||||
|
||||
User: {{user_message}}
|
||||
```
|
||||
|
||||
## Team-Based Access Control
|
||||
|
||||
BitBucket's built-in permission system provides team-based access control:
|
||||
|
||||
1. **Workspace-level permissions**: Control access to entire workspaces
|
||||
2. **Repository-level permissions**: Control access to specific repositories
|
||||
3. **Branch-level permissions**: Control access to specific branches
|
||||
4. **User and group management**: Manage team members and their access levels
|
||||
|
||||
### Setting up Team Access
|
||||
|
||||
1. **Create workspaces for each team**:
|
||||
```
|
||||
team-a-prompts/
|
||||
team-b-prompts/
|
||||
team-c-prompts/
|
||||
```
|
||||
|
||||
2. **Configure repository permissions**:
|
||||
- Grant read access to team members
|
||||
- Grant write access to prompt maintainers
|
||||
- Use branch protection rules for production prompts
|
||||
|
||||
3. **Use different access tokens**:
|
||||
- Each team can have their own access token
|
||||
- Tokens can be scoped to specific repositories
|
||||
- Use app passwords for additional security
|
||||
|
||||
## API Reference
|
||||
|
||||
### BitBucket Configuration
|
||||
|
||||
```python
|
||||
bitbucket_config = {
|
||||
"workspace": str, # Required: BitBucket workspace name
|
||||
"repository": str, # Required: Repository name
|
||||
"access_token": str, # Required: BitBucket access token or app password
|
||||
"branch": str, # Optional: Branch to fetch from (default: "main")
|
||||
"base_url": str, # Optional: Custom BitBucket API URL
|
||||
"auth_method": str, # Optional: "token" or "basic" (default: "token")
|
||||
"username": str, # Optional: Username for basic auth
|
||||
"base_url" : str # Optional: Incase where the base url is not https://api.bitbucket.org/2.0
|
||||
}
|
||||
```
|
||||
|
||||
### LiteLLM Integration
|
||||
|
||||
```python
|
||||
response = litellm.completion(
|
||||
model="bitbucket/<base_model>", # required (e.g., bitbucket/gpt-4)
|
||||
prompt_id=str, # required - the .prompt filename without extension
|
||||
prompt_variables=dict, # optional - variables for template rendering
|
||||
bitbucket_config=dict, # optional - BitBucket configuration (if not set globally)
|
||||
messages=list, # optional - additional messages
|
||||
)
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
The BitBucket integration provides detailed error messages for common issues:
|
||||
|
||||
- **Authentication errors**: Invalid access tokens or credentials
|
||||
- **Permission errors**: Insufficient access to workspace/repository
|
||||
- **File not found**: Missing .prompt files
|
||||
- **Network errors**: Connection issues with BitBucket API
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Access Token Security**: Store access tokens securely using environment variables or secret management systems
|
||||
2. **Repository Permissions**: Use BitBucket's permission system to control access
|
||||
3. **Branch Protection**: Protect main branches from unauthorized changes
|
||||
4. **Audit Logging**: BitBucket provides audit logs for all repository access
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **"Access denied" errors**: Check your BitBucket permissions for the workspace and repository
|
||||
2. **"Authentication failed" errors**: Verify your access token or credentials
|
||||
3. **"File not found" errors**: Ensure the .prompt file exists in the specified branch
|
||||
4. **Template rendering errors**: Check your Handlebars syntax in the .prompt file
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable debug logging to troubleshoot issues:
|
||||
|
||||
```python
|
||||
import litellm
|
||||
litellm.set_verbose = True
|
||||
|
||||
# Your BitBucket prompt calls will now show detailed logs
|
||||
response = litellm.completion(
|
||||
model="bitbucket/gpt-4",
|
||||
prompt_id="your_prompt",
|
||||
prompt_variables={"key": "value"}
|
||||
)
|
||||
```
|
||||
|
||||
## Migration from File-Based Prompts
|
||||
|
||||
If you're currently using file-based prompts with the dotprompt integration, you can easily migrate to BitBucket:
|
||||
|
||||
1. **Upload your .prompt files** to a BitBucket repository
|
||||
2. **Update your configuration** to use BitBucket instead of local files
|
||||
3. **Set up team access** using BitBucket's permission system
|
||||
4. **Update your code** to use `bitbucket/` model prefix instead of `dotprompt/`
|
||||
|
||||
This provides better collaboration, version control, and team-based access control for your prompts.
|
||||
66
litellm/integrations/bitbucket/__init__.py
Normal file
66
litellm/integrations/bitbucket/__init__.py
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .bitbucket_prompt_manager import BitBucketPromptManager
|
||||
from litellm.types.prompts.init_prompts import PromptLiteLLMParams, PromptSpec
|
||||
from litellm.integrations.custom_prompt_management import CustomPromptManagement
|
||||
|
||||
from litellm.types.prompts.init_prompts import SupportedPromptIntegrations
|
||||
|
||||
from .bitbucket_prompt_manager import BitBucketPromptManager
|
||||
|
||||
# Global instances
|
||||
global_bitbucket_config: Optional[dict] = None
|
||||
|
||||
|
||||
def set_global_bitbucket_config(config: dict) -> None:
|
||||
"""
|
||||
Set the global BitBucket configuration for prompt management.
|
||||
|
||||
Args:
|
||||
config: Dictionary containing BitBucket configuration
|
||||
- workspace: BitBucket workspace name
|
||||
- repository: Repository name
|
||||
- access_token: BitBucket access token
|
||||
- branch: Branch to fetch prompts from (default: main)
|
||||
"""
|
||||
import litellm
|
||||
|
||||
litellm.global_bitbucket_config = config # type: ignore
|
||||
|
||||
|
||||
def prompt_initializer(
|
||||
litellm_params: "PromptLiteLLMParams", prompt_spec: "PromptSpec"
|
||||
) -> "CustomPromptManagement":
|
||||
"""
|
||||
Initialize a prompt from a BitBucket repository.
|
||||
"""
|
||||
bitbucket_config = getattr(litellm_params, "bitbucket_config", None)
|
||||
prompt_id = getattr(litellm_params, "prompt_id", None)
|
||||
|
||||
if not bitbucket_config:
|
||||
raise ValueError(
|
||||
"bitbucket_config is required for BitBucket prompt integration"
|
||||
)
|
||||
|
||||
try:
|
||||
bitbucket_prompt_manager = BitBucketPromptManager(
|
||||
bitbucket_config=bitbucket_config,
|
||||
prompt_id=prompt_id,
|
||||
)
|
||||
|
||||
return bitbucket_prompt_manager
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
prompt_initializer_registry = {
|
||||
SupportedPromptIntegrations.BITBUCKET.value: prompt_initializer,
|
||||
}
|
||||
|
||||
# Export public API
|
||||
__all__ = [
|
||||
"BitBucketPromptManager",
|
||||
"set_global_bitbucket_config",
|
||||
"global_bitbucket_config",
|
||||
]
|
||||
241
litellm/integrations/bitbucket/bitbucket_client.py
Normal file
241
litellm/integrations/bitbucket/bitbucket_client.py
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
"""
|
||||
BitBucket API client for fetching .prompt files from BitBucket repositories.
|
||||
"""
|
||||
|
||||
import base64
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from litellm.llms.custom_httpx.http_handler import HTTPHandler
|
||||
|
||||
|
||||
class BitBucketClient:
|
||||
"""
|
||||
Client for interacting with BitBucket API to fetch .prompt files.
|
||||
|
||||
Supports:
|
||||
- Authentication with access tokens
|
||||
- Fetching file contents from repositories
|
||||
- Team-based access control through BitBucket permissions
|
||||
- Branch-specific file fetching
|
||||
"""
|
||||
|
||||
def __init__(self, config: Dict[str, Any]):
|
||||
"""
|
||||
Initialize the BitBucket client.
|
||||
|
||||
Args:
|
||||
config: Dictionary containing:
|
||||
- workspace: BitBucket workspace name
|
||||
- repository: Repository name
|
||||
- access_token: BitBucket access token (or app password)
|
||||
- branch: Branch to fetch from (default: main)
|
||||
- base_url: Custom BitBucket API base URL (optional)
|
||||
- auth_method: Authentication method ('token' or 'basic', default: 'token')
|
||||
- username: Username for basic auth (optional)
|
||||
"""
|
||||
self.workspace = config.get("workspace")
|
||||
self.repository = config.get("repository")
|
||||
self.access_token = config.get("access_token")
|
||||
self.branch = config.get("branch", "main")
|
||||
self.base_url = config.get("", "https://api.bitbucket.org/2.0")
|
||||
self.auth_method = config.get("auth_method", "token")
|
||||
self.username = config.get("username")
|
||||
|
||||
if not all([self.workspace, self.repository, self.access_token]):
|
||||
raise ValueError("workspace, repository, and access_token are required")
|
||||
|
||||
# Set up authentication headers
|
||||
self.headers = {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
if self.auth_method == "basic" and self.username:
|
||||
# Use basic auth with username and app password
|
||||
credentials = f"{self.username}:{self.access_token}"
|
||||
encoded_credentials = base64.b64encode(credentials.encode()).decode()
|
||||
self.headers["Authorization"] = f"Basic {encoded_credentials}"
|
||||
else:
|
||||
# Use token-based authentication (default)
|
||||
self.headers["Authorization"] = f"Bearer {self.access_token}"
|
||||
|
||||
# Initialize HTTPHandler
|
||||
self.http_handler = HTTPHandler()
|
||||
|
||||
def get_file_content(self, file_path: str) -> Optional[str]:
|
||||
"""
|
||||
Fetch the content of a file from the BitBucket repository.
|
||||
|
||||
Args:
|
||||
file_path: Path to the file in the repository
|
||||
|
||||
Returns:
|
||||
File content as string, or None if file not found
|
||||
"""
|
||||
url = f"{self.base_url}/repositories/{self.workspace}/{self.repository}/src/{self.branch}/{file_path}"
|
||||
|
||||
try:
|
||||
response = self.http_handler.get(url, headers=self.headers)
|
||||
response.raise_for_status()
|
||||
|
||||
# BitBucket returns file content as base64 encoded
|
||||
if response.headers.get("content-type", "").startswith("text/"):
|
||||
return response.text
|
||||
else:
|
||||
# For binary files or when content-type is not text, try to decode as base64
|
||||
try:
|
||||
return base64.b64decode(response.content).decode("utf-8")
|
||||
except Exception:
|
||||
return response.text
|
||||
|
||||
except Exception as e:
|
||||
# Check if it's an HTTP error
|
||||
if hasattr(e, "response") and hasattr(e.response, "status_code"):
|
||||
if e.response.status_code == 404:
|
||||
return None
|
||||
elif e.response.status_code == 403:
|
||||
raise Exception(
|
||||
f"Access denied to file '{file_path}'. Check your BitBucket permissions for workspace '{self.workspace}' and repository '{self.repository}'."
|
||||
)
|
||||
elif e.response.status_code == 401:
|
||||
raise Exception(
|
||||
"Authentication failed. Check your BitBucket access token and permissions."
|
||||
)
|
||||
else:
|
||||
raise Exception(f"Failed to fetch file '{file_path}': {e}")
|
||||
else:
|
||||
raise Exception(f"Error fetching file '{file_path}': {e}")
|
||||
|
||||
def list_files(
|
||||
self, directory_path: str = "", file_extension: str = ".prompt"
|
||||
) -> List[str]:
|
||||
"""
|
||||
List files in a directory with a specific extension.
|
||||
|
||||
Args:
|
||||
directory_path: Directory path in the repository (empty for root)
|
||||
file_extension: File extension to filter by (default: .prompt)
|
||||
|
||||
Returns:
|
||||
List of file paths
|
||||
"""
|
||||
url = f"{self.base_url}/repositories/{self.workspace}/{self.repository}/src/{self.branch}/{directory_path}"
|
||||
|
||||
try:
|
||||
response = self.http_handler.get(url, headers=self.headers)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
files = []
|
||||
|
||||
for item in data.get("values", []):
|
||||
if item.get("type") == "commit_file":
|
||||
file_path = item.get("path", "")
|
||||
if file_path.endswith(file_extension):
|
||||
files.append(file_path)
|
||||
|
||||
return files
|
||||
|
||||
except Exception as e:
|
||||
# Check if it's an HTTP error
|
||||
if hasattr(e, "response") and hasattr(e.response, "status_code"):
|
||||
if e.response.status_code == 404:
|
||||
return []
|
||||
elif e.response.status_code == 403:
|
||||
raise Exception(
|
||||
f"Access denied to directory '{directory_path}'. Check your BitBucket permissions for workspace '{self.workspace}' and repository '{self.repository}'."
|
||||
)
|
||||
elif e.response.status_code == 401:
|
||||
raise Exception(
|
||||
"Authentication failed. Check your BitBucket access token and permissions."
|
||||
)
|
||||
else:
|
||||
raise Exception(f"Failed to list files in '{directory_path}': {e}")
|
||||
else:
|
||||
raise Exception(f"Error listing files in '{directory_path}': {e}")
|
||||
|
||||
def get_repository_info(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Get information about the repository.
|
||||
|
||||
Returns:
|
||||
Dictionary containing repository information
|
||||
"""
|
||||
url = f"{self.base_url}/repositories/{self.workspace}/{self.repository}"
|
||||
|
||||
try:
|
||||
response = self.http_handler.get(url, headers=self.headers)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except Exception as e:
|
||||
raise Exception(f"Failed to get repository info: {e}")
|
||||
|
||||
def test_connection(self) -> bool:
|
||||
"""
|
||||
Test the connection to the BitBucket repository.
|
||||
|
||||
Returns:
|
||||
True if connection is successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
self.get_repository_info()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def get_branches(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get list of branches in the repository.
|
||||
|
||||
Returns:
|
||||
List of branch information dictionaries
|
||||
"""
|
||||
url = f"{self.base_url}/repositories/{self.workspace}/{self.repository}/refs/branches"
|
||||
|
||||
try:
|
||||
response = self.http_handler.get(url, headers=self.headers)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
return data.get("values", [])
|
||||
except Exception as e:
|
||||
raise Exception(f"Failed to get branches: {e}")
|
||||
|
||||
def get_file_metadata(self, file_path: str) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Get metadata about a file (size, last modified, etc.).
|
||||
|
||||
Args:
|
||||
file_path: Path to the file in the repository
|
||||
|
||||
Returns:
|
||||
Dictionary containing file metadata, or None if file not found
|
||||
"""
|
||||
url = f"{self.base_url}/repositories/{self.workspace}/{self.repository}/src/{self.branch}/{file_path}"
|
||||
|
||||
try:
|
||||
# Use GET with Range header to get just the headers (HEAD equivalent)
|
||||
headers = self.headers.copy()
|
||||
headers["Range"] = "bytes=0-0" # Request only first byte to get headers
|
||||
|
||||
response = self.http_handler.get(url, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
return {
|
||||
"content_type": response.headers.get("content-type"),
|
||||
"content_length": response.headers.get("content-length"),
|
||||
"last_modified": response.headers.get("last-modified"),
|
||||
}
|
||||
except Exception as e:
|
||||
# Check if it's an HTTP error
|
||||
if hasattr(e, "response") and hasattr(e.response, "status_code"):
|
||||
if e.response.status_code == 404:
|
||||
return None
|
||||
raise Exception(f"Failed to get file metadata for '{file_path}': {e}")
|
||||
else:
|
||||
raise Exception(f"Error getting file metadata for '{file_path}': {e}")
|
||||
|
||||
def close(self):
|
||||
"""Close the HTTP handler to free resources."""
|
||||
if hasattr(self, "http_handler"):
|
||||
self.http_handler.close()
|
||||
508
litellm/integrations/bitbucket/bitbucket_prompt_manager.py
Normal file
508
litellm/integrations/bitbucket/bitbucket_prompt_manager.py
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
"""
|
||||
BitBucket prompt manager that integrates with LiteLLM's prompt management system.
|
||||
Fetches .prompt files from BitBucket repositories and provides team-based access control.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from jinja2 import DictLoader, Environment, select_autoescape
|
||||
|
||||
from litellm.integrations.custom_prompt_management import CustomPromptManagement
|
||||
from litellm.integrations.prompt_management_base import (
|
||||
PromptManagementBase,
|
||||
PromptManagementClient,
|
||||
)
|
||||
from litellm.types.llms.openai import AllMessageValues
|
||||
from litellm.types.utils import StandardCallbackDynamicParams
|
||||
|
||||
from .bitbucket_client import BitBucketClient
|
||||
|
||||
|
||||
class BitBucketPromptTemplate:
|
||||
"""
|
||||
Represents a prompt template loaded from BitBucket.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
template_id: str,
|
||||
content: str,
|
||||
metadata: Dict[str, Any],
|
||||
model: Optional[str] = None,
|
||||
):
|
||||
self.template_id = template_id
|
||||
self.content = content
|
||||
self.metadata = metadata
|
||||
self.model = model or metadata.get("model")
|
||||
self.temperature = metadata.get("temperature")
|
||||
self.max_tokens = metadata.get("max_tokens")
|
||||
self.input_schema = metadata.get("input", {}).get("schema", {})
|
||||
self.optional_params = {
|
||||
k: v for k, v in metadata.items() if k not in ["model", "input", "content"]
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return f"BitBucketPromptTemplate(id='{self.template_id}', model='{self.model}')"
|
||||
|
||||
|
||||
class BitBucketTemplateManager:
|
||||
"""
|
||||
Manager for loading and rendering .prompt files from BitBucket repositories.
|
||||
|
||||
Supports:
|
||||
- Fetching .prompt files from BitBucket repositories
|
||||
- Team-based access control through BitBucket permissions
|
||||
- YAML frontmatter for metadata
|
||||
- Handlebars-style templating (using Jinja2)
|
||||
- Input/output schema validation
|
||||
- Model configuration
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
bitbucket_config: Dict[str, Any],
|
||||
prompt_id: Optional[str] = None,
|
||||
):
|
||||
self.bitbucket_config = bitbucket_config
|
||||
self.prompt_id = prompt_id
|
||||
self.prompts: Dict[str, BitBucketPromptTemplate] = {}
|
||||
self.bitbucket_client = BitBucketClient(bitbucket_config)
|
||||
|
||||
self.jinja_env = Environment(
|
||||
loader=DictLoader({}),
|
||||
autoescape=select_autoescape(["html", "xml"]),
|
||||
# Use Handlebars-style delimiters to match Dotprompt spec
|
||||
variable_start_string="{{",
|
||||
variable_end_string="}}",
|
||||
block_start_string="{%",
|
||||
block_end_string="%}",
|
||||
comment_start_string="{#",
|
||||
comment_end_string="#}",
|
||||
)
|
||||
|
||||
# Load prompts from BitBucket if prompt_id is provided
|
||||
if self.prompt_id:
|
||||
self._load_prompt_from_bitbucket(self.prompt_id)
|
||||
|
||||
def _load_prompt_from_bitbucket(self, prompt_id: str) -> None:
|
||||
"""Load a specific .prompt file from BitBucket."""
|
||||
try:
|
||||
# Fetch the .prompt file from BitBucket
|
||||
prompt_content = self.bitbucket_client.get_file_content(
|
||||
f"{prompt_id}.prompt"
|
||||
)
|
||||
|
||||
if prompt_content:
|
||||
template = self._parse_prompt_file(prompt_content, prompt_id)
|
||||
self.prompts[prompt_id] = template
|
||||
except Exception as e:
|
||||
raise Exception(f"Failed to load prompt '{prompt_id}' from BitBucket: {e}")
|
||||
|
||||
def _parse_prompt_file(
|
||||
self, content: str, prompt_id: str
|
||||
) -> BitBucketPromptTemplate:
|
||||
"""Parse a .prompt file content and extract metadata and template."""
|
||||
# Split frontmatter and content
|
||||
if content.startswith("---"):
|
||||
parts = content.split("---", 2)
|
||||
if len(parts) >= 3:
|
||||
frontmatter_str = parts[1].strip()
|
||||
template_content = parts[2].strip()
|
||||
else:
|
||||
frontmatter_str = ""
|
||||
template_content = content
|
||||
else:
|
||||
frontmatter_str = ""
|
||||
template_content = content
|
||||
|
||||
# Parse YAML frontmatter
|
||||
metadata = {}
|
||||
if frontmatter_str:
|
||||
try:
|
||||
import yaml
|
||||
|
||||
metadata = yaml.safe_load(frontmatter_str) or {}
|
||||
except ImportError:
|
||||
# Fallback to basic parsing if PyYAML is not available
|
||||
metadata = self._parse_yaml_basic(frontmatter_str)
|
||||
except Exception:
|
||||
metadata = {}
|
||||
|
||||
return BitBucketPromptTemplate(
|
||||
template_id=prompt_id,
|
||||
content=template_content,
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
def _parse_yaml_basic(self, yaml_str: str) -> Dict[str, Any]:
|
||||
"""Basic YAML parser for simple cases when PyYAML is not available."""
|
||||
result = {}
|
||||
for line in yaml_str.split("\n"):
|
||||
line = line.strip()
|
||||
if ":" in line and not line.startswith("#"):
|
||||
key, value = line.split(":", 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
|
||||
# Try to parse value as appropriate type
|
||||
if value.lower() in ["true", "false"]:
|
||||
result[key] = value.lower() == "true"
|
||||
elif value.isdigit():
|
||||
result[key] = int(value)
|
||||
elif value.replace(".", "").isdigit():
|
||||
result[key] = float(value)
|
||||
else:
|
||||
result[key] = value.strip("\"'")
|
||||
return result
|
||||
|
||||
def render_template(
|
||||
self, template_id: str, variables: Dict[str, Any] = None
|
||||
) -> str:
|
||||
"""Render a template with the given variables."""
|
||||
if template_id not in self.prompts:
|
||||
raise ValueError(f"Template '{template_id}' not found")
|
||||
|
||||
template = self.prompts[template_id]
|
||||
jinja_template = self.jinja_env.from_string(template.content)
|
||||
|
||||
return jinja_template.render(**(variables or {}))
|
||||
|
||||
def get_template(self, template_id: str) -> Optional[BitBucketPromptTemplate]:
|
||||
"""Get a template by ID."""
|
||||
return self.prompts.get(template_id)
|
||||
|
||||
def list_templates(self) -> List[str]:
|
||||
"""List all available template IDs."""
|
||||
return list(self.prompts.keys())
|
||||
|
||||
|
||||
class BitBucketPromptManager(CustomPromptManagement):
|
||||
"""
|
||||
BitBucket prompt manager that integrates with LiteLLM's prompt management system.
|
||||
|
||||
This class enables using .prompt files from BitBucket repositories with the
|
||||
litellm completion() function by implementing the PromptManagementBase interface.
|
||||
|
||||
Usage:
|
||||
# Configure BitBucket access
|
||||
bitbucket_config = {
|
||||
"workspace": "your-workspace",
|
||||
"repository": "your-repo",
|
||||
"access_token": "your-token",
|
||||
"branch": "main" # optional, defaults to main
|
||||
}
|
||||
|
||||
# Use with completion
|
||||
response = litellm.completion(
|
||||
model="bitbucket/gpt-4",
|
||||
prompt_id="my_prompt",
|
||||
prompt_variables={"variable": "value"},
|
||||
bitbucket_config=bitbucket_config,
|
||||
messages=[{"role": "user", "content": "This will be combined with the prompt"}]
|
||||
)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
bitbucket_config: Dict[str, Any],
|
||||
prompt_id: Optional[str] = None,
|
||||
):
|
||||
self.bitbucket_config = bitbucket_config
|
||||
self.prompt_id = prompt_id
|
||||
self._prompt_manager: Optional[BitBucketPromptManager] = None
|
||||
|
||||
@property
|
||||
def integration_name(self) -> str:
|
||||
"""Integration name used in model names like 'bitbucket/gpt-4'."""
|
||||
return "bitbucket"
|
||||
|
||||
@property
|
||||
def prompt_manager(self) -> BitBucketTemplateManager:
|
||||
"""Get or create the prompt manager instance."""
|
||||
if self._prompt_manager is None:
|
||||
self._prompt_manager = BitBucketTemplateManager(
|
||||
bitbucket_config=self.bitbucket_config,
|
||||
prompt_id=self.prompt_id,
|
||||
)
|
||||
return self._prompt_manager
|
||||
|
||||
def get_prompt_template(
|
||||
self,
|
||||
prompt_id: str,
|
||||
prompt_variables: Optional[Dict[str, Any]] = None,
|
||||
) -> Tuple[str, Dict[str, Any]]:
|
||||
"""
|
||||
Get a prompt template and render it with variables.
|
||||
|
||||
Args:
|
||||
prompt_id: The ID of the prompt template
|
||||
prompt_variables: Variables to substitute in the template
|
||||
|
||||
Returns:
|
||||
Tuple of (rendered_prompt, metadata)
|
||||
"""
|
||||
template = self.prompt_manager.get_template(prompt_id)
|
||||
if not template:
|
||||
raise ValueError(f"Prompt template '{prompt_id}' not found")
|
||||
|
||||
# Render the template
|
||||
rendered_prompt = self.prompt_manager.render_template(
|
||||
prompt_id, prompt_variables or {}
|
||||
)
|
||||
|
||||
# Extract metadata
|
||||
metadata = {
|
||||
"model": template.model,
|
||||
"temperature": template.temperature,
|
||||
"max_tokens": template.max_tokens,
|
||||
**template.optional_params,
|
||||
}
|
||||
|
||||
return rendered_prompt, metadata
|
||||
|
||||
def pre_call_hook(
|
||||
self,
|
||||
user_id: Optional[str],
|
||||
messages: List[AllMessageValues],
|
||||
function_call: Optional[Union[Dict[str, Any], str]] = None,
|
||||
litellm_params: Optional[Dict[str, Any]] = None,
|
||||
prompt_id: Optional[str] = None,
|
||||
prompt_variables: Optional[Dict[str, Any]] = None,
|
||||
**kwargs,
|
||||
) -> Tuple[List[AllMessageValues], Optional[Dict[str, Any]]]:
|
||||
"""
|
||||
Pre-call hook that processes the prompt template before making the LLM call.
|
||||
"""
|
||||
if not prompt_id:
|
||||
return messages, litellm_params
|
||||
|
||||
try:
|
||||
# Get the rendered prompt and metadata
|
||||
rendered_prompt, prompt_metadata = self.get_prompt_template(
|
||||
prompt_id, prompt_variables
|
||||
)
|
||||
|
||||
# Parse the rendered prompt into messages
|
||||
parsed_messages = self._parse_prompt_to_messages(rendered_prompt)
|
||||
|
||||
# Merge with existing messages
|
||||
if parsed_messages:
|
||||
# If we have parsed messages, use them instead of the original messages
|
||||
final_messages = parsed_messages
|
||||
else:
|
||||
# If no messages were parsed, prepend the prompt to existing messages
|
||||
final_messages = [
|
||||
{"role": "user", "content": rendered_prompt}
|
||||
] + messages
|
||||
|
||||
# Update litellm_params with prompt metadata
|
||||
if litellm_params is None:
|
||||
litellm_params = {}
|
||||
|
||||
# Apply model and parameters from prompt metadata
|
||||
if prompt_metadata.get("model"):
|
||||
litellm_params["model"] = prompt_metadata["model"]
|
||||
|
||||
for param in [
|
||||
"temperature",
|
||||
"max_tokens",
|
||||
"top_p",
|
||||
"frequency_penalty",
|
||||
"presence_penalty",
|
||||
]:
|
||||
if param in prompt_metadata:
|
||||
litellm_params[param] = prompt_metadata[param]
|
||||
|
||||
return final_messages, litellm_params
|
||||
|
||||
except Exception as e:
|
||||
# Log error but don't fail the call
|
||||
import litellm
|
||||
|
||||
litellm._logging.verbose_proxy_logger.error(
|
||||
f"Error in BitBucket prompt pre_call_hook: {e}"
|
||||
)
|
||||
return messages, litellm_params
|
||||
|
||||
def _parse_prompt_to_messages(self, prompt_content: str) -> List[AllMessageValues]:
|
||||
"""
|
||||
Parse prompt content into a list of messages.
|
||||
Handles both simple prompts and multi-role conversations.
|
||||
"""
|
||||
messages = []
|
||||
lines = prompt_content.strip().split("\n")
|
||||
current_role = None
|
||||
current_content = []
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
# Check for role indicators
|
||||
if line.lower().startswith("system:"):
|
||||
if current_role and current_content:
|
||||
messages.append(
|
||||
{
|
||||
"role": current_role,
|
||||
"content": "\n".join(current_content).strip(),
|
||||
}
|
||||
)
|
||||
current_role = "system"
|
||||
current_content = [line[7:].strip()] # Remove "System:" prefix
|
||||
elif line.lower().startswith("user:"):
|
||||
if current_role and current_content:
|
||||
messages.append(
|
||||
{
|
||||
"role": current_role,
|
||||
"content": "\n".join(current_content).strip(),
|
||||
}
|
||||
)
|
||||
current_role = "user"
|
||||
current_content = [line[5:].strip()] # Remove "User:" prefix
|
||||
elif line.lower().startswith("assistant:"):
|
||||
if current_role and current_content:
|
||||
messages.append(
|
||||
{
|
||||
"role": current_role,
|
||||
"content": "\n".join(current_content).strip(),
|
||||
}
|
||||
)
|
||||
current_role = "assistant"
|
||||
current_content = [line[10:].strip()] # Remove "Assistant:" prefix
|
||||
else:
|
||||
# Continue building current message
|
||||
current_content.append(line)
|
||||
|
||||
# Add the last message
|
||||
if current_role and current_content:
|
||||
messages.append(
|
||||
{"role": current_role, "content": "\n".join(current_content).strip()}
|
||||
)
|
||||
|
||||
# If no role indicators found, treat as a single user message
|
||||
if not messages and prompt_content.strip():
|
||||
messages = [{"role": "user", "content": prompt_content.strip()}]
|
||||
|
||||
return messages
|
||||
|
||||
def post_call_hook(
|
||||
self,
|
||||
user_id: Optional[str],
|
||||
response: Any,
|
||||
input_messages: List[AllMessageValues],
|
||||
function_call: Optional[Union[Dict[str, Any], str]] = None,
|
||||
litellm_params: Optional[Dict[str, Any]] = None,
|
||||
prompt_id: Optional[str] = None,
|
||||
prompt_variables: Optional[Dict[str, Any]] = None,
|
||||
**kwargs,
|
||||
) -> Any:
|
||||
"""
|
||||
Post-call hook for any post-processing after the LLM call.
|
||||
"""
|
||||
return response
|
||||
|
||||
def get_available_prompts(self) -> List[str]:
|
||||
"""Get list of available prompt IDs."""
|
||||
return self.prompt_manager.list_templates()
|
||||
|
||||
def reload_prompts(self) -> None:
|
||||
"""Reload prompts from BitBucket."""
|
||||
if self.prompt_id:
|
||||
self._prompt_manager = None # Reset to force reload
|
||||
self.prompt_manager # This will trigger reload
|
||||
|
||||
def should_run_prompt_management(
|
||||
self,
|
||||
prompt_id: str,
|
||||
dynamic_callback_params: StandardCallbackDynamicParams,
|
||||
) -> bool:
|
||||
"""
|
||||
Determine if prompt management should run based on the prompt_id.
|
||||
|
||||
For BitBucket, we always return True and handle the prompt loading
|
||||
in the _compile_prompt_helper method.
|
||||
"""
|
||||
return True
|
||||
|
||||
def _compile_prompt_helper(
|
||||
self,
|
||||
prompt_id: str,
|
||||
prompt_variables: Optional[dict],
|
||||
dynamic_callback_params: StandardCallbackDynamicParams,
|
||||
prompt_label: Optional[str] = None,
|
||||
prompt_version: Optional[int] = None,
|
||||
) -> PromptManagementClient:
|
||||
"""
|
||||
Compile a BitBucket prompt template into a PromptManagementClient structure.
|
||||
|
||||
This method:
|
||||
1. Loads the prompt template from BitBucket
|
||||
2. Renders it with the provided variables
|
||||
3. Converts the rendered text into chat messages
|
||||
4. Extracts model and optional parameters from metadata
|
||||
"""
|
||||
try:
|
||||
# Load the prompt from BitBucket if not already loaded
|
||||
if prompt_id not in self.prompt_manager.prompts:
|
||||
self.prompt_manager._load_prompt_from_bitbucket(prompt_id)
|
||||
|
||||
# Get the rendered prompt and metadata
|
||||
rendered_prompt, prompt_metadata = self.get_prompt_template(
|
||||
prompt_id, prompt_variables
|
||||
)
|
||||
|
||||
# Convert rendered content to chat messages
|
||||
messages = self._parse_prompt_to_messages(rendered_prompt)
|
||||
|
||||
# Extract model from metadata (if specified)
|
||||
template_model = prompt_metadata.get("model")
|
||||
|
||||
# Extract optional parameters from metadata
|
||||
optional_params = {}
|
||||
for param in [
|
||||
"temperature",
|
||||
"max_tokens",
|
||||
"top_p",
|
||||
"frequency_penalty",
|
||||
"presence_penalty",
|
||||
]:
|
||||
if param in prompt_metadata:
|
||||
optional_params[param] = prompt_metadata[param]
|
||||
|
||||
return PromptManagementClient(
|
||||
prompt_id=prompt_id,
|
||||
prompt_template=messages,
|
||||
prompt_template_model=template_model,
|
||||
prompt_template_optional_params=optional_params,
|
||||
completed_messages=None,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise ValueError(f"Error compiling prompt '{prompt_id}': {e}")
|
||||
|
||||
def get_chat_completion_prompt(
|
||||
self,
|
||||
model: str,
|
||||
messages: List[AllMessageValues],
|
||||
non_default_params: dict,
|
||||
prompt_id: Optional[str],
|
||||
prompt_variables: Optional[dict],
|
||||
dynamic_callback_params: StandardCallbackDynamicParams,
|
||||
prompt_label: Optional[str] = None,
|
||||
prompt_version: Optional[int] = None,
|
||||
) -> Tuple[str, List[AllMessageValues], dict]:
|
||||
"""
|
||||
Get chat completion prompt from BitBucket and return processed model, messages, and parameters.
|
||||
"""
|
||||
return PromptManagementBase.get_chat_completion_prompt(
|
||||
self,
|
||||
model,
|
||||
messages,
|
||||
non_default_params,
|
||||
prompt_id,
|
||||
prompt_variables,
|
||||
dynamic_callback_params,
|
||||
prompt_label,
|
||||
prompt_version,
|
||||
)
|
||||
|
|
@ -17,9 +17,9 @@ import asyncio
|
|||
import datetime
|
||||
import os
|
||||
import traceback
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from datetime import datetime as datetimeObj
|
||||
from typing import Any, List, Optional, Union
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import httpx
|
||||
from httpx import Response
|
||||
|
|
@ -71,6 +71,13 @@ class DataDogLogger(
|
|||
raise Exception("DD_API_KEY is not set, set 'DD_API_KEY=<>")
|
||||
if os.getenv("DD_SITE", None) is None:
|
||||
raise Exception("DD_SITE is not set in .env, set 'DD_SITE=<>")
|
||||
|
||||
#########################################################
|
||||
# Handle datadog_params set as litellm.datadog_params
|
||||
#########################################################
|
||||
dict_datadog_params = self._get_datadog_params()
|
||||
kwargs.update(dict_datadog_params)
|
||||
|
||||
self.async_client = get_async_httpx_client(
|
||||
llm_provider=httpxSpecialProvider.LoggingCallback
|
||||
)
|
||||
|
|
@ -101,6 +108,21 @@ class DataDogLogger(
|
|||
)
|
||||
raise e
|
||||
|
||||
def _get_datadog_params(self) -> Dict:
|
||||
"""
|
||||
Get the datadog_params from litellm.datadog_params
|
||||
|
||||
These are params specific to initializing the DataDogLogger e.g. turn_off_message_logging
|
||||
"""
|
||||
dict_datadog_params: Dict = {}
|
||||
if litellm.datadog_params is not None:
|
||||
if isinstance(litellm.datadog_params, DatadogInitParams):
|
||||
dict_datadog_params = litellm.datadog_params.model_dump()
|
||||
elif isinstance(litellm.datadog_params, Dict):
|
||||
# only allow params that are of DatadogInitParams
|
||||
dict_datadog_params = DatadogInitParams(**litellm.datadog_params).model_dump()
|
||||
return dict_datadog_params
|
||||
|
||||
async def async_log_success_event(self, kwargs, response_obj, start_time, end_time):
|
||||
"""
|
||||
Async Log success events to Datadog
|
||||
|
|
@ -458,6 +480,7 @@ class DataDogLogger(
|
|||
else:
|
||||
clean_metadata[key] = value
|
||||
|
||||
|
||||
# Build the initial payload
|
||||
payload = {
|
||||
"id": id,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ API Reference: https://docs.datadoghq.com/llm_observability/setup/api/?tab=examp
|
|||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Literal, Optional, Union
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import os
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from litellm.integrations.custom_logger import CustomLogger
|
||||
from litellm.integrations.deepeval.api import Api, Endpoints, HttpMethods
|
||||
from litellm.integrations.deepeval.types import (
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import os
|
||||
import traceback
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Any
|
||||
|
||||
import litellm
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
||||
from urllib.parse import quote
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Literal, Optional
|
||||
|
||||
import httpx
|
||||
|
|
|
|||
|
|
@ -671,6 +671,7 @@ class LangFuseLogger:
|
|||
|
||||
generation_id = None
|
||||
usage = None
|
||||
usage_details = None
|
||||
if response_obj is not None:
|
||||
if (
|
||||
hasattr(response_obj, "id")
|
||||
|
|
@ -687,6 +688,11 @@ class LangFuseLogger:
|
|||
"completion_tokens": _usage_obj.completion_tokens,
|
||||
"total_cost": cost if self._supports_costs() else None,
|
||||
}
|
||||
usage_details = LangfuseUsageDetails(input=_usage_obj.prompt_tokens,
|
||||
output=_usage_obj.completion_tokens,
|
||||
cache_creation_input_tokens=_usage_obj.get('cache_creation_input_tokens', 0),
|
||||
cache_read_input_tokens=_usage_obj.get('cache_read_input_tokens', 0))
|
||||
|
||||
generation_name = clean_metadata.pop("generation_name", None)
|
||||
if generation_name is None:
|
||||
# if `generation_name` is None, use sensible default values
|
||||
|
|
@ -719,6 +725,7 @@ class LangFuseLogger:
|
|||
"input": input if not mask_input else "redacted-by-litellm",
|
||||
"output": output if not mask_output else "redacted-by-litellm",
|
||||
"usage": usage,
|
||||
"usage_details": usage_details,
|
||||
"metadata": log_requester_metadata(clean_metadata),
|
||||
"level": level,
|
||||
"version": clean_metadata.pop("version", None),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import os
|
|||
import random
|
||||
import traceback
|
||||
import types
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# This file contains the LiteralAILogger class which is used to log steps to the LiteralAI observability platform.
|
||||
import asyncio
|
||||
import os
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import List, Optional
|
||||
|
||||
import httpx
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import os
|
||||
import traceback
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, NamedTuple
|
||||
|
||||
|
|
|
|||
|
|
@ -192,9 +192,25 @@ class OpikLogger(CustomBatchLogger):
|
|||
|
||||
# Extract opik metadata
|
||||
litellm_opik_metadata = litellm_params_metadata.get("opik", {})
|
||||
|
||||
# Use standard_logging_object to create metadata and input/output data
|
||||
standard_logging_object = kwargs.get("standard_logging_object", None)
|
||||
if standard_logging_object is None:
|
||||
verbose_logger.debug(
|
||||
"OpikLogger skipping event; no standard_logging_object found"
|
||||
)
|
||||
return []
|
||||
|
||||
# Update litellm_opik_metadata with opik metadata from requester
|
||||
standard_logging_metadata = standard_logging_object.get("metadata", {}) or {}
|
||||
requester_metadata = standard_logging_metadata.get("requester_metadata", {}) or {}
|
||||
requester_opik_metadata = requester_metadata.get("opik", {}) or {}
|
||||
litellm_opik_metadata.update(requester_opik_metadata)
|
||||
|
||||
verbose_logger.debug(
|
||||
f"litellm_opik_metadata - {json.dumps(litellm_opik_metadata, default=str)}"
|
||||
)
|
||||
|
||||
project_name = litellm_opik_metadata.get("project_name", self.opik_project_name)
|
||||
|
||||
# Extract trace_id and parent_span_id
|
||||
|
|
@ -208,19 +224,33 @@ class OpikLogger(CustomBatchLogger):
|
|||
else:
|
||||
trace_id = None
|
||||
parent_span_id = None
|
||||
|
||||
# Create Opik tags
|
||||
opik_tags = litellm_opik_metadata.get("tags", [])
|
||||
if kwargs.get("custom_llm_provider"):
|
||||
opik_tags.append(kwargs["custom_llm_provider"])
|
||||
|
||||
# Get thread_id if present
|
||||
thread_id = litellm_opik_metadata.get("thread_id", None)
|
||||
|
||||
# Use standard_logging_object to create metadata and input/output data
|
||||
standard_logging_object = kwargs.get("standard_logging_object", None)
|
||||
if standard_logging_object is None:
|
||||
verbose_logger.debug(
|
||||
"OpikLogger skipping event; no standard_logging_object found"
|
||||
)
|
||||
return []
|
||||
|
||||
# Override with any opik_ headers from proxy request
|
||||
proxy_server_request = _litellm_params.get("proxy_server_request", {}) or {}
|
||||
proxy_headers = proxy_server_request.get("headers", {}) or {}
|
||||
for key, value in proxy_headers.items():
|
||||
if key.startswith("opik_"):
|
||||
param_key = key.replace("opik_", "", 1)
|
||||
if param_key == "project_name" and value:
|
||||
project_name = value
|
||||
elif param_key == "thread_id" and value:
|
||||
thread_id = value
|
||||
elif param_key == "tags" and value:
|
||||
try:
|
||||
parsed_tags = json.loads(value)
|
||||
if isinstance(parsed_tags, list):
|
||||
opik_tags.extend(parsed_tags)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
pass
|
||||
|
||||
# Create input and output data
|
||||
input_data = standard_logging_object.get("messages", {})
|
||||
output_data = standard_logging_object.get("response", {})
|
||||
|
|
@ -243,7 +273,7 @@ class OpikLogger(CustomBatchLogger):
|
|||
del metadata["current_span_data"]
|
||||
metadata["created_from"] = "litellm"
|
||||
|
||||
metadata.update(standard_logging_object.get("metadata", {}))
|
||||
metadata.update(standard_logging_metadata)
|
||||
if "call_type" in standard_logging_object:
|
||||
metadata["type"] = standard_logging_object["call_type"]
|
||||
if "status" in standard_logging_object:
|
||||
|
|
@ -286,20 +316,20 @@ class OpikLogger(CustomBatchLogger):
|
|||
verbose_logger.debug(
|
||||
f"OpikLogger creating payload for trace with id {trace_id}"
|
||||
)
|
||||
|
||||
payload.append(
|
||||
{
|
||||
"project_name": project_name,
|
||||
"id": trace_id,
|
||||
"name": trace_name,
|
||||
"start_time": start_time.astimezone(timezone.utc).isoformat().replace("+00:00", "Z"),
|
||||
"end_time": end_time.astimezone(timezone.utc).isoformat().replace("+00:00", "Z"),
|
||||
"input": input_data,
|
||||
"output": output_data,
|
||||
"metadata": metadata,
|
||||
"tags": opik_tags,
|
||||
}
|
||||
)
|
||||
payload.append(
|
||||
{
|
||||
"project_name": project_name,
|
||||
"id": trace_id,
|
||||
"name": trace_name,
|
||||
"start_time": start_time.astimezone(timezone.utc).isoformat().replace("+00:00", "Z"),
|
||||
"end_time": end_time.astimezone(timezone.utc).isoformat().replace("+00:00", "Z"),
|
||||
"input": input_data,
|
||||
"output": output_data,
|
||||
"metadata": metadata,
|
||||
"tags": opik_tags,
|
||||
"thread_id": thread_id,
|
||||
}
|
||||
)
|
||||
|
||||
span_id = create_uuid7()
|
||||
verbose_logger.debug(
|
||||
|
|
@ -319,6 +349,7 @@ class OpikLogger(CustomBatchLogger):
|
|||
"output": output_data,
|
||||
"metadata": metadata,
|
||||
"tags": opik_tags,
|
||||
"thread_id": thread_id,
|
||||
"usage": usage,
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ For batching specific details see CustomBatchLogger class
|
|||
|
||||
import asyncio
|
||||
import os
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ try:
|
|||
from litellm_enterprise.integrations.prometheus import PrometheusLogger
|
||||
except Exception:
|
||||
PrometheusLogger = None
|
||||
from litellm.integrations.bitbucket import BitBucketPromptManager
|
||||
from litellm.integrations.cloudzero.cloudzero import CloudZeroLogger
|
||||
from litellm.integrations.dotprompt import DotpromptManager
|
||||
from litellm.integrations.s3_v2 import S3Logger
|
||||
|
|
@ -90,6 +91,7 @@ class CustomLoggerRegistry:
|
|||
"dynamic_rate_limiter_v3": _PROXY_DynamicRateLimitHandlerV3,
|
||||
"vector_store_pre_call_hook": VectorStorePreCallHook,
|
||||
"dotprompt": DotpromptManager,
|
||||
"bitbucket": BitBucketPromptManager,
|
||||
"cloudzero": CloudZeroLogger,
|
||||
"posthog": PostHogLogger,
|
||||
}
|
||||
|
|
@ -157,7 +159,6 @@ class CustomLoggerRegistry:
|
|||
if callback_class == class_type:
|
||||
callback_strs.append(callback_str)
|
||||
return callback_strs
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_class_type_for_custom_logger_name(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Optional
|
||||
|
||||
import litellm
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ from litellm.types.rerank import RerankResponse
|
|||
from litellm.types.router import CustomPricingLiteLLMParams
|
||||
from litellm.types.utils import (
|
||||
CallTypes,
|
||||
CostBreakdown,
|
||||
CostResponseTypes,
|
||||
DynamicPromptManagementParamLiteral,
|
||||
EmbeddingResponse,
|
||||
|
|
@ -343,6 +344,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
litellm_params = scrub_sensitive_keys_in_metadata(litellm_params)
|
||||
|
||||
self.litellm_params = litellm_params
|
||||
|
||||
# Initialize cost breakdown field
|
||||
self.cost_breakdown: Optional[CostBreakdown] = None
|
||||
|
||||
self.model_call_details: Dict[str, Any] = {
|
||||
"litellm_trace_id": litellm_trace_id,
|
||||
|
|
@ -1155,6 +1159,33 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
- self.model_call_details.get("start_time", datetime.datetime.now())
|
||||
).total_seconds() * 1000
|
||||
|
||||
def set_cost_breakdown(
|
||||
self,
|
||||
input_cost: float,
|
||||
output_cost: float,
|
||||
total_cost: float,
|
||||
cost_for_built_in_tools_cost_usd_dollar: float,
|
||||
) -> None:
|
||||
"""
|
||||
Helper method to store cost breakdown in the logging object.
|
||||
|
||||
Args:
|
||||
input_cost: Cost of input/prompt tokens
|
||||
output_cost: Cost of output/completion tokens
|
||||
cost_for_built_in_tools_cost_usd_dollar: Cost of built-in tools
|
||||
total_cost: Total cost of request
|
||||
"""
|
||||
|
||||
self.cost_breakdown = CostBreakdown(
|
||||
input_cost=input_cost,
|
||||
output_cost=output_cost,
|
||||
total_cost=total_cost,
|
||||
tool_usage_cost=cost_for_built_in_tools_cost_usd_dollar
|
||||
)
|
||||
verbose_logger.debug(
|
||||
f"Cost breakdown set - input: {input_cost}, output: {output_cost}, cost_for_built_in_tools_cost_usd_dollar: {cost_for_built_in_tools_cost_usd_dollar}, total: {total_cost}"
|
||||
)
|
||||
|
||||
def _response_cost_calculator(
|
||||
self,
|
||||
result: Union[
|
||||
|
|
@ -1228,7 +1259,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
"standard_built_in_tools_params": self.standard_built_in_tools_params,
|
||||
"router_model_id": router_model_id,
|
||||
"litellm_logging_obj": self,
|
||||
"service_tier": self.optional_params.get("service_tier") if self.optional_params else None,
|
||||
"service_tier": self.optional_params.get("service_tier")
|
||||
if self.optional_params
|
||||
else None,
|
||||
}
|
||||
except Exception as e: # error creating kwargs for cost calculation
|
||||
debug_info = StandardLoggingModelCostFailureDebugInformation(
|
||||
|
|
@ -3606,6 +3639,25 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915
|
|||
dotprompt_logger = DotpromptManager()
|
||||
_in_memory_loggers.append(dotprompt_logger)
|
||||
return dotprompt_logger # type: ignore
|
||||
elif logging_integration == "bitbucket":
|
||||
from litellm.integrations.bitbucket.bitbucket_prompt_manager import (
|
||||
BitBucketPromptManager,
|
||||
)
|
||||
|
||||
for callback in _in_memory_loggers:
|
||||
if isinstance(callback, BitBucketPromptManager):
|
||||
return callback
|
||||
|
||||
# Get global BitBucket config
|
||||
bitbucket_config = getattr(litellm, "global_bitbucket_config", None)
|
||||
if bitbucket_config is None:
|
||||
raise ValueError(
|
||||
"BitBucket configuration not found. Please set litellm.global_bitbucket_config first."
|
||||
)
|
||||
|
||||
bitbucket_logger = BitBucketPromptManager(bitbucket_config=bitbucket_config)
|
||||
_in_memory_loggers.append(bitbucket_logger)
|
||||
return bitbucket_logger # type: ignore
|
||||
return None
|
||||
except Exception as e:
|
||||
verbose_logger.exception(
|
||||
|
|
@ -4191,16 +4243,22 @@ class StandardLoggingPayloadSetup:
|
|||
|
||||
# Get the actual s3_path from the configured cold storage logger instance
|
||||
s3_path = "" # default value
|
||||
|
||||
|
||||
# Try to get the actual logger instance from the logger name
|
||||
try:
|
||||
custom_logger = litellm.logging_callback_manager.get_active_custom_logger_for_callback_name(configured_cold_storage_logger)
|
||||
if custom_logger and hasattr(custom_logger, 's3_path') and custom_logger.s3_path:
|
||||
custom_logger = litellm.logging_callback_manager.get_active_custom_logger_for_callback_name(
|
||||
configured_cold_storage_logger
|
||||
)
|
||||
if (
|
||||
custom_logger
|
||||
and hasattr(custom_logger, "s3_path")
|
||||
and custom_logger.s3_path
|
||||
):
|
||||
s3_path = custom_logger.s3_path
|
||||
except Exception:
|
||||
# If any error occurs in getting the logger instance, use default empty s3_path
|
||||
pass
|
||||
|
||||
|
||||
s3_object_key = get_s3_object_key(
|
||||
s3_path=s3_path, # Use actual s3_path from logger configuration
|
||||
team_alias_prefix="", # Don't split by team alias for cold storage
|
||||
|
|
@ -4533,6 +4591,7 @@ def get_standard_logging_object_payload(
|
|||
metadata=clean_metadata,
|
||||
cache_key=clean_hidden_params["cache_key"],
|
||||
response_cost=response_cost,
|
||||
cost_breakdown=logging_obj.cost_breakdown,
|
||||
total_tokens=usage.total_tokens,
|
||||
prompt_tokens=usage.prompt_tokens,
|
||||
completion_tokens=usage.completion_tokens,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import asyncio
|
|||
import json
|
||||
import time
|
||||
import traceback
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Dict, Iterable, List, Literal, Optional, Tuple, Union
|
||||
|
||||
import litellm
|
||||
|
|
|
|||
|
|
@ -1,137 +0,0 @@
|
|||
"""
|
||||
Generic object pooling utilities for LiteLLM.
|
||||
|
||||
This module provides a flexible object pooling system that can be used
|
||||
to pool any type of object, reducing memory allocation overhead and
|
||||
improving performance for frequently created/destroyed objects.
|
||||
|
||||
Memory Management Strategy:
|
||||
- Balanced eviction-based memory control to optimize reuse ratio
|
||||
- Moderate eviction frequency (300s) to maintain high object reuse
|
||||
- Conservative eviction weight (0.3) to avoid destroying useful objects
|
||||
- Lower pre-warm count (5) to reduce initial memory footprint
|
||||
- Always keeps at least one object available for high availability
|
||||
- Unlimited pools when maxsize is not specified (eviction controls actual usage)
|
||||
"""
|
||||
|
||||
from typing import Any, Callable, Optional, Type, TypeVar
|
||||
|
||||
from pond import Pond, PooledObject, PooledObjectFactory
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
class GenericPooledObjectFactory(PooledObjectFactory):
|
||||
"""Generic factory class for creating pooled objects of any type."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
object_class: Type[T],
|
||||
pooled_maxsize: Optional[int] = None, # None = unlimited pool with eviction-based memory control
|
||||
least_one: bool = True, # Always keep at least one for high concurrency
|
||||
initializer: Optional[Callable[[T], None]] = None
|
||||
):
|
||||
# Only pass maxsize to Pond if user specified it - otherwise let Pond handle unlimited pools
|
||||
if pooled_maxsize is not None:
|
||||
super().__init__(pooled_maxsize=pooled_maxsize, least_one=least_one)
|
||||
else:
|
||||
super().__init__(least_one=least_one)
|
||||
self.object_class = object_class
|
||||
self.initializer = initializer
|
||||
self._user_maxsize = pooled_maxsize # Store original user preference
|
||||
|
||||
def createInstance(self) -> PooledObject:
|
||||
"""Create a new instance wrapped in a PooledObject."""
|
||||
# Create a properly initialized instance
|
||||
obj = self.object_class()
|
||||
return PooledObject(obj)
|
||||
|
||||
def destroy(self, pooled_object: PooledObject):
|
||||
"""Destroy the pooled object."""
|
||||
if hasattr(pooled_object.keeped_object, '__dict__'):
|
||||
pooled_object.keeped_object.__dict__.clear()
|
||||
del pooled_object
|
||||
|
||||
def reset(self, pooled_object: PooledObject, **kwargs: Any) -> PooledObject:
|
||||
"""Reset the pooled object to a clean state."""
|
||||
obj = pooled_object.keeped_object
|
||||
# Reset the object by calling its reset method if it exists
|
||||
if hasattr(obj, 'reset') and callable(getattr(obj, 'reset')):
|
||||
obj.reset()
|
||||
else:
|
||||
# Fallback: clear all attributes to reset the object
|
||||
if hasattr(obj, '__dict__'):
|
||||
obj.__dict__.clear()
|
||||
return pooled_object
|
||||
|
||||
def validate(self, pooled_object: PooledObject) -> bool:
|
||||
"""Validate if the pooled object is still usable."""
|
||||
return pooled_object.keeped_object is not None
|
||||
|
||||
# Global pond instances
|
||||
_pools: dict[str, Pond] = {}
|
||||
|
||||
def get_object_pool(
|
||||
pool_name: str,
|
||||
object_class: Type[T],
|
||||
pooled_maxsize: Optional[int] = None, # None = unlimited pool with eviction-based memory control
|
||||
least_one: bool = True, # Always keep at least one
|
||||
borrowed_timeout: int = 10, # Longer timeout for high concurrency
|
||||
time_between_eviction_runs: int = 300, # Less frequent eviction to maintain high reuse ratio
|
||||
eviction_weight: float = 0.3, # Less aggressive eviction for better reuse
|
||||
prewarm_count: int = 5 # Lower pre-warm count to reduce initial memory usage
|
||||
) -> Pond:
|
||||
"""Get or create a global object pool instance with balanced eviction-based memory control.
|
||||
|
||||
Memory is controlled through moderate eviction to balance reuse ratio and memory usage:
|
||||
- Moderate eviction frequency (300s) to maintain high object reuse ratio
|
||||
- Conservative eviction weight (0.3) to avoid destroying useful objects
|
||||
- Lower pre-warm count (5) to reduce initial memory footprint
|
||||
|
||||
Args:
|
||||
pool_name: Unique name for the pool
|
||||
object_class: The class type to pool
|
||||
pooled_maxsize: Maximum number of objects in the pool (None = truly unlimited)
|
||||
least_one: Whether to keep at least one object in the pool (default: True)
|
||||
borrowed_timeout: Timeout for borrowing objects (seconds, default: 10)
|
||||
time_between_eviction_runs: Time between eviction runs (seconds, default: 300)
|
||||
eviction_weight: Weight for eviction algorithm (default: 0.3, conservative)
|
||||
prewarm_count: Number of objects to pre-warm the pool with (default: 5)
|
||||
|
||||
Returns:
|
||||
Pond instance for the specified object type
|
||||
"""
|
||||
|
||||
if pool_name in _pools:
|
||||
return _pools[pool_name]
|
||||
|
||||
# Create new pond
|
||||
pond = Pond(
|
||||
borrowed_timeout=borrowed_timeout,
|
||||
time_between_eviction_runs=time_between_eviction_runs,
|
||||
thread_daemon=True,
|
||||
eviction_weight=eviction_weight
|
||||
)
|
||||
|
||||
# Register the factory with user's maxsize preference
|
||||
factory = GenericPooledObjectFactory(
|
||||
object_class=object_class,
|
||||
pooled_maxsize=pooled_maxsize,
|
||||
least_one=least_one
|
||||
)
|
||||
pond.register(factory, name=f"{pool_name}Factory")
|
||||
|
||||
# Pre-warm the pool
|
||||
_prewarm_pool(pond, pool_name, prewarm_count)
|
||||
|
||||
_pools[pool_name] = pond
|
||||
return pond
|
||||
|
||||
def _prewarm_pool(pond: Pond, pool_name: str, prewarm_count: int = 20) -> None:
|
||||
"""Pre-warm the pool with initial objects for high concurrency."""
|
||||
for _ in range(prewarm_count):
|
||||
try:
|
||||
pooled_obj = pond.borrow(name=f"{pool_name}Factory")
|
||||
pond.recycle(pooled_obj, name=f"{pool_name}Factory")
|
||||
except Exception:
|
||||
# If pre-warming fails, just continue
|
||||
break
|
||||
|
|
@ -2,7 +2,7 @@ import copy
|
|||
import json
|
||||
import mimetypes
|
||||
import re
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
import xml.etree.ElementTree as ET
|
||||
from enum import Enum
|
||||
from typing import Any, List, Optional, Tuple, cast, overload
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import json
|
|||
import threading
|
||||
import time
|
||||
import traceback
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Any, Callable, Dict, List, Optional, Union, cast
|
||||
|
||||
import httpx
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ from litellm.litellm_core_utils.core_helpers import map_finish_reason
|
|||
from litellm.llms.base_llm.base_utils import type_to_response_format_param
|
||||
from litellm.llms.base_llm.chat.transformation import BaseConfig, BaseLLMException
|
||||
from litellm.types.llms.anthropic import (
|
||||
ANTHROPIC_BETA_HEADER_VALUES,
|
||||
ANTHROPIC_HOSTED_TOOLS,
|
||||
AllAnthropicMessageValues,
|
||||
AllAnthropicToolsValues,
|
||||
AnthropicCodeExecutionTool,
|
||||
|
|
@ -50,7 +52,10 @@ from litellm.types.utils import (
|
|||
CompletionTokensDetailsWrapper,
|
||||
)
|
||||
from litellm.types.utils import Message as LitellmMessage
|
||||
from litellm.types.utils import PromptTokensDetailsWrapper, ServerToolUse
|
||||
from litellm.types.utils import (
|
||||
PromptTokensDetailsWrapper,
|
||||
ServerToolUse,
|
||||
)
|
||||
from litellm.utils import (
|
||||
ModelResponse,
|
||||
Usage,
|
||||
|
|
@ -70,9 +75,6 @@ else:
|
|||
LoggingClass = Any
|
||||
|
||||
|
||||
ANTHROPIC_HOSTED_TOOLS = ["web_search", "bash", "text_editor", "code_execution"]
|
||||
|
||||
|
||||
class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
||||
"""
|
||||
Reference: https://docs.anthropic.com/claude/reference/messages_post
|
||||
|
|
@ -639,6 +641,14 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
)
|
||||
)
|
||||
return tools
|
||||
|
||||
def update_headers_with_optional_anthropic_beta(self, headers: dict, optional_params: dict) -> dict:
|
||||
"""Update headers with optional anthropic beta."""
|
||||
_tools = optional_params.get("tools", [])
|
||||
for tool in _tools:
|
||||
if tool.get("type", None) and tool.get("type").startswith(ANTHROPIC_HOSTED_TOOLS.WEB_FETCH.value):
|
||||
headers["anthropic-beta"] = ANTHROPIC_BETA_HEADER_VALUES.WEB_FETCH_2025_09_10.value
|
||||
return headers
|
||||
|
||||
def transform_request(
|
||||
self,
|
||||
|
|
@ -675,6 +685,8 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
llm_provider="anthropic",
|
||||
)
|
||||
|
||||
headers = self.update_headers_with_optional_anthropic_beta(headers=headers, optional_params=optional_params)
|
||||
|
||||
# Separate system prompt from rest of message
|
||||
anthropic_system_message_list = self.translate_system_message(messages=messages)
|
||||
# Handling anthropic API Prompt Caching
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
## Translates OpenAI call to Anthropic `/v1/messages` format
|
||||
import json
|
||||
import traceback
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from collections import deque
|
||||
from typing import TYPE_CHECKING, Any, AsyncIterator, Iterator, Literal, Optional
|
||||
|
||||
|
|
|
|||
|
|
@ -458,7 +458,7 @@ class LiteLLMAnthropicMessagesAdapter:
|
|||
Literal["text", "tool_use"],
|
||||
"ContentBlockContentBlockDict",
|
||||
]:
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
|
||||
from litellm.types.llms.anthropic import TextBlock, ToolUseBlock
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Any, Coroutine, Optional, Union
|
||||
|
||||
from openai import AsyncAzureOpenAI, AzureOpenAI
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent-runtime_Invoke
|
|||
"""
|
||||
import base64
|
||||
import json
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import httpx
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import json
|
|||
import time
|
||||
import types
|
||||
import urllib.parse
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from functools import partial
|
||||
from typing import (
|
||||
Any,
|
||||
|
|
|
|||
|
|
@ -802,7 +802,7 @@ class CommonBatchFilesUtils:
|
|||
Tuple of (bucket_name, object_key)
|
||||
"""
|
||||
import time
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
|
||||
# Get bucket name
|
||||
bucket_name = (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import json
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from httpx import Headers, Response
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Translates from Cohere's `/v1/rerank` input format to Bedrock's `/rerank` input
|
|||
Why separate file? Make it easy to see how transformation works
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from litellm.types.llms.bedrock import (
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Translate between Cohere's `/rerank` format and Deepinfra's `/rerank` format.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import httpx
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import json
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Any, List, Literal, Optional, Tuple, Union, cast
|
||||
|
||||
import httpx
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ This file contains the transformation logic for the Gemini realtime API.
|
|||
"""
|
||||
|
||||
import json
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Any, Dict, List, Optional, Union, cast
|
||||
|
||||
from litellm import verbose_logger
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Transformation logic for Hosted VLLM rerank
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from litellm.types.rerank import (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import os
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import httpx
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Transformation logic from Cohere's /v1/rerank format to Infinity's `/v1/rerank`
|
|||
Why separate file? Make it easy to see how transformation works
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import List, Optional
|
||||
|
||||
import httpx
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Why separate file? Make it easy to see how transformation works
|
|||
Docs - https://jina.ai/reranker
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from httpx import URL, Response
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import json
|
||||
import time
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import json
|
||||
import time
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import TYPE_CHECKING, Any, AsyncIterator, Iterator, List, Optional, Union
|
||||
|
||||
from httpx._models import Headers, Response
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import json
|
||||
import time
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Any, List, Optional, Union
|
||||
|
||||
import aiohttp
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ class OpenAIImageEditConfig(BaseImageEditConfig):
|
|||
#########################################################
|
||||
# Separate images and masks as `files` and send other parameters as `data`
|
||||
#########################################################
|
||||
_image = request_dict.get("image")
|
||||
_image_list = request_dict.get("image")
|
||||
_mask = request_dict.get("mask")
|
||||
data_without_files = {
|
||||
k: v for k, v in request_dict.items() if k not in ["image", "mask"]
|
||||
|
|
@ -90,23 +90,21 @@ class OpenAIImageEditConfig(BaseImageEditConfig):
|
|||
files_list: List[Tuple[str, Any]] = []
|
||||
|
||||
# Handle image parameter
|
||||
if _image is not None:
|
||||
# Handle case where image can be a list (extract first image)
|
||||
if isinstance(_image, list):
|
||||
_image = _image[0] if _image else None
|
||||
|
||||
if _image is not None:
|
||||
image_content_type: str = ImageEditRequestUtils.get_image_content_type(
|
||||
_image
|
||||
)
|
||||
if isinstance(_image, BufferedReader):
|
||||
files_list.append(
|
||||
("image", (_image.name, _image, image_content_type))
|
||||
)
|
||||
else:
|
||||
files_list.append(
|
||||
("image", ("image.png", _image, image_content_type))
|
||||
if _image_list is not None:
|
||||
image_list = [_image_list] if not isinstance(_image_list, list) else _image_list
|
||||
for _image in image_list:
|
||||
if _image is not None:
|
||||
image_content_type: str = ImageEditRequestUtils.get_image_content_type(
|
||||
_image
|
||||
)
|
||||
if isinstance(_image, BufferedReader):
|
||||
files_list.append(
|
||||
("image", (_image.name, _image, image_content_type))
|
||||
)
|
||||
else:
|
||||
files_list.append(
|
||||
("image", ("image.png", _image, image_content_type))
|
||||
)
|
||||
|
||||
# Handle mask parameter if provided
|
||||
if _mask is not None:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Transformation logic from Cohere's /v1/rerank format to Together AI's `/v1/rera
|
|||
Why separate file? Make it easy to see how transformation works
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import List, Optional
|
||||
|
||||
from litellm.types.rerank import (
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Dict
|
||||
|
||||
from litellm.llms.vertex_ai.common_utils import (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import json
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
from litellm._uuid import uuid
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from httpx import Headers, Response
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue