Merge remote-tracking branch 'origin/main' into add-health-check-success-modal
|
|
@ -1,6 +1,8 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
{{- toYaml .Values.deploymentAnnotations | nindent 4 }}
|
||||
name: {{ include "litellm.fullname" . }}
|
||||
labels:
|
||||
{{- include "litellm.labels" . | nindent 4 }}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ serviceAccount:
|
|||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
# annotations for litellm deployment
|
||||
deploymentAnnotations: {}
|
||||
# annotations for litellm pods
|
||||
podAnnotations: {}
|
||||
podLabels: {}
|
||||
|
||||
|
|
|
|||
|
|
@ -265,6 +265,182 @@ if __name__ == "__main__":
|
|||
</Tabs>
|
||||
|
||||
|
||||
## Using your MCP with client side credentials
|
||||
|
||||
Use this if you want to pass a client side authentication token to LiteLLM to then pass to your MCP to auth to your MCP.
|
||||
|
||||
You can specify your MCP auth token using the header `x-mcp-auth`. LiteLLM will forward this token to your MCP server for authentication.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI API">
|
||||
|
||||
#### Connect via OpenAI Responses API with MCP Auth
|
||||
|
||||
Use the OpenAI Responses API and include the `x-mcp-auth` header for your MCP server authentication:
|
||||
|
||||
```bash title="cURL Example with MCP Auth" showLineNumbers
|
||||
curl --location 'https://api.openai.com/v1/responses' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header "Authorization: Bearer $OPENAI_API_KEY" \
|
||||
--data '{
|
||||
"model": "gpt-4o",
|
||||
"tools": [
|
||||
{
|
||||
"type": "mcp",
|
||||
"server_label": "litellm",
|
||||
"server_url": "<your-litellm-proxy-base-url>/mcp",
|
||||
"require_approval": "never",
|
||||
"headers": {
|
||||
"x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY",
|
||||
"x-mcp-auth": YOUR_MCP_AUTH_TOKEN
|
||||
}
|
||||
}
|
||||
],
|
||||
"input": "Run available tools",
|
||||
"tool_choice": "required"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="litellm" label="LiteLLM Proxy">
|
||||
|
||||
#### Connect via LiteLLM Proxy Responses API with MCP Auth
|
||||
|
||||
Use this when calling LiteLLM Proxy for LLM API requests to `/v1/responses` endpoint with MCP authentication:
|
||||
|
||||
```bash title="cURL Example with MCP Auth" showLineNumbers
|
||||
curl --location '<your-litellm-proxy-base-url>/v1/responses' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header "Authorization: Bearer $LITELLM_API_KEY" \
|
||||
--data '{
|
||||
"model": "gpt-4o",
|
||||
"tools": [
|
||||
{
|
||||
"type": "mcp",
|
||||
"server_label": "litellm",
|
||||
"server_url": "<your-litellm-proxy-base-url>/mcp",
|
||||
"require_approval": "never",
|
||||
"headers": {
|
||||
"x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY",
|
||||
"x-mcp-auth": "YOUR_MCP_AUTH_TOKEN"
|
||||
}
|
||||
}
|
||||
],
|
||||
"input": "Run available tools",
|
||||
"tool_choice": "required"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="cursor" label="Cursor IDE">
|
||||
|
||||
#### Connect via Cursor IDE with MCP Auth
|
||||
|
||||
Use tools directly from Cursor IDE with LiteLLM MCP and include your MCP authentication token:
|
||||
|
||||
**Setup Instructions:**
|
||||
|
||||
1. **Open Cursor Settings**: Use `⇧+⌘+J` (Mac) or `Ctrl+Shift+J` (Windows/Linux)
|
||||
2. **Navigate to MCP Tools**: Go to the "MCP Tools" tab and click "New MCP Server"
|
||||
3. **Add Configuration**: Copy and paste the JSON configuration below, then save with `Cmd+S` or `Ctrl+S`
|
||||
|
||||
```json title="Cursor MCP Configuration with Auth" showLineNumbers
|
||||
{
|
||||
"mcpServers": {
|
||||
"LiteLLM": {
|
||||
"url": "<your-litellm-proxy-base-url>/mcp",
|
||||
"headers": {
|
||||
"x-litellm-api-key": "Bearer $LITELLM_API_KEY",
|
||||
"x-mcp-auth": "$MCP_AUTH_TOKEN"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="http" label="Streamable HTTP">
|
||||
|
||||
#### Connect via Streamable HTTP Transport with MCP Auth
|
||||
|
||||
Connect to LiteLLM MCP using HTTP transport with MCP authentication:
|
||||
|
||||
**Server URL:**
|
||||
```text showLineNumbers
|
||||
<your-litellm-proxy-base-url>/mcp
|
||||
```
|
||||
|
||||
**Headers:**
|
||||
```text showLineNumbers
|
||||
x-litellm-api-key: Bearer YOUR_LITELLM_API_KEY
|
||||
x-mcp-auth: Bearer YOUR_MCP_AUTH_TOKEN
|
||||
```
|
||||
|
||||
This URL can be used with any MCP client that supports HTTP transport. The `x-mcp-auth` header will be forwarded to your MCP server for authentication.
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="fastmcp" label="Python FastMCP">
|
||||
|
||||
#### Connect via Python FastMCP Client with MCP Auth
|
||||
|
||||
Use the Python FastMCP client to connect to your LiteLLM MCP server with MCP authentication:
|
||||
|
||||
```python title="Python FastMCP Example with MCP Auth" showLineNumbers
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.transports import StreamableHttpTransport
|
||||
|
||||
# Create the transport with your LiteLLM MCP server URL and auth headers
|
||||
server_url = "<your-litellm-proxy-base-url>/mcp"
|
||||
transport = StreamableHttpTransport(
|
||||
server_url,
|
||||
headers={
|
||||
"x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY",
|
||||
"x-mcp-auth": "Bearer YOUR_MCP_AUTH_TOKEN"
|
||||
}
|
||||
)
|
||||
|
||||
# Initialize the client with the transport
|
||||
client = Client(transport=transport)
|
||||
|
||||
|
||||
async def main():
|
||||
# Connection is established here
|
||||
print("Connecting to LiteLLM MCP server with authentication...")
|
||||
async with client:
|
||||
print(f"Client connected: {client.is_connected()}")
|
||||
|
||||
# Make MCP calls within the context
|
||||
print("Fetching available tools...")
|
||||
tools = await client.list_tools()
|
||||
|
||||
print(f"Available tools: {json.dumps([t.name for t in tools], indent=2)}")
|
||||
|
||||
# Example: Call a tool (replace 'tool_name' with an actual tool name)
|
||||
if tools:
|
||||
tool_name = tools[0].name
|
||||
print(f"Calling tool: {tool_name}")
|
||||
|
||||
# Call the tool with appropriate arguments
|
||||
result = await client.call_tool(tool_name, arguments={})
|
||||
print(f"Tool result: {result}")
|
||||
|
||||
|
||||
# Run the example
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## ✨ MCP Permission Management
|
||||
|
||||
LiteLLM supports managing permissions for MCP Servers by Keys, Teams, Organizations (entities) on LiteLLM. When a MCP client attempts to list tools, LiteLLM will only return the tools the entity has permissions to access.
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ os.environ["LLAMA_API_KEY"] = "" # your Meta Llama API key
|
|||
messages = [{"content": "Hello, how are you?", "role": "user"}]
|
||||
|
||||
# Meta Llama call
|
||||
response = completion(model="meta_llama/Llama-3.3-70B-Instruct", messages=messages)
|
||||
response = completion(model="meta_llama/Llama-4-Maverick-17B-128E-Instruct-FP8", messages=messages)
|
||||
```
|
||||
|
||||
### Streaming
|
||||
|
|
@ -61,7 +61,7 @@ messages = [{"content": "Hello, how are you?", "role": "user"}]
|
|||
|
||||
# Meta Llama call with streaming
|
||||
response = completion(
|
||||
model="meta_llama/Llama-3.3-70B-Instruct",
|
||||
model="meta_llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
|
||||
messages=messages,
|
||||
stream=True
|
||||
)
|
||||
|
|
@ -70,6 +70,104 @@ for chunk in response:
|
|||
print(chunk)
|
||||
```
|
||||
|
||||
### Function Calling
|
||||
|
||||
```python showLineNumbers title="Meta Llama Function Calling"
|
||||
import os
|
||||
import litellm
|
||||
from litellm import completion
|
||||
|
||||
os.environ["LLAMA_API_KEY"] = "" # your Meta Llama API key
|
||||
|
||||
messages = [{"content": "What's the weather like in San Francisco?", "role": "user"}]
|
||||
|
||||
# Define the function
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get the current weather in a given location",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string",
|
||||
"enum": ["celsius", "fahrenheit"]
|
||||
}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
# Meta Llama call with function calling
|
||||
response = completion(
|
||||
model="meta_llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
|
||||
messages=messages,
|
||||
tools=tools,
|
||||
tool_choice="auto"
|
||||
)
|
||||
|
||||
print(response.choices[0].message.tool_calls)
|
||||
```
|
||||
|
||||
### Tool Use
|
||||
|
||||
```python showLineNumbers title="Meta Llama Tool Use"
|
||||
import os
|
||||
import litellm
|
||||
from litellm import completion
|
||||
|
||||
os.environ["LLAMA_API_KEY"] = "" # your Meta Llama API key
|
||||
|
||||
messages = [{"content": "Create a chart showing the population growth of New York City from 2010 to 2020", "role": "user"}]
|
||||
|
||||
# Define the tools
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "create_chart",
|
||||
"description": "Create a chart with the provided data",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chart_type": {
|
||||
"type": "string",
|
||||
"enum": ["bar", "line", "pie", "scatter"],
|
||||
"description": "The type of chart to create"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the chart"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"description": "The data to plot in the chart"
|
||||
}
|
||||
},
|
||||
"required": ["chart_type", "title", "data"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
# Meta Llama call with tool use
|
||||
response = completion(
|
||||
model="meta_llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
|
||||
messages=messages,
|
||||
tools=tools,
|
||||
tool_choice="auto"
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
## Usage - LiteLLM Proxy
|
||||
|
||||
|
|
@ -111,7 +209,7 @@ client = OpenAI(
|
|||
|
||||
# Non-streaming response
|
||||
response = client.chat.completions.create(
|
||||
model="meta_llama/Llama-3.3-70B-Instruct",
|
||||
model="meta_llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
|
||||
messages=[{"role": "user", "content": "Write a short poem about AI."}]
|
||||
)
|
||||
|
||||
|
|
@ -129,7 +227,7 @@ client = OpenAI(
|
|||
|
||||
# Streaming response
|
||||
response = client.chat.completions.create(
|
||||
model="meta_llama/Llama-3.3-70B-Instruct",
|
||||
model="meta_llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
|
||||
messages=[{"role": "user", "content": "Write a short poem about AI."}],
|
||||
stream=True
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
title: "[PRE-RELEASE] v1.72.6-stable - MCP Gateway Permission Management"
|
||||
title: "v1.72.6-stable - MCP Gateway Permission Management"
|
||||
slug: "v1-72-6-stable"
|
||||
date: 2025-06-14T10:00:00
|
||||
authors:
|
||||
|
|
@ -36,14 +36,14 @@ The production version will be released on Wednesday.
|
|||
docker run
|
||||
-e STORE_MODEL_IN_DB=True
|
||||
-p 4000:4000
|
||||
ghcr.io/berriai/litellm:main-v1.72.6.post1-nightly
|
||||
ghcr.io/berriai/litellm:main-v1.72.6-stable
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="pip" label="Pip">
|
||||
|
||||
``` showLineNumbers title="pip install litellm"
|
||||
pip install litellm==1.72.6.post1
|
||||
pip install litellm==1.72.6.post2
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import warnings
|
||||
|
||||
warnings.filterwarnings("ignore", message=".*conflict with protected namespace.*")
|
||||
### INIT VARIABLES ###########
|
||||
### INIT VARIABLES ############
|
||||
import threading
|
||||
import os
|
||||
from typing import Callable, List, Optional, Dict, Union, Any, Literal, get_args
|
||||
|
|
|
|||
|
|
@ -0,0 +1,164 @@
|
|||
"""
|
||||
LiteLLM Proxy uses this MCP Client to connnect to other MCP servers.
|
||||
"""
|
||||
import base64
|
||||
from datetime import timedelta
|
||||
from typing import List, Optional
|
||||
|
||||
from mcp import ClientSession
|
||||
from mcp.client.sse import sse_client
|
||||
from mcp.client.streamable_http import streamablehttp_client
|
||||
from mcp.types import CallToolRequestParams as MCPCallToolRequestParams
|
||||
from mcp.types import CallToolResult as MCPCallToolResult
|
||||
from mcp.types import Tool as MCPTool
|
||||
|
||||
from litellm.types.mcp import MCPAuth, MCPAuthType, MCPTransport, MCPTransportType
|
||||
|
||||
|
||||
def to_basic_auth(auth_value: str) -> str:
|
||||
"""Convert auth value to Basic Auth format."""
|
||||
return base64.b64encode(auth_value.encode("utf-8")).decode()
|
||||
|
||||
|
||||
class MCPClient:
|
||||
"""
|
||||
MCP Client supporting:
|
||||
SSE and HTTP transports
|
||||
Authentication via Bearer token, Basic Auth, or API Key
|
||||
Tool calling with error handling and result parsing
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
server_url: str,
|
||||
transport_type: MCPTransportType = MCPTransport.http,
|
||||
auth_type: MCPAuthType = None,
|
||||
auth_value: Optional[str] = None,
|
||||
timeout: float = 60.0,
|
||||
):
|
||||
self.server_url: str = server_url
|
||||
self.transport_type: MCPTransport = transport_type
|
||||
self.auth_type: MCPAuthType = auth_type
|
||||
self.timeout: float = timeout
|
||||
self._mcp_auth_value: Optional[str] = None
|
||||
self._session: Optional[ClientSession] = None
|
||||
self._context = None
|
||||
self._transport_ctx = None
|
||||
self._transport = None
|
||||
self._session_ctx = None
|
||||
|
||||
# handle the basic auth value if provided
|
||||
if auth_value:
|
||||
self.update_auth_value(auth_value)
|
||||
|
||||
async def __aenter__(self):
|
||||
"""
|
||||
Enable async context manager support.
|
||||
Initializes the transport and session.
|
||||
"""
|
||||
await self.connect()
|
||||
return self
|
||||
|
||||
async def connect(self):
|
||||
"""Initialize the transport and session."""
|
||||
if self._session:
|
||||
return # Already connected
|
||||
|
||||
headers = self._get_auth_headers()
|
||||
|
||||
if self.transport_type == MCPTransport.sse:
|
||||
self._transport_ctx = sse_client(
|
||||
url=self.server_url,
|
||||
timeout=self.timeout,
|
||||
headers=headers,
|
||||
)
|
||||
self._transport = await self._transport_ctx.__aenter__()
|
||||
self._session_ctx = ClientSession(self._transport[0], self._transport[1])
|
||||
self._session = await self._session_ctx.__aenter__()
|
||||
await self._session.initialize()
|
||||
else:
|
||||
self._transport_ctx = streamablehttp_client(
|
||||
url=self.server_url,
|
||||
timeout=timedelta(seconds=self.timeout),
|
||||
headers=headers,
|
||||
)
|
||||
self._transport = await self._transport_ctx.__aenter__()
|
||||
self._session_ctx = ClientSession(self._transport[0], self._transport[1])
|
||||
self._session = await self._session_ctx.__aenter__()
|
||||
await self._session.initialize()
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||
"""Cleanup when exiting context manager."""
|
||||
if self._session:
|
||||
await self._session_ctx.__aexit__(exc_type, exc_val, exc_tb) # type: ignore
|
||||
if self._transport_ctx:
|
||||
await self._transport_ctx.__aexit__(exc_type, exc_val, exc_tb)
|
||||
|
||||
async def disconnect(self):
|
||||
"""Clean up session and connections."""
|
||||
if self._session:
|
||||
try:
|
||||
# Ensure session is properly closed
|
||||
await self._session.close() # type: ignore
|
||||
except Exception:
|
||||
pass
|
||||
self._session = None
|
||||
|
||||
if self._context:
|
||||
try:
|
||||
await self._context.__aexit__(None, None, None) # type: ignore
|
||||
except Exception:
|
||||
pass
|
||||
self._context = None
|
||||
|
||||
def update_auth_value(self, mcp_auth_value: str):
|
||||
"""
|
||||
Set the authentication header for the MCP client.
|
||||
"""
|
||||
if self.auth_type == MCPAuth.basic:
|
||||
# Assuming mcp_auth_value is in format "username:password", convert it when updating
|
||||
mcp_auth_value = to_basic_auth(mcp_auth_value)
|
||||
self._mcp_auth_value = mcp_auth_value
|
||||
|
||||
def _get_auth_headers(self) -> dict:
|
||||
"""Generate authentication headers based on auth type."""
|
||||
if not self._mcp_auth_value:
|
||||
return {}
|
||||
|
||||
if self.auth_type == MCPAuth.bearer_token:
|
||||
return {"Authorization": f"Bearer {self._mcp_auth_value}"}
|
||||
elif self.auth_type == MCPAuth.basic:
|
||||
return {"Authorization": f"Basic {self._mcp_auth_value}"}
|
||||
elif self.auth_type == MCPAuth.api_key:
|
||||
return {"X-API-Key": self._mcp_auth_value}
|
||||
return {}
|
||||
|
||||
async def list_tools(self) -> List[MCPTool]:
|
||||
"""List available tools from the server."""
|
||||
if not self._session:
|
||||
await self.connect()
|
||||
if self._session is None:
|
||||
raise ValueError("Session is not initialized")
|
||||
|
||||
result = await self._session.list_tools()
|
||||
return result.tools
|
||||
|
||||
async def call_tool(
|
||||
self, call_tool_request_params: MCPCallToolRequestParams
|
||||
) -> MCPCallToolResult:
|
||||
"""
|
||||
Call an MCP Tool.
|
||||
"""
|
||||
if not self._session:
|
||||
await self.connect()
|
||||
|
||||
if self._session is None:
|
||||
raise ValueError("Session is not initialized")
|
||||
|
||||
tool_result = await self._session.call_tool(
|
||||
name=call_tool_request_params.name,
|
||||
arguments=call_tool_request_params.arguments,
|
||||
)
|
||||
return tool_result
|
||||
|
||||
|
||||
|
|
@ -215,40 +215,25 @@ class PrometheusLogger(CustomLogger):
|
|||
self.litellm_remaining_requests_metric = self._gauge_factory(
|
||||
"litellm_remaining_requests",
|
||||
"LLM Deployment Analytics - remaining requests for model, returned from LLM API Provider",
|
||||
labelnames=[
|
||||
"model_group",
|
||||
"api_provider",
|
||||
"api_base",
|
||||
"litellm_model_name",
|
||||
"hashed_api_key",
|
||||
"api_key_alias",
|
||||
],
|
||||
labelnames=self.get_labels_for_metric(
|
||||
"litellm_remaining_requests_metric"
|
||||
),
|
||||
)
|
||||
|
||||
self.litellm_remaining_tokens_metric = self._gauge_factory(
|
||||
"litellm_remaining_tokens",
|
||||
"remaining tokens for model, returned from LLM API Provider",
|
||||
labelnames=[
|
||||
"model_group",
|
||||
"api_provider",
|
||||
"api_base",
|
||||
"litellm_model_name",
|
||||
"hashed_api_key",
|
||||
"api_key_alias",
|
||||
],
|
||||
labelnames=self.get_labels_for_metric(
|
||||
"litellm_remaining_tokens_metric"
|
||||
),
|
||||
)
|
||||
|
||||
self.litellm_overhead_latency_metric = self._histogram_factory(
|
||||
"litellm_overhead_latency_metric",
|
||||
"Latency overhead (milliseconds) added by LiteLLM processing",
|
||||
labelnames=[
|
||||
"model_group",
|
||||
"api_provider",
|
||||
"api_base",
|
||||
"litellm_model_name",
|
||||
"hashed_api_key",
|
||||
"api_key_alias",
|
||||
],
|
||||
labelnames=self.get_labels_for_metric(
|
||||
"litellm_overhead_latency_metric"
|
||||
),
|
||||
buckets=LATENCY_BUCKETS,
|
||||
)
|
||||
# llm api provider budget metrics
|
||||
|
|
@ -566,6 +551,7 @@ class PrometheusLogger(CustomLogger):
|
|||
hashed_api_key=user_api_key,
|
||||
api_key_alias=user_api_key_alias,
|
||||
requested_model=standard_logging_payload["model_group"],
|
||||
model_group=standard_logging_payload["model_group"],
|
||||
team=user_api_team,
|
||||
team_alias=user_api_team_alias,
|
||||
user=user_id,
|
||||
|
|
@ -1160,6 +1146,7 @@ class PrometheusLogger(CustomLogger):
|
|||
enum_values: UserAPIKeyLabelValues,
|
||||
output_tokens: float = 1.0,
|
||||
):
|
||||
|
||||
try:
|
||||
verbose_logger.debug("setting remaining tokens requests metric")
|
||||
standard_logging_payload: Optional[StandardLoggingPayload] = (
|
||||
|
|
@ -1292,7 +1279,7 @@ class PrometheusLogger(CustomLogger):
|
|||
).observe(latency_per_token)
|
||||
|
||||
except Exception as e:
|
||||
verbose_logger.error(
|
||||
verbose_logger.exception(
|
||||
"Prometheus Error: set_llm_deployment_success_metrics. Exception occured - {}".format(
|
||||
str(e)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1053,10 +1053,10 @@ def convert_to_gemini_tool_call_invoke(
|
|||
if tool_calls is not None:
|
||||
for tool in tool_calls:
|
||||
if "function" in tool:
|
||||
gemini_function_call: Optional[
|
||||
VertexFunctionCall
|
||||
] = _gemini_tool_call_invoke_helper(
|
||||
function_call_params=tool["function"]
|
||||
gemini_function_call: Optional[VertexFunctionCall] = (
|
||||
_gemini_tool_call_invoke_helper(
|
||||
function_call_params=tool["function"]
|
||||
)
|
||||
)
|
||||
if gemini_function_call is not None:
|
||||
_parts_list.append(
|
||||
|
|
@ -1573,9 +1573,9 @@ def anthropic_messages_pt( # noqa: PLR0915
|
|||
)
|
||||
|
||||
if "cache_control" in _content_element:
|
||||
_anthropic_content_element[
|
||||
"cache_control"
|
||||
] = _content_element["cache_control"]
|
||||
_anthropic_content_element["cache_control"] = (
|
||||
_content_element["cache_control"]
|
||||
)
|
||||
user_content.append(_anthropic_content_element)
|
||||
elif m.get("type", "") == "text":
|
||||
m = cast(ChatCompletionTextObject, m)
|
||||
|
|
@ -1613,9 +1613,9 @@ def anthropic_messages_pt( # noqa: PLR0915
|
|||
)
|
||||
|
||||
if "cache_control" in _content_element:
|
||||
_anthropic_content_text_element[
|
||||
"cache_control"
|
||||
] = _content_element["cache_control"]
|
||||
_anthropic_content_text_element["cache_control"] = (
|
||||
_content_element["cache_control"]
|
||||
)
|
||||
|
||||
user_content.append(_anthropic_content_text_element)
|
||||
|
||||
|
|
@ -2433,8 +2433,10 @@ class BedrockImageProcessor:
|
|||
|
||||
# Extract MIME type using regular expression
|
||||
mime_type_match = re.match(r"data:(.*?);base64", image_metadata)
|
||||
|
||||
if mime_type_match:
|
||||
mime_type = mime_type_match.group(1)
|
||||
mime_type = mime_type.split(";")[0]
|
||||
image_format = mime_type.split("/")[1]
|
||||
else:
|
||||
mime_type = "image/jpeg"
|
||||
|
|
@ -2458,6 +2460,7 @@ class BedrockImageProcessor:
|
|||
|
||||
document_types = ["application", "text"]
|
||||
is_document = any(mime_type.startswith(doc_type) for doc_type in document_types)
|
||||
|
||||
supported_image_and_video_formats: List[str] = (
|
||||
supported_video_formats + supported_image_formats
|
||||
)
|
||||
|
|
|
|||
|
|
@ -77,9 +77,9 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
to pass metadata to anthropic, it's {"user_id": "any-relevant-information"}
|
||||
"""
|
||||
|
||||
max_tokens: Optional[
|
||||
int
|
||||
] = DEFAULT_ANTHROPIC_CHAT_MAX_TOKENS # anthropic requires a default value (Opus, Sonnet, and Haiku have the same default)
|
||||
max_tokens: Optional[int] = (
|
||||
DEFAULT_ANTHROPIC_CHAT_MAX_TOKENS # anthropic requires a default value (Opus, Sonnet, and Haiku have the same default)
|
||||
)
|
||||
stop_sequences: Optional[list] = None
|
||||
temperature: Optional[int] = None
|
||||
top_p: Optional[int] = None
|
||||
|
|
@ -104,11 +104,16 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
if key != "self" and value is not None:
|
||||
setattr(self.__class__, key, value)
|
||||
|
||||
@property
|
||||
def custom_llm_provider(self) -> Optional[str]:
|
||||
return "anthropic"
|
||||
|
||||
@classmethod
|
||||
def get_config(cls):
|
||||
return super().get_config()
|
||||
|
||||
def get_supported_openai_params(self, model: str):
|
||||
|
||||
params = [
|
||||
"stream",
|
||||
"stop",
|
||||
|
|
@ -447,11 +452,11 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
if mcp_servers:
|
||||
optional_params["mcp_servers"] = mcp_servers
|
||||
if param == "tool_choice" or param == "parallel_tool_calls":
|
||||
_tool_choice: Optional[
|
||||
AnthropicMessagesToolChoice
|
||||
] = self._map_tool_choice(
|
||||
tool_choice=non_default_params.get("tool_choice"),
|
||||
parallel_tool_use=non_default_params.get("parallel_tool_calls"),
|
||||
_tool_choice: Optional[AnthropicMessagesToolChoice] = (
|
||||
self._map_tool_choice(
|
||||
tool_choice=non_default_params.get("tool_choice"),
|
||||
parallel_tool_use=non_default_params.get("parallel_tool_calls"),
|
||||
)
|
||||
)
|
||||
|
||||
if _tool_choice is not None:
|
||||
|
|
@ -557,9 +562,9 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
text=system_message_block["content"],
|
||||
)
|
||||
if "cache_control" in system_message_block:
|
||||
anthropic_system_message_content[
|
||||
"cache_control"
|
||||
] = system_message_block["cache_control"]
|
||||
anthropic_system_message_content["cache_control"] = (
|
||||
system_message_block["cache_control"]
|
||||
)
|
||||
anthropic_system_message_list.append(
|
||||
anthropic_system_message_content
|
||||
)
|
||||
|
|
@ -573,9 +578,9 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
)
|
||||
)
|
||||
if "cache_control" in _content:
|
||||
anthropic_system_message_content[
|
||||
"cache_control"
|
||||
] = _content["cache_control"]
|
||||
anthropic_system_message_content["cache_control"] = (
|
||||
_content["cache_control"]
|
||||
)
|
||||
|
||||
anthropic_system_message_list.append(
|
||||
anthropic_system_message_content
|
||||
|
|
@ -735,9 +740,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
)
|
||||
return _message
|
||||
|
||||
def extract_response_content(
|
||||
self, completion_response: dict
|
||||
) -> Tuple[
|
||||
def extract_response_content(self, completion_response: dict) -> Tuple[
|
||||
str,
|
||||
Optional[List[Any]],
|
||||
Optional[
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ class BaseConfig(ABC):
|
|||
types.BuiltinFunctionType,
|
||||
classmethod,
|
||||
staticmethod,
|
||||
property,
|
||||
),
|
||||
)
|
||||
and v is not None
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ class AmazonAnthropicClaude3Config(AmazonInvokeConfig, AnthropicConfig):
|
|||
|
||||
anthropic_version: str = "bedrock-2023-05-31"
|
||||
|
||||
@property
|
||||
def custom_llm_provider(self) -> Optional[str]:
|
||||
return "bedrock"
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> List[str]:
|
||||
return AnthropicConfig.get_supported_openai_params(self, model)
|
||||
|
||||
|
|
|
|||
|
|
@ -533,6 +533,39 @@ class AsyncHTTPHandler:
|
|||
verbose_logger.debug("Using AiohttpTransport...")
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _get_ssl_connector_kwargs(
|
||||
ssl_verify: Optional[bool] = None,
|
||||
ssl_context: Optional[ssl.SSLContext] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Helper method to get SSL connector initialization arguments for aiohttp TCPConnector.
|
||||
|
||||
SSL Configuration Priority:
|
||||
1. If ssl_context is provided -> use the custom SSL context
|
||||
2. If ssl_verify is False -> disable SSL verification (ssl=False)
|
||||
3. If ssl_verify is True/None -> use default SSL context with certifi CA bundle
|
||||
|
||||
Returns:
|
||||
Dict with appropriate SSL configuration for TCPConnector
|
||||
"""
|
||||
connector_kwargs: Dict[str, Any] = {
|
||||
"local_addr": ("0.0.0.0", 0) if litellm.force_ipv4 else None,
|
||||
}
|
||||
|
||||
if ssl_context is not None:
|
||||
# Priority 1: Use the provided custom SSL context
|
||||
connector_kwargs["ssl"] = ssl_context
|
||||
elif ssl_verify is False:
|
||||
# Priority 2: Explicitly disable SSL verification
|
||||
connector_kwargs["verify_ssl"] = False
|
||||
else:
|
||||
# Priority 3: Use our default SSL context with certifi CA bundle
|
||||
# This covers ssl_verify=True and ssl_verify=None cases
|
||||
connector_kwargs["ssl"] = AsyncHTTPHandler._get_ssl_context()
|
||||
|
||||
return connector_kwargs
|
||||
|
||||
@staticmethod
|
||||
def _create_aiohttp_transport(
|
||||
ssl_verify: Optional[bool] = None,
|
||||
|
|
@ -541,29 +574,34 @@ class AsyncHTTPHandler:
|
|||
"""
|
||||
Creates an AiohttpTransport with RequestNotRead error handling
|
||||
|
||||
- If force_ipv4 is True, it will create an AiohttpTransport with local_addr set to "0.0.0.0"
|
||||
- [Default] If force_ipv4 is False, it will create an AiohttpTransport with default settings
|
||||
Note: aiohttp TCPConnector ssl parameter accepts:
|
||||
- SSLContext: custom SSL context
|
||||
- False: disable SSL verification
|
||||
- True: use default SSL verification (equivalent to ssl.create_default_context())
|
||||
"""
|
||||
from litellm.llms.custom_httpx.aiohttp_transport import LiteLLMAiohttpTransport
|
||||
|
||||
#########################################################
|
||||
# If ssl_verify is None, set it to True
|
||||
# TCP Connector does not allow ssl_verify to be None
|
||||
# by default aiohttp sets ssl_verify to True
|
||||
#########################################################
|
||||
if ssl_verify is None:
|
||||
ssl_verify = True
|
||||
connector_kwargs = AsyncHTTPHandler._get_ssl_connector_kwargs(
|
||||
ssl_verify=ssl_verify, ssl_context=ssl_context
|
||||
)
|
||||
|
||||
verbose_logger.debug("Creating AiohttpTransport...")
|
||||
return LiteLLMAiohttpTransport(
|
||||
client=lambda: ClientSession(
|
||||
connector=TCPConnector(
|
||||
verify_ssl=ssl_verify,
|
||||
ssl_context=ssl_context,
|
||||
local_addr=("0.0.0.0", 0) if litellm.force_ipv4 else None,
|
||||
)
|
||||
connector=TCPConnector(**connector_kwargs)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _get_ssl_context() -> ssl.SSLContext:
|
||||
"""
|
||||
Get the SSL context for the AiohttpTransport
|
||||
"""
|
||||
import certifi
|
||||
return ssl.create_default_context(
|
||||
cafile=certifi.where()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _create_httpx_transport() -> Optional[AsyncHTTPTransport]:
|
||||
|
|
|
|||
|
|
@ -2,13 +2,16 @@
|
|||
Translate from OpenAI's `/v1/chat/completions` to VLLM's `/v1/chat/completions`
|
||||
"""
|
||||
|
||||
from typing import List, Optional, Tuple
|
||||
from typing import TYPE_CHECKING, List, Optional, Tuple
|
||||
|
||||
from litellm.secret_managers.main import get_secret_bool, get_secret_str
|
||||
from litellm.types.router import LiteLLM_Params
|
||||
|
||||
from ...openai.chat.gpt_transformation import OpenAIGPTConfig
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from litellm.types.llms.openai import AllMessageValues
|
||||
|
||||
|
||||
class LiteLLMProxyChatConfig(OpenAIGPTConfig):
|
||||
def get_supported_openai_params(self, model: str) -> List:
|
||||
|
|
@ -113,3 +116,33 @@ class LiteLLMProxyChatConfig(OpenAIGPTConfig):
|
|||
)
|
||||
|
||||
return model, custom_llm_provider, api_key, api_base
|
||||
|
||||
def transform_request(
|
||||
self,
|
||||
model: str,
|
||||
messages: List["AllMessageValues"],
|
||||
optional_params: dict,
|
||||
litellm_params: dict,
|
||||
headers: dict,
|
||||
) -> dict:
|
||||
# don't transform the request
|
||||
return {
|
||||
"model": model,
|
||||
"messages": messages,
|
||||
**optional_params,
|
||||
}
|
||||
|
||||
async def async_transform_request(
|
||||
self,
|
||||
model: str,
|
||||
messages: List["AllMessageValues"],
|
||||
optional_params: dict,
|
||||
litellm_params: dict,
|
||||
headers: dict,
|
||||
) -> dict:
|
||||
# don't transform the request
|
||||
return {
|
||||
"model": model,
|
||||
"messages": messages,
|
||||
**optional_params,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ Calls done in OpenAI/openai.py as Llama API is openai-compatible.
|
|||
Docs: https://llama.developer.meta.com/docs/features/compatibility/
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
import warnings
|
||||
|
||||
# Suppress Pydantic serialization warnings for Meta Llama responses
|
||||
warnings.filterwarnings("ignore", message="Pydantic serializer warnings")
|
||||
|
||||
from litellm import get_model_info, verbose_logger
|
||||
from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig
|
||||
|
||||
|
||||
|
|
@ -17,27 +19,11 @@ class LlamaAPIConfig(OpenAIGPTConfig):
|
|||
"""
|
||||
Llama API has limited support for OpenAI parameters
|
||||
|
||||
Tool calling, Functional Calling, tool choice are not working right now
|
||||
function_call, tools, and tool_choice are working
|
||||
response_format: only json_schema is working
|
||||
"""
|
||||
supports_function_calling: Optional[bool] = None
|
||||
supports_tool_choice: Optional[bool] = None
|
||||
try:
|
||||
model_info = get_model_info(model, custom_llm_provider="meta_llama")
|
||||
supports_function_calling = model_info.get(
|
||||
"supports_function_calling", False
|
||||
)
|
||||
supports_tool_choice = model_info.get("supports_tool_choice", False)
|
||||
except Exception as e:
|
||||
verbose_logger.debug(f"Error getting supported openai params: {e}")
|
||||
pass
|
||||
|
||||
# Function calling and tool choice are now supported on Llama API
|
||||
optional_params = super().get_supported_openai_params(model)
|
||||
if not supports_function_calling:
|
||||
optional_params.remove("function_call")
|
||||
if not supports_tool_choice:
|
||||
optional_params.remove("tools")
|
||||
optional_params.remove("tool_choice")
|
||||
return optional_params
|
||||
|
||||
def map_openai_params(
|
||||
|
|
|
|||
|
|
@ -86,8 +86,9 @@ class MistralConfig(OpenAIGPTConfig):
|
|||
"seed",
|
||||
"stop",
|
||||
"response_format",
|
||||
"parallel_tool_calls",
|
||||
]
|
||||
|
||||
|
||||
# Add reasoning support for magistral models
|
||||
if "magistral" in model.lower():
|
||||
supported_params.extend(["thinking", "reasoning_effort"])
|
||||
|
|
@ -154,6 +155,8 @@ Then provide a clear, concise answer based on your reasoning."""
|
|||
if param == "thinking" and "magistral" in model.lower():
|
||||
# Flag that we need to add reasoning system prompt
|
||||
optional_params["_add_reasoning_prompt"] = True
|
||||
if param == "parallel_tool_calls":
|
||||
optional_params["parallel_tool_calls"] = value
|
||||
return optional_params
|
||||
|
||||
def _get_openai_compatible_provider_info(
|
||||
|
|
@ -287,12 +290,18 @@ Then provide a clear, concise answer based on your reasoning."""
|
|||
"""
|
||||
Mistral API only supports `name` in tool messages
|
||||
|
||||
If role == tool, then we keep `name`
|
||||
If role == tool, then we keep `name` if it's not an empty string
|
||||
Otherwise, we drop `name`
|
||||
"""
|
||||
_name = message.get("name") # type: ignore
|
||||
if _name is not None and message["role"] != "tool":
|
||||
message.pop("name", None) # type: ignore
|
||||
|
||||
if _name is not None:
|
||||
# Remove name if not a tool message
|
||||
if message["role"] != "tool":
|
||||
message.pop("name", None) # type: ignore
|
||||
# For tool messages, remove name if it's an empty string
|
||||
elif isinstance(_name, str) and len(_name.strip()) == 0:
|
||||
message.pop("name", None) # type: ignore
|
||||
|
||||
return message
|
||||
|
||||
|
|
|
|||
|
|
@ -1025,9 +1025,9 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
|
|||
response_tokens_details = CompletionTokensDetailsWrapper()
|
||||
for detail in usage_metadata["responseTokensDetails"]:
|
||||
if detail["modality"] == "TEXT":
|
||||
response_tokens_details.text_tokens = detail["tokenCount"]
|
||||
response_tokens_details.text_tokens = detail.get("tokenCount", 0)
|
||||
elif detail["modality"] == "AUDIO":
|
||||
response_tokens_details.audio_tokens = detail["tokenCount"]
|
||||
response_tokens_details.audio_tokens = detail.get("tokenCount", 0)
|
||||
#########################################################
|
||||
|
||||
if "promptTokensDetails" in usage_metadata:
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ class VertexAIAnthropicConfig(AnthropicConfig):
|
|||
|
||||
Note: Please make sure to modify the default parameters as required for your use case.
|
||||
"""
|
||||
|
||||
@property
|
||||
def custom_llm_provider(self) -> Optional[str]:
|
||||
return "vertex_ai"
|
||||
|
|
|
|||
|
|
@ -79,7 +79,15 @@ class VertexBase:
|
|||
|
||||
# Check if the JSON object contains Workload Identity Federation configuration
|
||||
if "type" in json_obj and json_obj["type"] == "external_account":
|
||||
creds = self._credentials_from_identity_pool(json_obj)
|
||||
# If environment_id key contains "aws" value it corresponds to an AWS config file
|
||||
if (
|
||||
"credential_source" in json_obj
|
||||
and "environment_id" in json_obj["credential_source"]
|
||||
and "aws" in json_obj["credential_source"]["environment_id"]
|
||||
):
|
||||
creds = self._credentials_from_identity_pool_with_aws(json_obj)
|
||||
else:
|
||||
creds = self._credentials_from_identity_pool(json_obj)
|
||||
# Check if the JSON object contains Authorized User configuration (via gcloud auth application-default login)
|
||||
elif "type" in json_obj and json_obj["type"] == "authorized_user":
|
||||
creds = self._credentials_from_authorized_user(
|
||||
|
|
@ -122,6 +130,11 @@ class VertexBase:
|
|||
from google.auth import identity_pool
|
||||
|
||||
return identity_pool.Credentials.from_info(json_obj)
|
||||
|
||||
def _credentials_from_identity_pool_with_aws(self, json_obj):
|
||||
from google.auth import aws
|
||||
|
||||
return aws.Credentials.from_info(json_obj)
|
||||
|
||||
def _credentials_from_authorized_user(self, json_obj, scopes):
|
||||
import google.oauth2.credentials
|
||||
|
|
|
|||
|
|
@ -451,9 +451,9 @@
|
|||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 16384,
|
||||
"input_cost_per_token": 2.5e-06,
|
||||
"input_cost_per_audio_token": 4.0e-5,
|
||||
"input_cost_per_audio_token": 4e-05,
|
||||
"output_cost_per_token": 1e-05,
|
||||
"output_cost_per_audio_token": 8.0e-5,
|
||||
"output_cost_per_audio_token": 8e-05,
|
||||
"litellm_provider": "openai",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -594,7 +594,7 @@
|
|||
"max_output_tokens": 100000,
|
||||
"input_cost_per_token": 1.5e-06,
|
||||
"output_cost_per_token": 6e-06,
|
||||
"cache_read_input_token_cost": 0.375e-06,
|
||||
"cache_read_input_token_cost": 3.75e-07,
|
||||
"litellm_provider": "openai",
|
||||
"mode": "responses",
|
||||
"supports_pdf_input": true,
|
||||
|
|
@ -744,10 +744,10 @@
|
|||
"max_tokens": 100000,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 100000,
|
||||
"input_cost_per_token": 20e-06,
|
||||
"input_cost_per_token_batches": 10e-06,
|
||||
"output_cost_per_token_batches": 40e-06,
|
||||
"output_cost_per_token": 80e-06,
|
||||
"input_cost_per_token": 2e-05,
|
||||
"input_cost_per_token_batches": 1e-05,
|
||||
"output_cost_per_token_batches": 4e-05,
|
||||
"output_cost_per_token": 8e-05,
|
||||
"litellm_provider": "openai",
|
||||
"mode": "responses",
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -774,10 +774,10 @@
|
|||
"max_tokens": 100000,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 100000,
|
||||
"input_cost_per_token": 20e-06,
|
||||
"input_cost_per_token_batches": 10e-06,
|
||||
"output_cost_per_token_batches": 40e-06,
|
||||
"output_cost_per_token": 80e-06,
|
||||
"input_cost_per_token": 2e-05,
|
||||
"input_cost_per_token_batches": 1e-05,
|
||||
"output_cost_per_token_batches": 4e-05,
|
||||
"output_cost_per_token": 8e-05,
|
||||
"litellm_provider": "openai",
|
||||
"mode": "responses",
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -806,7 +806,7 @@
|
|||
"max_output_tokens": 100000,
|
||||
"input_cost_per_token": 2e-06,
|
||||
"output_cost_per_token": 8e-06,
|
||||
"cache_read_input_token_cost": 0.5e-06,
|
||||
"cache_read_input_token_cost": 5e-07,
|
||||
"litellm_provider": "openai",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -837,7 +837,7 @@
|
|||
"max_output_tokens": 100000,
|
||||
"input_cost_per_token": 2e-06,
|
||||
"output_cost_per_token": 8e-06,
|
||||
"cache_read_input_token_cost": 0.5e-06,
|
||||
"cache_read_input_token_cost": 5e-07,
|
||||
"litellm_provider": "openai",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -2685,7 +2685,7 @@
|
|||
"max_output_tokens": 100000,
|
||||
"input_cost_per_token": 1.5e-06,
|
||||
"output_cost_per_token": 6e-06,
|
||||
"cache_read_input_token_cost": 0.375e-06,
|
||||
"cache_read_input_token_cost": 3.75e-07,
|
||||
"litellm_provider": "azure",
|
||||
"mode": "responses",
|
||||
"supports_pdf_input": true,
|
||||
|
|
@ -4295,8 +4295,8 @@
|
|||
"max_tokens": 40000,
|
||||
"max_input_tokens": 40000,
|
||||
"max_output_tokens": 40000,
|
||||
"input_cost_per_token": 0.5e-6,
|
||||
"output_cost_per_token": 1.5e-6,
|
||||
"input_cost_per_token": 5e-07,
|
||||
"output_cost_per_token": 1.5e-06,
|
||||
"litellm_provider": "mistral",
|
||||
"mode": "chat",
|
||||
"source": "https://mistral.ai/pricing#api-pricing",
|
||||
|
|
@ -4309,7 +4309,7 @@
|
|||
"max_tokens": 40000,
|
||||
"max_input_tokens": 40000,
|
||||
"max_output_tokens": 40000,
|
||||
"input_cost_per_token": 0.5e-06,
|
||||
"input_cost_per_token": 5e-07,
|
||||
"output_cost_per_token": 1.5e-06,
|
||||
"litellm_provider": "mistral",
|
||||
"mode": "chat",
|
||||
|
|
@ -4579,9 +4579,9 @@
|
|||
"output_cost_per_token": 4e-06,
|
||||
"litellm_provider": "xai",
|
||||
"mode": "chat",
|
||||
"supports_reasoning": true,
|
||||
"supports_function_calling": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": false,
|
||||
"source": "https://x.ai/api#pricing",
|
||||
"supports_web_search": true
|
||||
|
|
@ -4616,21 +4616,6 @@
|
|||
"source": "https://x.ai/api#pricing",
|
||||
"supports_web_search": true
|
||||
},
|
||||
"xai/grok-3-mini-fast-latest": {
|
||||
"max_tokens": 131072,
|
||||
"max_input_tokens": 131072,
|
||||
"max_output_tokens": 131072,
|
||||
"input_cost_per_token": 6e-07,
|
||||
"output_cost_per_token": 4e-06,
|
||||
"litellm_provider": "xai",
|
||||
"mode": "chat",
|
||||
"supports_reasoning": true,
|
||||
"supports_function_calling": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_response_schema": false,
|
||||
"source": "https://x.ai/api#pricing",
|
||||
"supports_web_search": true
|
||||
},
|
||||
"xai/grok-vision-beta": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
|
|
@ -5812,9 +5797,9 @@
|
|||
"max_output_tokens": 4028,
|
||||
"litellm_provider": "meta_llama",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": false,
|
||||
"supports_function_calling": true,
|
||||
"source": "https://llama.developer.meta.com/docs/models",
|
||||
"supports_tool_choice": false,
|
||||
"supports_tool_choice": true,
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image"
|
||||
|
|
@ -5829,9 +5814,9 @@
|
|||
"max_output_tokens": 4028,
|
||||
"litellm_provider": "meta_llama",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": false,
|
||||
"supports_function_calling": true,
|
||||
"source": "https://llama.developer.meta.com/docs/models",
|
||||
"supports_tool_choice": false,
|
||||
"supports_tool_choice": true,
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image"
|
||||
|
|
@ -5846,9 +5831,9 @@
|
|||
"max_output_tokens": 4028,
|
||||
"litellm_provider": "meta_llama",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": false,
|
||||
"supports_function_calling": true,
|
||||
"source": "https://llama.developer.meta.com/docs/models",
|
||||
"supports_tool_choice": false,
|
||||
"supports_tool_choice": true,
|
||||
"supported_modalities": [
|
||||
"text"
|
||||
],
|
||||
|
|
@ -5862,9 +5847,9 @@
|
|||
"max_output_tokens": 4028,
|
||||
"litellm_provider": "meta_llama",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": false,
|
||||
"supports_function_calling": true,
|
||||
"source": "https://llama.developer.meta.com/docs/models",
|
||||
"supports_tool_choice": false,
|
||||
"supports_tool_choice": true,
|
||||
"supported_modalities": [
|
||||
"text"
|
||||
],
|
||||
|
|
@ -6735,8 +6720,8 @@
|
|||
"output_cost_per_token_above_200k_tokens": 1.5e-05,
|
||||
"litellm_provider": "gemini",
|
||||
"mode": "chat",
|
||||
"rpm": 2e-3,
|
||||
"tpm": 8e-6,
|
||||
"rpm": 2000,
|
||||
"tpm": 800000,
|
||||
"supports_system_messages": true,
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
|
|
@ -6802,8 +6787,9 @@
|
|||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true,
|
||||
"tpm": 8e-6,
|
||||
"rpm": 1e-5
|
||||
"tpm": 8000000,
|
||||
"rpm": 100000,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.5-flash": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -6845,7 +6831,8 @@
|
|||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini/gemini-2.5-flash-preview-tts": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -6896,9 +6883,9 @@
|
|||
"max_audio_per_prompt": 1,
|
||||
"max_pdf_size_mb": 30,
|
||||
"input_cost_per_audio_token": 1e-06,
|
||||
"input_cost_per_token": 1.5e-07,
|
||||
"output_cost_per_token": 6e-07,
|
||||
"output_cost_per_reasoning_token": 3.5e-06,
|
||||
"input_cost_per_token": 3e-07,
|
||||
"output_cost_per_token": 2.5e-06,
|
||||
"output_cost_per_reasoning_token": 2.5e-06,
|
||||
"litellm_provider": "gemini",
|
||||
"mode": "chat",
|
||||
"rpm": 10,
|
||||
|
|
@ -6925,7 +6912,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini/gemini-2.5-flash-preview-04-17": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -6966,7 +6954,53 @@
|
|||
"text"
|
||||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_web_search": true
|
||||
"supports_web_search": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini/gemini-2.5-flash-lite-preview-06-17": {
|
||||
"max_tokens": 65535,
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 65535,
|
||||
"max_images_per_prompt": 3000,
|
||||
"max_videos_per_prompt": 10,
|
||||
"max_video_length": 1,
|
||||
"max_audio_length_hours": 8.4,
|
||||
"max_audio_per_prompt": 1,
|
||||
"max_pdf_size_mb": 30,
|
||||
"input_cost_per_audio_token": 5e-07,
|
||||
"input_cost_per_token": 1e-07,
|
||||
"output_cost_per_token": 4e-07,
|
||||
"output_cost_per_reasoning_token": 4e-07,
|
||||
"litellm_provider": "gemini",
|
||||
"mode": "chat",
|
||||
"rpm": 15,
|
||||
"tpm": 250000,
|
||||
"supports_reasoning": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_audio_output": false,
|
||||
"supports_tool_choice": true,
|
||||
"supported_endpoints": [
|
||||
"/v1/chat/completions",
|
||||
"/v1/completions",
|
||||
"/v1/batch"
|
||||
],
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image",
|
||||
"audio",
|
||||
"video"
|
||||
],
|
||||
"supported_output_modalities": [
|
||||
"text"
|
||||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-lite",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.5-flash-preview-05-20": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -6979,9 +7013,9 @@
|
|||
"max_audio_per_prompt": 1,
|
||||
"max_pdf_size_mb": 30,
|
||||
"input_cost_per_audio_token": 1e-06,
|
||||
"input_cost_per_token": 1.5e-07,
|
||||
"output_cost_per_token": 6e-07,
|
||||
"output_cost_per_reasoning_token": 3.5e-06,
|
||||
"input_cost_per_token": 3e-07,
|
||||
"output_cost_per_token": 2.5e-06,
|
||||
"output_cost_per_reasoning_token": 2.5e-06,
|
||||
"litellm_provider": "vertex_ai-language-models",
|
||||
"mode": "chat",
|
||||
"supports_reasoning": true,
|
||||
|
|
@ -7008,7 +7042,8 @@
|
|||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.5-flash-preview-04-17": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -7049,7 +7084,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true
|
||||
"supports_web_search": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.5-flash-lite-preview-06-17": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -7061,7 +7097,7 @@
|
|||
"max_audio_length_hours": 8.4,
|
||||
"max_audio_per_prompt": 1,
|
||||
"max_pdf_size_mb": 30,
|
||||
"input_cost_per_audio_token": 1e-06,
|
||||
"input_cost_per_audio_token": 5e-07,
|
||||
"input_cost_per_token": 1e-07,
|
||||
"output_cost_per_token": 4e-07,
|
||||
"output_cost_per_reasoning_token": 4e-07,
|
||||
|
|
@ -7091,7 +7127,8 @@
|
|||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.0-flash": {
|
||||
"max_tokens": 8192,
|
||||
|
|
@ -7237,7 +7274,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true
|
||||
"supports_web_search": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.5-pro-preview-05-06": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -7282,7 +7320,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true
|
||||
"supports_web_search": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.5-pro-preview-03-25": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -7324,7 +7363,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true
|
||||
"supports_web_search": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.0-flash-preview-image-generation": {
|
||||
"max_tokens": 8192,
|
||||
|
|
@ -7649,7 +7689,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/pricing#gemini-2.5-pro-preview",
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini/gemini-2.5-pro-preview-05-06": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -7687,7 +7728,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/pricing#gemini-2.5-pro-preview",
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini/gemini-2.5-pro-preview-03-25": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -7724,7 +7766,8 @@
|
|||
"text"
|
||||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/pricing#gemini-2.5-pro-preview",
|
||||
"supports_web_search": true
|
||||
"supports_web_search": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini/gemini-2.0-flash-exp": {
|
||||
"max_tokens": 8192,
|
||||
|
|
@ -8562,13 +8605,13 @@
|
|||
"litellm_provider": "vertex_ai-image-models",
|
||||
"mode": "image_generation",
|
||||
"source": "https://cloud.google.com/vertex-ai/generative-ai/pricing"
|
||||
},
|
||||
},
|
||||
"vertex_ai/imagen-4.0-fast-generate-preview-06-06": {
|
||||
"output_cost_per_image": 0.02,
|
||||
"litellm_provider": "vertex_ai-image-models",
|
||||
"mode": "image_generation",
|
||||
"source": "https://cloud.google.com/vertex-ai/generative-ai/pricing"
|
||||
},
|
||||
},
|
||||
"vertex_ai/imagen-3.0-generate-002": {
|
||||
"output_cost_per_image": 0.04,
|
||||
"litellm_provider": "vertex_ai-image-models",
|
||||
|
|
@ -10830,6 +10873,46 @@
|
|||
"supports_response_schema": true,
|
||||
"source": "https://aws.amazon.com/bedrock/pricing/"
|
||||
},
|
||||
"apac.amazon.nova-micro-v1:0": {
|
||||
"max_tokens": 10000,
|
||||
"max_input_tokens": 300000,
|
||||
"max_output_tokens": 10000,
|
||||
"input_cost_per_token": 3.7e-08,
|
||||
"output_cost_per_token": 1.48e-07,
|
||||
"litellm_provider": "bedrock_converse",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true
|
||||
},
|
||||
"apac.amazon.nova-lite-v1:0": {
|
||||
"max_tokens": 10000,
|
||||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 10000,
|
||||
"input_cost_per_token": 6.3e-08,
|
||||
"output_cost_per_token": 2.52e-07,
|
||||
"litellm_provider": "bedrock_converse",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true
|
||||
},
|
||||
"apac.amazon.nova-pro-v1:0": {
|
||||
"max_tokens": 10000,
|
||||
"max_input_tokens": 300000,
|
||||
"max_output_tokens": 10000,
|
||||
"input_cost_per_token": 8.4e-07,
|
||||
"output_cost_per_token": 3.36e-06,
|
||||
"litellm_provider": "bedrock_converse",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true
|
||||
},
|
||||
"us.amazon.nova-premier-v1:0": {
|
||||
"max_tokens": 10000,
|
||||
"max_input_tokens": 1000000,
|
||||
|
|
@ -11315,6 +11398,93 @@
|
|||
"supports_reasoning": true,
|
||||
"supports_computer_use": true
|
||||
},
|
||||
"apac.anthropic.claude-3-haiku-20240307-v1:0": {
|
||||
"max_tokens": 4096,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 2.5e-07,
|
||||
"output_cost_per_token": 1.25e-06,
|
||||
"litellm_provider": "bedrock",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_vision": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"apac.anthropic.claude-3-sonnet-20240229-v1:0": {
|
||||
"max_tokens": 4096,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 3e-06,
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"litellm_provider": "bedrock",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_vision": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"apac.anthropic.claude-3-5-sonnet-20240620-v1:0": {
|
||||
"max_tokens": 4096,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 3e-06,
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"litellm_provider": "bedrock",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_vision": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"apac.anthropic.claude-3-5-sonnet-20241022-v2:0": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 3e-06,
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"cache_creation_input_token_cost": 3.75e-06,
|
||||
"cache_read_input_token_cost": 3e-07,
|
||||
"litellm_provider": "bedrock",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_computer_use": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"apac.anthropic.claude-sonnet-4-20250514-v1:0": {
|
||||
"max_tokens": 64000,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 64000,
|
||||
"input_cost_per_token": 3e-06,
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_low": 0.01,
|
||||
"search_context_size_medium": 0.01,
|
||||
"search_context_size_high": 0.01
|
||||
},
|
||||
"cache_creation_input_token_cost": 3.75e-06,
|
||||
"cache_read_input_token_cost": 3e-07,
|
||||
"litellm_provider": "bedrock_converse",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
"tool_use_system_prompt_tokens": 159,
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_computer_use": true
|
||||
},
|
||||
"eu.anthropic.claude-3-5-haiku-20241022-v1:0": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 200000,
|
||||
|
|
@ -14770,7 +14940,7 @@
|
|||
},
|
||||
"deepgram/nova-3": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14784,7 +14954,7 @@
|
|||
},
|
||||
"deepgram/nova-3-general": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14798,7 +14968,7 @@
|
|||
},
|
||||
"deepgram/nova-3-medical": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00008667,
|
||||
"input_cost_per_second": 8.667e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14812,7 +14982,7 @@
|
|||
},
|
||||
"deepgram/nova-2": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14826,7 +14996,7 @@
|
|||
},
|
||||
"deepgram/nova-2-general": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14840,7 +15010,7 @@
|
|||
},
|
||||
"deepgram/nova-2-meeting": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14854,7 +15024,7 @@
|
|||
},
|
||||
"deepgram/nova-2-phonecall": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14868,7 +15038,7 @@
|
|||
},
|
||||
"deepgram/nova-2-voicemail": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14882,7 +15052,7 @@
|
|||
},
|
||||
"deepgram/nova-2-finance": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14896,7 +15066,7 @@
|
|||
},
|
||||
"deepgram/nova-2-conversationalai": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14910,7 +15080,7 @@
|
|||
},
|
||||
"deepgram/nova-2-video": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14924,7 +15094,7 @@
|
|||
},
|
||||
"deepgram/nova-2-drivethru": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14938,7 +15108,7 @@
|
|||
},
|
||||
"deepgram/nova-2-automotive": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14952,7 +15122,7 @@
|
|||
},
|
||||
"deepgram/nova-2-atc": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14966,7 +15136,7 @@
|
|||
},
|
||||
"deepgram/nova": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14980,7 +15150,7 @@
|
|||
},
|
||||
"deepgram/nova-general": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14994,7 +15164,7 @@
|
|||
},
|
||||
"deepgram/nova-phonecall": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -15266,4 +15436,4 @@
|
|||
"notes": "Deepgram's hosted OpenAI Whisper models - pricing may differ from native Deepgram models"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
from typing import Optional
|
||||
|
||||
from mcp.server.auth.middleware.bearer_auth import AuthenticatedUser
|
||||
|
||||
from litellm.proxy._types import UserAPIKeyAuth
|
||||
|
|
@ -8,5 +10,6 @@ class LiteLLMAuthenticatedUser(AuthenticatedUser):
|
|||
Wrapper class to make UserAPIKeyAuth compatible with MCP's AuthenticatedUser
|
||||
"""
|
||||
|
||||
def __init__(self, user_api_key_auth: UserAPIKeyAuth):
|
||||
def __init__(self, user_api_key_auth: UserAPIKeyAuth, mcp_auth_header: Optional[str] = None):
|
||||
self.user_api_key_auth = user_api_key_auth
|
||||
self.mcp_auth_header = mcp_auth_header
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
from typing import List, Optional
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from starlette.datastructures import Headers
|
||||
from starlette.requests import Request
|
||||
from starlette.types import Scope
|
||||
|
||||
from litellm._logging import verbose_logger
|
||||
from litellm.proxy._types import LiteLLM_TeamTable, UserAPIKeyAuth
|
||||
from litellm.proxy._types import LiteLLM_TeamTable, SpecialHeaders, UserAPIKeyAuth
|
||||
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
|
||||
|
||||
|
||||
|
|
@ -16,11 +16,14 @@ class UserAPIKeyAuthMCP:
|
|||
Utilizes the main `user_api_key_auth` function to validate the request
|
||||
"""
|
||||
|
||||
LITELLM_API_KEY_HEADER_NAME_PRIMARY = "x-litellm-api-key"
|
||||
LITELLM_API_KEY_HEADER_NAME_SECONDARY = "Authorization"
|
||||
LITELLM_API_KEY_HEADER_NAME_PRIMARY = SpecialHeaders.custom_litellm_api_key.value
|
||||
LITELLM_API_KEY_HEADER_NAME_SECONDARY = SpecialHeaders.openai_authorization.value
|
||||
|
||||
# This is the header to use if you want LiteLLM to use this header for authenticating to the MCP server
|
||||
LITELLM_MCP_AUTH_HEADER_NAME = SpecialHeaders.mcp_auth.value
|
||||
|
||||
@staticmethod
|
||||
async def user_api_key_auth_mcp(scope: Scope) -> UserAPIKeyAuth:
|
||||
async def user_api_key_auth_mcp(scope: Scope) -> Tuple[UserAPIKeyAuth, Optional[str]]:
|
||||
"""
|
||||
Validate and extract headers from the ASGI scope for MCP requests.
|
||||
|
||||
|
|
@ -29,6 +32,7 @@ class UserAPIKeyAuthMCP:
|
|||
|
||||
Returns:
|
||||
UserAPIKeyAuth containing validated authentication information
|
||||
mcp_auth_header: Optional[str] MCP auth header to be passed to the MCP server
|
||||
|
||||
Raises:
|
||||
HTTPException: If headers are invalid or missing required headers
|
||||
|
|
@ -37,6 +41,7 @@ class UserAPIKeyAuthMCP:
|
|||
litellm_api_key = (
|
||||
UserAPIKeyAuthMCP.get_litellm_api_key_from_headers(headers) or ""
|
||||
)
|
||||
mcp_auth_header = headers.get(UserAPIKeyAuthMCP.LITELLM_MCP_AUTH_HEADER_NAME)
|
||||
|
||||
# Create a proper Request object with mock body method to avoid ASGI receive channel issues
|
||||
request = Request(scope=scope)
|
||||
|
|
@ -52,7 +57,7 @@ class UserAPIKeyAuthMCP:
|
|||
api_key=litellm_api_key, request=request
|
||||
)
|
||||
|
||||
return validated_user_api_key_auth
|
||||
return validated_user_api_key_auth, mcp_auth_header
|
||||
|
||||
@staticmethod
|
||||
def get_litellm_api_key_from_headers(headers: Headers) -> Optional[str]:
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ import hashlib
|
|||
import json
|
||||
from typing import Any, Dict, List, Optional, cast
|
||||
|
||||
from mcp import ClientSession
|
||||
from mcp.client.sse import sse_client
|
||||
from mcp.types import CallToolRequestParams as MCPCallToolRequestParams
|
||||
from mcp.types import CallToolResult
|
||||
from mcp.types import Tool as MCPTool
|
||||
|
||||
from litellm._logging import verbose_logger
|
||||
from litellm.experimental_mcp_client.client import MCPClient
|
||||
from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import (
|
||||
UserAPIKeyAuthMCP,
|
||||
)
|
||||
|
|
@ -29,12 +29,6 @@ from litellm.proxy._types import (
|
|||
MCPTransportType,
|
||||
UserAPIKeyAuth,
|
||||
)
|
||||
|
||||
try:
|
||||
from mcp.client.streamable_http import streamablehttp_client
|
||||
except ImportError:
|
||||
streamablehttp_client = None # type: ignore
|
||||
|
||||
from litellm.types.mcp_server.mcp_server_manager import MCPInfo, MCPServer
|
||||
|
||||
|
||||
|
|
@ -164,7 +158,9 @@ class MCPServerManager:
|
|||
return list(self.get_registry().keys())
|
||||
|
||||
async def list_tools(
|
||||
self, user_api_key_auth: Optional[UserAPIKeyAuth] = None
|
||||
self,
|
||||
user_api_key_auth: Optional[UserAPIKeyAuth] = None,
|
||||
mcp_auth_header: Optional[str] = None,
|
||||
) -> List[MCPTool]:
|
||||
"""
|
||||
List all tools available across all MCP Servers.
|
||||
|
|
@ -183,7 +179,10 @@ class MCPServerManager:
|
|||
verbose_logger.warning(f"MCP Server {server_id} not found")
|
||||
continue
|
||||
try:
|
||||
tools = await self._get_tools_from_server(server)
|
||||
tools = await self._get_tools_from_server(
|
||||
server=server,
|
||||
mcp_auth_header=mcp_auth_header,
|
||||
)
|
||||
list_tools_result.extend(tools)
|
||||
except Exception as e:
|
||||
verbose_logger.exception(
|
||||
|
|
@ -192,7 +191,30 @@ class MCPServerManager:
|
|||
|
||||
return list_tools_result
|
||||
|
||||
async def _get_tools_from_server(self, server: MCPServer) -> List[MCPTool]:
|
||||
#########################################################
|
||||
# Methods that call the upstream MCP servers
|
||||
#########################################################
|
||||
def _create_mcp_client(self, server: MCPServer, mcp_auth_header: Optional[str] = None) -> MCPClient:
|
||||
"""
|
||||
Create an MCPClient instance for the given server.
|
||||
|
||||
Args:
|
||||
server (MCPServer): The server configuration
|
||||
mcp_auth_header: MCP auth header to be passed to the MCP server. This is optional and will be used if provided.
|
||||
|
||||
Returns:
|
||||
MCPClient: Configured MCP client instance
|
||||
"""
|
||||
transport = server.transport or MCPTransport.sse
|
||||
return MCPClient(
|
||||
server_url=server.url,
|
||||
transport_type=transport,
|
||||
auth_type=server.auth_type,
|
||||
auth_value=mcp_auth_header or server.authentication_token,
|
||||
timeout=60.0,
|
||||
)
|
||||
|
||||
async def _get_tools_from_server(self, server: MCPServer, mcp_auth_header: Optional[str] = None) -> List[MCPTool]:
|
||||
"""
|
||||
Helper method to get tools from a single MCP server.
|
||||
|
||||
|
|
@ -203,57 +225,51 @@ class MCPServerManager:
|
|||
List[MCPTool]: List of tools available on the server
|
||||
"""
|
||||
verbose_logger.debug(f"Connecting to url: {server.url}")
|
||||
|
||||
verbose_logger.info("_get_tools_from_server...")
|
||||
# send transport to connect to the server
|
||||
if server.transport is None or server.transport == MCPTransport.sse:
|
||||
async with sse_client(url=server.url) as (read, write):
|
||||
async with ClientSession(read, write) as session:
|
||||
await session.initialize()
|
||||
|
||||
tools_result = await session.list_tools()
|
||||
verbose_logger.debug(f"Tools from {server.name}: {tools_result}")
|
||||
client = self._create_mcp_client(
|
||||
server=server,
|
||||
mcp_auth_header=mcp_auth_header,
|
||||
)
|
||||
async with client:
|
||||
tools = await client.list_tools()
|
||||
verbose_logger.debug(f"Tools from {server.name}: {tools}")
|
||||
|
||||
# Update tool to server mapping
|
||||
for tool in tools_result.tools:
|
||||
self.tool_name_to_mcp_server_name_mapping[tool.name] = (
|
||||
server.name
|
||||
)
|
||||
# Update tool to server mapping
|
||||
for tool in tools:
|
||||
self.tool_name_to_mcp_server_name_mapping[tool.name] = server.name
|
||||
|
||||
return tools_result.tools
|
||||
elif server.transport == MCPTransport.http:
|
||||
if streamablehttp_client is None:
|
||||
verbose_logger.error(
|
||||
"streamablehttp_client not available - install mcp with HTTP support"
|
||||
)
|
||||
raise ValueError(
|
||||
"streamablehttp_client not available - please run `pip install mcp -U`"
|
||||
)
|
||||
verbose_logger.debug(f"Using HTTP streamable transport for {server.url}")
|
||||
async with streamablehttp_client(
|
||||
url=server.url,
|
||||
) as (read_stream, write_stream, get_session_id):
|
||||
async with ClientSession(read_stream, write_stream) as session:
|
||||
await session.initialize()
|
||||
return tools
|
||||
|
||||
async def call_tool(
|
||||
self,
|
||||
name: str,
|
||||
arguments: Dict[str, Any],
|
||||
user_api_key_auth: Optional[UserAPIKeyAuth] = None,
|
||||
mcp_auth_header: Optional[str] = None,
|
||||
) -> CallToolResult:
|
||||
"""
|
||||
Call a tool with the given name and arguments
|
||||
"""
|
||||
mcp_server = self._get_mcp_server_from_tool_name(name)
|
||||
if mcp_server is None:
|
||||
raise ValueError(f"Tool {name} not found")
|
||||
|
||||
if get_session_id is not None:
|
||||
session_id = get_session_id()
|
||||
if session_id:
|
||||
verbose_logger.debug(f"HTTP session ID: {session_id}")
|
||||
client = self._create_mcp_client(
|
||||
server=mcp_server,
|
||||
mcp_auth_header=mcp_auth_header,
|
||||
)
|
||||
async with client:
|
||||
call_tool_params = MCPCallToolRequestParams(
|
||||
name=name,
|
||||
arguments=arguments,
|
||||
)
|
||||
return await client.call_tool(call_tool_params)
|
||||
|
||||
#########################################################
|
||||
# End of Methods that call the upstream MCP servers
|
||||
#########################################################
|
||||
|
||||
tools_result = await session.list_tools()
|
||||
verbose_logger.debug(f"Tools from {server.name}: {tools_result}")
|
||||
|
||||
# Update tool to server mapping
|
||||
for tool in tools_result.tools:
|
||||
self.tool_name_to_mcp_server_name_mapping[tool.name] = (
|
||||
server.name
|
||||
)
|
||||
|
||||
return tools_result.tools
|
||||
else:
|
||||
verbose_logger.warning(f"Unsupported transport type: {server.transport}")
|
||||
return []
|
||||
|
||||
def initialize_tool_name_to_mcp_server_name_mapping(self):
|
||||
"""
|
||||
|
|
@ -278,46 +294,6 @@ class MCPServerManager:
|
|||
for tool in tools:
|
||||
self.tool_name_to_mcp_server_name_mapping[tool.name] = server.name
|
||||
|
||||
async def call_tool(self, name: str, arguments: Dict[str, Any]):
|
||||
"""
|
||||
Call a tool with the given name and arguments
|
||||
"""
|
||||
mcp_server = self._get_mcp_server_from_tool_name(name)
|
||||
if mcp_server is None:
|
||||
raise ValueError(f"Tool {name} not found")
|
||||
elif mcp_server.transport is None or mcp_server.transport == MCPTransport.sse:
|
||||
async with sse_client(url=mcp_server.url) as (read, write):
|
||||
async with ClientSession(read, write) as session:
|
||||
await session.initialize()
|
||||
return await session.call_tool(name, arguments)
|
||||
elif mcp_server.transport == MCPTransport.http:
|
||||
if streamablehttp_client is None:
|
||||
verbose_logger.error(
|
||||
"streamablehttp_client not available - install mcp with HTTP support"
|
||||
)
|
||||
raise ValueError(
|
||||
"streamablehttp_client not available - please run `pip install mcp -U`"
|
||||
)
|
||||
verbose_logger.debug(
|
||||
f"Using HTTP streamable transport for tool call: {name}"
|
||||
)
|
||||
async with streamablehttp_client(
|
||||
url=mcp_server.url,
|
||||
) as (read_stream, write_stream, get_session_id):
|
||||
async with ClientSession(read_stream, write_stream) as session:
|
||||
await session.initialize()
|
||||
|
||||
if get_session_id is not None:
|
||||
session_id = get_session_id()
|
||||
if session_id:
|
||||
verbose_logger.debug(
|
||||
f"HTTP session ID for tool call: {session_id}"
|
||||
)
|
||||
|
||||
return await session.call_tool(name, arguments)
|
||||
else:
|
||||
return CallToolResult(content=[], isError=True)
|
||||
|
||||
def _get_mcp_server_from_tool_name(self, tool_name: str) -> Optional[MCPServer]:
|
||||
"""
|
||||
Get the MCP Server from the tool name
|
||||
|
|
|
|||
|
|
@ -70,7 +70,9 @@ if MCP_AVAILABLE:
|
|||
if server_id and server.server_id != server_id:
|
||||
continue
|
||||
try:
|
||||
tools = await global_mcp_server_manager._get_tools_from_server(server)
|
||||
tools = await global_mcp_server_manager._get_tools_from_server(
|
||||
server=server,
|
||||
)
|
||||
for tool in tools:
|
||||
list_tools_result.append(
|
||||
ListMCPToolsRestAPIResponseObject(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ LiteLLM MCP Server Routes
|
|||
|
||||
import asyncio
|
||||
import contextlib
|
||||
from typing import Any, AsyncIterator, Dict, List, Optional, Union
|
||||
from typing import Any, AsyncIterator, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from pydantic import ConfigDict
|
||||
|
|
@ -166,11 +166,14 @@ if MCP_AVAILABLE:
|
|||
List all available tools
|
||||
"""
|
||||
# Get user authentication from context variable
|
||||
user_api_key_auth = get_auth_context()
|
||||
user_api_key_auth, mcp_auth_header = get_auth_context()
|
||||
verbose_logger.debug(
|
||||
f"MCP list_tools - User API Key Auth from context: {user_api_key_auth}"
|
||||
)
|
||||
return await _list_mcp_tools(user_api_key_auth)
|
||||
return await _list_mcp_tools(
|
||||
user_api_key_auth=user_api_key_auth,
|
||||
mcp_auth_header=mcp_auth_header,
|
||||
)
|
||||
|
||||
@server.call_tool()
|
||||
async def mcp_server_tool_call(
|
||||
|
|
@ -190,9 +193,15 @@ if MCP_AVAILABLE:
|
|||
HTTPException: If tool not found or arguments missing
|
||||
"""
|
||||
# Validate arguments
|
||||
user_api_key_auth, mcp_auth_header = get_auth_context()
|
||||
verbose_logger.debug(
|
||||
f"MCP mcp_server_tool_call - User API Key Auth from context: {user_api_key_auth}"
|
||||
)
|
||||
response = await call_mcp_tool(
|
||||
name=name,
|
||||
arguments=arguments,
|
||||
user_api_key_auth=user_api_key_auth,
|
||||
mcp_auth_header=mcp_auth_header,
|
||||
)
|
||||
return response
|
||||
|
||||
|
|
@ -206,6 +215,7 @@ if MCP_AVAILABLE:
|
|||
|
||||
async def _list_mcp_tools(
|
||||
user_api_key_auth: Optional[UserAPIKeyAuth] = None,
|
||||
mcp_auth_header: Optional[str] = None,
|
||||
) -> List[MCPTool]:
|
||||
"""
|
||||
List all available tools
|
||||
|
|
@ -229,6 +239,7 @@ if MCP_AVAILABLE:
|
|||
tools_from_mcp_servers: List[MCPTool] = (
|
||||
await global_mcp_server_manager.list_tools(
|
||||
user_api_key_auth=user_api_key_auth,
|
||||
mcp_auth_header=mcp_auth_header,
|
||||
)
|
||||
)
|
||||
verbose_logger.debug("TOOLS FROM MCP SERVERS: %s", tools_from_mcp_servers)
|
||||
|
|
@ -238,7 +249,11 @@ if MCP_AVAILABLE:
|
|||
|
||||
@client
|
||||
async def call_mcp_tool(
|
||||
name: str, arguments: Optional[Dict[str, Any]] = None, **kwargs: Any
|
||||
name: str,
|
||||
arguments: Optional[Dict[str, Any]] = None,
|
||||
user_api_key_auth: Optional[UserAPIKeyAuth] = None,
|
||||
mcp_auth_header: Optional[str] = None,
|
||||
**kwargs: Any
|
||||
) -> List[Union[MCPTextContent, MCPImageContent, MCPEmbeddedResource]]:
|
||||
"""
|
||||
Call a specific tool with the provided arguments
|
||||
|
|
@ -270,7 +285,12 @@ if MCP_AVAILABLE:
|
|||
|
||||
# Try managed server tool first
|
||||
if name in global_mcp_server_manager.tool_name_to_mcp_server_name_mapping:
|
||||
return await _handle_managed_mcp_tool(name, arguments)
|
||||
return await _handle_managed_mcp_tool(
|
||||
name=name,
|
||||
arguments=arguments,
|
||||
user_api_key_auth=user_api_key_auth,
|
||||
mcp_auth_header=mcp_auth_header,
|
||||
)
|
||||
|
||||
# Fall back to local tool registry
|
||||
return await _handle_local_mcp_tool(name, arguments)
|
||||
|
|
@ -295,12 +315,17 @@ if MCP_AVAILABLE:
|
|||
)
|
||||
|
||||
async def _handle_managed_mcp_tool(
|
||||
name: str, arguments: Dict[str, Any]
|
||||
name: str,
|
||||
arguments: Dict[str, Any],
|
||||
user_api_key_auth: Optional[UserAPIKeyAuth] = None,
|
||||
mcp_auth_header: Optional[str] = None,
|
||||
) -> List[Union[MCPTextContent, MCPImageContent, MCPEmbeddedResource]]:
|
||||
"""Handle tool execution for managed server tools"""
|
||||
call_tool_result = await global_mcp_server_manager.call_tool(
|
||||
name=name,
|
||||
arguments=arguments,
|
||||
user_api_key_auth=user_api_key_auth,
|
||||
mcp_auth_header=mcp_auth_header,
|
||||
)
|
||||
verbose_logger.debug("CALL TOOL RESULT: %s", call_tool_result)
|
||||
return call_tool_result.content
|
||||
|
|
@ -325,11 +350,14 @@ if MCP_AVAILABLE:
|
|||
"""Handle MCP requests through StreamableHTTP."""
|
||||
try:
|
||||
# Validate headers and log request info
|
||||
user_api_key_auth: UserAPIKeyAuth = (
|
||||
user_api_key_auth, mcp_auth_header = (
|
||||
await UserAPIKeyAuthMCP.user_api_key_auth_mcp(scope)
|
||||
)
|
||||
# Set the auth context variable for easy access in MCP functions
|
||||
set_auth_context(user_api_key_auth)
|
||||
set_auth_context(
|
||||
user_api_key_auth=user_api_key_auth,
|
||||
mcp_auth_header=mcp_auth_header,
|
||||
)
|
||||
|
||||
# Ensure session managers are initialized
|
||||
if not _SESSION_MANAGERS_INITIALIZED:
|
||||
|
|
@ -346,11 +374,14 @@ if MCP_AVAILABLE:
|
|||
"""Handle MCP requests through SSE."""
|
||||
try:
|
||||
# Validate headers and log request info
|
||||
user_api_key_auth: UserAPIKeyAuth = (
|
||||
user_api_key_auth, mcp_auth_header = (
|
||||
await UserAPIKeyAuthMCP.user_api_key_auth_mcp(scope)
|
||||
)
|
||||
# Set the auth context variable for easy access in MCP functions
|
||||
set_auth_context(user_api_key_auth)
|
||||
set_auth_context(
|
||||
user_api_key_auth=user_api_key_auth,
|
||||
mcp_auth_header=mcp_auth_header,
|
||||
)
|
||||
|
||||
# Ensure session managers are initialized
|
||||
if not _SESSION_MANAGERS_INITIALIZED:
|
||||
|
|
@ -390,17 +421,31 @@ if MCP_AVAILABLE:
|
|||
############ Auth Context Functions ####################
|
||||
########################################################
|
||||
|
||||
def set_auth_context(user_api_key_auth: UserAPIKeyAuth) -> None:
|
||||
"""Set the UserAPIKeyAuth in the auth context variable."""
|
||||
auth_user = LiteLLMAuthenticatedUser(user_api_key_auth)
|
||||
def set_auth_context(user_api_key_auth: UserAPIKeyAuth, mcp_auth_header: Optional[str] = None) -> None:
|
||||
"""
|
||||
Set the UserAPIKeyAuth in the auth context variable.
|
||||
|
||||
Args:
|
||||
user_api_key_auth: UserAPIKeyAuth object
|
||||
mcp_auth_header: MCP auth header to be passed to the MCP server
|
||||
"""
|
||||
auth_user = LiteLLMAuthenticatedUser(
|
||||
user_api_key_auth=user_api_key_auth,
|
||||
mcp_auth_header=mcp_auth_header,
|
||||
)
|
||||
auth_context_var.set(auth_user)
|
||||
|
||||
def get_auth_context() -> Optional[UserAPIKeyAuth]:
|
||||
"""Get the UserAPIKeyAuth from the auth context variable."""
|
||||
def get_auth_context() -> Tuple[Optional[UserAPIKeyAuth], Optional[str]]:
|
||||
"""
|
||||
Get the UserAPIKeyAuth from the auth context variable.
|
||||
|
||||
Returns:
|
||||
Tuple[Optional[UserAPIKeyAuth], Optional[str]]: UserAPIKeyAuth object and MCP auth header
|
||||
"""
|
||||
auth_user = auth_context_var.get()
|
||||
if auth_user and isinstance(auth_user, LiteLLMAuthenticatedUser):
|
||||
return auth_user.user_api_key_auth
|
||||
return None
|
||||
return auth_user.user_api_key_auth, auth_user.mcp_auth_header
|
||||
return None, None
|
||||
|
||||
########################################################
|
||||
############ End of Auth Context Functions #############
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
!function(){"use strict";var e,t,n,r,o,u,i,c,f,a={},l={};function d(e){var t=l[e];if(void 0!==t)return t.exports;var n=l[e]={id:e,loaded:!1,exports:{}},r=!0;try{a[e].call(n.exports,n,n.exports,d),r=!1}finally{r&&delete l[e]}return n.loaded=!0,n.exports}d.m=a,e=[],d.O=function(t,n,r,o){if(n){o=o||0;for(var u=e.length;u>0&&e[u-1][2]>o;u--)e[u]=e[u-1];e[u]=[n,r,o];return}for(var i=1/0,u=0;u<e.length;u++){for(var n=e[u][0],r=e[u][1],o=e[u][2],c=!0,f=0;f<n.length;f++)i>=o&&Object.keys(d.O).every(function(e){return d.O[e](n[f])})?n.splice(f--,1):(c=!1,o<i&&(i=o));if(c){e.splice(u--,1);var a=r();void 0!==a&&(t=a)}}return t},d.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return d.d(t,{a:t}),t},n=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__},d.t=function(e,r){if(1&r&&(e=this(e)),8&r||"object"==typeof e&&e&&(4&r&&e.__esModule||16&r&&"function"==typeof e.then))return e;var o=Object.create(null);d.r(o);var u={};t=t||[null,n({}),n([]),n(n)];for(var i=2&r&&e;"object"==typeof i&&!~t.indexOf(i);i=n(i))Object.getOwnPropertyNames(i).forEach(function(t){u[t]=function(){return e[t]}});return u.default=function(){return e},d.d(o,u),o},d.d=function(e,t){for(var n in t)d.o(t,n)&&!d.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},d.f={},d.e=function(e){return Promise.all(Object.keys(d.f).reduce(function(t,n){return d.f[n](e,t),t},[]))},d.u=function(e){},d.miniCssF=function(e){},d.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),d.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r={},o="_N_E:",d.l=function(e,t,n,u){if(r[e]){r[e].push(t);return}if(void 0!==n)for(var i,c,f=document.getElementsByTagName("script"),a=0;a<f.length;a++){var l=f[a];if(l.getAttribute("src")==e||l.getAttribute("data-webpack")==o+n){i=l;break}}i||(c=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,d.nc&&i.setAttribute("nonce",d.nc),i.setAttribute("data-webpack",o+n),i.src=d.tu(e)),r[e]=[t];var s=function(t,n){i.onerror=i.onload=null,clearTimeout(p);var o=r[e];if(delete r[e],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach(function(e){return e(n)}),t)return t(n)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=s.bind(null,i.onerror),i.onload=s.bind(null,i.onload),c&&document.head.appendChild(i)},d.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},d.nmd=function(e){return e.paths=[],e.children||(e.children=[]),e},d.tt=function(){return void 0===u&&(u={createScriptURL:function(e){return e}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(u=trustedTypes.createPolicy("nextjs#bundler",u))),u},d.tu=function(e){return d.tt().createScriptURL(e)},d.p="/_next/",i={272:0,919:0,986:0},d.f.j=function(e,t){var n=d.o(i,e)?i[e]:void 0;if(0!==n){if(n)t.push(n[2]);else if(/^(272|919|986)$/.test(e))i[e]=0;else{var r=new Promise(function(t,r){n=i[e]=[t,r]});t.push(n[2]=r);var o=d.p+d.u(e),u=Error();d.l(o,function(t){if(d.o(i,e)&&(0!==(n=i[e])&&(i[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;u.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",u.name="ChunkLoadError",u.type=r,u.request=o,n[1](u)}},"chunk-"+e,e)}}},d.O.j=function(e){return 0===i[e]},c=function(e,t){var n,r,o=t[0],u=t[1],c=t[2],f=0;if(o.some(function(e){return 0!==i[e]})){for(n in u)d.o(u,n)&&(d.m[n]=u[n]);if(c)var a=c(d)}for(e&&e(t);f<o.length;f++)r=o[f],d.o(i,r)&&i[r]&&i[r][0](),i[r]=0;return d.O(a)},(f=self.webpackChunk_N_E=self.webpackChunk_N_E||[]).forEach(c.bind(null,0)),f.push=c.bind(null,f.push.bind(f))}();
|
||||
!function(){"use strict";var e,t,n,r,o,u,i,c,f,a={},l={};function d(e){var t=l[e];if(void 0!==t)return t.exports;var n=l[e]={id:e,loaded:!1,exports:{}},r=!0;try{a[e].call(n.exports,n,n.exports,d),r=!1}finally{r&&delete l[e]}return n.loaded=!0,n.exports}d.m=a,e=[],d.O=function(t,n,r,o){if(n){o=o||0;for(var u=e.length;u>0&&e[u-1][2]>o;u--)e[u]=e[u-1];e[u]=[n,r,o];return}for(var i=1/0,u=0;u<e.length;u++){for(var n=e[u][0],r=e[u][1],o=e[u][2],c=!0,f=0;f<n.length;f++)i>=o&&Object.keys(d.O).every(function(e){return d.O[e](n[f])})?n.splice(f--,1):(c=!1,o<i&&(i=o));if(c){e.splice(u--,1);var a=r();void 0!==a&&(t=a)}}return t},d.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return d.d(t,{a:t}),t},n=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__},d.t=function(e,r){if(1&r&&(e=this(e)),8&r||"object"==typeof e&&e&&(4&r&&e.__esModule||16&r&&"function"==typeof e.then))return e;var o=Object.create(null);d.r(o);var u={};t=t||[null,n({}),n([]),n(n)];for(var i=2&r&&e;"object"==typeof i&&!~t.indexOf(i);i=n(i))Object.getOwnPropertyNames(i).forEach(function(t){u[t]=function(){return e[t]}});return u.default=function(){return e},d.d(o,u),o},d.d=function(e,t){for(var n in t)d.o(t,n)&&!d.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},d.f={},d.e=function(e){return Promise.all(Object.keys(d.f).reduce(function(t,n){return d.f[n](e,t),t},[]))},d.u=function(e){},d.miniCssF=function(e){},d.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),d.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r={},o="_N_E:",d.l=function(e,t,n,u){if(r[e]){r[e].push(t);return}if(void 0!==n)for(var i,c,f=document.getElementsByTagName("script"),a=0;a<f.length;a++){var l=f[a];if(l.getAttribute("src")==e||l.getAttribute("data-webpack")==o+n){i=l;break}}i||(c=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,d.nc&&i.setAttribute("nonce",d.nc),i.setAttribute("data-webpack",o+n),i.src=d.tu(e)),r[e]=[t];var s=function(t,n){i.onerror=i.onload=null,clearTimeout(p);var o=r[e];if(delete r[e],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach(function(e){return e(n)}),t)return t(n)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=s.bind(null,i.onerror),i.onload=s.bind(null,i.onload),c&&document.head.appendChild(i)},d.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},d.nmd=function(e){return e.paths=[],e.children||(e.children=[]),e},d.tt=function(){return void 0===u&&(u={createScriptURL:function(e){return e}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(u=trustedTypes.createPolicy("nextjs#bundler",u))),u},d.tu=function(e){return d.tt().createScriptURL(e)},d.p="/litellm-asset-prefix/_next/",i={272:0,919:0,986:0},d.f.j=function(e,t){var n=d.o(i,e)?i[e]:void 0;if(0!==n){if(n)t.push(n[2]);else if(/^(272|919|986)$/.test(e))i[e]=0;else{var r=new Promise(function(t,r){n=i[e]=[t,r]});t.push(n[2]=r);var o=d.p+d.u(e),u=Error();d.l(o,function(t){if(d.o(i,e)&&(0!==(n=i[e])&&(i[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;u.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",u.name="ChunkLoadError",u.type=r,u.request=o,n[1](u)}},"chunk-"+e,e)}}},d.O.j=function(e){return 0===i[e]},c=function(e,t){var n,r,o=t[0],u=t[1],c=t[2],f=0;if(o.some(function(e){return 0!==i[e]})){for(n in u)d.o(u,n)&&(d.m[n]=u[n]);if(c)var a=c(d)}for(e&&e(t);f<o.length;f++)r=o[f],d.o(i,r)&&i[r]&&i[r][0](),i[r]=0;return d.O(a)},(f=self.webpackChunk_N_E=self.webpackChunk_N_E||[]).forEach(c.bind(null,0)),f.push=c.bind(null,f.push.bind(f))}();
|
||||
|
|
@ -1 +1 @@
|
|||
@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/55c55f0601d81cf3-s.woff2) format("woff2");unicode-range:u+0460-052f,u+1c80-1c8a,u+20b4,u+2de0-2dff,u+a640-a69f,u+fe2e-fe2f}@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/26a46d62cd723877-s.woff2) format("woff2");unicode-range:u+0301,u+0400-045f,u+0490-0491,u+04b0-04b1,u+2116}@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/97e0cb1ae144a2a9-s.woff2) format("woff2");unicode-range:u+1f??}@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/581909926a08bbc8-s.woff2) format("woff2");unicode-range:u+0370-0377,u+037a-037f,u+0384-038a,u+038c,u+038e-03a1,u+03a3-03ff}@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/df0a9ae256c0569c-s.woff2) format("woff2");unicode-range:u+0102-0103,u+0110-0111,u+0128-0129,u+0168-0169,u+01a0-01a1,u+01af-01b0,u+0300-0301,u+0303-0304,u+0308-0309,u+0323,u+0329,u+1ea0-1ef9,u+20ab}@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/8e9860b6e62d6359-s.woff2) format("woff2");unicode-range:u+0100-02ba,u+02bd-02c5,u+02c7-02cc,u+02ce-02d7,u+02dd-02ff,u+0304,u+0308,u+0329,u+1d00-1dbf,u+1e00-1e9f,u+1ef2-1eff,u+2020,u+20a0-20ab,u+20ad-20c0,u+2113,u+2c60-2c7f,u+a720-a7ff}@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/e4af272ccee01ff0-s.p.woff2) format("woff2");unicode-range:u+00??,u+0131,u+0152-0153,u+02bb-02bc,u+02c6,u+02da,u+02dc,u+0304,u+0308,u+0329,u+2000-206f,u+20ac,u+2122,u+2191,u+2193,u+2212,u+2215,u+feff,u+fffd}@font-face{font-family:__Inter_Fallback_b0dd8a;src:local("Arial");ascent-override:90.49%;descent-override:22.56%;line-gap-override:0.00%;size-adjust:107.06%}.__className_b0dd8a{font-family:__Inter_b0dd8a,__Inter_Fallback_b0dd8a;font-style:normal}
|
||||
@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/litellm-asset-prefix/_next/static/media/55c55f0601d81cf3-s.woff2) format("woff2");unicode-range:u+0460-052f,u+1c80-1c8a,u+20b4,u+2de0-2dff,u+a640-a69f,u+fe2e-fe2f}@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/litellm-asset-prefix/_next/static/media/26a46d62cd723877-s.woff2) format("woff2");unicode-range:u+0301,u+0400-045f,u+0490-0491,u+04b0-04b1,u+2116}@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/litellm-asset-prefix/_next/static/media/97e0cb1ae144a2a9-s.woff2) format("woff2");unicode-range:u+1f??}@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/litellm-asset-prefix/_next/static/media/581909926a08bbc8-s.woff2) format("woff2");unicode-range:u+0370-0377,u+037a-037f,u+0384-038a,u+038c,u+038e-03a1,u+03a3-03ff}@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/litellm-asset-prefix/_next/static/media/df0a9ae256c0569c-s.woff2) format("woff2");unicode-range:u+0102-0103,u+0110-0111,u+0128-0129,u+0168-0169,u+01a0-01a1,u+01af-01b0,u+0300-0301,u+0303-0304,u+0308-0309,u+0323,u+0329,u+1ea0-1ef9,u+20ab}@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/litellm-asset-prefix/_next/static/media/8e9860b6e62d6359-s.woff2) format("woff2");unicode-range:u+0100-02ba,u+02bd-02c5,u+02c7-02cc,u+02ce-02d7,u+02dd-02ff,u+0304,u+0308,u+0329,u+1d00-1dbf,u+1e00-1e9f,u+1ef2-1eff,u+2020,u+20a0-20ab,u+20ad-20c0,u+2113,u+2c60-2c7f,u+a720-a7ff}@font-face{font-family:__Inter_b0dd8a;font-style:normal;font-weight:100 900;font-display:swap;src:url(/litellm-asset-prefix/_next/static/media/e4af272ccee01ff0-s.p.woff2) format("woff2");unicode-range:u+00??,u+0131,u+0152-0153,u+02bb-02bc,u+02c6,u+02da,u+02dc,u+0304,u+0308,u+0329,u+2000-206f,u+20ac,u+2122,u+2191,u+2193,u+2212,u+2215,u+feff,u+fffd}@font-face{font-family:__Inter_Fallback_b0dd8a;src:local("Arial");ascent-override:90.49%;descent-override:22.56%;line-gap-override:0.00%;size-adjust:107.06%}.__className_b0dd8a{font-family:__Inter_b0dd8a,__Inter_Fallback_b0dd8a;font-style:normal}
|
||||
|
|
@ -1,34 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="katman_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 600 450" style="enable-background:new 0 0 600 450;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#343B45;}
|
||||
.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#F4981A;}
|
||||
</style>
|
||||
<g id="_x31__stroke">
|
||||
<g id="Amazon_1_">
|
||||
<rect x="161.2" y="86.5" class="st0" width="277.8" height="277.8"/>
|
||||
<g id="Amazon">
|
||||
<path class="st1" d="M315,163.7c-8,0.6-17.2,1.2-26.4,2.4c-14.1,1.9-28.2,4.3-39.8,9.8c-22.7,9.2-38,28.8-38,57.6
|
||||
c0,36.2,23.3,54.6,52.7,54.6c9.8,0,17.8-1.2,25.1-3.1c11.7-3.7,21.5-10.4,33.1-22.7c6.7,9.2,8.6,13.5,20.2,23.3
|
||||
c3.1,1.2,6.1,1.2,8.6-0.6c7.4-6.1,20.3-17.2,27-23.3c3.1-2.5,2.5-6.1,0.6-9.2c-6.7-8.6-13.5-16-13.5-32.5V165
|
||||
c0-23.3,1.9-44.8-15.3-60.7c-14.1-12.9-36.2-17.8-53.4-17.8h-7.4c-31.2,1.8-64.3,15.3-71.7,54c-1.2,4.9,2.5,6.8,4.9,7.4l34.3,4.3
|
||||
c3.7-0.6,5.5-3.7,6.1-6.7c3.1-13.5,14.1-20.2,26.3-21.5h2.5c7.4,0,15.3,3.1,19.6,9.2c4.9,7.4,4.3,17.2,4.3,25.8L315,163.7
|
||||
L315,163.7z M308.2,236.7c-4.3,8.6-11.7,14.1-19.6,16c-1.2,0-3.1,0.6-4.9,0.6c-13.5,0-21.4-10.4-21.4-25.8
|
||||
c0-19.6,11.6-28.8,26.3-33.1c8-1.8,17.2-2.5,26.4-2.5v7.4C315,213.4,315.6,224.4,308.2,236.7z"/>
|
||||
<path class="st2" d="M398.8,311.4c-1.4,0-2.8,0.3-4.1,0.9c-1.5,0.6-3,1.3-4.4,1.9l-2.1,0.9l-2.7,1.1v0
|
||||
c-29.8,12.1-61.1,19.2-90.1,19.8c-1.1,0-2.1,0-3.2,0c-45.6,0-82.8-21.1-120.3-42c-1.3-0.7-2.7-1-4-1c-1.7,0-3.4,0.6-4.7,1.8
|
||||
c-1.3,1.2-2,2.9-2,4.7c0,2.3,1.2,4.4,2.9,5.7c35.2,30.6,73.8,59,125.7,59c1,0,2,0,3.1,0c33-0.7,70.3-11.9,99.3-30.1l0.2-0.1
|
||||
c3.8-2.3,7.6-4.9,11.2-7.7c2.2-1.6,3.8-4.2,3.8-6.9C407.2,314.6,403.2,311.4,398.8,311.4z M439,294.5L439,294.5
|
||||
c-0.1-2.9-0.7-5.1-1.9-6.9l-0.1-0.2l-0.1-0.2c-1.2-1.3-2.4-1.8-3.7-2.4c-3.8-1.5-9.3-2.3-16-2.3c-4.8,0-10.1,0.5-15.4,1.6l0-0.4
|
||||
l-5.3,1.8l-0.1,0l-3,1v0.1c-3.5,1.5-6.8,3.3-9.8,5.5c-1.9,1.4-3.4,3.2-3.5,6.1c0,1.5,0.7,3.3,2,4.3c1.3,1,2.8,1.4,4.1,1.4
|
||||
c0.3,0,0.6,0,0.9-0.1l0.3,0l0.2,0c2.6-0.6,6.4-0.9,10.9-1.6c3.8-0.4,7.9-0.7,11.4-0.7c2.5,0,4.7,0.2,6.3,0.5
|
||||
c0.8,0.2,1.3,0.4,1.6,0.5c0.1,0,0.2,0.1,0.2,0.1c0.1,0.2,0.2,0.8,0.1,1.5c0,2.9-1.2,8.4-2.9,13.7c-1.7,5.3-3.7,10.7-5,14.2
|
||||
c-0.3,0.8-0.5,1.7-0.5,2.7c0,1.4,0.6,3.2,1.8,4.3c1.2,1.1,2.8,1.6,4.1,1.6h0.1c2,0,3.6-0.8,5.1-1.9
|
||||
c13.6-12.2,18.3-31.7,18.5-42.6L439,294.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="katman_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 600 450" style="enable-background:new 0 0 600 450;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#343B45;}
|
||||
.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#F4981A;}
|
||||
</style>
|
||||
<g id="_x31__stroke">
|
||||
<g id="Amazon_1_">
|
||||
<rect x="161.2" y="86.5" class="st0" width="277.8" height="277.8"/>
|
||||
<g id="Amazon">
|
||||
<path class="st1" d="M315,163.7c-8,0.6-17.2,1.2-26.4,2.4c-14.1,1.9-28.2,4.3-39.8,9.8c-22.7,9.2-38,28.8-38,57.6
|
||||
c0,36.2,23.3,54.6,52.7,54.6c9.8,0,17.8-1.2,25.1-3.1c11.7-3.7,21.5-10.4,33.1-22.7c6.7,9.2,8.6,13.5,20.2,23.3
|
||||
c3.1,1.2,6.1,1.2,8.6-0.6c7.4-6.1,20.3-17.2,27-23.3c3.1-2.5,2.5-6.1,0.6-9.2c-6.7-8.6-13.5-16-13.5-32.5V165
|
||||
c0-23.3,1.9-44.8-15.3-60.7c-14.1-12.9-36.2-17.8-53.4-17.8h-7.4c-31.2,1.8-64.3,15.3-71.7,54c-1.2,4.9,2.5,6.8,4.9,7.4l34.3,4.3
|
||||
c3.7-0.6,5.5-3.7,6.1-6.7c3.1-13.5,14.1-20.2,26.3-21.5h2.5c7.4,0,15.3,3.1,19.6,9.2c4.9,7.4,4.3,17.2,4.3,25.8L315,163.7
|
||||
L315,163.7z M308.2,236.7c-4.3,8.6-11.7,14.1-19.6,16c-1.2,0-3.1,0.6-4.9,0.6c-13.5,0-21.4-10.4-21.4-25.8
|
||||
c0-19.6,11.6-28.8,26.3-33.1c8-1.8,17.2-2.5,26.4-2.5v7.4C315,213.4,315.6,224.4,308.2,236.7z"/>
|
||||
<path class="st2" d="M398.8,311.4c-1.4,0-2.8,0.3-4.1,0.9c-1.5,0.6-3,1.3-4.4,1.9l-2.1,0.9l-2.7,1.1v0
|
||||
c-29.8,12.1-61.1,19.2-90.1,19.8c-1.1,0-2.1,0-3.2,0c-45.6,0-82.8-21.1-120.3-42c-1.3-0.7-2.7-1-4-1c-1.7,0-3.4,0.6-4.7,1.8
|
||||
c-1.3,1.2-2,2.9-2,4.7c0,2.3,1.2,4.4,2.9,5.7c35.2,30.6,73.8,59,125.7,59c1,0,2,0,3.1,0c33-0.7,70.3-11.9,99.3-30.1l0.2-0.1
|
||||
c3.8-2.3,7.6-4.9,11.2-7.7c2.2-1.6,3.8-4.2,3.8-6.9C407.2,314.6,403.2,311.4,398.8,311.4z M439,294.5L439,294.5
|
||||
c-0.1-2.9-0.7-5.1-1.9-6.9l-0.1-0.2l-0.1-0.2c-1.2-1.3-2.4-1.8-3.7-2.4c-3.8-1.5-9.3-2.3-16-2.3c-4.8,0-10.1,0.5-15.4,1.6l0-0.4
|
||||
l-5.3,1.8l-0.1,0l-3,1v0.1c-3.5,1.5-6.8,3.3-9.8,5.5c-1.9,1.4-3.4,3.2-3.5,6.1c0,1.5,0.7,3.3,2,4.3c1.3,1,2.8,1.4,4.1,1.4
|
||||
c0.3,0,0.6,0,0.9-0.1l0.3,0l0.2,0c2.6-0.6,6.4-0.9,10.9-1.6c3.8-0.4,7.9-0.7,11.4-0.7c2.5,0,4.7,0.2,6.3,0.5
|
||||
c0.8,0.2,1.3,0.4,1.6,0.5c0.1,0,0.2,0.1,0.2,0.1c0.1,0.2,0.2,0.8,0.1,1.5c0,2.9-1.2,8.4-2.9,13.7c-1.7,5.3-3.7,10.7-5,14.2
|
||||
c-0.3,0.8-0.5,1.7-0.5,2.7c0,1.4,0.6,3.2,1.8,4.3c1.2,1.1,2.8,1.6,4.1,1.6h0.1c2,0,3.6-0.8,5.1-1.9
|
||||
c13.6-12.2,18.3-31.7,18.5-42.6L439,294.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
|
@ -1,89 +1,89 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="katman_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 800 600" style="enable-background:new 0 0 800 600;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#F05A28;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#231F20;}
|
||||
</style>
|
||||
<g id="Contact">
|
||||
<g id="Contact-us" transform="translate(-234.000000, -1114.000000)">
|
||||
<g id="map" transform="translate(-6.000000, 1027.000000)">
|
||||
<g id="Contact-box" transform="translate(190.000000, 36.000000)">
|
||||
<g id="Group-26" transform="translate(50.000000, 51.000000)">
|
||||
<g id="Group-3">
|
||||
<path id="Fill-1" class="st0" d="M220.9,421c-17,0-33.1-3.4-47.8-9.5c-22-9.2-40.8-24.6-54.1-44c-13.3-19.4-21-42.7-21-67.9
|
||||
c0-16.8,3.4-32.7,9.7-47.3c9.3-21.8,24.9-40.3,44.5-53.4c19.6-13.1,43.2-20.7,68.7-20.7v-18.3c-19.5,0-38.1,3.9-55.1,11
|
||||
c-25.4,10.6-47,28.3-62.2,50.6c-15.3,22.3-24.2,49.2-24.2,78.1c0,19.3,4,37.7,11.1,54.4c10.7,25.1,28.7,46.4,51.2,61.5
|
||||
c22.6,15.1,49.8,23.9,79.1,23.9V421z"/>
|
||||
<path id="Fill-4" class="st0" d="M157.9,374.1c-11.5-9.6-20.1-21.2-25.9-33.9c-5.8-12.7-8.8-26.4-8.8-40.2
|
||||
c0-11,1.9-22,5.6-32.5c3.8-10.5,9.4-20.5,17.1-29.6c9.6-11.4,21.3-20,34-25.8c12.7-5.8,26.6-8.7,40.4-8.7
|
||||
c11,0,22.1,1.9,32.6,5.6c10.6,3.8,20.6,9.4,29.7,17l11.9-14.1c-10.8-9-22.8-15.8-35.4-20.2c-12.6-4.5-25.7-6.7-38.8-6.7
|
||||
c-16.5,0-32.9,3.5-48.1,10.4c-15.2,6.9-29.1,17.2-40.5,30.7c-9.1,10.8-15.8,22.7-20.3,35.2c-4.5,12.5-6.7,25.6-6.7,38.7
|
||||
c0,16.4,3.5,32.8,10.4,47.9c6.9,15.1,17.3,29,30.9,40.3L157.9,374.1z"/>
|
||||
<path id="Fill-6" class="st0" d="M186.4,362.2c-12.1-6.4-21.6-15.7-28.1-26.6c-6.5-10.9-9.9-23.5-9.9-36.2
|
||||
c0-11.2,2.6-22.5,8.3-33c6.4-12.1,15.8-21.5,26.8-27.9c11-6.5,23.6-9.9,36.4-9.9c11.2,0,22.6,2.6,33.2,8.2l8.6-16.3
|
||||
c-13.3-7-27.7-10.4-41.9-10.3c-16.1,0-32,4.3-45.8,12.4c-13.8,8.1-25.7,20.1-33.7,35.2c-7,13.3-10.4,27.6-10.4,41.6
|
||||
c0,16,4.3,31.8,12.5,45.5c8.2,13.8,20.2,25.5,35.4,33.5L186.4,362.2z"/>
|
||||
<path id="Fill-8" class="st0" d="M221,344.6c-6.3,0-12.3-1.3-17.7-3.6c-8.2-3.4-15.1-9.2-20-16.5c-4.9-7.3-7.8-16-7.8-25.4
|
||||
c0-6.3,1.3-12.3,3.6-17.7c3.4-8.1,9.2-15.1,16.5-20c7.3-4.9,16-7.8,25.4-7.8v-18.4c-8.8,0-17.2,1.8-24.9,5
|
||||
c-11.5,4.9-21.2,12.9-28.1,23.1C161,273.6,157,286,157,299.2c0,8.8,1.8,17.2,5,24.9c4.9,11.5,13,21.2,23.2,28.1
|
||||
C195.4,359,207.7,363,221,363V344.6z"/>
|
||||
</g>
|
||||
<g id="Group" transform="translate(22.000000, 13.000000)">
|
||||
<path id="Fill-10" class="st1" d="M214,271.6c-2.1-2.2-4.4-4-6.7-5.3c-2.3-1.3-4.7-2-7.2-2c-3.4,0-6.3,0.6-9,1.8
|
||||
c-2.6,1.2-4.9,2.8-6.8,4.9c-1.9,2-3.3,4.4-4.3,7c-1,2.6-1.4,5.4-1.4,8.2c0,2.8,0.5,5.6,1.4,8.2c1,2.6,2.4,5,4.3,7
|
||||
c1.9,2,4.1,3.7,6.8,4.9c2.6,1.2,5.6,1.8,9,1.8c2.8,0,5.5-0.6,7.9-1.7c2.4-1.2,4.5-2.9,6.2-5.1l12.2,13.1
|
||||
c-1.8,1.8-3.9,3.4-6.3,4.7c-2.4,1.3-4.8,2.4-7.2,3.2s-4.8,1.4-7,1.7c-2.2,0.4-4.2,0.5-5.8,0.5c-5.5,0-10.7-0.9-15.5-2.7
|
||||
c-4.9-1.8-9.1-4.4-12.6-7.8c-3.6-3.3-6.4-7.4-8.5-12.1c-2.1-4.7-3.1-10-3.1-15.7c0-5.8,1-11,3.1-15.7
|
||||
c2.1-4.7,4.9-8.7,8.5-12.1c3.6-3.3,7.8-5.9,12.6-7.8c4.9-1.8,10.1-2.7,15.5-2.7c4.7,0,9.4,0.9,14.1,2.7
|
||||
c4.7,1.8,8.9,4.6,12.4,8.4L214,271.6z"/>
|
||||
<path id="Fill-12" class="st1" d="M280.4,278.9c-0.1-5.4-1.8-9.6-5-12.7c-3.3-3.1-7.8-4.6-13.6-4.6c-5.5,0-9.8,1.6-13,4.7
|
||||
c-3.2,3.1-5.2,7.4-5.9,12.6H280.4z M243,292.6c0.6,5.5,2.7,9.7,6.4,12.8c3.7,3,8.1,4.6,13.3,4.6c4.6,0,8.4-0.9,11.5-2.8
|
||||
c3.1-1.9,5.8-4.2,8.2-7.1l13.1,9.9c-4.3,5.3-9,9-14.3,11.3c-5.3,2.2-10.8,3.3-16.6,3.3c-5.5,0-10.7-0.9-15.5-2.7
|
||||
c-4.9-1.8-9.1-4.4-12.6-7.8c-3.6-3.3-6.4-7.4-8.5-12.1c-2.1-4.7-3.1-10-3.1-15.7c0-5.8,1-11,3.1-15.7
|
||||
c2.1-4.7,4.9-8.7,8.5-12.1c3.6-3.3,7.8-5.9,12.6-7.8c4.9-1.8,10.1-2.7,15.5-2.7c5.1,0,9.7,0.9,13.9,2.7
|
||||
c4.2,1.8,7.8,4.3,10.8,7.7c3,3.3,5.3,7.5,7,12.4c1.7,4.9,2.5,10.6,2.5,17v5H243z"/>
|
||||
<path id="Fill-14" class="st1" d="M306.5,249.7h18.3v11.5h0.3c2-4.3,4.9-7.5,8.7-9.9c3.8-2.3,8.1-3.5,12.9-3.5
|
||||
c1.1,0,2.2,0.1,3.3,0.3c1.1,0.2,2.2,0.5,3.3,0.8v17.6c-1.5-0.4-3-0.7-4.5-1c-1.5-0.3-2.9-0.4-4.3-0.4c-4.3,0-7.7,0.8-10.3,2.4
|
||||
c-2.6,1.6-4.6,3.4-5.9,5.4c-1.4,2-2.3,4.1-2.7,6.1c-0.5,2-0.7,3.5-0.7,4.6v39h-18.3V249.7z"/>
|
||||
<path id="Fill-16" class="st1" d="M409,278.9c-0.1-5.4-1.8-9.6-5-12.7c-3.3-3.1-7.8-4.6-13.6-4.6c-5.5,0-9.8,1.6-13,4.7
|
||||
c-3.2,3.1-5.2,7.4-5.9,12.6H409z M371.6,292.6c0.6,5.5,2.7,9.7,6.4,12.8c3.7,3,8.1,4.6,13.3,4.6c4.6,0,8.4-0.9,11.5-2.8
|
||||
c3.1-1.9,5.8-4.2,8.2-7.1l13.1,9.9c-4.3,5.3-9,9-14.3,11.3c-5.3,2.2-10.8,3.3-16.6,3.3c-5.5,0-10.7-0.9-15.5-2.7
|
||||
c-4.9-1.8-9.1-4.4-12.6-7.8c-3.6-3.3-6.4-7.4-8.5-12.1c-2.1-4.7-3.1-10-3.1-15.7c0-5.8,1-11,3.1-15.7
|
||||
c2.1-4.7,4.9-8.7,8.5-12.1c3.6-3.3,7.8-5.9,12.6-7.8c4.9-1.8,10.1-2.7,15.5-2.7c5.1,0,9.7,0.9,13.9,2.7
|
||||
c4.2,1.8,7.8,4.3,10.8,7.7c3,3.3,5.3,7.5,7,12.4c1.7,4.9,2.5,10.6,2.5,17v5H371.6z"/>
|
||||
<path id="Fill-18" class="st1" d="M494.6,286.2c0-2.8-0.5-5.6-1.5-8.2c-1-2.6-2.4-5-4.3-7c-1.9-2-4.2-3.7-6.9-4.9
|
||||
c-2.7-1.2-5.7-1.8-9.1-1.8c-3.4,0-6.4,0.6-9.1,1.8c-2.7,1.2-5,2.8-6.9,4.9c-1.9,2-3.3,4.4-4.3,7c-1,2.6-1.5,5.4-1.5,8.2
|
||||
c0,2.8,0.5,5.6,1.5,8.2c1,2.6,2.4,5,4.3,7c1.9,2,4.2,3.7,6.9,4.9c2.7,1.2,5.7,1.8,9.1,1.8c3.4,0,6.4-0.6,9.1-1.8
|
||||
c2.7-1.2,5-2.8,6.9-4.9c1.9-2,3.3-4.4,4.3-7C494.1,291.8,494.6,289,494.6,286.2L494.6,286.2z M433.2,207.6h18.5v51.3h0.5
|
||||
c0.9-1.2,2.1-2.5,3.5-3.7c1.4-1.3,3.2-2.5,5.2-3.6c2.1-1.1,4.4-2,7.1-2.7c2.7-0.7,5.8-1.1,9.3-1.1c5.2,0,10.1,1,14.5,3
|
||||
c4.4,2,8.2,4.7,11.3,8.1c3.1,3.5,5.6,7.5,7.3,12.2c1.7,4.7,2.6,9.7,2.6,15.1c0,5.4-0.8,10.4-2.5,15.1
|
||||
c-1.6,4.7-4.1,8.7-7.2,12.2c-3.2,3.5-7,6.2-11.6,8.1c-4.5,2-9.6,3-15.3,3c-5.2,0-10.1-1-14.7-3c-4.5-2-8.1-5.3-10.8-9.7h-0.3
|
||||
v11h-17.6V207.6z"/>
|
||||
<path id="Fill-20" class="st1" d="M520.9,249.7h18.3v11.5h0.3c2-4.3,4.9-7.5,8.7-9.9c3.8-2.3,8.1-3.5,12.9-3.5
|
||||
c1.1,0,2.2,0.1,3.3,0.3c1.1,0.2,2.2,0.5,3.3,0.8v17.6c-1.5-0.4-3-0.7-4.5-1c-1.5-0.3-2.9-0.4-4.3-0.4c-4.3,0-7.7,0.8-10.3,2.4
|
||||
c-2.6,1.6-4.6,3.4-5.9,5.4c-1.4,2-2.3,4.1-2.7,6.1c-0.5,2-0.7,3.5-0.7,4.6v39h-18.3V249.7z"/>
|
||||
<path id="Fill-22" class="st1" d="M616,290h-3.9c-2.6,0-5.5,0.1-8.7,0.3c-3.2,0.2-6.2,0.7-9.1,1.4c-2.8,0.8-5.2,1.9-7.2,3.3
|
||||
c-2,1.5-2.9,3.5-2.9,6.2c0,1.7,0.4,3.2,1.2,4.3c0.8,1.2,1.8,2.2,3,3c1.2,0.8,2.6,1.4,4.2,1.8c1.5,0.4,3.1,0.5,4.6,0.5
|
||||
c6.4,0,11.1-1.5,14.2-4.5c3-3,4.6-7.1,4.6-12.2V290z M617.1,312.7h-0.5c-2.7,4.2-6.1,7.2-10.2,9.1c-4.1,1.9-8.7,2.8-13.6,2.8
|
||||
c-3.4,0-6.7-0.5-10-1.4s-6.1-2.3-8.7-4.1c-2.5-1.8-4.6-4.1-6.1-6.8s-2.3-5.9-2.3-9.6c0-4,0.7-7.3,2.2-10.1
|
||||
c1.4-2.8,3.4-5.1,5.8-7c2.4-1.9,5.2-3.4,8.4-4.5c3.2-1.1,6.5-2,10-2.5c3.5-0.6,6.9-0.9,10.5-1.1c3.5-0.2,6.8-0.2,9.9-0.2h4.6
|
||||
v-2c0-4.6-1.6-8-4.8-10.3c-3.2-2.3-7.3-3.4-12.2-3.4c-3.9,0-7.6,0.7-11,2.1c-3.4,1.4-6.4,3.2-8.8,5.6l-9.8-9.6
|
||||
c4.1-4.2,9-7.1,14.5-9c5.5-1.8,11.2-2.7,17.1-2.7c5.3,0,9.7,0.6,13.3,1.7c3.6,1.2,6.6,2.7,9,4.5c2.4,1.8,4.2,3.9,5.5,6.3
|
||||
c1.3,2.4,2.2,4.8,2.8,7.2c0.6,2.4,0.9,4.8,1,7.1c0.1,2.3,0.2,4.3,0.2,6v42h-16.7V312.7z"/>
|
||||
<path id="Fill-24" class="st1" d="M683.6,269.9c-3.6-5-8.4-7.5-14.4-7.5c-2.5,0-4.9,0.6-7.2,1.8c-2.4,1.2-3.5,3.2-3.5,5.9
|
||||
c0,2.2,1,3.9,2.9,4.9c1.9,1,4.4,1.9,7.4,2.6c3,0.7,6.2,1.4,9.6,2.2c3.4,0.8,6.6,1.9,9.6,3.5c3,1.6,5.4,3.7,7.4,6.5
|
||||
c1.9,2.7,2.9,6.5,2.9,11.3c0,4.4-0.9,8-2.8,11c-1.9,3-4.3,5.4-7.4,7.2c-3,1.8-6.4,3.1-10.2,4c-3.8,0.8-7.6,1.2-11.3,1.2
|
||||
c-5.7,0-11-0.8-15.8-2.4c-4.8-1.6-9.1-4.6-12.9-8.8l12.3-11.4c2.4,2.6,4.9,4.8,7.6,6.5c2.7,1.7,6,2.5,9.9,2.5
|
||||
c1.3,0,2.7-0.2,4.1-0.5c1.4-0.3,2.8-0.8,4-1.5c1.2-0.7,2.2-1.6,3-2.7c0.8-1.1,1.1-2.3,1.1-3.7c0-2.5-1-4.4-2.9-5.6
|
||||
c-1.9-1.2-4.4-2.2-7.4-3c-3-0.8-6.2-1.5-9.6-2.1c-3.4-0.7-6.6-1.7-9.6-3.2c-3-1.5-5.4-3.5-7.4-6.2c-1.9-2.6-2.9-6.3-2.9-11
|
||||
c0-4.1,0.8-7.6,2.5-10.6c1.7-3,3.9-5.4,6.7-7.4c2.8-1.9,5.9-3.3,9.5-4.3c3.6-0.9,7.2-1.4,10.9-1.4c4.9,0,9.8,0.8,14.6,2.5
|
||||
c4.8,1.7,8.7,4.5,11.7,8.6L683.6,269.9z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="katman_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 800 600" style="enable-background:new 0 0 800 600;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#F05A28;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#231F20;}
|
||||
</style>
|
||||
<g id="Contact">
|
||||
<g id="Contact-us" transform="translate(-234.000000, -1114.000000)">
|
||||
<g id="map" transform="translate(-6.000000, 1027.000000)">
|
||||
<g id="Contact-box" transform="translate(190.000000, 36.000000)">
|
||||
<g id="Group-26" transform="translate(50.000000, 51.000000)">
|
||||
<g id="Group-3">
|
||||
<path id="Fill-1" class="st0" d="M220.9,421c-17,0-33.1-3.4-47.8-9.5c-22-9.2-40.8-24.6-54.1-44c-13.3-19.4-21-42.7-21-67.9
|
||||
c0-16.8,3.4-32.7,9.7-47.3c9.3-21.8,24.9-40.3,44.5-53.4c19.6-13.1,43.2-20.7,68.7-20.7v-18.3c-19.5,0-38.1,3.9-55.1,11
|
||||
c-25.4,10.6-47,28.3-62.2,50.6c-15.3,22.3-24.2,49.2-24.2,78.1c0,19.3,4,37.7,11.1,54.4c10.7,25.1,28.7,46.4,51.2,61.5
|
||||
c22.6,15.1,49.8,23.9,79.1,23.9V421z"/>
|
||||
<path id="Fill-4" class="st0" d="M157.9,374.1c-11.5-9.6-20.1-21.2-25.9-33.9c-5.8-12.7-8.8-26.4-8.8-40.2
|
||||
c0-11,1.9-22,5.6-32.5c3.8-10.5,9.4-20.5,17.1-29.6c9.6-11.4,21.3-20,34-25.8c12.7-5.8,26.6-8.7,40.4-8.7
|
||||
c11,0,22.1,1.9,32.6,5.6c10.6,3.8,20.6,9.4,29.7,17l11.9-14.1c-10.8-9-22.8-15.8-35.4-20.2c-12.6-4.5-25.7-6.7-38.8-6.7
|
||||
c-16.5,0-32.9,3.5-48.1,10.4c-15.2,6.9-29.1,17.2-40.5,30.7c-9.1,10.8-15.8,22.7-20.3,35.2c-4.5,12.5-6.7,25.6-6.7,38.7
|
||||
c0,16.4,3.5,32.8,10.4,47.9c6.9,15.1,17.3,29,30.9,40.3L157.9,374.1z"/>
|
||||
<path id="Fill-6" class="st0" d="M186.4,362.2c-12.1-6.4-21.6-15.7-28.1-26.6c-6.5-10.9-9.9-23.5-9.9-36.2
|
||||
c0-11.2,2.6-22.5,8.3-33c6.4-12.1,15.8-21.5,26.8-27.9c11-6.5,23.6-9.9,36.4-9.9c11.2,0,22.6,2.6,33.2,8.2l8.6-16.3
|
||||
c-13.3-7-27.7-10.4-41.9-10.3c-16.1,0-32,4.3-45.8,12.4c-13.8,8.1-25.7,20.1-33.7,35.2c-7,13.3-10.4,27.6-10.4,41.6
|
||||
c0,16,4.3,31.8,12.5,45.5c8.2,13.8,20.2,25.5,35.4,33.5L186.4,362.2z"/>
|
||||
<path id="Fill-8" class="st0" d="M221,344.6c-6.3,0-12.3-1.3-17.7-3.6c-8.2-3.4-15.1-9.2-20-16.5c-4.9-7.3-7.8-16-7.8-25.4
|
||||
c0-6.3,1.3-12.3,3.6-17.7c3.4-8.1,9.2-15.1,16.5-20c7.3-4.9,16-7.8,25.4-7.8v-18.4c-8.8,0-17.2,1.8-24.9,5
|
||||
c-11.5,4.9-21.2,12.9-28.1,23.1C161,273.6,157,286,157,299.2c0,8.8,1.8,17.2,5,24.9c4.9,11.5,13,21.2,23.2,28.1
|
||||
C195.4,359,207.7,363,221,363V344.6z"/>
|
||||
</g>
|
||||
<g id="Group" transform="translate(22.000000, 13.000000)">
|
||||
<path id="Fill-10" class="st1" d="M214,271.6c-2.1-2.2-4.4-4-6.7-5.3c-2.3-1.3-4.7-2-7.2-2c-3.4,0-6.3,0.6-9,1.8
|
||||
c-2.6,1.2-4.9,2.8-6.8,4.9c-1.9,2-3.3,4.4-4.3,7c-1,2.6-1.4,5.4-1.4,8.2c0,2.8,0.5,5.6,1.4,8.2c1,2.6,2.4,5,4.3,7
|
||||
c1.9,2,4.1,3.7,6.8,4.9c2.6,1.2,5.6,1.8,9,1.8c2.8,0,5.5-0.6,7.9-1.7c2.4-1.2,4.5-2.9,6.2-5.1l12.2,13.1
|
||||
c-1.8,1.8-3.9,3.4-6.3,4.7c-2.4,1.3-4.8,2.4-7.2,3.2s-4.8,1.4-7,1.7c-2.2,0.4-4.2,0.5-5.8,0.5c-5.5,0-10.7-0.9-15.5-2.7
|
||||
c-4.9-1.8-9.1-4.4-12.6-7.8c-3.6-3.3-6.4-7.4-8.5-12.1c-2.1-4.7-3.1-10-3.1-15.7c0-5.8,1-11,3.1-15.7
|
||||
c2.1-4.7,4.9-8.7,8.5-12.1c3.6-3.3,7.8-5.9,12.6-7.8c4.9-1.8,10.1-2.7,15.5-2.7c4.7,0,9.4,0.9,14.1,2.7
|
||||
c4.7,1.8,8.9,4.6,12.4,8.4L214,271.6z"/>
|
||||
<path id="Fill-12" class="st1" d="M280.4,278.9c-0.1-5.4-1.8-9.6-5-12.7c-3.3-3.1-7.8-4.6-13.6-4.6c-5.5,0-9.8,1.6-13,4.7
|
||||
c-3.2,3.1-5.2,7.4-5.9,12.6H280.4z M243,292.6c0.6,5.5,2.7,9.7,6.4,12.8c3.7,3,8.1,4.6,13.3,4.6c4.6,0,8.4-0.9,11.5-2.8
|
||||
c3.1-1.9,5.8-4.2,8.2-7.1l13.1,9.9c-4.3,5.3-9,9-14.3,11.3c-5.3,2.2-10.8,3.3-16.6,3.3c-5.5,0-10.7-0.9-15.5-2.7
|
||||
c-4.9-1.8-9.1-4.4-12.6-7.8c-3.6-3.3-6.4-7.4-8.5-12.1c-2.1-4.7-3.1-10-3.1-15.7c0-5.8,1-11,3.1-15.7
|
||||
c2.1-4.7,4.9-8.7,8.5-12.1c3.6-3.3,7.8-5.9,12.6-7.8c4.9-1.8,10.1-2.7,15.5-2.7c5.1,0,9.7,0.9,13.9,2.7
|
||||
c4.2,1.8,7.8,4.3,10.8,7.7c3,3.3,5.3,7.5,7,12.4c1.7,4.9,2.5,10.6,2.5,17v5H243z"/>
|
||||
<path id="Fill-14" class="st1" d="M306.5,249.7h18.3v11.5h0.3c2-4.3,4.9-7.5,8.7-9.9c3.8-2.3,8.1-3.5,12.9-3.5
|
||||
c1.1,0,2.2,0.1,3.3,0.3c1.1,0.2,2.2,0.5,3.3,0.8v17.6c-1.5-0.4-3-0.7-4.5-1c-1.5-0.3-2.9-0.4-4.3-0.4c-4.3,0-7.7,0.8-10.3,2.4
|
||||
c-2.6,1.6-4.6,3.4-5.9,5.4c-1.4,2-2.3,4.1-2.7,6.1c-0.5,2-0.7,3.5-0.7,4.6v39h-18.3V249.7z"/>
|
||||
<path id="Fill-16" class="st1" d="M409,278.9c-0.1-5.4-1.8-9.6-5-12.7c-3.3-3.1-7.8-4.6-13.6-4.6c-5.5,0-9.8,1.6-13,4.7
|
||||
c-3.2,3.1-5.2,7.4-5.9,12.6H409z M371.6,292.6c0.6,5.5,2.7,9.7,6.4,12.8c3.7,3,8.1,4.6,13.3,4.6c4.6,0,8.4-0.9,11.5-2.8
|
||||
c3.1-1.9,5.8-4.2,8.2-7.1l13.1,9.9c-4.3,5.3-9,9-14.3,11.3c-5.3,2.2-10.8,3.3-16.6,3.3c-5.5,0-10.7-0.9-15.5-2.7
|
||||
c-4.9-1.8-9.1-4.4-12.6-7.8c-3.6-3.3-6.4-7.4-8.5-12.1c-2.1-4.7-3.1-10-3.1-15.7c0-5.8,1-11,3.1-15.7
|
||||
c2.1-4.7,4.9-8.7,8.5-12.1c3.6-3.3,7.8-5.9,12.6-7.8c4.9-1.8,10.1-2.7,15.5-2.7c5.1,0,9.7,0.9,13.9,2.7
|
||||
c4.2,1.8,7.8,4.3,10.8,7.7c3,3.3,5.3,7.5,7,12.4c1.7,4.9,2.5,10.6,2.5,17v5H371.6z"/>
|
||||
<path id="Fill-18" class="st1" d="M494.6,286.2c0-2.8-0.5-5.6-1.5-8.2c-1-2.6-2.4-5-4.3-7c-1.9-2-4.2-3.7-6.9-4.9
|
||||
c-2.7-1.2-5.7-1.8-9.1-1.8c-3.4,0-6.4,0.6-9.1,1.8c-2.7,1.2-5,2.8-6.9,4.9c-1.9,2-3.3,4.4-4.3,7c-1,2.6-1.5,5.4-1.5,8.2
|
||||
c0,2.8,0.5,5.6,1.5,8.2c1,2.6,2.4,5,4.3,7c1.9,2,4.2,3.7,6.9,4.9c2.7,1.2,5.7,1.8,9.1,1.8c3.4,0,6.4-0.6,9.1-1.8
|
||||
c2.7-1.2,5-2.8,6.9-4.9c1.9-2,3.3-4.4,4.3-7C494.1,291.8,494.6,289,494.6,286.2L494.6,286.2z M433.2,207.6h18.5v51.3h0.5
|
||||
c0.9-1.2,2.1-2.5,3.5-3.7c1.4-1.3,3.2-2.5,5.2-3.6c2.1-1.1,4.4-2,7.1-2.7c2.7-0.7,5.8-1.1,9.3-1.1c5.2,0,10.1,1,14.5,3
|
||||
c4.4,2,8.2,4.7,11.3,8.1c3.1,3.5,5.6,7.5,7.3,12.2c1.7,4.7,2.6,9.7,2.6,15.1c0,5.4-0.8,10.4-2.5,15.1
|
||||
c-1.6,4.7-4.1,8.7-7.2,12.2c-3.2,3.5-7,6.2-11.6,8.1c-4.5,2-9.6,3-15.3,3c-5.2,0-10.1-1-14.7-3c-4.5-2-8.1-5.3-10.8-9.7h-0.3
|
||||
v11h-17.6V207.6z"/>
|
||||
<path id="Fill-20" class="st1" d="M520.9,249.7h18.3v11.5h0.3c2-4.3,4.9-7.5,8.7-9.9c3.8-2.3,8.1-3.5,12.9-3.5
|
||||
c1.1,0,2.2,0.1,3.3,0.3c1.1,0.2,2.2,0.5,3.3,0.8v17.6c-1.5-0.4-3-0.7-4.5-1c-1.5-0.3-2.9-0.4-4.3-0.4c-4.3,0-7.7,0.8-10.3,2.4
|
||||
c-2.6,1.6-4.6,3.4-5.9,5.4c-1.4,2-2.3,4.1-2.7,6.1c-0.5,2-0.7,3.5-0.7,4.6v39h-18.3V249.7z"/>
|
||||
<path id="Fill-22" class="st1" d="M616,290h-3.9c-2.6,0-5.5,0.1-8.7,0.3c-3.2,0.2-6.2,0.7-9.1,1.4c-2.8,0.8-5.2,1.9-7.2,3.3
|
||||
c-2,1.5-2.9,3.5-2.9,6.2c0,1.7,0.4,3.2,1.2,4.3c0.8,1.2,1.8,2.2,3,3c1.2,0.8,2.6,1.4,4.2,1.8c1.5,0.4,3.1,0.5,4.6,0.5
|
||||
c6.4,0,11.1-1.5,14.2-4.5c3-3,4.6-7.1,4.6-12.2V290z M617.1,312.7h-0.5c-2.7,4.2-6.1,7.2-10.2,9.1c-4.1,1.9-8.7,2.8-13.6,2.8
|
||||
c-3.4,0-6.7-0.5-10-1.4s-6.1-2.3-8.7-4.1c-2.5-1.8-4.6-4.1-6.1-6.8s-2.3-5.9-2.3-9.6c0-4,0.7-7.3,2.2-10.1
|
||||
c1.4-2.8,3.4-5.1,5.8-7c2.4-1.9,5.2-3.4,8.4-4.5c3.2-1.1,6.5-2,10-2.5c3.5-0.6,6.9-0.9,10.5-1.1c3.5-0.2,6.8-0.2,9.9-0.2h4.6
|
||||
v-2c0-4.6-1.6-8-4.8-10.3c-3.2-2.3-7.3-3.4-12.2-3.4c-3.9,0-7.6,0.7-11,2.1c-3.4,1.4-6.4,3.2-8.8,5.6l-9.8-9.6
|
||||
c4.1-4.2,9-7.1,14.5-9c5.5-1.8,11.2-2.7,17.1-2.7c5.3,0,9.7,0.6,13.3,1.7c3.6,1.2,6.6,2.7,9,4.5c2.4,1.8,4.2,3.9,5.5,6.3
|
||||
c1.3,2.4,2.2,4.8,2.8,7.2c0.6,2.4,0.9,4.8,1,7.1c0.1,2.3,0.2,4.3,0.2,6v42h-16.7V312.7z"/>
|
||||
<path id="Fill-24" class="st1" d="M683.6,269.9c-3.6-5-8.4-7.5-14.4-7.5c-2.5,0-4.9,0.6-7.2,1.8c-2.4,1.2-3.5,3.2-3.5,5.9
|
||||
c0,2.2,1,3.9,2.9,4.9c1.9,1,4.4,1.9,7.4,2.6c3,0.7,6.2,1.4,9.6,2.2c3.4,0.8,6.6,1.9,9.6,3.5c3,1.6,5.4,3.7,7.4,6.5
|
||||
c1.9,2.7,2.9,6.5,2.9,11.3c0,4.4-0.9,8-2.8,11c-1.9,3-4.3,5.4-7.4,7.2c-3,1.8-6.4,3.1-10.2,4c-3.8,0.8-7.6,1.2-11.3,1.2
|
||||
c-5.7,0-11-0.8-15.8-2.4c-4.8-1.6-9.1-4.6-12.9-8.8l12.3-11.4c2.4,2.6,4.9,4.8,7.6,6.5c2.7,1.7,6,2.5,9.9,2.5
|
||||
c1.3,0,2.7-0.2,4.1-0.5c1.4-0.3,2.8-0.8,4-1.5c1.2-0.7,2.2-1.6,3-2.7c0.8-1.1,1.1-2.3,1.1-3.7c0-2.5-1-4.4-2.9-5.6
|
||||
c-1.9-1.2-4.4-2.2-7.4-3c-3-0.8-6.2-1.5-9.6-2.1c-3.4-0.7-6.6-1.7-9.6-3.2c-3-1.5-5.4-3.5-7.4-6.2c-1.9-2.6-2.9-6.3-2.9-11
|
||||
c0-4.1,0.8-7.6,2.5-10.6c1.7-3,3.9-5.4,6.7-7.4c2.8-1.9,5.9-3.3,9.5-4.3c3.6-0.9,7.2-1.4,10.9-1.4c4.9,0,9.8,0.8,14.6,2.5
|
||||
c4.8,1.7,8.7,4.5,11.7,8.6L683.6,269.9z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 8 KiB After Width: | Height: | Size: 8 KiB |
|
|
@ -1,25 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 292.6 215.3" style="enable-background:new 0 0 292.6 215.3;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#566AB2;}
|
||||
</style>
|
||||
<path class="st0" d="M191.3,123.7c-2.4,1-4.9,1.8-7.2,1.9c-3.6,0.2-7.6-1.3-9.7-3.1c-3.3-2.8-5.7-4.4-6.7-9.2
|
||||
c-0.4-2.1-0.2-5.3,0.2-7.2c0.9-4-0.1-6.5-2.9-8.9c-2.3-1.9-5.2-2.4-8.4-2.4s-2.3-0.5-3.1-1c-1.3-0.7-2.4-2.3-1.4-4.4
|
||||
c0.3-0.7,2-2.3,2.3-2.5c4.3-2.5,9.4-1.7,14,0.2c4.3,1.7,7.5,5,12.2,9.5c4.8,5.5,5.6,7,8.4,11.1c2.1,3.2,4.1,6.6,5.4,10.4
|
||||
C195.2,120.5,194.2,122.4,191.3,123.7L191.3,123.7z M153.4,104.3c0-2.1,1.7-3.7,3.8-3.7s0.9,0.1,1.3,0.2c0.5,0.2,1,0.5,1.4,0.9
|
||||
c0.7,0.7,1.1,1.6,1.1,2.6c0,2.1-1.7,3.8-3.8,3.8s-3.7-1.7-3.7-3.8H153.4z M141.2,182.8c-25.5-20-37.8-26.6-42.9-26.3
|
||||
c-4.8,0.3-3.9,5.7-2.8,9.3c1.1,3.5,2.5,5.9,4.5,9c1.4,2,2.3,5.1-1.4,7.3c-8.2,5.1-22.5-1.7-23.1-2c-16.6-9.8-30.5-22.7-40.2-40.3
|
||||
c-9.5-17-14.9-35.2-15.8-54.6c-0.2-4.7,1.1-6.4,5.8-7.2c6.2-1.1,12.5-1.4,18.7-0.5c26,3.8,48.1,15.4,66.7,33.8
|
||||
c10.6,10.5,18.6,23,26.8,35.2c8.8,13,18.2,25.4,30.2,35.5c4.3,3.6,7.6,6.3,10.9,8.2c-9.8,1.1-26.1,1.3-37.2-7.5L141.2,182.8z
|
||||
M289.5,18c-3.1-1.5-4.4,1.4-6.3,2.8c-0.6,0.5-1.1,1.1-1.7,1.7c-4.5,4.8-9.8,8-16.8,7.6c-10.1-0.6-18.7,2.6-26.4,10.4
|
||||
c-1.6-9.5-7-15.2-15.2-18.9c-4.3-1.9-8.6-3.8-11.6-7.9c-2.1-2.9-2.7-6.2-3.7-9.4c-0.7-2-1.3-3.9-3.6-4.3c-2.4-0.4-3.4,1.7-4.3,3.4
|
||||
c-3.8,7-5.3,14.6-5.2,22.4c0.3,17.5,7.7,31.5,22.4,41.4c1.7,1.1,2.1,2.3,1.6,3.9c-1,3.4-2.2,6.7-3.3,10.1c-0.7,2.2-1.7,2.7-4,1.7
|
||||
c-8.1-3.4-15-8.4-21.2-14.4c-10.4-10.1-19.9-21.2-31.6-30c-2.8-2.1-5.5-4-8.4-5.7c-12-11.7,1.6-21.3,4.7-22.4
|
||||
c3.3-1.2,1.2-5.3-9.5-5.2c-10.6,0-20.3,3.6-32.8,8.4c-1.8,0.7-3.7,1.2-5.7,1.7c-11.3-2.1-22.9-2.6-35.1-1.2
|
||||
c-23,2.5-41.4,13.4-54.8,32C1,68.3-2.8,93.6,1.9,120c4.9,27.8,19.1,50.9,41,68.9c22.6,18.7,48.7,27.8,78.5,26.1
|
||||
c18.1-1,38.2-3.5,60.9-22.7c5.7,2.8,11.7,4,21.7,4.8c7.7,0.7,15.1-0.4,20.8-1.5c9-1.9,8.4-10.2,5.1-11.7
|
||||
c-26.3-12.3-20.5-7.3-25.7-11.3c13.3-15.8,33.5-32.2,41.3-85.4c0.6-4.2,0.1-6.9,0-10.3c0-2.1,0.4-2.9,2.8-3.1
|
||||
c6.6-0.8,13-2.6,18.8-5.8c17-9.3,23.9-24.6,25.5-42.9c0.2-2.8,0-5.7-3-7.2L289.5,18z"/>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 292.6 215.3" style="enable-background:new 0 0 292.6 215.3;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#566AB2;}
|
||||
</style>
|
||||
<path class="st0" d="M191.3,123.7c-2.4,1-4.9,1.8-7.2,1.9c-3.6,0.2-7.6-1.3-9.7-3.1c-3.3-2.8-5.7-4.4-6.7-9.2
|
||||
c-0.4-2.1-0.2-5.3,0.2-7.2c0.9-4-0.1-6.5-2.9-8.9c-2.3-1.9-5.2-2.4-8.4-2.4s-2.3-0.5-3.1-1c-1.3-0.7-2.4-2.3-1.4-4.4
|
||||
c0.3-0.7,2-2.3,2.3-2.5c4.3-2.5,9.4-1.7,14,0.2c4.3,1.7,7.5,5,12.2,9.5c4.8,5.5,5.6,7,8.4,11.1c2.1,3.2,4.1,6.6,5.4,10.4
|
||||
C195.2,120.5,194.2,122.4,191.3,123.7L191.3,123.7z M153.4,104.3c0-2.1,1.7-3.7,3.8-3.7s0.9,0.1,1.3,0.2c0.5,0.2,1,0.5,1.4,0.9
|
||||
c0.7,0.7,1.1,1.6,1.1,2.6c0,2.1-1.7,3.8-3.8,3.8s-3.7-1.7-3.7-3.8H153.4z M141.2,182.8c-25.5-20-37.8-26.6-42.9-26.3
|
||||
c-4.8,0.3-3.9,5.7-2.8,9.3c1.1,3.5,2.5,5.9,4.5,9c1.4,2,2.3,5.1-1.4,7.3c-8.2,5.1-22.5-1.7-23.1-2c-16.6-9.8-30.5-22.7-40.2-40.3
|
||||
c-9.5-17-14.9-35.2-15.8-54.6c-0.2-4.7,1.1-6.4,5.8-7.2c6.2-1.1,12.5-1.4,18.7-0.5c26,3.8,48.1,15.4,66.7,33.8
|
||||
c10.6,10.5,18.6,23,26.8,35.2c8.8,13,18.2,25.4,30.2,35.5c4.3,3.6,7.6,6.3,10.9,8.2c-9.8,1.1-26.1,1.3-37.2-7.5L141.2,182.8z
|
||||
M289.5,18c-3.1-1.5-4.4,1.4-6.3,2.8c-0.6,0.5-1.1,1.1-1.7,1.7c-4.5,4.8-9.8,8-16.8,7.6c-10.1-0.6-18.7,2.6-26.4,10.4
|
||||
c-1.6-9.5-7-15.2-15.2-18.9c-4.3-1.9-8.6-3.8-11.6-7.9c-2.1-2.9-2.7-6.2-3.7-9.4c-0.7-2-1.3-3.9-3.6-4.3c-2.4-0.4-3.4,1.7-4.3,3.4
|
||||
c-3.8,7-5.3,14.6-5.2,22.4c0.3,17.5,7.7,31.5,22.4,41.4c1.7,1.1,2.1,2.3,1.6,3.9c-1,3.4-2.2,6.7-3.3,10.1c-0.7,2.2-1.7,2.7-4,1.7
|
||||
c-8.1-3.4-15-8.4-21.2-14.4c-10.4-10.1-19.9-21.2-31.6-30c-2.8-2.1-5.5-4-8.4-5.7c-12-11.7,1.6-21.3,4.7-22.4
|
||||
c3.3-1.2,1.2-5.3-9.5-5.2c-10.6,0-20.3,3.6-32.8,8.4c-1.8,0.7-3.7,1.2-5.7,1.7c-11.3-2.1-22.9-2.6-35.1-1.2
|
||||
c-23,2.5-41.4,13.4-54.8,32C1,68.3-2.8,93.6,1.9,120c4.9,27.8,19.1,50.9,41,68.9c22.6,18.7,48.7,27.8,78.5,26.1
|
||||
c18.1-1,38.2-3.5,60.9-22.7c5.7,2.8,11.7,4,21.7,4.8c7.7,0.7,15.1-0.4,20.8-1.5c9-1.9,8.4-10.2,5.1-11.7
|
||||
c-26.3-12.3-20.5-7.3-25.7-11.3c13.3-15.8,33.5-32.2,41.3-85.4c0.6-4.2,0.1-6.9,0-10.3c0-2.1,0.4-2.9,2.8-3.1
|
||||
c6.6-0.8,13-2.6,18.8-5.8c17-9.3,23.9-24.6,25.5-42.9c0.2-2.8,0-5.7-3-7.2L289.5,18z"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
|
@ -1,16 +1,16 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 26.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="10.5862" y1="1.61" x2="36.0543" y2="44.1206">
|
||||
<stop offset="0.002" style="stop-color:#9C55D4"/>
|
||||
<stop offset="0.003" style="stop-color:#20808D"/>
|
||||
<stop offset="0.3731" style="stop-color:#218F9B"/>
|
||||
<stop offset="1" style="stop-color:#22B1BC"/>
|
||||
</linearGradient>
|
||||
<path style="fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_1_);" d="M11.469,4l11.39,10.494v-0.002V4.024h2.217v10.517
|
||||
L36.518,4v11.965h4.697v17.258h-4.683v10.654L25.077,33.813v10.18h-2.217V33.979L11.482,44V33.224H6.785V15.965h4.685V4z
|
||||
M21.188,18.155H9.002v12.878h2.477v-4.062L21.188,18.155z M13.699,27.943v11.17l9.16-8.068V19.623L13.699,27.943z M25.141,30.938
|
||||
V19.612l9.163,8.321v5.291h0.012v5.775L25.141,30.938z M36.532,31.033h2.466V18.155H26.903l9.629,8.725V31.033z M34.301,15.965
|
||||
V9.038l-7.519,6.927H34.301z M21.205,15.965h-7.519V9.038L21.205,15.965z"/>
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 26.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="10.5862" y1="1.61" x2="36.0543" y2="44.1206">
|
||||
<stop offset="0.002" style="stop-color:#9C55D4"/>
|
||||
<stop offset="0.003" style="stop-color:#20808D"/>
|
||||
<stop offset="0.3731" style="stop-color:#218F9B"/>
|
||||
<stop offset="1" style="stop-color:#22B1BC"/>
|
||||
</linearGradient>
|
||||
<path style="fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_1_);" d="M11.469,4l11.39,10.494v-0.002V4.024h2.217v10.517
|
||||
L36.518,4v11.965h4.697v17.258h-4.683v10.654L25.077,33.813v10.18h-2.217V33.979L11.482,44V33.224H6.785V15.965h4.685V4z
|
||||
M21.188,18.155H9.002v12.878h2.477v-4.062L21.188,18.155z M13.699,27.943v11.17l9.16-8.068V19.623L13.699,27.943z M25.141,30.938
|
||||
V19.612l9.163,8.321v5.291h0.012v5.775L25.141,30.938z M36.532,31.033h2.466V18.155H26.903l9.629,8.725V31.033z M34.301,15.965
|
||||
V9.038l-7.519,6.927H34.301z M21.205,15.965h-7.519V9.038L21.205,15.965z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
|
@ -1,7 +1,7 @@
|
|||
2:I[19107,[],"ClientPageRoot"]
|
||||
3:I[86917,["665","static/chunks/3014691f-b7b79b78e27792f3.js","990","static/chunks/13b76428-ebdf3012af0e4489.js","402","static/chunks/402-ba983153c902cb17.js","313","static/chunks/313-cf4a28394ee560d6.js","899","static/chunks/899-ebef5c8ffc176e86.js","276","static/chunks/276-859a00a1fe124231.js","250","static/chunks/250-562275aca86e6d43.js","699","static/chunks/699-7660d0629950118b.js","931","static/chunks/app/page-00691d283cb03cc8.js"],"default",1]
|
||||
3:I[34125,["665","static/chunks/3014691f-b7b79b78e27792f3.js","990","static/chunks/13b76428-ebdf3012af0e4489.js","402","static/chunks/402-239a4ed70dc393da.js","313","static/chunks/313-cf4a28394ee560d6.js","899","static/chunks/899-0459bccb48a6666d.js","966","static/chunks/966-d23842bbd48c2bc2.js","250","static/chunks/250-8d759a787d622b48.js","699","static/chunks/699-96a60f79614507d5.js","931","static/chunks/app/page-86bbb3edba7ec5f0.js"],"default",1]
|
||||
4:I[4707,[],""]
|
||||
5:I[36423,[],""]
|
||||
0:["aV7wX1vsNVbDvWm3r-ZF3",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/31b7f215e119031e.css","precedence":"next","crossOrigin":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/_next/static/css/20729c6c90dc7f33.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_b0dd8a","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]],null],null],["$L6",null]]]]
|
||||
0:["zyqLTLglamGh14G70gBJG",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/css/31b7f215e119031e.css","precedence":"next","crossOrigin":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/css/27aa365198bdc7c8.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_b0dd8a","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]],null],null],["$L6",null]]]]
|
||||
6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"LiteLLM Dashboard"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","link","5",{"rel":"icon","href":"./favicon.ico"}],["$","meta","6",{"name":"next-size-adjust"}]]
|
||||
1:null
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
2:I[19107,[],"ClientPageRoot"]
|
||||
3:I[52829,["402","static/chunks/402-ba983153c902cb17.js","313","static/chunks/313-cf4a28394ee560d6.js","250","static/chunks/250-562275aca86e6d43.js","699","static/chunks/699-7660d0629950118b.js","418","static/chunks/app/model_hub/page-ce40c5a05f3174ca.js"],"default",1]
|
||||
3:I[52829,["402","static/chunks/402-239a4ed70dc393da.js","313","static/chunks/313-cf4a28394ee560d6.js","250","static/chunks/250-8d759a787d622b48.js","699","static/chunks/699-96a60f79614507d5.js","418","static/chunks/app/model_hub/page-ce40c5a05f3174ca.js"],"default",1]
|
||||
4:I[4707,[],""]
|
||||
5:I[36423,[],""]
|
||||
0:["aV7wX1vsNVbDvWm3r-ZF3",[[["",{"children":["model_hub",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["model_hub",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[null,["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","model_hub","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/31b7f215e119031e.css","precedence":"next","crossOrigin":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/_next/static/css/20729c6c90dc7f33.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_b0dd8a","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]],null],null],["$L6",null]]]]
|
||||
0:["zyqLTLglamGh14G70gBJG",[[["",{"children":["model_hub",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["model_hub",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[null,["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","model_hub","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/css/31b7f215e119031e.css","precedence":"next","crossOrigin":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/css/27aa365198bdc7c8.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_b0dd8a","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]],null],null],["$L6",null]]]]
|
||||
6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"LiteLLM Dashboard"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","link","5",{"rel":"icon","href":"./favicon.ico"}],["$","meta","6",{"name":"next-size-adjust"}]]
|
||||
1:null
|
||||
|
|
|
|||
1
litellm/proxy/_experimental/out/onboarding.html
Normal file
|
|
@ -1,7 +1,7 @@
|
|||
2:I[19107,[],"ClientPageRoot"]
|
||||
3:I[12011,["665","static/chunks/3014691f-b7b79b78e27792f3.js","402","static/chunks/402-ba983153c902cb17.js","899","static/chunks/899-ebef5c8ffc176e86.js","250","static/chunks/250-562275aca86e6d43.js","461","static/chunks/app/onboarding/page-74706b15590d94d7.js"],"default",1]
|
||||
3:I[12011,["665","static/chunks/3014691f-b7b79b78e27792f3.js","402","static/chunks/402-239a4ed70dc393da.js","899","static/chunks/899-0459bccb48a6666d.js","250","static/chunks/250-8d759a787d622b48.js","461","static/chunks/app/onboarding/page-be52e5770ada890a.js"],"default",1]
|
||||
4:I[4707,[],""]
|
||||
5:I[36423,[],""]
|
||||
0:["aV7wX1vsNVbDvWm3r-ZF3",[[["",{"children":["onboarding",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["onboarding",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[null,["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","onboarding","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/31b7f215e119031e.css","precedence":"next","crossOrigin":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/_next/static/css/20729c6c90dc7f33.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_b0dd8a","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]],null],null],["$L6",null]]]]
|
||||
0:["zyqLTLglamGh14G70gBJG",[[["",{"children":["onboarding",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["onboarding",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[null,["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","onboarding","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/css/31b7f215e119031e.css","precedence":"next","crossOrigin":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/litellm-asset-prefix/_next/static/css/27aa365198bdc7c8.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_b0dd8a","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]],null],null],["$L6",null]]]]
|
||||
6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"LiteLLM Dashboard"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","link","5",{"rel":"icon","href":"./favicon.ico"}],["$","meta","6",{"name":"next-size-adjust"}]]
|
||||
1:null
|
||||
|
|
|
|||
|
|
@ -3,6 +3,12 @@ model_list:
|
|||
litellm_params:
|
||||
model: codex-mini-latest
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
- model_name: bedrock/*
|
||||
litellm_params:
|
||||
model: bedrock/*
|
||||
- model_name: eu.anthropic.claude-3-5-sonnet-20240620-v1:0
|
||||
litellm_params:
|
||||
model: eu.anthropic.claude-3-5-sonnet-20240620-v1:0
|
||||
- model_name: "gpt-4o-mini-openai"
|
||||
litellm_params:
|
||||
model: gpt-4o-mini
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ from typing_extensions import Required, TypedDict
|
|||
|
||||
from litellm.types.integrations.slack_alerting import AlertType
|
||||
from litellm.types.llms.openai import AllMessageValues, OpenAIFileObject
|
||||
from litellm.types.mcp import (
|
||||
MCPAuthType,
|
||||
MCPSpecVersion,
|
||||
MCPSpecVersionType,
|
||||
MCPTransport,
|
||||
MCPTransportType,
|
||||
)
|
||||
from litellm.types.router import RouterErrors, UpdateRouterConfig
|
||||
from litellm.types.utils import (
|
||||
CallTypes,
|
||||
|
|
@ -830,32 +837,6 @@ class SpecialMCPServerName(str, enum.Enum):
|
|||
all_team_servers = "all-team-mcpservers"
|
||||
all_proxy_servers = "all-proxy-mcpservers"
|
||||
|
||||
|
||||
class MCPTransport(str, enum.Enum):
|
||||
sse = "sse"
|
||||
http = "http"
|
||||
|
||||
|
||||
class MCPSpecVersion(str, enum.Enum):
|
||||
nov_2024 = "2024-11-05"
|
||||
mar_2025 = "2025-03-26"
|
||||
|
||||
|
||||
class MCPAuth(str, enum.Enum):
|
||||
none = "none"
|
||||
api_key = "api_key"
|
||||
bearer_token = "bearer_token"
|
||||
basic = "basic"
|
||||
|
||||
|
||||
# MCP Literals
|
||||
MCPTransportType = Literal[MCPTransport.sse, MCPTransport.http]
|
||||
MCPSpecVersionType = Literal[MCPSpecVersion.nov_2024, MCPSpecVersion.mar_2025]
|
||||
MCPAuthType = Optional[
|
||||
Literal[MCPAuth.none, MCPAuth.api_key, MCPAuth.bearer_token, MCPAuth.basic]
|
||||
]
|
||||
|
||||
|
||||
# MCP Proxy Request Types
|
||||
class NewMCPServerRequest(LiteLLMPydanticObjectBase):
|
||||
server_id: Optional[str] = None
|
||||
|
|
@ -2703,6 +2684,7 @@ class SpecialHeaders(enum.Enum):
|
|||
google_ai_studio_authorization = "x-goog-api-key"
|
||||
azure_apim_authorization = "Ocp-Apim-Subscription-Key"
|
||||
custom_litellm_api_key = "x-litellm-api-key"
|
||||
mcp_auth = "x-mcp-auth"
|
||||
|
||||
|
||||
class LitellmDataForBackendLLMCall(TypedDict, total=False):
|
||||
|
|
|
|||
|
|
@ -199,14 +199,17 @@ async def _add_user_to_team(
|
|||
str(e)
|
||||
)
|
||||
)
|
||||
except ProxyException as e:
|
||||
except Exception as e:
|
||||
if "already exists" in str(e) or "doesn't exist" in str(e):
|
||||
verbose_proxy_logger.debug(
|
||||
"litellm.proxy.management_endpoints.internal_user_endpoints.new_user(): User already exists in team - {}".format(
|
||||
str(e)
|
||||
)
|
||||
)
|
||||
elif ProxyErrorTypes.team_member_already_in_team in e.type:
|
||||
elif (
|
||||
isinstance(e, ProxyException)
|
||||
and ProxyErrorTypes.team_member_already_in_team in e.type
|
||||
):
|
||||
verbose_proxy_logger.debug(
|
||||
"litellm.proxy.management_endpoints.internal_user_endpoints.new_user(): User already exists in team - {}".format(
|
||||
str(e)
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
"""
|
||||
TAG MANAGEMENT
|
||||
|
||||
All /tag management endpoints
|
||||
All /tag management endpoints
|
||||
|
||||
/tag/new
|
||||
/tag/new
|
||||
/tag/info
|
||||
/tag/update
|
||||
/tag/delete
|
||||
/tag/list
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import datetime
|
||||
import json
|
||||
from typing import Dict, List, Optional
|
||||
from typing import TYPE_CHECKING, Dict, List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
|
||||
|
|
@ -33,6 +34,10 @@ from litellm.types.tag_management import (
|
|||
TagUpdateRequest,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from litellm import Router
|
||||
from litellm.types.router import Deployment
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
|
|
@ -111,6 +116,33 @@ async def _save_tags_config(prisma_client, tags_config: Dict[str, TagConfig]):
|
|||
)
|
||||
|
||||
|
||||
async def get_deployments_by_model(
|
||||
model: str, llm_router: "Router"
|
||||
) -> List["Deployment"]:
|
||||
"""
|
||||
Get all deployments by model
|
||||
"""
|
||||
from litellm.types.router import Deployment, LiteLLM_Params, ModelInfo
|
||||
|
||||
# Check if model id
|
||||
deployment = llm_router.get_deployment(model_id=model)
|
||||
if deployment is not None:
|
||||
return [deployment]
|
||||
|
||||
# Check if model name
|
||||
deployments = llm_router.get_model_list(model_name=model)
|
||||
if deployments is None:
|
||||
return []
|
||||
return [
|
||||
Deployment(
|
||||
model_name=deployment["model_name"],
|
||||
litellm_params=LiteLLM_Params(**deployment["litellm_params"]), # type: ignore
|
||||
model_info=ModelInfo(**deployment.get("model_info") or {}),
|
||||
)
|
||||
for deployment in deployments
|
||||
]
|
||||
|
||||
|
||||
@router.post(
|
||||
"/tag/new",
|
||||
tags=["tag management"],
|
||||
|
|
@ -126,12 +158,19 @@ async def new_tag(
|
|||
Parameters:
|
||||
- name: str - The name of the tag
|
||||
- description: Optional[str] - Description of what this tag represents
|
||||
- models: List[str] - List of LLM models allowed for this tag
|
||||
- models: List[str] - List of either 'model_id' or 'model_name' allowed for this tag
|
||||
"""
|
||||
from litellm.proxy.proxy_server import prisma_client
|
||||
from litellm.proxy._types import CommonProxyErrors
|
||||
from litellm.proxy.proxy_server import llm_router, prisma_client
|
||||
|
||||
if prisma_client is None:
|
||||
raise HTTPException(status_code=500, detail="Database not connected")
|
||||
raise HTTPException(
|
||||
status_code=500, detail=CommonProxyErrors.db_not_connected_error.value
|
||||
)
|
||||
if llm_router is None:
|
||||
raise HTTPException(
|
||||
status_code=500, detail=CommonProxyErrors.no_llm_router.value
|
||||
)
|
||||
try:
|
||||
# Get existing tags config
|
||||
tags_config = await _get_tags_config(prisma_client)
|
||||
|
|
@ -160,11 +199,19 @@ async def new_tag(
|
|||
|
||||
# Update models with new tag
|
||||
if tag.models:
|
||||
for model_id in tag.models:
|
||||
await _add_tag_to_deployment(
|
||||
model_id=model_id,
|
||||
tag=tag.name,
|
||||
tasks = []
|
||||
for model in tag.models:
|
||||
deployments = await get_deployments_by_model(model, llm_router)
|
||||
tasks.extend(
|
||||
[
|
||||
_add_tag_to_deployment(
|
||||
deployment=deployment,
|
||||
tag=tag.name,
|
||||
)
|
||||
for deployment in deployments
|
||||
]
|
||||
)
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
# Get model names for response
|
||||
model_info = await _get_model_names(prisma_client, tag.models or [])
|
||||
|
|
@ -179,27 +226,26 @@ async def new_tag(
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
async def _add_tag_to_deployment(model_id: str, tag: str):
|
||||
async def _add_tag_to_deployment(deployment: "Deployment", tag: str):
|
||||
"""Helper function to add tag to deployment"""
|
||||
from litellm.proxy.proxy_server import prisma_client
|
||||
|
||||
if prisma_client is None:
|
||||
raise HTTPException(status_code=500, detail="Database not connected")
|
||||
|
||||
deployment = await prisma_client.db.litellm_proxymodeltable.find_unique(
|
||||
where={"model_id": model_id}
|
||||
)
|
||||
if deployment is None:
|
||||
raise HTTPException(status_code=404, detail=f"Deployment {model_id} not found")
|
||||
|
||||
litellm_params = deployment.litellm_params
|
||||
if "tags" not in litellm_params:
|
||||
litellm_params["tags"] = []
|
||||
litellm_params["tags"].append(tag)
|
||||
await prisma_client.db.litellm_proxymodeltable.update(
|
||||
where={"model_id": model_id},
|
||||
data={"litellm_params": safe_dumps(litellm_params)},
|
||||
)
|
||||
|
||||
try:
|
||||
await prisma_client.db.litellm_proxymodeltable.update(
|
||||
where={"model_id": deployment.model_info.id},
|
||||
data={"litellm_params": safe_dumps(litellm_params)},
|
||||
)
|
||||
except Exception as e:
|
||||
verbose_proxy_logger.exception(f"Error adding tag to deployment: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post(
|
||||
|
|
|
|||
|
|
@ -752,21 +752,6 @@ try:
|
|||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
ui_path = os.path.join(current_dir, "_experimental", "out")
|
||||
litellm_asset_prefix = "/litellm-asset-prefix"
|
||||
# # Mount the _next directory at the root level
|
||||
app.mount(
|
||||
"/_next",
|
||||
StaticFiles(directory=os.path.join(ui_path, "_next")),
|
||||
name="next_static",
|
||||
)
|
||||
app.mount(
|
||||
f"{litellm_asset_prefix}/_next",
|
||||
StaticFiles(directory=os.path.join(ui_path, "_next")),
|
||||
name="next_static",
|
||||
)
|
||||
# print(f"mounted _next at {server_root_path}/ui/_next")
|
||||
|
||||
app.mount("/ui", StaticFiles(directory=ui_path, html=True), name="ui")
|
||||
|
||||
# Iterate through files in the UI directory
|
||||
for root, dirs, files in os.walk(ui_path):
|
||||
for filename in files:
|
||||
|
|
@ -792,19 +777,37 @@ try:
|
|||
|
||||
# Replace the asset prefix with the server root path
|
||||
modified_content = content.replace(
|
||||
f"{litellm_asset_prefix}", server_root_path
|
||||
f"{litellm_asset_prefix}",
|
||||
f"{server_root_path}",
|
||||
)
|
||||
|
||||
# Replace the /.well-known/litellm-ui-config with the server root path
|
||||
modified_content = modified_content.replace(
|
||||
"/litellm/.well-known/litellm-ui-config",
|
||||
f"{server_root_path}/.well-known/litellm-ui-config",
|
||||
)
|
||||
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.write(modified_content)
|
||||
except UnicodeDecodeError:
|
||||
# Skip binary files that can't be decoded
|
||||
continue
|
||||
|
||||
# # Mount the _next directory at the root level
|
||||
app.mount(
|
||||
"/_next",
|
||||
StaticFiles(directory=os.path.join(ui_path, "_next")),
|
||||
name="next_static",
|
||||
)
|
||||
app.mount(
|
||||
f"{litellm_asset_prefix}/_next",
|
||||
StaticFiles(directory=os.path.join(ui_path, "_next")),
|
||||
name="next_static",
|
||||
)
|
||||
# print(f"mounted _next at {server_root_path}/ui/_next")
|
||||
|
||||
app.mount("/ui", StaticFiles(directory=ui_path, html=True), name="ui")
|
||||
|
||||
# Handle HTML file restructuring
|
||||
for filename in os.listdir(ui_path):
|
||||
if filename.endswith(".html") and filename != "index.html":
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Handles transforming from Responses API -> LiteLLM completion (Chat Completion API)
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, List, Optional, Union, cast
|
||||
from typing import Any, Dict, List, Literal, Optional, Tuple, Union, cast
|
||||
|
||||
from openai.types.responses.tool_param import FunctionToolParam
|
||||
from typing_extensions import TypedDict
|
||||
|
|
@ -32,6 +32,8 @@ from litellm.types.llms.openai import (
|
|||
ChatCompletionUserMessage,
|
||||
GenericChatCompletionMessage,
|
||||
OpenAIMcpServerTool,
|
||||
OpenAIWebSearchOptions,
|
||||
OpenAIWebSearchUserLocation,
|
||||
Reasoning,
|
||||
ResponseAPIUsage,
|
||||
ResponseInputParam,
|
||||
|
|
@ -109,6 +111,9 @@ class LiteLLMCompletionResponsesConfig:
|
|||
"""
|
||||
Transform a Responses API request into a Chat Completion request
|
||||
"""
|
||||
tools, web_search_options = LiteLLMCompletionResponsesConfig.transform_responses_api_tools_to_chat_completion_tools(
|
||||
responses_api_request.get("tools") or [] # type: ignore
|
||||
)
|
||||
litellm_completion_request: dict = {
|
||||
"messages": LiteLLMCompletionResponsesConfig.transform_responses_api_input_to_messages(
|
||||
input=input,
|
||||
|
|
@ -116,9 +121,7 @@ class LiteLLMCompletionResponsesConfig:
|
|||
),
|
||||
"model": model,
|
||||
"tool_choice": responses_api_request.get("tool_choice"),
|
||||
"tools": LiteLLMCompletionResponsesConfig.transform_responses_api_tools_to_chat_completion_tools(
|
||||
responses_api_request.get("tools") or [] # type: ignore
|
||||
),
|
||||
"tools": tools,
|
||||
"top_p": responses_api_request.get("top_p"),
|
||||
"user": responses_api_request.get("user"),
|
||||
"temperature": responses_api_request.get("temperature"),
|
||||
|
|
@ -127,6 +130,7 @@ class LiteLLMCompletionResponsesConfig:
|
|||
"stream": stream,
|
||||
"metadata": kwargs.get("metadata"),
|
||||
"service_tier": kwargs.get("service_tier"),
|
||||
"web_search_options": web_search_options,
|
||||
# litellm specific params
|
||||
"custom_llm_provider": custom_llm_provider,
|
||||
}
|
||||
|
|
@ -468,32 +472,40 @@ class LiteLLMCompletionResponsesConfig:
|
|||
@staticmethod
|
||||
def transform_responses_api_tools_to_chat_completion_tools(
|
||||
tools: Optional[List[Union[FunctionToolParam, OpenAIMcpServerTool]]],
|
||||
) -> List[Union[ChatCompletionToolParam, OpenAIMcpServerTool]]:
|
||||
) -> Tuple[List[Union[ChatCompletionToolParam, OpenAIMcpServerTool]], Optional[OpenAIWebSearchOptions]]:
|
||||
"""
|
||||
Transform a Responses API tools into a Chat Completion tools
|
||||
"""
|
||||
if tools is None:
|
||||
return []
|
||||
return [], None
|
||||
chat_completion_tools: List[
|
||||
Union[ChatCompletionToolParam, OpenAIMcpServerTool]
|
||||
] = []
|
||||
web_search_options: Optional[OpenAIWebSearchOptions] = None
|
||||
for tool in tools:
|
||||
if tool.get("type") == "mcp":
|
||||
chat_completion_tools.append(cast(OpenAIMcpServerTool, tool))
|
||||
elif tool.get("type") == "web_search_preview" or tool.get("type") == "web_search":
|
||||
_search_context_size: Literal["low", "medium", "high"] = cast(Literal["low", "medium", "high"], tool.get("search_context_size"))
|
||||
_user_location: Optional[OpenAIWebSearchUserLocation] = cast(Optional[OpenAIWebSearchUserLocation], tool.get("user_location") or None)
|
||||
web_search_options = OpenAIWebSearchOptions(
|
||||
search_context_size=_search_context_size,
|
||||
user_location=_user_location,
|
||||
)
|
||||
else:
|
||||
typed_tool = cast(FunctionToolParam, tool)
|
||||
chat_completion_tools.append(
|
||||
ChatCompletionToolParam(
|
||||
type="function",
|
||||
function=ChatCompletionToolParamFunctionChunk(
|
||||
name=typed_tool["name"],
|
||||
name=typed_tool.get("name") or "",
|
||||
description=typed_tool.get("description") or "",
|
||||
parameters=dict(typed_tool.get("parameters", {}) or {}),
|
||||
strict=typed_tool.get("strict", False) or False,
|
||||
),
|
||||
)
|
||||
)
|
||||
return chat_completion_tools
|
||||
return chat_completion_tools, web_search_options
|
||||
|
||||
@staticmethod
|
||||
def transform_chat_completion_tools_to_responses_tools(
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ class UserAPIKeyLabelNames(Enum):
|
|||
STATUS_CODE = "status_code"
|
||||
FALLBACK_MODEL = "fallback_model"
|
||||
ROUTE = "route"
|
||||
MODEL_GROUP = "model_group"
|
||||
|
||||
|
||||
DEFINED_PROMETHEUS_METRICS = Literal[
|
||||
|
|
@ -161,7 +162,7 @@ class PrometheusMetricLabels:
|
|||
]
|
||||
|
||||
litellm_overhead_latency_metric = [
|
||||
UserAPIKeyLabelNames.REQUESTED_MODEL.value,
|
||||
UserAPIKeyLabelNames.MODEL_GROUP.value,
|
||||
UserAPIKeyLabelNames.API_PROVIDER.value,
|
||||
UserAPIKeyLabelNames.API_BASE.value,
|
||||
UserAPIKeyLabelNames.v2_LITELLM_MODEL_NAME.value,
|
||||
|
|
@ -170,7 +171,7 @@ class PrometheusMetricLabels:
|
|||
]
|
||||
|
||||
litellm_remaining_requests_metric = [
|
||||
UserAPIKeyLabelNames.REQUESTED_MODEL.value,
|
||||
UserAPIKeyLabelNames.MODEL_GROUP.value,
|
||||
UserAPIKeyLabelNames.API_PROVIDER.value,
|
||||
UserAPIKeyLabelNames.API_BASE.value,
|
||||
UserAPIKeyLabelNames.v2_LITELLM_MODEL_NAME.value,
|
||||
|
|
@ -179,7 +180,7 @@ class PrometheusMetricLabels:
|
|||
]
|
||||
|
||||
litellm_remaining_tokens_metric = [
|
||||
UserAPIKeyLabelNames.REQUESTED_MODEL.value,
|
||||
UserAPIKeyLabelNames.MODEL_GROUP.value,
|
||||
UserAPIKeyLabelNames.API_PROVIDER.value,
|
||||
UserAPIKeyLabelNames.API_BASE.value,
|
||||
UserAPIKeyLabelNames.v2_LITELLM_MODEL_NAME.value,
|
||||
|
|
@ -335,6 +336,9 @@ class UserAPIKeyLabelValues(BaseModel):
|
|||
team_alias: Annotated[
|
||||
Optional[str], Field(..., alias=UserAPIKeyLabelNames.TEAM_ALIAS.value)
|
||||
] = None
|
||||
model_group: Annotated[
|
||||
Optional[str], Field(..., alias=UserAPIKeyLabelNames.MODEL_GROUP.value)
|
||||
] = None
|
||||
requested_model: Annotated[
|
||||
Optional[str], Field(..., alias=UserAPIKeyLabelNames.REQUESTED_MODEL.value)
|
||||
] = None
|
||||
|
|
|
|||
29
litellm/types/mcp.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import enum
|
||||
from typing import Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
|
||||
class MCPTransport(str, enum.Enum):
|
||||
sse = "sse"
|
||||
http = "http"
|
||||
|
||||
|
||||
class MCPSpecVersion(str, enum.Enum):
|
||||
nov_2024 = "2024-11-05"
|
||||
mar_2025 = "2025-03-26"
|
||||
|
||||
class MCPAuth(str, enum.Enum):
|
||||
none = "none"
|
||||
api_key = "api_key"
|
||||
bearer_token = "bearer_token"
|
||||
basic = "basic"
|
||||
|
||||
|
||||
# MCP Literals
|
||||
MCPTransportType = Literal[MCPTransport.sse, MCPTransport.http]
|
||||
MCPSpecVersionType = Literal[MCPSpecVersion.nov_2024, MCPSpecVersion.mar_2025]
|
||||
MCPAuthType = Optional[
|
||||
Literal[MCPAuth.none, MCPAuth.api_key, MCPAuth.bearer_token, MCPAuth.basic]
|
||||
]
|
||||
|
|
@ -16,9 +16,9 @@ class MCPServer(BaseModel):
|
|||
server_id: str
|
||||
name: str
|
||||
url: str
|
||||
# TODO: alter the types to be the Literal explicit
|
||||
transport: MCPTransportType
|
||||
spec_version: MCPSpecVersionType
|
||||
auth_type: Optional[MCPAuthType] = None
|
||||
authentication_token: Optional[str] = None
|
||||
mcp_info: Optional[MCPInfo] = None
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
|
|
|||
|
|
@ -451,9 +451,9 @@
|
|||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 16384,
|
||||
"input_cost_per_token": 2.5e-06,
|
||||
"input_cost_per_audio_token": 4.0e-5,
|
||||
"input_cost_per_audio_token": 4e-05,
|
||||
"output_cost_per_token": 1e-05,
|
||||
"output_cost_per_audio_token": 8.0e-5,
|
||||
"output_cost_per_audio_token": 8e-05,
|
||||
"litellm_provider": "openai",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -594,7 +594,7 @@
|
|||
"max_output_tokens": 100000,
|
||||
"input_cost_per_token": 1.5e-06,
|
||||
"output_cost_per_token": 6e-06,
|
||||
"cache_read_input_token_cost": 0.375e-06,
|
||||
"cache_read_input_token_cost": 3.75e-07,
|
||||
"litellm_provider": "openai",
|
||||
"mode": "responses",
|
||||
"supports_pdf_input": true,
|
||||
|
|
@ -744,10 +744,10 @@
|
|||
"max_tokens": 100000,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 100000,
|
||||
"input_cost_per_token": 20e-06,
|
||||
"input_cost_per_token_batches": 10e-06,
|
||||
"output_cost_per_token_batches": 40e-06,
|
||||
"output_cost_per_token": 80e-06,
|
||||
"input_cost_per_token": 2e-05,
|
||||
"input_cost_per_token_batches": 1e-05,
|
||||
"output_cost_per_token_batches": 4e-05,
|
||||
"output_cost_per_token": 8e-05,
|
||||
"litellm_provider": "openai",
|
||||
"mode": "responses",
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -774,10 +774,10 @@
|
|||
"max_tokens": 100000,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 100000,
|
||||
"input_cost_per_token": 20e-06,
|
||||
"input_cost_per_token_batches": 10e-06,
|
||||
"output_cost_per_token_batches": 40e-06,
|
||||
"output_cost_per_token": 80e-06,
|
||||
"input_cost_per_token": 2e-05,
|
||||
"input_cost_per_token_batches": 1e-05,
|
||||
"output_cost_per_token_batches": 4e-05,
|
||||
"output_cost_per_token": 8e-05,
|
||||
"litellm_provider": "openai",
|
||||
"mode": "responses",
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -806,7 +806,7 @@
|
|||
"max_output_tokens": 100000,
|
||||
"input_cost_per_token": 2e-06,
|
||||
"output_cost_per_token": 8e-06,
|
||||
"cache_read_input_token_cost": 0.5e-06,
|
||||
"cache_read_input_token_cost": 5e-07,
|
||||
"litellm_provider": "openai",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -837,7 +837,7 @@
|
|||
"max_output_tokens": 100000,
|
||||
"input_cost_per_token": 2e-06,
|
||||
"output_cost_per_token": 8e-06,
|
||||
"cache_read_input_token_cost": 0.5e-06,
|
||||
"cache_read_input_token_cost": 5e-07,
|
||||
"litellm_provider": "openai",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -2685,7 +2685,7 @@
|
|||
"max_output_tokens": 100000,
|
||||
"input_cost_per_token": 1.5e-06,
|
||||
"output_cost_per_token": 6e-06,
|
||||
"cache_read_input_token_cost": 0.375e-06,
|
||||
"cache_read_input_token_cost": 3.75e-07,
|
||||
"litellm_provider": "azure",
|
||||
"mode": "responses",
|
||||
"supports_pdf_input": true,
|
||||
|
|
@ -4295,8 +4295,8 @@
|
|||
"max_tokens": 40000,
|
||||
"max_input_tokens": 40000,
|
||||
"max_output_tokens": 40000,
|
||||
"input_cost_per_token": 0.5e-6,
|
||||
"output_cost_per_token": 1.5e-6,
|
||||
"input_cost_per_token": 5e-07,
|
||||
"output_cost_per_token": 1.5e-06,
|
||||
"litellm_provider": "mistral",
|
||||
"mode": "chat",
|
||||
"source": "https://mistral.ai/pricing#api-pricing",
|
||||
|
|
@ -4309,7 +4309,7 @@
|
|||
"max_tokens": 40000,
|
||||
"max_input_tokens": 40000,
|
||||
"max_output_tokens": 40000,
|
||||
"input_cost_per_token": 0.5e-06,
|
||||
"input_cost_per_token": 5e-07,
|
||||
"output_cost_per_token": 1.5e-06,
|
||||
"litellm_provider": "mistral",
|
||||
"mode": "chat",
|
||||
|
|
@ -4579,9 +4579,9 @@
|
|||
"output_cost_per_token": 4e-06,
|
||||
"litellm_provider": "xai",
|
||||
"mode": "chat",
|
||||
"supports_reasoning": true,
|
||||
"supports_function_calling": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": false,
|
||||
"source": "https://x.ai/api#pricing",
|
||||
"supports_web_search": true
|
||||
|
|
@ -4616,21 +4616,6 @@
|
|||
"source": "https://x.ai/api#pricing",
|
||||
"supports_web_search": true
|
||||
},
|
||||
"xai/grok-3-mini-fast-latest": {
|
||||
"max_tokens": 131072,
|
||||
"max_input_tokens": 131072,
|
||||
"max_output_tokens": 131072,
|
||||
"input_cost_per_token": 6e-07,
|
||||
"output_cost_per_token": 4e-06,
|
||||
"litellm_provider": "xai",
|
||||
"mode": "chat",
|
||||
"supports_reasoning": true,
|
||||
"supports_function_calling": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_response_schema": false,
|
||||
"source": "https://x.ai/api#pricing",
|
||||
"supports_web_search": true
|
||||
},
|
||||
"xai/grok-vision-beta": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
|
|
@ -5812,9 +5797,9 @@
|
|||
"max_output_tokens": 4028,
|
||||
"litellm_provider": "meta_llama",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": false,
|
||||
"supports_function_calling": true,
|
||||
"source": "https://llama.developer.meta.com/docs/models",
|
||||
"supports_tool_choice": false,
|
||||
"supports_tool_choice": true,
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image"
|
||||
|
|
@ -5829,9 +5814,9 @@
|
|||
"max_output_tokens": 4028,
|
||||
"litellm_provider": "meta_llama",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": false,
|
||||
"supports_function_calling": true,
|
||||
"source": "https://llama.developer.meta.com/docs/models",
|
||||
"supports_tool_choice": false,
|
||||
"supports_tool_choice": true,
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image"
|
||||
|
|
@ -5846,9 +5831,9 @@
|
|||
"max_output_tokens": 4028,
|
||||
"litellm_provider": "meta_llama",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": false,
|
||||
"supports_function_calling": true,
|
||||
"source": "https://llama.developer.meta.com/docs/models",
|
||||
"supports_tool_choice": false,
|
||||
"supports_tool_choice": true,
|
||||
"supported_modalities": [
|
||||
"text"
|
||||
],
|
||||
|
|
@ -5862,9 +5847,9 @@
|
|||
"max_output_tokens": 4028,
|
||||
"litellm_provider": "meta_llama",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": false,
|
||||
"supports_function_calling": true,
|
||||
"source": "https://llama.developer.meta.com/docs/models",
|
||||
"supports_tool_choice": false,
|
||||
"supports_tool_choice": true,
|
||||
"supported_modalities": [
|
||||
"text"
|
||||
],
|
||||
|
|
@ -6735,8 +6720,8 @@
|
|||
"output_cost_per_token_above_200k_tokens": 1.5e-05,
|
||||
"litellm_provider": "gemini",
|
||||
"mode": "chat",
|
||||
"rpm": 2e-3,
|
||||
"tpm": 8e-6,
|
||||
"rpm": 2000,
|
||||
"tpm": 800000,
|
||||
"supports_system_messages": true,
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
|
|
@ -6802,8 +6787,9 @@
|
|||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true,
|
||||
"tpm": 8e-6,
|
||||
"rpm": 1e-5
|
||||
"tpm": 8000000,
|
||||
"rpm": 100000,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.5-flash": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -6845,7 +6831,8 @@
|
|||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini/gemini-2.5-flash-preview-tts": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -6896,9 +6883,9 @@
|
|||
"max_audio_per_prompt": 1,
|
||||
"max_pdf_size_mb": 30,
|
||||
"input_cost_per_audio_token": 1e-06,
|
||||
"input_cost_per_token": 1.5e-07,
|
||||
"output_cost_per_token": 6e-07,
|
||||
"output_cost_per_reasoning_token": 3.5e-06,
|
||||
"input_cost_per_token": 3e-07,
|
||||
"output_cost_per_token": 2.5e-06,
|
||||
"output_cost_per_reasoning_token": 2.5e-06,
|
||||
"litellm_provider": "gemini",
|
||||
"mode": "chat",
|
||||
"rpm": 10,
|
||||
|
|
@ -6925,7 +6912,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini/gemini-2.5-flash-preview-04-17": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -6966,7 +6954,53 @@
|
|||
"text"
|
||||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_web_search": true
|
||||
"supports_web_search": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini/gemini-2.5-flash-lite-preview-06-17": {
|
||||
"max_tokens": 65535,
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 65535,
|
||||
"max_images_per_prompt": 3000,
|
||||
"max_videos_per_prompt": 10,
|
||||
"max_video_length": 1,
|
||||
"max_audio_length_hours": 8.4,
|
||||
"max_audio_per_prompt": 1,
|
||||
"max_pdf_size_mb": 30,
|
||||
"input_cost_per_audio_token": 5e-07,
|
||||
"input_cost_per_token": 1e-07,
|
||||
"output_cost_per_token": 4e-07,
|
||||
"output_cost_per_reasoning_token": 4e-07,
|
||||
"litellm_provider": "gemini",
|
||||
"mode": "chat",
|
||||
"rpm": 15,
|
||||
"tpm": 250000,
|
||||
"supports_reasoning": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_audio_output": false,
|
||||
"supports_tool_choice": true,
|
||||
"supported_endpoints": [
|
||||
"/v1/chat/completions",
|
||||
"/v1/completions",
|
||||
"/v1/batch"
|
||||
],
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image",
|
||||
"audio",
|
||||
"video"
|
||||
],
|
||||
"supported_output_modalities": [
|
||||
"text"
|
||||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-lite",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.5-flash-preview-05-20": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -6979,9 +7013,9 @@
|
|||
"max_audio_per_prompt": 1,
|
||||
"max_pdf_size_mb": 30,
|
||||
"input_cost_per_audio_token": 1e-06,
|
||||
"input_cost_per_token": 1.5e-07,
|
||||
"output_cost_per_token": 6e-07,
|
||||
"output_cost_per_reasoning_token": 3.5e-06,
|
||||
"input_cost_per_token": 3e-07,
|
||||
"output_cost_per_token": 2.5e-06,
|
||||
"output_cost_per_reasoning_token": 2.5e-06,
|
||||
"litellm_provider": "vertex_ai-language-models",
|
||||
"mode": "chat",
|
||||
"supports_reasoning": true,
|
||||
|
|
@ -7008,7 +7042,8 @@
|
|||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.5-flash-preview-04-17": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -7049,7 +7084,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true
|
||||
"supports_web_search": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.5-flash-lite-preview-06-17": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -7061,7 +7097,7 @@
|
|||
"max_audio_length_hours": 8.4,
|
||||
"max_audio_per_prompt": 1,
|
||||
"max_pdf_size_mb": 30,
|
||||
"input_cost_per_audio_token": 1e-06,
|
||||
"input_cost_per_audio_token": 5e-07,
|
||||
"input_cost_per_token": 1e-07,
|
||||
"output_cost_per_token": 4e-07,
|
||||
"output_cost_per_reasoning_token": 4e-07,
|
||||
|
|
@ -7091,7 +7127,8 @@
|
|||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.0-flash": {
|
||||
"max_tokens": 8192,
|
||||
|
|
@ -7237,7 +7274,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true
|
||||
"supports_web_search": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.5-pro-preview-05-06": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -7282,7 +7320,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true
|
||||
"supports_web_search": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.5-pro-preview-03-25": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -7324,7 +7363,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview",
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_web_search": true
|
||||
"supports_web_search": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini-2.0-flash-preview-image-generation": {
|
||||
"max_tokens": 8192,
|
||||
|
|
@ -7649,7 +7689,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/pricing#gemini-2.5-pro-preview",
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini/gemini-2.5-pro-preview-05-06": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -7687,7 +7728,8 @@
|
|||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/pricing#gemini-2.5-pro-preview",
|
||||
"supports_web_search": true,
|
||||
"supports_url_context": true
|
||||
"supports_url_context": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini/gemini-2.5-pro-preview-03-25": {
|
||||
"max_tokens": 65535,
|
||||
|
|
@ -7724,7 +7766,8 @@
|
|||
"text"
|
||||
],
|
||||
"source": "https://ai.google.dev/gemini-api/docs/pricing#gemini-2.5-pro-preview",
|
||||
"supports_web_search": true
|
||||
"supports_web_search": true,
|
||||
"supports_pdf_input": true
|
||||
},
|
||||
"gemini/gemini-2.0-flash-exp": {
|
||||
"max_tokens": 8192,
|
||||
|
|
@ -8562,13 +8605,13 @@
|
|||
"litellm_provider": "vertex_ai-image-models",
|
||||
"mode": "image_generation",
|
||||
"source": "https://cloud.google.com/vertex-ai/generative-ai/pricing"
|
||||
},
|
||||
},
|
||||
"vertex_ai/imagen-4.0-fast-generate-preview-06-06": {
|
||||
"output_cost_per_image": 0.02,
|
||||
"litellm_provider": "vertex_ai-image-models",
|
||||
"mode": "image_generation",
|
||||
"source": "https://cloud.google.com/vertex-ai/generative-ai/pricing"
|
||||
},
|
||||
},
|
||||
"vertex_ai/imagen-3.0-generate-002": {
|
||||
"output_cost_per_image": 0.04,
|
||||
"litellm_provider": "vertex_ai-image-models",
|
||||
|
|
@ -10830,6 +10873,46 @@
|
|||
"supports_response_schema": true,
|
||||
"source": "https://aws.amazon.com/bedrock/pricing/"
|
||||
},
|
||||
"apac.amazon.nova-micro-v1:0": {
|
||||
"max_tokens": 10000,
|
||||
"max_input_tokens": 300000,
|
||||
"max_output_tokens": 10000,
|
||||
"input_cost_per_token": 3.7e-08,
|
||||
"output_cost_per_token": 1.48e-07,
|
||||
"litellm_provider": "bedrock_converse",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true
|
||||
},
|
||||
"apac.amazon.nova-lite-v1:0": {
|
||||
"max_tokens": 10000,
|
||||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 10000,
|
||||
"input_cost_per_token": 6.3e-08,
|
||||
"output_cost_per_token": 2.52e-07,
|
||||
"litellm_provider": "bedrock_converse",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true
|
||||
},
|
||||
"apac.amazon.nova-pro-v1:0": {
|
||||
"max_tokens": 10000,
|
||||
"max_input_tokens": 300000,
|
||||
"max_output_tokens": 10000,
|
||||
"input_cost_per_token": 8.4e-07,
|
||||
"output_cost_per_token": 3.36e-06,
|
||||
"litellm_provider": "bedrock_converse",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true
|
||||
},
|
||||
"us.amazon.nova-premier-v1:0": {
|
||||
"max_tokens": 10000,
|
||||
"max_input_tokens": 1000000,
|
||||
|
|
@ -11315,6 +11398,93 @@
|
|||
"supports_reasoning": true,
|
||||
"supports_computer_use": true
|
||||
},
|
||||
"apac.anthropic.claude-3-haiku-20240307-v1:0": {
|
||||
"max_tokens": 4096,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 2.5e-07,
|
||||
"output_cost_per_token": 1.25e-06,
|
||||
"litellm_provider": "bedrock",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_vision": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"apac.anthropic.claude-3-sonnet-20240229-v1:0": {
|
||||
"max_tokens": 4096,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 3e-06,
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"litellm_provider": "bedrock",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_vision": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"apac.anthropic.claude-3-5-sonnet-20240620-v1:0": {
|
||||
"max_tokens": 4096,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 3e-06,
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"litellm_provider": "bedrock",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_vision": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"apac.anthropic.claude-3-5-sonnet-20241022-v2:0": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 3e-06,
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"cache_creation_input_token_cost": 3.75e-06,
|
||||
"cache_read_input_token_cost": 3e-07,
|
||||
"litellm_provider": "bedrock",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_computer_use": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"apac.anthropic.claude-sonnet-4-20250514-v1:0": {
|
||||
"max_tokens": 64000,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 64000,
|
||||
"input_cost_per_token": 3e-06,
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_low": 0.01,
|
||||
"search_context_size_medium": 0.01,
|
||||
"search_context_size_high": 0.01
|
||||
},
|
||||
"cache_creation_input_token_cost": 3.75e-06,
|
||||
"cache_read_input_token_cost": 3e-07,
|
||||
"litellm_provider": "bedrock_converse",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
"tool_use_system_prompt_tokens": 159,
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_computer_use": true
|
||||
},
|
||||
"eu.anthropic.claude-3-5-haiku-20241022-v1:0": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 200000,
|
||||
|
|
@ -14770,7 +14940,7 @@
|
|||
},
|
||||
"deepgram/nova-3": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14784,7 +14954,7 @@
|
|||
},
|
||||
"deepgram/nova-3-general": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14798,7 +14968,7 @@
|
|||
},
|
||||
"deepgram/nova-3-medical": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00008667,
|
||||
"input_cost_per_second": 8.667e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14812,7 +14982,7 @@
|
|||
},
|
||||
"deepgram/nova-2": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14826,7 +14996,7 @@
|
|||
},
|
||||
"deepgram/nova-2-general": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14840,7 +15010,7 @@
|
|||
},
|
||||
"deepgram/nova-2-meeting": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14854,7 +15024,7 @@
|
|||
},
|
||||
"deepgram/nova-2-phonecall": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14868,7 +15038,7 @@
|
|||
},
|
||||
"deepgram/nova-2-voicemail": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14882,7 +15052,7 @@
|
|||
},
|
||||
"deepgram/nova-2-finance": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14896,7 +15066,7 @@
|
|||
},
|
||||
"deepgram/nova-2-conversationalai": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14910,7 +15080,7 @@
|
|||
},
|
||||
"deepgram/nova-2-video": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14924,7 +15094,7 @@
|
|||
},
|
||||
"deepgram/nova-2-drivethru": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14938,7 +15108,7 @@
|
|||
},
|
||||
"deepgram/nova-2-automotive": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14952,7 +15122,7 @@
|
|||
},
|
||||
"deepgram/nova-2-atc": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14966,7 +15136,7 @@
|
|||
},
|
||||
"deepgram/nova": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14980,7 +15150,7 @@
|
|||
},
|
||||
"deepgram/nova-general": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -14994,7 +15164,7 @@
|
|||
},
|
||||
"deepgram/nova-phonecall": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00007167,
|
||||
"input_cost_per_second": 7.167e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "deepgram",
|
||||
"supported_endpoints": [
|
||||
|
|
@ -15266,4 +15436,4 @@
|
|||
"notes": "Deepgram's hosted OpenAI Whisper models - pricing may differ from native Deepgram models"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "litellm"
|
||||
version = "1.72.7"
|
||||
version = "1.72.9"
|
||||
description = "Library to easily interface with LLM API providers"
|
||||
authors = ["BerriAI"]
|
||||
license = "MIT"
|
||||
|
|
@ -141,7 +141,7 @@ requires = ["poetry-core", "wheel"]
|
|||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.commitizen]
|
||||
version = "1.72.7"
|
||||
version = "1.72.9"
|
||||
version_files = [
|
||||
"pyproject.toml:^version"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# LITELLM PROXY DEPENDENCIES #
|
||||
anyio==4.5.0 # openai + http req.
|
||||
httpx==0.27.0 # Pin Httpx dependency
|
||||
openai==1.81.0 # openai req.
|
||||
openai==1.81.0 # openai req.
|
||||
fastapi==0.115.5 # server dep
|
||||
backoff==2.2.1 # server dep
|
||||
pyyaml==6.0.2 # server dep
|
||||
|
|
@ -14,7 +14,7 @@ prisma==0.11.0 # for db
|
|||
mangum==0.17.0 # for aws lambda functions
|
||||
pynacl==1.5.0 # for encrypting keys
|
||||
google-cloud-aiplatform==1.47.0 # for vertex ai calls
|
||||
anthropic[vertex]==0.21.3
|
||||
anthropic[vertex]==0.54.0
|
||||
mcp==1.9.3 # for MCP server
|
||||
google-generativeai==0.5.0 # for vertex ai calls
|
||||
async_generator==1.10.0 # for async ollama calls
|
||||
|
|
@ -22,7 +22,7 @@ langfuse==2.45.0 # for langfuse self-hosted logging
|
|||
prometheus_client==0.20.0 # for /metrics endpoint on proxy
|
||||
ddtrace==2.19.0 # for advanced DD tracing / profiling
|
||||
orjson==3.10.12 # fast /embedding responses
|
||||
apscheduler==3.10.4 # for resetting budget in background
|
||||
apscheduler==3.10.4 # for resetting budget in background
|
||||
fastapi-sso==0.16.0 # admin UI, SSO
|
||||
pyjwt[crypto]==2.9.0
|
||||
python-multipart==0.0.18 # admin UI
|
||||
|
|
@ -39,12 +39,12 @@ cryptography==43.0.1
|
|||
tzdata==2025.1 # IANA time zone database
|
||||
litellm-proxy-extras==0.2.5 # for proxy extras - e.g. prisma migrations
|
||||
### LITELLM PACKAGE DEPENDENCIES
|
||||
python-dotenv==1.0.0 # for env
|
||||
python-dotenv==1.0.0 # for env
|
||||
tiktoken==0.8.0 # for calculating usage
|
||||
importlib-metadata==6.8.0 # for random utils
|
||||
tokenizers==0.20.2 # for calculating usage
|
||||
click==8.1.7 # for proxy cli
|
||||
rich==13.7.1 # for litellm proxy cli
|
||||
click==8.1.7 # for proxy cli
|
||||
rich==13.7.1 # for litellm proxy cli
|
||||
jinja2==3.1.6 # for prompt templates
|
||||
aiohttp==3.10.2 # for network calls
|
||||
aioboto3==12.3.0 # for async sagemaker calls
|
||||
|
|
|
|||
|
|
@ -4,10 +4,21 @@ import json
|
|||
gemini_model_cost_map = json.load(open("model_prices_and_context_window.json"))
|
||||
|
||||
for model, model_info in gemini_model_cost_map.items():
|
||||
if model_info.get("litellm_provider") == "gemini" and model_info.get("mode") == "chat" and "gemini-2" in model:
|
||||
if (
|
||||
(
|
||||
model_info.get("litellm_provider") == "gemini"
|
||||
or model_info.get("litellm_provider") == "vertex_ai-language-models"
|
||||
)
|
||||
and model_info.get("mode") == "chat"
|
||||
and ("gemini-2.5" in model and "tts" not in model)
|
||||
and model_info.get("supports_pdf_input") is None
|
||||
):
|
||||
"""
|
||||
Update all gemini chat models to support web search
|
||||
Update all gemini chat models to support pdf input
|
||||
"""
|
||||
model_info["supports_web_search"] = True
|
||||
model_info["supports_pdf_input"] = True
|
||||
print(f"Updated {model} to support pdf input")
|
||||
|
||||
json.dump(gemini_model_cost_map, open("model_prices_and_context_window.json", "w"), indent=4)
|
||||
json.dump(
|
||||
gemini_model_cost_map, open("model_prices_and_context_window.json", "w"), indent=4
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
import os
|
||||
import sys
|
||||
import pytest
|
||||
from unittest.mock import patch, AsyncMock
|
||||
sys.path.insert(0, os.path.abspath("../.."))
|
||||
import litellm
|
||||
import json
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_basic_google_ai_studio_responses_api_with_tools():
|
||||
litellm._turn_on_debug()
|
||||
litellm.set_verbose = True
|
||||
request_model = "gemini/gemini-2.5-flash"
|
||||
response = await litellm.aresponses(
|
||||
model=request_model,
|
||||
input="what is the latest version of supabase python package and when was it released?",
|
||||
tools=[
|
||||
{
|
||||
"type": "web_search_preview",
|
||||
"search_context_size": "low"
|
||||
}
|
||||
]
|
||||
)
|
||||
print("litellm response=", json.dumps(response, indent=4, default=str))
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mock_basic_google_ai_studio_responses_api_with_tools():
|
||||
"""
|
||||
- Ensure that this is the request that litellm.completion gets when we pass web search options
|
||||
|
||||
litellm.acompletion(messages=[{'role': 'user', 'content': 'what is the latest version of supabase python package and when was it released?'}], model='gemini-2.5-flash', tools=[], web_search_options={'search_context_size': 'low', 'user_location': None})
|
||||
"""
|
||||
# Mock the acompletion function
|
||||
litellm._turn_on_debug()
|
||||
mock_response = litellm.ModelResponse(
|
||||
id="test-id",
|
||||
created=1234567890,
|
||||
model="gemini/gemini-2.5-flash",
|
||||
object="chat.completion",
|
||||
choices=[
|
||||
litellm.utils.Choices(
|
||||
index=0,
|
||||
message=litellm.utils.Message(
|
||||
role="assistant",
|
||||
content="Test response"
|
||||
),
|
||||
finish_reason="stop"
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
with patch('litellm.acompletion', new_callable=AsyncMock) as mock_acompletion:
|
||||
mock_acompletion.return_value = mock_response
|
||||
|
||||
request_model = "gemini/gemini-2.5-flash"
|
||||
await litellm.aresponses(
|
||||
model=request_model,
|
||||
input="what is the latest version of supabase python package and when was it released?",
|
||||
tools=[
|
||||
{
|
||||
"type": "web_search_preview",
|
||||
"search_context_size": "low"
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
# Verify that acompletion was called
|
||||
assert mock_acompletion.called
|
||||
|
||||
# Get the call arguments
|
||||
call_args, call_kwargs = mock_acompletion.call_args
|
||||
|
||||
# Verify the expected parameters were passed
|
||||
print("call kwargs to litellm.completion=", json.dumps(call_kwargs, indent=4, default=str))
|
||||
assert "web_search_options" in call_kwargs
|
||||
assert call_kwargs["web_search_options"] is not None
|
||||
assert call_kwargs["web_search_options"]["search_context_size"] == "low"
|
||||
assert call_kwargs["web_search_options"]["user_location"] is None
|
||||
|
||||
# Verify other expected parameters
|
||||
assert call_kwargs["model"] == "gemini-2.5-flash"
|
||||
assert len(call_kwargs["messages"]) == 1
|
||||
assert call_kwargs["messages"][0]["role"] == "user"
|
||||
assert call_kwargs["messages"][0]["content"] == "what is the latest version of supabase python package and when was it released?"
|
||||
assert call_kwargs["tools"] == [] # web search tools are converted to web_search_options, not kept as tools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -35,4 +35,4 @@ class TestMistralCompletion(BaseLLMChatTest):
|
|||
|
||||
def test_tool_call_no_arguments(self, tool_call_no_arguments):
|
||||
"""Test that tool calls with no arguments is translated correctly. Relevant issue: https://github.com/BerriAI/litellm/issues/6833"""
|
||||
pass
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -219,7 +219,14 @@ def test_increment_token_metrics(prometheus_logger):
|
|||
)
|
||||
|
||||
prometheus_logger.litellm_tokens_metric.labels.assert_called_once_with(
|
||||
end_user=None, user=None, hashed_api_key='test_hash', api_key_alias='test_alias', team='test_team', team_alias='test_team_alias', requested_model=None, model='gpt-3.5-turbo'
|
||||
end_user=None,
|
||||
user=None,
|
||||
hashed_api_key="test_hash",
|
||||
api_key_alias="test_alias",
|
||||
team="test_team",
|
||||
team_alias="test_team_alias",
|
||||
requested_model=None,
|
||||
model="gpt-3.5-turbo",
|
||||
)
|
||||
prometheus_logger.litellm_tokens_metric.labels().inc.assert_called_once_with(100)
|
||||
|
||||
|
|
@ -836,12 +843,12 @@ def test_set_llm_deployment_success_metrics(prometheus_logger):
|
|||
|
||||
# Verify remaining requests metric
|
||||
prometheus_logger.litellm_remaining_requests_metric.labels.assert_called_once_with(
|
||||
api_base="https://api.openai.com",
|
||||
api_key_alias=standard_logging_payload["metadata"]["user_api_key_alias"],
|
||||
api_provider="openai",
|
||||
model_group="my_custom_model_group", # model_group / requested model from create_standard_logging_payload()
|
||||
api_provider="openai", # llm provider
|
||||
api_base="https://api.openai.com", # api base
|
||||
litellm_model_name="gpt-3.5-turbo", # actual model used - litellm model name
|
||||
hashed_api_key=standard_logging_payload["metadata"]["user_api_key_hash"],
|
||||
litellm_model_name="gpt-3.5-turbo",
|
||||
requested_model="my_custom_model_group",
|
||||
api_key_alias=standard_logging_payload["metadata"]["user_api_key_alias"],
|
||||
)
|
||||
|
||||
prometheus_logger.litellm_remaining_requests_metric.labels().set.assert_called_once_with(
|
||||
|
|
@ -855,7 +862,7 @@ def test_set_llm_deployment_success_metrics(prometheus_logger):
|
|||
api_provider="openai",
|
||||
hashed_api_key=standard_logging_payload["metadata"]["user_api_key_hash"],
|
||||
litellm_model_name="gpt-3.5-turbo",
|
||||
requested_model="my_custom_model_group",
|
||||
model_group="my_custom_model_group",
|
||||
)
|
||||
|
||||
prometheus_logger.litellm_remaining_tokens_metric.labels().set.assert_called_once_with(
|
||||
|
|
@ -915,7 +922,7 @@ def test_set_llm_deployment_success_metrics(prometheus_logger):
|
|||
api_provider="openai",
|
||||
hashed_api_key=standard_logging_payload["metadata"]["user_api_key_hash"],
|
||||
litellm_model_name="gpt-3.5-turbo",
|
||||
requested_model="my_custom_model_group",
|
||||
model_group="my_custom_model_group",
|
||||
)
|
||||
|
||||
# Calculate expected latency per token (1 second / 10 tokens = 0.1 seconds per token)
|
||||
|
|
@ -1499,60 +1506,66 @@ def test_get_exception_class_name(prometheus_logger):
|
|||
"""
|
||||
# Test case 1: Exception with llm_provider
|
||||
rate_limit_error = litellm.RateLimitError(
|
||||
message="Rate limit exceeded",
|
||||
llm_provider="openai",
|
||||
model="gpt-3.5-turbo"
|
||||
message="Rate limit exceeded", llm_provider="openai", model="gpt-3.5-turbo"
|
||||
)
|
||||
assert (
|
||||
prometheus_logger._get_exception_class_name(rate_limit_error)
|
||||
== "Openai.RateLimitError"
|
||||
)
|
||||
assert prometheus_logger._get_exception_class_name(rate_limit_error) == "Openai.RateLimitError"
|
||||
|
||||
# Test case 2: Exception with empty llm_provider
|
||||
auth_error = litellm.AuthenticationError(
|
||||
message="Invalid API key",
|
||||
llm_provider="",
|
||||
model="gpt-4"
|
||||
message="Invalid API key", llm_provider="", model="gpt-4"
|
||||
)
|
||||
assert (
|
||||
prometheus_logger._get_exception_class_name(auth_error) == "AuthenticationError"
|
||||
)
|
||||
assert prometheus_logger._get_exception_class_name(auth_error) == "AuthenticationError"
|
||||
|
||||
# Test case 3: Exception with None llm_provider
|
||||
context_window_error = litellm.ContextWindowExceededError(
|
||||
message="Context length exceeded",
|
||||
llm_provider=None,
|
||||
model="gpt-4"
|
||||
message="Context length exceeded", llm_provider=None, model="gpt-4"
|
||||
)
|
||||
assert (
|
||||
prometheus_logger._get_exception_class_name(context_window_error)
|
||||
== "ContextWindowExceededError"
|
||||
)
|
||||
assert prometheus_logger._get_exception_class_name(context_window_error) == "ContextWindowExceededError"
|
||||
|
||||
|
||||
def test_set_llm_deployment_success_metrics_with_label_filtering():
|
||||
"""
|
||||
Test that set_llm_deployment_success_metrics correctly uses prometheus_label_factory
|
||||
Test that set_llm_deployment_success_metrics correctly uses prometheus_label_factory
|
||||
and respects label filtering configuration to prevent "Incorrect label names" errors.
|
||||
"""
|
||||
from litellm.types.integrations.prometheus import PrometheusMetricsConfig
|
||||
|
||||
|
||||
# Create a prometheus logger with label filtering configuration
|
||||
config = [
|
||||
PrometheusMetricsConfig(
|
||||
group="test_group",
|
||||
metrics=[
|
||||
"litellm_overhead_latency_metric",
|
||||
"litellm_remaining_requests_metric",
|
||||
"litellm_remaining_requests_metric",
|
||||
"litellm_remaining_tokens_metric",
|
||||
"litellm_deployment_success_responses",
|
||||
"litellm_deployment_total_requests"
|
||||
"litellm_deployment_total_requests",
|
||||
],
|
||||
include_labels=["requested_model", "api_provider", "hashed_api_key"] # Limited labels
|
||||
include_labels=[
|
||||
"requested_model",
|
||||
"api_provider",
|
||||
"hashed_api_key",
|
||||
], # Limited labels
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
# Mock litellm.prometheus_metrics_config
|
||||
with patch('litellm.prometheus_metrics_config', config):
|
||||
with patch("litellm.prometheus_metrics_config", config):
|
||||
# Clear registry before creating new logger
|
||||
collectors = list(REGISTRY._collector_to_names.keys())
|
||||
for collector in collectors:
|
||||
REGISTRY.unregister(collector)
|
||||
|
||||
|
||||
prometheus_logger = PrometheusLogger()
|
||||
|
||||
|
||||
# Mock all the metrics used in the method
|
||||
prometheus_logger.litellm_overhead_latency_metric = MagicMock()
|
||||
prometheus_logger.litellm_remaining_requests_metric = MagicMock()
|
||||
|
|
@ -1607,46 +1620,59 @@ def test_set_llm_deployment_success_metrics_with_label_filtering():
|
|||
|
||||
# Verify that metrics were called with filtered labels (only the configured ones)
|
||||
# The exact labels depend on what get_labels_for_metric returns for each metric
|
||||
|
||||
|
||||
# Verify overhead latency metric was called with filtered labels
|
||||
prometheus_logger.litellm_overhead_latency_metric.labels.assert_called_once()
|
||||
overhead_labels = prometheus_logger.litellm_overhead_latency_metric.labels.call_args[1]
|
||||
|
||||
overhead_labels = (
|
||||
prometheus_logger.litellm_overhead_latency_metric.labels.call_args[1]
|
||||
)
|
||||
|
||||
# Should only contain the filtered labels that are supported for this metric
|
||||
expected_filtered_labels = {"requested_model", "api_provider", "hashed_api_key"}
|
||||
actual_labels = set(k for k in overhead_labels.keys() if k is not None)
|
||||
|
||||
|
||||
# Verify that only expected labels are present (subset of configured labels)
|
||||
assert actual_labels <= expected_filtered_labels
|
||||
|
||||
# Verify remaining requests metric was called with filtered labels
|
||||
|
||||
# Verify remaining requests metric was called with filtered labels
|
||||
prometheus_logger.litellm_remaining_requests_metric.labels.assert_called_once()
|
||||
requests_labels = prometheus_logger.litellm_remaining_requests_metric.labels.call_args[1]
|
||||
requests_labels = (
|
||||
prometheus_logger.litellm_remaining_requests_metric.labels.call_args[1]
|
||||
)
|
||||
actual_labels = set(k for k in requests_labels.keys() if k is not None)
|
||||
assert actual_labels <= expected_filtered_labels
|
||||
|
||||
|
||||
# Verify remaining tokens metric was called with filtered labels
|
||||
prometheus_logger.litellm_remaining_tokens_metric.labels.assert_called_once()
|
||||
tokens_labels = prometheus_logger.litellm_remaining_tokens_metric.labels.call_args[1]
|
||||
tokens_labels = (
|
||||
prometheus_logger.litellm_remaining_tokens_metric.labels.call_args[1]
|
||||
)
|
||||
actual_labels = set(k for k in tokens_labels.keys() if k is not None)
|
||||
assert actual_labels <= expected_filtered_labels
|
||||
|
||||
|
||||
# Verify deployment success responses metric was called with filtered labels
|
||||
prometheus_logger.litellm_deployment_success_responses.labels.assert_called_once()
|
||||
success_labels = prometheus_logger.litellm_deployment_success_responses.labels.call_args[1]
|
||||
success_labels = (
|
||||
prometheus_logger.litellm_deployment_success_responses.labels.call_args[1]
|
||||
)
|
||||
actual_labels = set(k for k in success_labels.keys() if k is not None)
|
||||
assert actual_labels <= expected_filtered_labels
|
||||
|
||||
|
||||
# Verify deployment total requests metric was called with filtered labels
|
||||
prometheus_logger.litellm_deployment_total_requests.labels.assert_called_once()
|
||||
total_labels = prometheus_logger.litellm_deployment_total_requests.labels.call_args[1]
|
||||
total_labels = (
|
||||
prometheus_logger.litellm_deployment_total_requests.labels.call_args[1]
|
||||
)
|
||||
actual_labels = set(total_labels.keys())
|
||||
assert actual_labels.issubset(expected_filtered_labels.union({None}))
|
||||
|
||||
|
||||
# Verify all metrics were actually called (no exceptions were raised)
|
||||
prometheus_logger.litellm_overhead_latency_metric.labels().observe.assert_called_once()
|
||||
prometheus_logger.litellm_remaining_requests_metric.labels().set.assert_called_once_with(123)
|
||||
prometheus_logger.litellm_remaining_tokens_metric.labels().set.assert_called_once_with(4321)
|
||||
prometheus_logger.litellm_remaining_requests_metric.labels().set.assert_called_once_with(
|
||||
123
|
||||
)
|
||||
prometheus_logger.litellm_remaining_tokens_metric.labels().set.assert_called_once_with(
|
||||
4321
|
||||
)
|
||||
prometheus_logger.litellm_deployment_success_responses.labels().inc.assert_called_once()
|
||||
prometheus_logger.litellm_deployment_total_requests.labels().inc.assert_called_once()
|
||||
|
||||
|
|
|
|||
169
tests/mcp_tests/test_mcp_client_unit.py
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
"""
|
||||
Unit tests for the MCPClient class - critical functionality only.
|
||||
"""
|
||||
import base64
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
# Add the project root to the path
|
||||
sys.path.insert(0, os.path.abspath("../../.."))
|
||||
|
||||
from litellm.experimental_mcp_client.client import MCPClient
|
||||
from litellm.types.mcp import MCPAuth, MCPTransport
|
||||
from mcp.types import Tool as MCPTool, CallToolResult as MCPCallToolResult
|
||||
|
||||
|
||||
class TestMCPClientUnitTests:
|
||||
"""Unit tests for MCPClient functionality."""
|
||||
|
||||
def test_init_with_auth(self):
|
||||
"""Test initialization with authentication."""
|
||||
client = MCPClient(
|
||||
server_url="http://example.com",
|
||||
transport_type=MCPTransport.sse,
|
||||
auth_type=MCPAuth.bearer_token,
|
||||
auth_value="test_token",
|
||||
timeout=30.0
|
||||
)
|
||||
assert client.server_url == "http://example.com"
|
||||
assert client.transport_type == MCPTransport.sse
|
||||
assert client.auth_type == MCPAuth.bearer_token
|
||||
assert client.timeout == 30.0
|
||||
assert client._mcp_auth_value == "test_token"
|
||||
|
||||
def test_get_auth_headers(self):
|
||||
"""Test authentication header generation for different auth types."""
|
||||
# Bearer token
|
||||
client = MCPClient(
|
||||
"http://example.com",
|
||||
auth_type=MCPAuth.bearer_token,
|
||||
auth_value="test_token"
|
||||
)
|
||||
headers = client._get_auth_headers()
|
||||
assert headers == {"Authorization": "Bearer test_token"}
|
||||
|
||||
# Basic auth
|
||||
client = MCPClient(
|
||||
"http://example.com",
|
||||
auth_type=MCPAuth.basic,
|
||||
auth_value="user:pass"
|
||||
)
|
||||
expected_encoded = base64.b64encode("user:pass".encode("utf-8")).decode()
|
||||
headers = client._get_auth_headers()
|
||||
assert headers == {"Authorization": f"Basic {expected_encoded}"}
|
||||
|
||||
# API key
|
||||
client = MCPClient(
|
||||
"http://example.com",
|
||||
auth_type=MCPAuth.api_key,
|
||||
auth_value="api_key_123"
|
||||
)
|
||||
headers = client._get_auth_headers()
|
||||
assert headers == {"X-API-Key": "api_key_123"}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('litellm.experimental_mcp_client.client.streamablehttp_client')
|
||||
@patch('litellm.experimental_mcp_client.client.ClientSession')
|
||||
async def test_connect(self, mock_session_class, mock_transport):
|
||||
"""Test connecting to MCP server with authentication."""
|
||||
# Setup mocks
|
||||
mock_transport_ctx = AsyncMock()
|
||||
mock_transport.return_value = mock_transport_ctx
|
||||
mock_transport_instance = MagicMock()
|
||||
mock_transport_ctx.__aenter__ = AsyncMock(return_value=mock_transport_instance)
|
||||
|
||||
mock_session_ctx = AsyncMock()
|
||||
mock_session_class.return_value = mock_session_ctx
|
||||
mock_session_instance = AsyncMock()
|
||||
mock_session_ctx.__aenter__ = AsyncMock(return_value=mock_session_instance)
|
||||
|
||||
client = MCPClient(
|
||||
"http://example.com",
|
||||
auth_type=MCPAuth.bearer_token,
|
||||
auth_value="test_token"
|
||||
)
|
||||
await client.connect()
|
||||
|
||||
# Verify transport was created with auth headers
|
||||
call_args = mock_transport.call_args
|
||||
assert call_args[1]['headers'] == {"Authorization": "Bearer test_token"}
|
||||
|
||||
# Verify session was initialized
|
||||
mock_session_instance.initialize.assert_called_once()
|
||||
assert client._session == mock_session_instance
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('litellm.experimental_mcp_client.client.streamablehttp_client')
|
||||
@patch('litellm.experimental_mcp_client.client.ClientSession')
|
||||
async def test_list_tools(self, mock_session_class, mock_transport):
|
||||
"""Test listing tools from the server."""
|
||||
# Setup mocks
|
||||
mock_transport_ctx = AsyncMock()
|
||||
mock_transport.return_value = mock_transport_ctx
|
||||
mock_transport_instance = MagicMock()
|
||||
mock_transport_ctx.__aenter__ = AsyncMock(return_value=mock_transport_instance)
|
||||
|
||||
mock_session_ctx = AsyncMock()
|
||||
mock_session_class.return_value = mock_session_ctx
|
||||
mock_session_instance = AsyncMock()
|
||||
mock_session_ctx.__aenter__ = AsyncMock(return_value=mock_session_instance)
|
||||
|
||||
mock_tools = [
|
||||
MCPTool(
|
||||
name="test_tool",
|
||||
description="Test tool",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {"arg1": {"type": "string"}},
|
||||
"required": ["arg1"]
|
||||
}
|
||||
)
|
||||
]
|
||||
mock_result = MagicMock()
|
||||
mock_result.tools = mock_tools
|
||||
mock_session_instance.list_tools.return_value = mock_result
|
||||
|
||||
client = MCPClient("http://example.com")
|
||||
result = await client.list_tools()
|
||||
|
||||
assert result == mock_tools
|
||||
mock_session_instance.initialize.assert_called_once()
|
||||
mock_session_instance.list_tools.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('litellm.experimental_mcp_client.client.streamablehttp_client')
|
||||
@patch('litellm.experimental_mcp_client.client.ClientSession')
|
||||
async def test_call_tool(self, mock_session_class, mock_transport):
|
||||
"""Test calling a tool."""
|
||||
from mcp.types import CallToolRequestParams
|
||||
|
||||
# Setup mocks
|
||||
mock_transport_ctx = AsyncMock()
|
||||
mock_transport.return_value = mock_transport_ctx
|
||||
mock_transport_instance = MagicMock()
|
||||
mock_transport_ctx.__aenter__ = AsyncMock(return_value=mock_transport_instance)
|
||||
|
||||
mock_session_ctx = AsyncMock()
|
||||
mock_session_class.return_value = mock_session_ctx
|
||||
mock_session_instance = AsyncMock()
|
||||
mock_session_ctx.__aenter__ = AsyncMock(return_value=mock_session_instance)
|
||||
|
||||
mock_result = MCPCallToolResult(content=[])
|
||||
mock_session_instance.call_tool.return_value = mock_result
|
||||
|
||||
client = MCPClient("http://example.com")
|
||||
params = CallToolRequestParams(name="test_tool", arguments={"arg1": "value1"})
|
||||
result = await client.call_tool(params)
|
||||
|
||||
assert result == mock_result
|
||||
mock_session_instance.initialize.assert_called_once()
|
||||
mock_session_instance.call_tool.assert_called_once_with(
|
||||
name="test_tool",
|
||||
arguments={"arg1": "value1"}
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
|
|
@ -101,26 +101,17 @@ async def test_mcp_http_transport_list_tools_mock():
|
|||
)
|
||||
]
|
||||
|
||||
# Mock the session and its methods
|
||||
mock_session = AsyncMock()
|
||||
mock_session.initialize = AsyncMock()
|
||||
mock_session.list_tools = AsyncMock(return_value=ListToolsResult(tools=mock_tools))
|
||||
# Create a mock MCPClient that returns our test tools
|
||||
mock_client = AsyncMock()
|
||||
mock_client.list_tools = AsyncMock(return_value=mock_tools)
|
||||
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
|
||||
mock_client.__aexit__ = AsyncMock(return_value=None)
|
||||
|
||||
# Create an async context manager mock for streamablehttp_client
|
||||
@asynccontextmanager
|
||||
async def mock_streamablehttp_client(url):
|
||||
read_stream = AsyncMock()
|
||||
write_stream = AsyncMock()
|
||||
get_session_id = MagicMock(return_value="test-session-123")
|
||||
yield (read_stream, write_stream, get_session_id)
|
||||
# Mock the MCPClient constructor to return our mock
|
||||
def mock_client_constructor(*args, **kwargs):
|
||||
return mock_client
|
||||
|
||||
# Create an async context manager mock for ClientSession
|
||||
@asynccontextmanager
|
||||
async def mock_client_session(read_stream, write_stream):
|
||||
yield mock_session
|
||||
|
||||
with patch('litellm.proxy._experimental.mcp_server.mcp_server_manager.streamablehttp_client', mock_streamablehttp_client), \
|
||||
patch('litellm.proxy._experimental.mcp_server.mcp_server_manager.ClientSession', mock_client_session):
|
||||
with patch('litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient', mock_client_constructor):
|
||||
|
||||
# Load server config with HTTP transport
|
||||
test_manager.load_servers_from_config({
|
||||
|
|
@ -139,9 +130,9 @@ async def test_mcp_http_transport_list_tools_mock():
|
|||
assert tools[0].name == "gmail_send_email"
|
||||
assert tools[1].name == "calendar_create_event"
|
||||
|
||||
# Verify session methods were called
|
||||
mock_session.initialize.assert_called_once()
|
||||
mock_session.list_tools.assert_called_once()
|
||||
# Verify client methods were called
|
||||
mock_client.__aenter__.assert_called()
|
||||
mock_client.list_tools.assert_called_once()
|
||||
|
||||
# Verify tool mapping was updated
|
||||
assert test_manager.tool_name_to_mcp_server_name_mapping["gmail_send_email"] == "test_http_server"
|
||||
|
|
@ -166,26 +157,17 @@ async def test_mcp_http_transport_call_tool_mock():
|
|||
isError=False
|
||||
)
|
||||
|
||||
# Mock the session and its methods
|
||||
mock_session = AsyncMock()
|
||||
mock_session.initialize = AsyncMock()
|
||||
mock_session.call_tool = AsyncMock(return_value=mock_result)
|
||||
# Create a mock MCPClient that returns our test result
|
||||
mock_client = AsyncMock()
|
||||
mock_client.call_tool = AsyncMock(return_value=mock_result)
|
||||
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
|
||||
mock_client.__aexit__ = AsyncMock(return_value=None)
|
||||
|
||||
# Create an async context manager mock for streamablehttp_client
|
||||
@asynccontextmanager
|
||||
async def mock_streamablehttp_client(url):
|
||||
read_stream = AsyncMock()
|
||||
write_stream = AsyncMock()
|
||||
get_session_id = MagicMock(return_value="test-session-456")
|
||||
yield (read_stream, write_stream, get_session_id)
|
||||
# Mock the MCPClient constructor to return our mock
|
||||
def mock_client_constructor(*args, **kwargs):
|
||||
return mock_client
|
||||
|
||||
# Create an async context manager mock for ClientSession
|
||||
@asynccontextmanager
|
||||
async def mock_client_session(read_stream, write_stream):
|
||||
yield mock_session
|
||||
|
||||
with patch('litellm.proxy._experimental.mcp_server.mcp_server_manager.streamablehttp_client', mock_streamablehttp_client), \
|
||||
patch('litellm.proxy._experimental.mcp_server.mcp_server_manager.ClientSession', mock_client_session):
|
||||
with patch('litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient', mock_client_constructor):
|
||||
|
||||
# Load server config with HTTP transport
|
||||
test_manager.load_servers_from_config({
|
||||
|
|
@ -216,16 +198,9 @@ async def test_mcp_http_transport_call_tool_mock():
|
|||
assert isinstance(result.content[0], TextContent)
|
||||
assert result.content[0].text == "Email sent successfully to test@example.com"
|
||||
|
||||
# Verify session methods were called
|
||||
mock_session.initialize.assert_called_once()
|
||||
mock_session.call_tool.assert_called_once_with(
|
||||
"gmail_send_email",
|
||||
{
|
||||
"to": "test@example.com",
|
||||
"subject": "Test Subject",
|
||||
"body": "Test email body"
|
||||
}
|
||||
)
|
||||
# Verify client methods were called
|
||||
mock_client.__aenter__.assert_called()
|
||||
mock_client.call_tool.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -246,26 +221,17 @@ async def test_mcp_http_transport_call_tool_error_mock():
|
|||
isError=True
|
||||
)
|
||||
|
||||
# Mock the session and its methods
|
||||
mock_session = AsyncMock()
|
||||
mock_session.initialize = AsyncMock()
|
||||
mock_session.call_tool = AsyncMock(return_value=mock_error_result)
|
||||
# Create a mock MCPClient that returns our test error result
|
||||
mock_client = AsyncMock()
|
||||
mock_client.call_tool = AsyncMock(return_value=mock_error_result)
|
||||
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
|
||||
mock_client.__aexit__ = AsyncMock(return_value=None)
|
||||
|
||||
# Create an async context manager mock for streamablehttp_client
|
||||
@asynccontextmanager
|
||||
async def mock_streamablehttp_client(url):
|
||||
read_stream = AsyncMock()
|
||||
write_stream = AsyncMock()
|
||||
get_session_id = MagicMock(return_value="test-session-789")
|
||||
yield (read_stream, write_stream, get_session_id)
|
||||
# Mock the MCPClient constructor to return our mock
|
||||
def mock_client_constructor(*args, **kwargs):
|
||||
return mock_client
|
||||
|
||||
# Create an async context manager mock for ClientSession
|
||||
@asynccontextmanager
|
||||
async def mock_client_session(read_stream, write_stream):
|
||||
yield mock_session
|
||||
|
||||
with patch('litellm.proxy._experimental.mcp_server.mcp_server_manager.streamablehttp_client', mock_streamablehttp_client), \
|
||||
patch('litellm.proxy._experimental.mcp_server.mcp_server_manager.ClientSession', mock_client_session):
|
||||
with patch('litellm.proxy._experimental.mcp_server.mcp_server_manager.MCPClient', mock_client_constructor):
|
||||
|
||||
# Load server config with HTTP transport
|
||||
test_manager.load_servers_from_config({
|
||||
|
|
@ -292,9 +258,9 @@ async def test_mcp_http_transport_call_tool_error_mock():
|
|||
assert isinstance(result.content[0], TextContent)
|
||||
assert "Error: Invalid email address" in result.content[0].text
|
||||
|
||||
# Verify session methods were called
|
||||
mock_session.initialize.assert_called_once()
|
||||
mock_session.call_tool.assert_called_once()
|
||||
# Verify client methods were called
|
||||
mock_client.__aenter__.assert_called()
|
||||
mock_client.call_tool.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import os
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from litellm.integrations.langfuse.langfuse_otel import LangfuseOtelLogger
|
||||
from litellm.types.integrations.langfuse_otel import LangfuseOtelConfig
|
||||
|
||||
|
|
@ -308,3 +308,20 @@ def test_vertex_ai_transform_empty_function_call_arguments():
|
|||
assert result["args"] == {
|
||||
"type": "object",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bedrock_process_image_async_factory():
|
||||
"""
|
||||
Test that the _process_image_async_factory method handles image input correctly
|
||||
"""
|
||||
from litellm.litellm_core_utils.prompt_templates.factory import (
|
||||
BedrockImageProcessor,
|
||||
)
|
||||
|
||||
image_url = "data:application/pdf; qs=0.001;base64,JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4"
|
||||
|
||||
content_block = await BedrockImageProcessor.process_image_async(
|
||||
image_url=image_url, format=None
|
||||
)
|
||||
print(f"content_block: {content_block}")
|
||||
|
|
|
|||
|
|
@ -218,6 +218,7 @@ def test_map_tool_choice():
|
|||
assert result["type"] == "none"
|
||||
print(result)
|
||||
|
||||
|
||||
def test_transform_response_with_prefix_prompt():
|
||||
import httpx
|
||||
|
||||
|
|
@ -262,3 +263,8 @@ def test_transform_response_with_prefix_prompt():
|
|||
== "You are a helpful assistant. The grass is green."
|
||||
)
|
||||
|
||||
|
||||
def test_get_supported_params_thinking():
|
||||
config = AnthropicConfig()
|
||||
params = config.get_supported_openai_params(model="claude-sonnet-4-20250514")
|
||||
assert "thinking" in params
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
# Ensure the project root is on the import path so `litellm` can be imported when
|
||||
# tests are executed from any working directory.
|
||||
sys.path.insert(0, os.path.abspath("../../../../../.."))
|
||||
|
||||
from litellm.llms.bedrock.chat.invoke_transformations.anthropic_claude3_transformation import (
|
||||
AmazonAnthropicClaude3Config,
|
||||
)
|
||||
|
||||
|
||||
def test_get_supported_params_thinking():
|
||||
config = AmazonAnthropicClaude3Config()
|
||||
params = config.get_supported_openai_params(
|
||||
model="anthropic.claude-sonnet-4-20250514-v1:0"
|
||||
)
|
||||
assert "thinking" in params
|
||||
|
|
@ -5,6 +5,7 @@ import ssl
|
|||
import sys
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import certifi
|
||||
import httpx
|
||||
import pytest
|
||||
from aiohttp import ClientSession, TCPConnector
|
||||
|
|
@ -120,3 +121,34 @@ async def test_ssl_verification_with_aiohttp_transport():
|
|||
|
||||
# assert both litellm transport and aiohttp session have ssl_verify=False
|
||||
assert transport_connector._ssl == aiohttp_session.connector._ssl
|
||||
|
||||
|
||||
def test_get_ssl_context():
|
||||
"""Test that _get_ssl_context() returns a proper SSL context with certifi CA bundle"""
|
||||
with patch('ssl.create_default_context') as mock_create_context:
|
||||
# Mock the return value
|
||||
mock_ssl_context = MagicMock(spec=ssl.SSLContext)
|
||||
mock_create_context.return_value = mock_ssl_context
|
||||
|
||||
# Call the static method
|
||||
result = AsyncHTTPHandler._get_ssl_context()
|
||||
|
||||
# Verify ssl.create_default_context was called with certifi's CA file
|
||||
expected_ca_file = certifi.where()
|
||||
mock_create_context.assert_called_once_with(cafile=expected_ca_file)
|
||||
|
||||
# Verify it returns the mocked SSL context
|
||||
assert result == mock_ssl_context
|
||||
|
||||
|
||||
def test_get_ssl_context_integration():
|
||||
"""Integration test that _get_ssl_context() returns a working SSL context"""
|
||||
# Call the static method without mocking
|
||||
ssl_context = AsyncHTTPHandler._get_ssl_context()
|
||||
|
||||
# Verify it returns an SSLContext instance
|
||||
assert isinstance(ssl_context, ssl.SSLContext)
|
||||
|
||||
# Verify it has basic SSL context properties
|
||||
assert ssl_context.protocol is not None
|
||||
assert ssl_context.verify_mode is not None
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
from typing import Optional
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
import litellm
|
||||
from litellm.llms.litellm_proxy.chat.transformation import LiteLLMProxyChatConfig
|
||||
|
||||
|
||||
def test_litellm_proxy_chat_transformation():
|
||||
"""
|
||||
Assert messages are not transformed when calling litellm proxy
|
||||
"""
|
||||
config = LiteLLMProxyChatConfig()
|
||||
file_content = [
|
||||
{"type": "text", "text": "What is this document about?"},
|
||||
{
|
||||
"type": "file",
|
||||
"file": {
|
||||
"file_id": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
|
||||
"format": "application/pdf",
|
||||
},
|
||||
},
|
||||
]
|
||||
messages = [{"role": "user", "content": file_content}]
|
||||
assert config.transform_request(
|
||||
model="model",
|
||||
messages=messages,
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
headers={},
|
||||
) == {"model": "model", "messages": messages}
|
||||
|
|
@ -11,18 +11,6 @@ sys.path.insert(
|
|||
from litellm.llms.meta_llama.chat.transformation import LlamaAPIConfig
|
||||
|
||||
|
||||
def test_get_supported_openai_params():
|
||||
"""Test that LlamaAPIConfig correctly filters unsupported parameters"""
|
||||
config = LlamaAPIConfig()
|
||||
|
||||
# Test error handling
|
||||
with patch("litellm.get_model_info", side_effect=Exception("Test error")):
|
||||
params = config.get_supported_openai_params("llama-3.3-8B-instruct")
|
||||
assert "function_call" not in params
|
||||
assert "tools" not in params
|
||||
assert "tool_choice" not in params
|
||||
|
||||
|
||||
def test_map_openai_params():
|
||||
"""Test that LlamaAPIConfig correctly maps OpenAI parameters"""
|
||||
config = LlamaAPIConfig()
|
||||
|
|
|
|||
|
|
@ -349,3 +349,67 @@ class TestMistralReasoningSupport:
|
|||
assert result["messages"][1]["content"] == "Solve for x: 2x + 5 = 13"
|
||||
assert result.get("temperature") == 0.7
|
||||
assert "_add_reasoning_prompt" not in result
|
||||
|
||||
|
||||
|
||||
class TestMistralNameHandling:
|
||||
"""Test suite for Mistral name handling in messages."""
|
||||
|
||||
def test_handle_name_in_message_tool_role_empty_name_removes_name(self):
|
||||
"""Test that empty name is removed for tool messages."""
|
||||
# Test with empty string
|
||||
tool_message = {"role": "tool", "content": "Function result", "name": ""}
|
||||
result = MistralConfig._handle_name_in_message(tool_message)
|
||||
assert "name" not in result
|
||||
assert result["role"] == "tool"
|
||||
assert result["content"] == "Function result"
|
||||
|
||||
def test_handle_name_in_message_tool_role_valid_name_keeps_name(self):
|
||||
"""Test that valid name is kept for tool messages."""
|
||||
# Test with normal function name
|
||||
tool_message = {"role": "tool", "content": "Function result", "name": "get_weather"}
|
||||
result = MistralConfig._handle_name_in_message(tool_message)
|
||||
assert "name" in result
|
||||
assert result["name"] == "get_weather"
|
||||
assert result["role"] == "tool"
|
||||
assert result["content"] == "Function result"
|
||||
|
||||
def test_handle_name_in_message_no_name_field(self):
|
||||
"""Test that messages without name field are unchanged."""
|
||||
# Test with user role
|
||||
user_message = {"role": "user", "content": "Hello"}
|
||||
result = MistralConfig._handle_name_in_message(user_message)
|
||||
assert "name" not in result
|
||||
assert result["role"] == "user"
|
||||
assert result["content"] == "Hello"
|
||||
|
||||
|
||||
class TestMistralParallelToolCalls:
|
||||
"""Test suite for Mistral parallel tool calls functionality."""
|
||||
|
||||
def test_get_supported_openai_params_includes_parallel_tool_calls(self):
|
||||
"""Test that parallel_tool_calls is in supported parameters."""
|
||||
mistral_config = MistralConfig()
|
||||
supported_params = mistral_config.get_supported_openai_params("mistral/mistral-large-latest")
|
||||
assert "parallel_tool_calls" in supported_params
|
||||
|
||||
def test_transform_request_preserves_parallel_tool_calls(self):
|
||||
"""Test that transform_request preserves parallel_tool_calls parameter."""
|
||||
mistral_config = MistralConfig()
|
||||
|
||||
messages = [
|
||||
{"role": "user", "content": "What's the weather like?"}
|
||||
]
|
||||
optional_params = {"parallel_tool_calls": True}
|
||||
|
||||
result = mistral_config.transform_request(
|
||||
model="mistral/mistral-large-latest",
|
||||
messages=messages,
|
||||
optional_params=optional_params,
|
||||
litellm_params={},
|
||||
headers={}
|
||||
)
|
||||
|
||||
assert result.get("parallel_tool_calls") is True
|
||||
assert len(result["messages"]) == 1
|
||||
assert result["messages"][0]["role"] == "user"
|
||||
|
|
@ -713,3 +713,31 @@ def test_vertex_ai_transform_parts():
|
|||
assert function["name"] == "simple_function"
|
||||
assert function["arguments"] == "{}"
|
||||
assert tools is None
|
||||
|
||||
|
||||
def test_vertex_ai_usage_metadata_missing_token_count():
|
||||
"""Test that missing tokenCount in responseTokensDetails defaults to 0"""
|
||||
from litellm.types.utils import PromptTokensDetailsWrapper
|
||||
|
||||
v = VertexGeminiConfig()
|
||||
usage_metadata = {
|
||||
"promptTokenCount": 57,
|
||||
"responseTokenCount": 74,
|
||||
"totalTokenCount": 131,
|
||||
"promptTokensDetails": [{"modality": "TEXT", "tokenCount": 57}],
|
||||
"responseTokensDetails": [
|
||||
{"modality": "TEXT"}, # Missing tokenCount
|
||||
{"modality": "AUDIO"}, # Missing tokenCount
|
||||
],
|
||||
}
|
||||
usage_metadata = UsageMetadata(**usage_metadata)
|
||||
result = v._calculate_usage(completion_response={"usageMetadata": usage_metadata})
|
||||
|
||||
# Should not crash and should default missing tokenCount to 0
|
||||
assert result.prompt_tokens == 57
|
||||
assert result.completion_tokens == 74
|
||||
assert result.total_tokens == 131
|
||||
assert result.completion_tokens_details.text_tokens == 0 # Default value for missing tokenCount
|
||||
assert result.completion_tokens_details.audio_tokens == 0 # Default value for missing tokenCount
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -243,6 +243,98 @@ class TestVertexBase:
|
|||
assert token == "refreshed-token"
|
||||
assert project == not_quota_project_id
|
||||
|
||||
@pytest.mark.parametrize("is_async", [True, False], ids=["async", "sync"])
|
||||
@pytest.mark.asyncio
|
||||
async def test_identity_pool_credentials(self, is_async):
|
||||
vertex_base = VertexBase()
|
||||
|
||||
# Test case: Using Workload Identity Federation for Microsoft Azure and
|
||||
# OIDC identity providers (default behavior)
|
||||
credentials = {
|
||||
"project_id": "test-project",
|
||||
"refresh_token": "fake-refresh-token",
|
||||
"type": "external_account",
|
||||
}
|
||||
mock_creds = MagicMock()
|
||||
mock_creds.token = "token-1"
|
||||
mock_creds.expired = False
|
||||
mock_creds.project_id = "test-project"
|
||||
|
||||
with patch.object(
|
||||
vertex_base, "_credentials_from_identity_pool", return_value=mock_creds
|
||||
) as mock_credentials_from_identity_pool, patch.object(
|
||||
vertex_base, "refresh_auth"
|
||||
) as mock_refresh:
|
||||
|
||||
def mock_refresh_impl(creds):
|
||||
creds.token = "refreshed-token"
|
||||
|
||||
mock_refresh.side_effect = mock_refresh_impl
|
||||
|
||||
if is_async:
|
||||
token, _ = await vertex_base._ensure_access_token_async(
|
||||
credentials=credentials,
|
||||
project_id=None,
|
||||
custom_llm_provider="vertex_ai",
|
||||
)
|
||||
else:
|
||||
token, _ = vertex_base._ensure_access_token(
|
||||
credentials=credentials,
|
||||
project_id=None,
|
||||
custom_llm_provider="vertex_ai",
|
||||
)
|
||||
|
||||
assert mock_credentials_from_identity_pool.called
|
||||
assert token == "refreshed-token"
|
||||
|
||||
@pytest.mark.parametrize("is_async", [True, False], ids=["async", "sync"])
|
||||
@pytest.mark.asyncio
|
||||
async def test_identity_pool_credentials_with_aws(self, is_async):
|
||||
vertex_base = VertexBase()
|
||||
|
||||
# Test case: Using Workload Identity Federation for Microsoft Azure and
|
||||
# OIDC identity providers (default behavior)
|
||||
credentials = {
|
||||
"project_id": "test-project",
|
||||
"refresh_token": "fake-refresh-token",
|
||||
"type": "external_account",
|
||||
"credential_source": {
|
||||
"environment_id": "aws1"
|
||||
}
|
||||
}
|
||||
mock_creds = MagicMock()
|
||||
mock_creds.token = "token-1"
|
||||
mock_creds.expired = False
|
||||
mock_creds.project_id = "test-project"
|
||||
|
||||
with patch.object(
|
||||
vertex_base, "_credentials_from_identity_pool_with_aws", return_value=mock_creds
|
||||
) as mock_credentials_from_identity_pool_with_aws, patch.object(
|
||||
vertex_base, "refresh_auth"
|
||||
) as mock_refresh:
|
||||
|
||||
def mock_refresh_impl(creds):
|
||||
creds.token = "refreshed-token"
|
||||
|
||||
mock_refresh.side_effect = mock_refresh_impl
|
||||
|
||||
if is_async:
|
||||
token, _ = await vertex_base._ensure_access_token_async(
|
||||
credentials=credentials,
|
||||
project_id=None,
|
||||
custom_llm_provider="vertex_ai",
|
||||
)
|
||||
else:
|
||||
token, _ = vertex_base._ensure_access_token(
|
||||
credentials=credentials,
|
||||
project_id=None,
|
||||
custom_llm_provider="vertex_ai",
|
||||
)
|
||||
|
||||
assert mock_credentials_from_identity_pool_with_aws.called
|
||||
assert token == "refreshed-token"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"api_base, vertex_location, expected",
|
||||
[
|
||||
|
|
@ -270,6 +362,7 @@ class TestVertexBase:
|
|||
),
|
||||
],
|
||||
)
|
||||
|
||||
def test_get_api_base(self, api_base, vertex_location, expected):
|
||||
vertex_base = VertexBase()
|
||||
assert (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import sys
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
|
|
@ -14,15 +14,21 @@ from litellm.llms.vertex_ai.vertex_ai_partner_models.anthropic.transformation im
|
|||
@pytest.mark.parametrize(
|
||||
"model, expected_thinking",
|
||||
[
|
||||
("claude-sonnet-4@20250514", True),
|
||||
("claude-sonnet-4@20250514", True),
|
||||
],
|
||||
)
|
||||
def test_vertex_ai_anthropic_thinking_param(model, expected_thinking):
|
||||
supported_openai_params = VertexAIAnthropicConfig().get_supported_openai_params(
|
||||
model=model
|
||||
)
|
||||
|
||||
model=model
|
||||
)
|
||||
|
||||
if expected_thinking:
|
||||
assert "thinking" in supported_openai_params
|
||||
else:
|
||||
assert "thinking" not in supported_openai_params
|
||||
|
||||
|
||||
def test_get_supported_params_thinking():
|
||||
config = VertexAIAnthropicConfig()
|
||||
params = config.get_supported_openai_params(model="claude-sonnet-4")
|
||||
assert "thinking" in params
|
||||
|
|
|
|||
|
|
@ -116,17 +116,19 @@ class TestUserAPIKeyAuthMCP:
|
|||
mock_find_unique.assert_not_called()
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"headers,expected_api_key",
|
||||
"headers,expected_api_key,expected_mcp_auth_header",
|
||||
[
|
||||
# Test case 1: x-litellm-api-key header present
|
||||
(
|
||||
[(b"x-litellm-api-key", b"test-api-key-123")],
|
||||
"test-api-key-123",
|
||||
None,
|
||||
),
|
||||
# Test case 2: Authorization header present (fallback)
|
||||
(
|
||||
[(b"authorization", b"Bearer test-auth-token")],
|
||||
"Bearer test-auth-token",
|
||||
None,
|
||||
),
|
||||
# Test case 3: Both headers present (primary should win)
|
||||
(
|
||||
|
|
@ -135,22 +137,40 @@ class TestUserAPIKeyAuthMCP:
|
|||
(b"authorization", b"Bearer fallback-token"),
|
||||
],
|
||||
"primary-key",
|
||||
None,
|
||||
),
|
||||
# Test case 4: Case insensitive headers
|
||||
(
|
||||
[(b"X-LITELLM-API-KEY", b"case-insensitive-key")],
|
||||
"case-insensitive-key",
|
||||
None,
|
||||
),
|
||||
# Test case 5: No relevant headers
|
||||
(
|
||||
[(b"content-type", b"application/json")],
|
||||
"",
|
||||
None,
|
||||
),
|
||||
# Test case 6: Empty headers
|
||||
([], ""),
|
||||
([], "", None),
|
||||
# Test case 7: MCP auth header present
|
||||
(
|
||||
[
|
||||
(b"x-litellm-api-key", b"test-api-key-123"),
|
||||
(b"x-mcp-auth", b"mcp-auth-token"),
|
||||
],
|
||||
"test-api-key-123",
|
||||
"mcp-auth-token",
|
||||
),
|
||||
# Test case 8: Only MCP auth header present (no API key)
|
||||
(
|
||||
[(b"x-mcp-auth", b"mcp-auth-token")],
|
||||
"",
|
||||
"mcp-auth-token",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_user_api_key_auth_mcp(self, headers, expected_api_key):
|
||||
async def test_user_api_key_auth_mcp(self, headers, expected_api_key, expected_mcp_auth_header):
|
||||
"""Test user_api_key_auth_mcp method with various header scenarios"""
|
||||
|
||||
# Create ASGI scope with headers
|
||||
|
|
@ -174,10 +194,11 @@ class TestUserAPIKeyAuthMCP:
|
|||
mock_user_api_key_auth.return_value = mock_auth_result
|
||||
|
||||
# Call the method
|
||||
result = await UserAPIKeyAuthMCP.user_api_key_auth_mcp(scope)
|
||||
auth_result, mcp_auth_header = await UserAPIKeyAuthMCP.user_api_key_auth_mcp(scope)
|
||||
|
||||
# Assert the result
|
||||
assert result == mock_auth_result
|
||||
# Assert the results
|
||||
assert auth_result == mock_auth_result
|
||||
assert mcp_auth_header == expected_mcp_auth_header
|
||||
|
||||
# Verify user_api_key_auth was called with correct parameters
|
||||
mock_user_api_key_auth.assert_called_once()
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ from typing import Optional
|
|||
from litellm.proxy._types import (
|
||||
LiteLLM_MCPServerTable,
|
||||
LitellmUserRoles,
|
||||
MCPAuth,
|
||||
MCPSpecVersion,
|
||||
MCPTransport,
|
||||
UserAPIKeyAuth,
|
||||
)
|
||||
from litellm.types.mcp import MCPAuth
|
||||
from litellm.types.mcp_server.mcp_server_manager import MCPInfo, MCPServer
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ async def test_create_and_get_tag():
|
|||
"""
|
||||
# Mock the prisma client and _get_tags_config and _save_tags_config
|
||||
with patch("litellm.proxy.proxy_server.prisma_client") as mock_prisma, patch(
|
||||
"litellm.proxy.proxy_server.llm_router"
|
||||
) as mock_router, patch(
|
||||
"litellm.proxy.management_endpoints.tag_management_endpoints._get_tags_config"
|
||||
) as mock_get_tags, patch(
|
||||
"litellm.proxy.management_endpoints.tag_management_endpoints._save_tags_config"
|
||||
|
|
@ -50,6 +52,7 @@ async def test_create_and_get_tag():
|
|||
|
||||
# Test tag creation
|
||||
response = client.post("/tag/new", json=tag_data, headers=headers)
|
||||
print(f"response: {response.text}")
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["message"] == "Tag test-tag created successfully"
|
||||
|
|
@ -158,3 +161,93 @@ async def test_delete_tag():
|
|||
|
||||
# Verify _save_tags_config was called without the deleted tag
|
||||
mock_save_tags.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_deployments_by_model_id():
|
||||
"""
|
||||
Test get_deployments_by_model when model is found by model_id
|
||||
"""
|
||||
from unittest.mock import Mock
|
||||
|
||||
from litellm.proxy.management_endpoints.tag_management_endpoints import (
|
||||
get_deployments_by_model,
|
||||
)
|
||||
from litellm.types.router import Deployment, LiteLLM_Params, ModelInfo
|
||||
|
||||
# Create a mock router
|
||||
mock_router = Mock()
|
||||
|
||||
# Setup mock to return deployment by model_id
|
||||
mock_deployment = Deployment(
|
||||
model_name="gpt-3.5-turbo",
|
||||
litellm_params=LiteLLM_Params(model="gpt-3.5-turbo"),
|
||||
model_info=ModelInfo(),
|
||||
)
|
||||
mock_router.get_deployment.return_value = mock_deployment
|
||||
|
||||
result = await get_deployments_by_model("model-123", mock_router)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0] == mock_deployment
|
||||
mock_router.get_deployment.assert_called_once_with(model_id="model-123")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_deployments_by_model_name():
|
||||
"""
|
||||
Test get_deployments_by_model when model is found by model_name
|
||||
"""
|
||||
from unittest.mock import Mock
|
||||
|
||||
from litellm.proxy.management_endpoints.tag_management_endpoints import (
|
||||
get_deployments_by_model,
|
||||
)
|
||||
from litellm.types.router import Deployment
|
||||
|
||||
# Create a mock router
|
||||
mock_router = Mock()
|
||||
|
||||
# Setup mock to not find by model_id but find by model_name
|
||||
mock_router.get_deployment.return_value = None
|
||||
mock_router.get_model_list.return_value = [
|
||||
{
|
||||
"model_name": "gpt-3.5-turbo",
|
||||
"litellm_params": {"model": "gpt-3.5-turbo", "api_key": "test-key"},
|
||||
"model_info": {"id": "model-1", "description": "Test model"},
|
||||
}
|
||||
]
|
||||
|
||||
result = await get_deployments_by_model("gpt-3.5-turbo", mock_router)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].model_name == "gpt-3.5-turbo"
|
||||
assert isinstance(result[0], Deployment)
|
||||
mock_router.get_deployment.assert_called_once_with(model_id="gpt-3.5-turbo")
|
||||
mock_router.get_model_list.assert_called_once_with(model_name="gpt-3.5-turbo")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_deployments_by_model_not_found():
|
||||
"""
|
||||
Test get_deployments_by_model when model is not found
|
||||
"""
|
||||
from unittest.mock import Mock
|
||||
|
||||
from litellm.proxy.management_endpoints.tag_management_endpoints import (
|
||||
get_deployments_by_model,
|
||||
)
|
||||
|
||||
# Create a mock router
|
||||
mock_router = Mock()
|
||||
|
||||
# Setup mock to not find model by either method
|
||||
mock_router.get_deployment.return_value = None
|
||||
mock_router.get_model_list.return_value = None
|
||||
|
||||
result = await get_deployments_by_model("nonexistent-model", mock_router)
|
||||
|
||||
assert len(result) == 0
|
||||
assert result == []
|
||||
mock_router.get_deployment.assert_called_once_with(model_id="nonexistent-model")
|
||||
mock_router.get_model_list.assert_called_once_with(model_name="nonexistent-model")
|
||||
|
|
|
|||