forked from phoenix/litellm-mirror
Merge branch 'BerriAI:main' into feature/watsonx-integration
This commit is contained in:
commit
2ef4fb2efa
49 changed files with 383 additions and 372 deletions
|
@ -57,7 +57,7 @@ os.environ["LANGSMITH_API_KEY"] = ""
|
||||||
os.environ['OPENAI_API_KEY']=""
|
os.environ['OPENAI_API_KEY']=""
|
||||||
|
|
||||||
# set langfuse as a callback, litellm will send the data to langfuse
|
# set langfuse as a callback, litellm will send the data to langfuse
|
||||||
litellm.success_callback = ["langfuse"]
|
litellm.success_callback = ["langsmith"]
|
||||||
|
|
||||||
response = litellm.completion(
|
response = litellm.completion(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo",
|
||||||
|
|
|
@ -25,27 +25,27 @@ class PrometheusLogger:
|
||||||
self.litellm_llm_api_failed_requests_metric = Counter(
|
self.litellm_llm_api_failed_requests_metric = Counter(
|
||||||
name="litellm_llm_api_failed_requests_metric",
|
name="litellm_llm_api_failed_requests_metric",
|
||||||
documentation="Total number of failed LLM API calls via litellm",
|
documentation="Total number of failed LLM API calls via litellm",
|
||||||
labelnames=["end_user", "hashed_api_key", "model", "team"],
|
labelnames=["end_user", "hashed_api_key", "model", "team", "user"],
|
||||||
)
|
)
|
||||||
|
|
||||||
self.litellm_requests_metric = Counter(
|
self.litellm_requests_metric = Counter(
|
||||||
name="litellm_requests_metric",
|
name="litellm_requests_metric",
|
||||||
documentation="Total number of LLM calls to litellm",
|
documentation="Total number of LLM calls to litellm",
|
||||||
labelnames=["end_user", "hashed_api_key", "model", "team"],
|
labelnames=["end_user", "hashed_api_key", "model", "team", "user"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Counter for spend
|
# Counter for spend
|
||||||
self.litellm_spend_metric = Counter(
|
self.litellm_spend_metric = Counter(
|
||||||
"litellm_spend_metric",
|
"litellm_spend_metric",
|
||||||
"Total spend on LLM requests",
|
"Total spend on LLM requests",
|
||||||
labelnames=["end_user", "hashed_api_key", "model", "team"],
|
labelnames=["end_user", "hashed_api_key", "model", "team", "user"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Counter for total_output_tokens
|
# Counter for total_output_tokens
|
||||||
self.litellm_tokens_metric = Counter(
|
self.litellm_tokens_metric = Counter(
|
||||||
"litellm_total_tokens",
|
"litellm_total_tokens",
|
||||||
"Total number of input + output tokens from LLM requests",
|
"Total number of input + output tokens from LLM requests",
|
||||||
labelnames=["end_user", "hashed_api_key", "model", "team"],
|
labelnames=["end_user", "hashed_api_key", "model", "team", "user"],
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print_verbose(f"Got exception on init prometheus client {str(e)}")
|
print_verbose(f"Got exception on init prometheus client {str(e)}")
|
||||||
|
@ -71,6 +71,9 @@ class PrometheusLogger:
|
||||||
litellm_params = kwargs.get("litellm_params", {}) or {}
|
litellm_params = kwargs.get("litellm_params", {}) or {}
|
||||||
proxy_server_request = litellm_params.get("proxy_server_request") or {}
|
proxy_server_request = litellm_params.get("proxy_server_request") or {}
|
||||||
end_user_id = proxy_server_request.get("body", {}).get("user", None)
|
end_user_id = proxy_server_request.get("body", {}).get("user", None)
|
||||||
|
user_id = proxy_server_request.get("metadata", {}).get(
|
||||||
|
"user_api_key_user_id", None
|
||||||
|
)
|
||||||
user_api_key = litellm_params.get("metadata", {}).get("user_api_key", None)
|
user_api_key = litellm_params.get("metadata", {}).get("user_api_key", None)
|
||||||
user_api_team = litellm_params.get("metadata", {}).get(
|
user_api_team = litellm_params.get("metadata", {}).get(
|
||||||
"user_api_key_team_id", None
|
"user_api_key_team_id", None
|
||||||
|
@ -94,19 +97,19 @@ class PrometheusLogger:
|
||||||
user_api_key = hash_token(user_api_key)
|
user_api_key = hash_token(user_api_key)
|
||||||
|
|
||||||
self.litellm_requests_metric.labels(
|
self.litellm_requests_metric.labels(
|
||||||
end_user_id, user_api_key, model, user_api_team
|
end_user_id, user_api_key, model, user_api_team, user_id
|
||||||
).inc()
|
).inc()
|
||||||
self.litellm_spend_metric.labels(
|
self.litellm_spend_metric.labels(
|
||||||
end_user_id, user_api_key, model, user_api_team
|
end_user_id, user_api_key, model, user_api_team, user_id
|
||||||
).inc(response_cost)
|
).inc(response_cost)
|
||||||
self.litellm_tokens_metric.labels(
|
self.litellm_tokens_metric.labels(
|
||||||
end_user_id, user_api_key, model, user_api_team
|
end_user_id, user_api_key, model, user_api_team, user_id
|
||||||
).inc(tokens_used)
|
).inc(tokens_used)
|
||||||
|
|
||||||
### FAILURE INCREMENT ###
|
### FAILURE INCREMENT ###
|
||||||
if "exception" in kwargs:
|
if "exception" in kwargs:
|
||||||
self.litellm_llm_api_failed_requests_metric.labels(
|
self.litellm_llm_api_failed_requests_metric.labels(
|
||||||
end_user_id, user_api_key, model, user_api_team
|
end_user_id, user_api_key, model, user_api_team, user_id
|
||||||
).inc()
|
).inc()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
|
@ -22,6 +22,35 @@ class VertexAIError(Exception):
|
||||||
) # Call the base class constructor with the parameters it needs
|
) # Call the base class constructor with the parameters it needs
|
||||||
|
|
||||||
|
|
||||||
|
class ExtendedGenerationConfig(dict):
|
||||||
|
"""Extended parameters for the generation."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
temperature: Optional[float] = None,
|
||||||
|
top_p: Optional[float] = None,
|
||||||
|
top_k: Optional[int] = None,
|
||||||
|
candidate_count: Optional[int] = None,
|
||||||
|
max_output_tokens: Optional[int] = None,
|
||||||
|
stop_sequences: Optional[List[str]] = None,
|
||||||
|
response_mime_type: Optional[str] = None,
|
||||||
|
frequency_penalty: Optional[float] = None,
|
||||||
|
presence_penalty: Optional[float] = None,
|
||||||
|
):
|
||||||
|
super().__init__(
|
||||||
|
temperature=temperature,
|
||||||
|
top_p=top_p,
|
||||||
|
top_k=top_k,
|
||||||
|
candidate_count=candidate_count,
|
||||||
|
max_output_tokens=max_output_tokens,
|
||||||
|
stop_sequences=stop_sequences,
|
||||||
|
response_mime_type=response_mime_type,
|
||||||
|
frequency_penalty=frequency_penalty,
|
||||||
|
presence_penalty=presence_penalty,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class VertexAIConfig:
|
class VertexAIConfig:
|
||||||
"""
|
"""
|
||||||
Reference: https://cloud.google.com/vertex-ai/docs/generative-ai/chat/test-chat-prompts
|
Reference: https://cloud.google.com/vertex-ai/docs/generative-ai/chat/test-chat-prompts
|
||||||
|
@ -43,6 +72,10 @@ class VertexAIConfig:
|
||||||
|
|
||||||
- `stop_sequences` (List[str]): The set of character sequences (up to 5) that will stop output generation. If specified, the API will stop at the first appearance of a stop sequence. The stop sequence will not be included as part of the response.
|
- `stop_sequences` (List[str]): The set of character sequences (up to 5) that will stop output generation. If specified, the API will stop at the first appearance of a stop sequence. The stop sequence will not be included as part of the response.
|
||||||
|
|
||||||
|
- `frequency_penalty` (float): This parameter is used to penalize the model from repeating the same output. The default value is 0.0.
|
||||||
|
|
||||||
|
- `presence_penalty` (float): This parameter is used to penalize the model from generating the same output as the input. The default value is 0.0.
|
||||||
|
|
||||||
Note: Please make sure to modify the default parameters as required for your use case.
|
Note: Please make sure to modify the default parameters as required for your use case.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -53,6 +86,8 @@ class VertexAIConfig:
|
||||||
response_mime_type: Optional[str] = None
|
response_mime_type: Optional[str] = None
|
||||||
candidate_count: Optional[int] = None
|
candidate_count: Optional[int] = None
|
||||||
stop_sequences: Optional[list] = None
|
stop_sequences: Optional[list] = None
|
||||||
|
frequency_penalty: Optional[float] = None
|
||||||
|
presence_penalty: Optional[float] = None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -63,6 +98,8 @@ class VertexAIConfig:
|
||||||
response_mime_type: Optional[str] = None,
|
response_mime_type: Optional[str] = None,
|
||||||
candidate_count: Optional[int] = None,
|
candidate_count: Optional[int] = None,
|
||||||
stop_sequences: Optional[list] = None,
|
stop_sequences: Optional[list] = None,
|
||||||
|
frequency_penalty: Optional[float] = None,
|
||||||
|
presence_penalty: Optional[float] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
locals_ = locals()
|
locals_ = locals()
|
||||||
for key, value in locals_.items():
|
for key, value in locals_.items():
|
||||||
|
@ -119,6 +156,10 @@ class VertexAIConfig:
|
||||||
optional_params["max_output_tokens"] = value
|
optional_params["max_output_tokens"] = value
|
||||||
if param == "response_format" and value["type"] == "json_object":
|
if param == "response_format" and value["type"] == "json_object":
|
||||||
optional_params["response_mime_type"] = "application/json"
|
optional_params["response_mime_type"] = "application/json"
|
||||||
|
if param == "frequency_penalty":
|
||||||
|
optional_params["frequency_penalty"] = value
|
||||||
|
if param == "presence_penalty":
|
||||||
|
optional_params["presence_penalty"] = value
|
||||||
if param == "tools" and isinstance(value, list):
|
if param == "tools" and isinstance(value, list):
|
||||||
from vertexai.preview import generative_models
|
from vertexai.preview import generative_models
|
||||||
|
|
||||||
|
@ -363,42 +404,6 @@ def completion(
|
||||||
from google.cloud.aiplatform_v1beta1.types import content as gapic_content_types # type: ignore
|
from google.cloud.aiplatform_v1beta1.types import content as gapic_content_types # type: ignore
|
||||||
import google.auth # type: ignore
|
import google.auth # type: ignore
|
||||||
|
|
||||||
class ExtendedGenerationConfig(GenerationConfig):
|
|
||||||
"""Extended parameters for the generation."""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
temperature: Optional[float] = None,
|
|
||||||
top_p: Optional[float] = None,
|
|
||||||
top_k: Optional[int] = None,
|
|
||||||
candidate_count: Optional[int] = None,
|
|
||||||
max_output_tokens: Optional[int] = None,
|
|
||||||
stop_sequences: Optional[List[str]] = None,
|
|
||||||
response_mime_type: Optional[str] = None,
|
|
||||||
):
|
|
||||||
args_spec = inspect.getfullargspec(gapic_content_types.GenerationConfig)
|
|
||||||
|
|
||||||
if "response_mime_type" in args_spec.args:
|
|
||||||
self._raw_generation_config = gapic_content_types.GenerationConfig(
|
|
||||||
temperature=temperature,
|
|
||||||
top_p=top_p,
|
|
||||||
top_k=top_k,
|
|
||||||
candidate_count=candidate_count,
|
|
||||||
max_output_tokens=max_output_tokens,
|
|
||||||
stop_sequences=stop_sequences,
|
|
||||||
response_mime_type=response_mime_type,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self._raw_generation_config = gapic_content_types.GenerationConfig(
|
|
||||||
temperature=temperature,
|
|
||||||
top_p=top_p,
|
|
||||||
top_k=top_k,
|
|
||||||
candidate_count=candidate_count,
|
|
||||||
max_output_tokens=max_output_tokens,
|
|
||||||
stop_sequences=stop_sequences,
|
|
||||||
)
|
|
||||||
|
|
||||||
## Load credentials with the correct quota project ref: https://github.com/googleapis/python-aiplatform/issues/2557#issuecomment-1709284744
|
## Load credentials with the correct quota project ref: https://github.com/googleapis/python-aiplatform/issues/2557#issuecomment-1709284744
|
||||||
print_verbose(
|
print_verbose(
|
||||||
f"VERTEX AI: vertex_project={vertex_project}; vertex_location={vertex_location}"
|
f"VERTEX AI: vertex_project={vertex_project}; vertex_location={vertex_location}"
|
||||||
|
@ -550,12 +555,12 @@ def completion(
|
||||||
|
|
||||||
model_response = llm_model.generate_content(
|
model_response = llm_model.generate_content(
|
||||||
contents=content,
|
contents=content,
|
||||||
generation_config=ExtendedGenerationConfig(**optional_params),
|
generation_config=optional_params,
|
||||||
safety_settings=safety_settings,
|
safety_settings=safety_settings,
|
||||||
stream=True,
|
stream=True,
|
||||||
tools=tools,
|
tools=tools,
|
||||||
)
|
)
|
||||||
optional_params["stream"] = True
|
|
||||||
return model_response
|
return model_response
|
||||||
|
|
||||||
request_str += f"response = llm_model.generate_content({content})\n"
|
request_str += f"response = llm_model.generate_content({content})\n"
|
||||||
|
@ -572,7 +577,7 @@ def completion(
|
||||||
## LLM Call
|
## LLM Call
|
||||||
response = llm_model.generate_content(
|
response = llm_model.generate_content(
|
||||||
contents=content,
|
contents=content,
|
||||||
generation_config=ExtendedGenerationConfig(**optional_params),
|
generation_config=optional_params,
|
||||||
safety_settings=safety_settings,
|
safety_settings=safety_settings,
|
||||||
tools=tools,
|
tools=tools,
|
||||||
)
|
)
|
||||||
|
@ -627,7 +632,7 @@ def completion(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
model_response = chat.send_message_streaming(prompt, **optional_params)
|
model_response = chat.send_message_streaming(prompt, **optional_params)
|
||||||
optional_params["stream"] = True
|
|
||||||
return model_response
|
return model_response
|
||||||
|
|
||||||
request_str += f"chat.send_message({prompt}, **{optional_params}).text\n"
|
request_str += f"chat.send_message({prompt}, **{optional_params}).text\n"
|
||||||
|
@ -659,7 +664,7 @@ def completion(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
model_response = llm_model.predict_streaming(prompt, **optional_params)
|
model_response = llm_model.predict_streaming(prompt, **optional_params)
|
||||||
optional_params["stream"] = True
|
|
||||||
return model_response
|
return model_response
|
||||||
|
|
||||||
request_str += f"llm_model.predict({prompt}, **{optional_params}).text\n"
|
request_str += f"llm_model.predict({prompt}, **{optional_params}).text\n"
|
||||||
|
@ -811,45 +816,6 @@ async def async_completion(
|
||||||
Add support for acompletion calls for gemini-pro
|
Add support for acompletion calls for gemini-pro
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from vertexai.preview.generative_models import GenerationConfig
|
|
||||||
from google.cloud.aiplatform_v1beta1.types import content as gapic_content_types # type: ignore
|
|
||||||
|
|
||||||
class ExtendedGenerationConfig(GenerationConfig):
|
|
||||||
"""Extended parameters for the generation."""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
temperature: Optional[float] = None,
|
|
||||||
top_p: Optional[float] = None,
|
|
||||||
top_k: Optional[int] = None,
|
|
||||||
candidate_count: Optional[int] = None,
|
|
||||||
max_output_tokens: Optional[int] = None,
|
|
||||||
stop_sequences: Optional[List[str]] = None,
|
|
||||||
response_mime_type: Optional[str] = None,
|
|
||||||
):
|
|
||||||
args_spec = inspect.getfullargspec(gapic_content_types.GenerationConfig)
|
|
||||||
|
|
||||||
if "response_mime_type" in args_spec.args:
|
|
||||||
self._raw_generation_config = gapic_content_types.GenerationConfig(
|
|
||||||
temperature=temperature,
|
|
||||||
top_p=top_p,
|
|
||||||
top_k=top_k,
|
|
||||||
candidate_count=candidate_count,
|
|
||||||
max_output_tokens=max_output_tokens,
|
|
||||||
stop_sequences=stop_sequences,
|
|
||||||
response_mime_type=response_mime_type,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self._raw_generation_config = gapic_content_types.GenerationConfig(
|
|
||||||
temperature=temperature,
|
|
||||||
top_p=top_p,
|
|
||||||
top_k=top_k,
|
|
||||||
candidate_count=candidate_count,
|
|
||||||
max_output_tokens=max_output_tokens,
|
|
||||||
stop_sequences=stop_sequences,
|
|
||||||
)
|
|
||||||
|
|
||||||
if mode == "vision":
|
if mode == "vision":
|
||||||
print_verbose("\nMaking VertexAI Gemini Pro Vision Call")
|
print_verbose("\nMaking VertexAI Gemini Pro Vision Call")
|
||||||
print_verbose(f"\nProcessing input messages = {messages}")
|
print_verbose(f"\nProcessing input messages = {messages}")
|
||||||
|
@ -872,7 +838,7 @@ async def async_completion(
|
||||||
## LLM Call
|
## LLM Call
|
||||||
response = await llm_model._generate_content_async(
|
response = await llm_model._generate_content_async(
|
||||||
contents=content,
|
contents=content,
|
||||||
generation_config=ExtendedGenerationConfig(**optional_params),
|
generation_config=optional_params,
|
||||||
tools=tools,
|
tools=tools,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1056,45 +1022,6 @@ async def async_streaming(
|
||||||
"""
|
"""
|
||||||
Add support for async streaming calls for gemini-pro
|
Add support for async streaming calls for gemini-pro
|
||||||
"""
|
"""
|
||||||
from vertexai.preview.generative_models import GenerationConfig
|
|
||||||
from google.cloud.aiplatform_v1beta1.types import content as gapic_content_types # type: ignore
|
|
||||||
|
|
||||||
class ExtendedGenerationConfig(GenerationConfig):
|
|
||||||
"""Extended parameters for the generation."""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
temperature: Optional[float] = None,
|
|
||||||
top_p: Optional[float] = None,
|
|
||||||
top_k: Optional[int] = None,
|
|
||||||
candidate_count: Optional[int] = None,
|
|
||||||
max_output_tokens: Optional[int] = None,
|
|
||||||
stop_sequences: Optional[List[str]] = None,
|
|
||||||
response_mime_type: Optional[str] = None,
|
|
||||||
):
|
|
||||||
args_spec = inspect.getfullargspec(gapic_content_types.GenerationConfig)
|
|
||||||
|
|
||||||
if "response_mime_type" in args_spec.args:
|
|
||||||
self._raw_generation_config = gapic_content_types.GenerationConfig(
|
|
||||||
temperature=temperature,
|
|
||||||
top_p=top_p,
|
|
||||||
top_k=top_k,
|
|
||||||
candidate_count=candidate_count,
|
|
||||||
max_output_tokens=max_output_tokens,
|
|
||||||
stop_sequences=stop_sequences,
|
|
||||||
response_mime_type=response_mime_type,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self._raw_generation_config = gapic_content_types.GenerationConfig(
|
|
||||||
temperature=temperature,
|
|
||||||
top_p=top_p,
|
|
||||||
top_k=top_k,
|
|
||||||
candidate_count=candidate_count,
|
|
||||||
max_output_tokens=max_output_tokens,
|
|
||||||
stop_sequences=stop_sequences,
|
|
||||||
)
|
|
||||||
|
|
||||||
if mode == "vision":
|
if mode == "vision":
|
||||||
stream = optional_params.pop("stream")
|
stream = optional_params.pop("stream")
|
||||||
tools = optional_params.pop("tools", None)
|
tools = optional_params.pop("tools", None)
|
||||||
|
@ -1115,11 +1042,10 @@ async def async_streaming(
|
||||||
|
|
||||||
response = await llm_model._generate_content_streaming_async(
|
response = await llm_model._generate_content_streaming_async(
|
||||||
contents=content,
|
contents=content,
|
||||||
generation_config=ExtendedGenerationConfig(**optional_params),
|
generation_config=optional_params,
|
||||||
tools=tools,
|
tools=tools,
|
||||||
)
|
)
|
||||||
optional_params["stream"] = True
|
|
||||||
optional_params["tools"] = tools
|
|
||||||
elif mode == "chat":
|
elif mode == "chat":
|
||||||
chat = llm_model.start_chat()
|
chat = llm_model.start_chat()
|
||||||
optional_params.pop(
|
optional_params.pop(
|
||||||
|
@ -1138,7 +1064,7 @@ async def async_streaming(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
response = chat.send_message_streaming_async(prompt, **optional_params)
|
response = chat.send_message_streaming_async(prompt, **optional_params)
|
||||||
optional_params["stream"] = True
|
|
||||||
elif mode == "text":
|
elif mode == "text":
|
||||||
optional_params.pop(
|
optional_params.pop(
|
||||||
"stream", None
|
"stream", None
|
||||||
|
|
|
@ -236,19 +236,17 @@ def completion(
|
||||||
if client is None:
|
if client is None:
|
||||||
if vertex_credentials is not None and isinstance(vertex_credentials, str):
|
if vertex_credentials is not None and isinstance(vertex_credentials, str):
|
||||||
import google.oauth2.service_account
|
import google.oauth2.service_account
|
||||||
|
|
||||||
|
json_obj = json.loads(vertex_credentials)
|
||||||
|
|
||||||
creds = (
|
creds = (
|
||||||
google.oauth2.service_account.Credentials.from_service_account_info(
|
google.oauth2.service_account.Credentials.from_service_account_info(
|
||||||
json.loads(vertex_credentials),
|
json_obj,
|
||||||
scopes=["https://www.googleapis.com/auth/cloud-platform"],
|
scopes=["https://www.googleapis.com/auth/cloud-platform"],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
### CHECK IF ACCESS
|
### CHECK IF ACCESS
|
||||||
access_token = refresh_auth(credentials=creds)
|
access_token = refresh_auth(credentials=creds)
|
||||||
else:
|
|
||||||
import google.auth
|
|
||||||
creds, _ = google.auth.default()
|
|
||||||
### CHECK IF ACCESS
|
|
||||||
access_token = refresh_auth(credentials=creds)
|
|
||||||
|
|
||||||
vertex_ai_client = AnthropicVertex(
|
vertex_ai_client = AnthropicVertex(
|
||||||
project_id=vertex_project,
|
project_id=vertex_project,
|
||||||
|
|
|
@ -12,7 +12,6 @@ from typing import Any, Literal, Union, BinaryIO
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import dotenv, traceback, random, asyncio, time, contextvars
|
import dotenv, traceback, random, asyncio, time, contextvars
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
import litellm
|
import litellm
|
||||||
from ._logging import verbose_logger
|
from ._logging import verbose_logger
|
||||||
|
@ -1685,13 +1684,14 @@ def completion(
|
||||||
or optional_params.pop("vertex_ai_credentials", None)
|
or optional_params.pop("vertex_ai_credentials", None)
|
||||||
or get_secret("VERTEXAI_CREDENTIALS")
|
or get_secret("VERTEXAI_CREDENTIALS")
|
||||||
)
|
)
|
||||||
|
new_params = deepcopy(optional_params)
|
||||||
if "claude-3" in model:
|
if "claude-3" in model:
|
||||||
model_response = vertex_ai_anthropic.completion(
|
model_response = vertex_ai_anthropic.completion(
|
||||||
model=model,
|
model=model,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
model_response=model_response,
|
model_response=model_response,
|
||||||
print_verbose=print_verbose,
|
print_verbose=print_verbose,
|
||||||
optional_params=optional_params,
|
optional_params=new_params,
|
||||||
litellm_params=litellm_params,
|
litellm_params=litellm_params,
|
||||||
logger_fn=logger_fn,
|
logger_fn=logger_fn,
|
||||||
encoding=encoding,
|
encoding=encoding,
|
||||||
|
@ -1707,7 +1707,7 @@ def completion(
|
||||||
messages=messages,
|
messages=messages,
|
||||||
model_response=model_response,
|
model_response=model_response,
|
||||||
print_verbose=print_verbose,
|
print_verbose=print_verbose,
|
||||||
optional_params=optional_params,
|
optional_params=new_params,
|
||||||
litellm_params=litellm_params,
|
litellm_params=litellm_params,
|
||||||
logger_fn=logger_fn,
|
logger_fn=logger_fn,
|
||||||
encoding=encoding,
|
encoding=encoding,
|
||||||
|
|
|
@ -1535,6 +1535,13 @@
|
||||||
"litellm_provider": "openrouter",
|
"litellm_provider": "openrouter",
|
||||||
"mode": "chat"
|
"mode": "chat"
|
||||||
},
|
},
|
||||||
|
"openrouter/meta-llama/llama-3-70b-instruct": {
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"input_cost_per_token": 0.0000008,
|
||||||
|
"output_cost_per_token": 0.0000008,
|
||||||
|
"litellm_provider": "openrouter",
|
||||||
|
"mode": "chat"
|
||||||
|
},
|
||||||
"j2-ultra": {
|
"j2-ultra": {
|
||||||
"max_tokens": 8192,
|
"max_tokens": 8192,
|
||||||
"max_input_tokens": 8192,
|
"max_input_tokens": 8192,
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -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](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){return"static/css/dc347b0d22ffde5d.css"},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="/ui/_next/",i={272: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!=e){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)}else i[e]=0}},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](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){return"static/css/11285608926963e0.css"},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="/ui/_next/",i={272: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!=e){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)}else i[e]=0}},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))}();
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
||||||
<!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/ui/_next/static/chunks/webpack-75b5d58291566cf9.js" crossorigin=""/><script src="/ui/_next/static/chunks/fd9d1056-dafd44dfa2da140c.js" async="" crossorigin=""></script><script src="/ui/_next/static/chunks/69-e49705773ae41779.js" async="" crossorigin=""></script><script src="/ui/_next/static/chunks/main-app-9b4fb13a7db53edf.js" async="" crossorigin=""></script><title>LiteLLM Dashboard</title><meta name="description" content="LiteLLM Proxy Admin UI"/><link rel="icon" href="/ui/favicon.ico" type="image/x-icon" sizes="16x16"/><meta name="next-size-adjust"/><script src="/ui/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js" crossorigin="" noModule=""></script></head><body><script src="/ui/_next/static/chunks/webpack-75b5d58291566cf9.js" crossorigin="" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/ui/_next/static/media/c9a5bc6a7c948fb0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n2:HL[\"/ui/_next/static/css/dc347b0d22ffde5d.css\",\"style\",{\"crossOrigin\":\"\"}]\n0:\"$L3\"\n"])</script><script>self.__next_f.push([1,"4:I[47690,[],\"\"]\n6:I[77831,[],\"\"]\n7:I[65249,[\"968\",\"static/chunks/968-0cc23fee51b47e4e.js\",\"931\",\"static/chunks/app/page-6ba29bc4256320f4.js\"],\"\"]\n8:I[5613,[],\"\"]\n9:I[31778,[],\"\"]\nb:I[48955,[],\"\"]\nc:[]\n"])</script><script>self.__next_f.push([1,"3:[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/ui/_next/static/css/dc347b0d22ffde5d.css\",\"precedence\":\"next\",\"crossOrigin\":\"\"}]],[\"$\",\"$L4\",null,{\"buildId\":\"Vjlnu8AomhCFg4fkGtcUs\",\"assetPrefix\":\"/ui\",\"initialCanonicalUrl\":\"/\",\"initialTree\":[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"__PAGE__\",{},[\"$L5\",[\"$\",\"$L6\",null,{\"propsForComponent\":{\"params\":{}},\"Component\":\"$7\",\"isStaticGeneration\":true}],null]]},[null,[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"className\":\"__className_c23dc8\",\"children\":[\"$\",\"$L8\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"loading\":\"$undefined\",\"loadingStyles\":\"$undefined\",\"loadingScripts\":\"$undefined\",\"hasLoading\":false,\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L9\",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\":[],\"styles\":null}]}]}],null]],\"initialHead\":[false,\"$La\"],\"globalErrorComponent\":\"$b\",\"missingSlots\":\"$Wc\"}]]\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"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\":\"/ui/favicon.ico\",\"type\":\"image/x-icon\",\"sizes\":\"16x16\"}],[\"$\",\"meta\",\"5\",{\"name\":\"next-size-adjust\"}]]\n5:null\n"])</script><script>self.__next_f.push([1,""])</script></body></html>
|
<!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/ui/_next/static/chunks/webpack-df98554e08b2d9e3.js" crossorigin=""/><script src="/ui/_next/static/chunks/fd9d1056-dafd44dfa2da140c.js" async="" crossorigin=""></script><script src="/ui/_next/static/chunks/69-e49705773ae41779.js" async="" crossorigin=""></script><script src="/ui/_next/static/chunks/main-app-9b4fb13a7db53edf.js" async="" crossorigin=""></script><title>LiteLLM Dashboard</title><meta name="description" content="LiteLLM Proxy Admin UI"/><link rel="icon" href="/ui/favicon.ico" type="image/x-icon" sizes="16x16"/><meta name="next-size-adjust"/><script src="/ui/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js" crossorigin="" noModule=""></script></head><body><script src="/ui/_next/static/chunks/webpack-df98554e08b2d9e3.js" crossorigin="" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/ui/_next/static/media/c9a5bc6a7c948fb0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n2:HL[\"/ui/_next/static/css/11285608926963e0.css\",\"style\",{\"crossOrigin\":\"\"}]\n0:\"$L3\"\n"])</script><script>self.__next_f.push([1,"4:I[47690,[],\"\"]\n6:I[77831,[],\"\"]\n7:I[38919,[\"294\",\"static/chunks/294-843d8469c5bf2129.js\",\"931\",\"static/chunks/app/page-dd2e6236dd637c10.js\"],\"\"]\n8:I[5613,[],\"\"]\n9:I[31778,[],\"\"]\nb:I[48955,[],\"\"]\nc:[]\n"])</script><script>self.__next_f.push([1,"3:[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/ui/_next/static/css/11285608926963e0.css\",\"precedence\":\"next\",\"crossOrigin\":\"\"}]],[\"$\",\"$L4\",null,{\"buildId\":\"hJl7wGLdUQXe4Q17Ixjho\",\"assetPrefix\":\"/ui\",\"initialCanonicalUrl\":\"/\",\"initialTree\":[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"__PAGE__\",{},[\"$L5\",[\"$\",\"$L6\",null,{\"propsForComponent\":{\"params\":{}},\"Component\":\"$7\",\"isStaticGeneration\":true}],null]]},[null,[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"className\":\"__className_c23dc8\",\"children\":[\"$\",\"$L8\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"loading\":\"$undefined\",\"loadingStyles\":\"$undefined\",\"loadingScripts\":\"$undefined\",\"hasLoading\":false,\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L9\",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\":[],\"styles\":null}]}]}],null]],\"initialHead\":[false,\"$La\"],\"globalErrorComponent\":\"$b\",\"missingSlots\":\"$Wc\"}]]\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"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\":\"/ui/favicon.ico\",\"type\":\"image/x-icon\",\"sizes\":\"16x16\"}],[\"$\",\"meta\",\"5\",{\"name\":\"next-size-adjust\"}]]\n5:null\n"])</script><script>self.__next_f.push([1,""])</script></body></html>
|
|
@ -1,7 +1,7 @@
|
||||||
2:I[77831,[],""]
|
2:I[77831,[],""]
|
||||||
3:I[65249,["968","static/chunks/968-0cc23fee51b47e4e.js","931","static/chunks/app/page-6ba29bc4256320f4.js"],""]
|
3:I[38919,["294","static/chunks/294-843d8469c5bf2129.js","931","static/chunks/app/page-dd2e6236dd637c10.js"],""]
|
||||||
4:I[5613,[],""]
|
4:I[5613,[],""]
|
||||||
5:I[31778,[],""]
|
5:I[31778,[],""]
|
||||||
0:["Vjlnu8AomhCFg4fkGtcUs",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_c23dc8","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"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":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/dc347b0d22ffde5d.css","precedence":"next","crossOrigin":""}]],"$L6"]]]]
|
0:["hJl7wGLdUQXe4Q17Ixjho",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_c23dc8","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"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":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/11285608926963e0.css","precedence":"next","crossOrigin":""}]],"$L6"]]]]
|
||||||
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":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]]
|
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":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]]
|
||||||
1:null
|
1:null
|
||||||
|
|
|
@ -29,7 +29,7 @@ model_list:
|
||||||
# api_base: https://exampleopenaiendpoint-production.up.railway.app/
|
# api_base: https://exampleopenaiendpoint-production.up.railway.app/
|
||||||
|
|
||||||
router_settings:
|
router_settings:
|
||||||
routing_strategy: usage-based-routing-v2
|
# routing_strategy: usage-based-routing-v2
|
||||||
# redis_url: "os.environ/REDIS_URL"
|
# redis_url: "os.environ/REDIS_URL"
|
||||||
redis_host: os.environ/REDIS_HOST
|
redis_host: os.environ/REDIS_HOST
|
||||||
redis_port: os.environ/REDIS_PORT
|
redis_port: os.environ/REDIS_PORT
|
||||||
|
|
|
@ -3471,6 +3471,7 @@ async def completion(
|
||||||
fastapi_response.headers["x-litellm-model-id"] = model_id
|
fastapi_response.headers["x-litellm-model-id"] = model_id
|
||||||
return response
|
return response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
data["litellm_status"] = "fail" # used for alerting
|
||||||
verbose_proxy_logger.debug("EXCEPTION RAISED IN PROXY MAIN.PY")
|
verbose_proxy_logger.debug("EXCEPTION RAISED IN PROXY MAIN.PY")
|
||||||
verbose_proxy_logger.debug(
|
verbose_proxy_logger.debug(
|
||||||
"\033[1;31mAn error occurred: %s\n\n Debug this by setting `--debug`, e.g. `litellm --model gpt-3.5-turbo --debug`",
|
"\033[1;31mAn error occurred: %s\n\n Debug this by setting `--debug`, e.g. `litellm --model gpt-3.5-turbo --debug`",
|
||||||
|
@ -3720,6 +3721,7 @@ async def chat_completion(
|
||||||
|
|
||||||
return response
|
return response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
data["litellm_status"] = "fail" # used for alerting
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
await proxy_logging_obj.post_call_failure_hook(
|
await proxy_logging_obj.post_call_failure_hook(
|
||||||
user_api_key_dict=user_api_key_dict, original_exception=e
|
user_api_key_dict=user_api_key_dict, original_exception=e
|
||||||
|
@ -3914,6 +3916,7 @@ async def embeddings(
|
||||||
|
|
||||||
return response
|
return response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
data["litellm_status"] = "fail" # used for alerting
|
||||||
await proxy_logging_obj.post_call_failure_hook(
|
await proxy_logging_obj.post_call_failure_hook(
|
||||||
user_api_key_dict=user_api_key_dict, original_exception=e
|
user_api_key_dict=user_api_key_dict, original_exception=e
|
||||||
)
|
)
|
||||||
|
@ -4069,6 +4072,7 @@ async def image_generation(
|
||||||
|
|
||||||
return response
|
return response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
data["litellm_status"] = "fail" # used for alerting
|
||||||
await proxy_logging_obj.post_call_failure_hook(
|
await proxy_logging_obj.post_call_failure_hook(
|
||||||
user_api_key_dict=user_api_key_dict, original_exception=e
|
user_api_key_dict=user_api_key_dict, original_exception=e
|
||||||
)
|
)
|
||||||
|
@ -4247,6 +4251,7 @@ async def audio_transcriptions(
|
||||||
data["litellm_status"] = "success" # used for alerting
|
data["litellm_status"] = "success" # used for alerting
|
||||||
return response
|
return response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
data["litellm_status"] = "fail" # used for alerting
|
||||||
await proxy_logging_obj.post_call_failure_hook(
|
await proxy_logging_obj.post_call_failure_hook(
|
||||||
user_api_key_dict=user_api_key_dict, original_exception=e
|
user_api_key_dict=user_api_key_dict, original_exception=e
|
||||||
)
|
)
|
||||||
|
@ -4408,6 +4413,7 @@ async def moderations(
|
||||||
|
|
||||||
return response
|
return response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
data["litellm_status"] = "fail" # used for alerting
|
||||||
await proxy_logging_obj.post_call_failure_hook(
|
await proxy_logging_obj.post_call_failure_hook(
|
||||||
user_api_key_dict=user_api_key_dict, original_exception=e
|
user_api_key_dict=user_api_key_dict, original_exception=e
|
||||||
)
|
)
|
||||||
|
@ -5552,10 +5558,12 @@ async def global_spend_per_tea():
|
||||||
# get the team_id for this entry
|
# get the team_id for this entry
|
||||||
# get the spend for this entry
|
# get the spend for this entry
|
||||||
spend = row["total_spend"]
|
spend = row["total_spend"]
|
||||||
|
spend = round(spend, 2)
|
||||||
current_date_entries = spend_by_date[row_date]
|
current_date_entries = spend_by_date[row_date]
|
||||||
current_date_entries[team_alias] = spend
|
current_date_entries[team_alias] = spend
|
||||||
else:
|
else:
|
||||||
spend = row["total_spend"]
|
spend = row["total_spend"]
|
||||||
|
spend = round(spend, 2)
|
||||||
spend_by_date[row_date] = {team_alias: spend}
|
spend_by_date[row_date] = {team_alias: spend}
|
||||||
|
|
||||||
if team_alias in total_spend_per_team:
|
if team_alias in total_spend_per_team:
|
||||||
|
@ -5909,11 +5917,18 @@ async def user_info(
|
||||||
user_id=user_api_key_dict.user_id
|
user_id=user_api_key_dict.user_id
|
||||||
)
|
)
|
||||||
# *NEW* get all teams in user 'teams' field
|
# *NEW* get all teams in user 'teams' field
|
||||||
teams_2 = await prisma_client.get_data(
|
if getattr(caller_user_info, "user_role", None) == "proxy_admin":
|
||||||
team_id_list=caller_user_info.teams,
|
teams_2 = await prisma_client.get_data(
|
||||||
table_name="team",
|
table_name="team",
|
||||||
query_type="find_all",
|
query_type="find_all",
|
||||||
)
|
team_id_list=None,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
teams_2 = await prisma_client.get_data(
|
||||||
|
team_id_list=caller_user_info.teams,
|
||||||
|
table_name="team",
|
||||||
|
query_type="find_all",
|
||||||
|
)
|
||||||
|
|
||||||
if teams_2 is not None and isinstance(teams_2, list):
|
if teams_2 is not None and isinstance(teams_2, list):
|
||||||
for team in teams_2:
|
for team in teams_2:
|
||||||
|
@ -7873,7 +7888,7 @@ async def login(request: Request):
|
||||||
)
|
)
|
||||||
if os.getenv("DATABASE_URL") is not None:
|
if os.getenv("DATABASE_URL") is not None:
|
||||||
response = await generate_key_helper_fn(
|
response = await generate_key_helper_fn(
|
||||||
**{"user_role": "proxy_admin", "duration": "1hr", "key_max_budget": 5, "models": [], "aliases": {}, "config": {}, "spend": 0, "user_id": key_user_id, "team_id": "litellm-dashboard"} # type: ignore
|
**{"user_role": "proxy_admin", "duration": "2hr", "key_max_budget": 5, "models": [], "aliases": {}, "config": {}, "spend": 0, "user_id": key_user_id, "team_id": "litellm-dashboard"} # type: ignore
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise ProxyException(
|
raise ProxyException(
|
||||||
|
@ -8125,7 +8140,7 @@ async def auth_callback(request: Request):
|
||||||
# User might not be already created on first generation of key
|
# User might not be already created on first generation of key
|
||||||
# But if it is, we want their models preferences
|
# But if it is, we want their models preferences
|
||||||
default_ui_key_values = {
|
default_ui_key_values = {
|
||||||
"duration": "1hr",
|
"duration": "2hr",
|
||||||
"key_max_budget": 0.01,
|
"key_max_budget": 0.01,
|
||||||
"aliases": {},
|
"aliases": {},
|
||||||
"config": {},
|
"config": {},
|
||||||
|
@ -8137,6 +8152,7 @@ async def auth_callback(request: Request):
|
||||||
"user_id": user_id,
|
"user_id": user_id,
|
||||||
"user_email": user_email,
|
"user_email": user_email,
|
||||||
}
|
}
|
||||||
|
_user_id_from_sso = user_id
|
||||||
try:
|
try:
|
||||||
user_role = None
|
user_role = None
|
||||||
if prisma_client is not None:
|
if prisma_client is not None:
|
||||||
|
@ -8160,7 +8176,7 @@ async def auth_callback(request: Request):
|
||||||
if user_info is not None:
|
if user_info is not None:
|
||||||
user_defined_values = {
|
user_defined_values = {
|
||||||
"models": getattr(user_info, "models", user_id_models),
|
"models": getattr(user_info, "models", user_id_models),
|
||||||
"user_id": getattr(user_info, "user_id", user_id),
|
"user_id": user_id,
|
||||||
"user_email": getattr(user_info, "user_id", user_email),
|
"user_email": getattr(user_info, "user_id", user_email),
|
||||||
"user_role": getattr(user_info, "user_role", None),
|
"user_role": getattr(user_info, "user_role", None),
|
||||||
}
|
}
|
||||||
|
@ -8191,6 +8207,10 @@ async def auth_callback(request: Request):
|
||||||
)
|
)
|
||||||
key = response["token"] # type: ignore
|
key = response["token"] # type: ignore
|
||||||
user_id = response["user_id"] # type: ignore
|
user_id = response["user_id"] # type: ignore
|
||||||
|
|
||||||
|
# This should always be true
|
||||||
|
# User_id on SSO == user_id in the LiteLLM_VerificationToken Table
|
||||||
|
assert user_id == _user_id_from_sso
|
||||||
litellm_dashboard_ui = "/ui/"
|
litellm_dashboard_ui = "/ui/"
|
||||||
user_role = user_role or "app_owner"
|
user_role = user_role or "app_owner"
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -271,11 +271,43 @@ class ProxyLogging:
|
||||||
request_info = f"\nRequest Model: `{model}`\nAPI Base: `{api_base}`\nMessages: `{messages}`"
|
request_info = f"\nRequest Model: `{model}`\nAPI Base: `{api_base}`\nMessages: `{messages}`"
|
||||||
slow_message = f"`Responses are slow - {round(time_difference_float,2)}s response time > Alerting threshold: {self.alerting_threshold}s`"
|
slow_message = f"`Responses are slow - {round(time_difference_float,2)}s response time > Alerting threshold: {self.alerting_threshold}s`"
|
||||||
if time_difference_float > self.alerting_threshold:
|
if time_difference_float > self.alerting_threshold:
|
||||||
|
if "langfuse" in litellm.success_callback:
|
||||||
|
request_info = self._add_langfuse_trace_id_to_alert(
|
||||||
|
request_info=request_info, kwargs=kwargs
|
||||||
|
)
|
||||||
await self.alerting_handler(
|
await self.alerting_handler(
|
||||||
message=slow_message + request_info,
|
message=slow_message + request_info,
|
||||||
level="Low",
|
level="Low",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _add_langfuse_trace_id_to_alert(
|
||||||
|
self,
|
||||||
|
request_info: str,
|
||||||
|
request_data: Optional[dict] = None,
|
||||||
|
kwargs: Optional[dict] = None,
|
||||||
|
):
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
if request_data is not None:
|
||||||
|
trace_id = request_data.get("metadata", {}).get(
|
||||||
|
"trace_id", None
|
||||||
|
) # get langfuse trace id
|
||||||
|
if trace_id is None:
|
||||||
|
trace_id = "litellm-alert-trace-" + str(uuid.uuid4())
|
||||||
|
request_data["metadata"]["trace_id"] = trace_id
|
||||||
|
elif kwargs is not None:
|
||||||
|
_litellm_params = kwargs.get("litellm_params", {})
|
||||||
|
trace_id = _litellm_params.get("metadata", {}).get(
|
||||||
|
"trace_id", None
|
||||||
|
) # get langfuse trace id
|
||||||
|
if trace_id is None:
|
||||||
|
trace_id = "litellm-alert-trace-" + str(uuid.uuid4())
|
||||||
|
_litellm_params["metadata"]["trace_id"] = trace_id
|
||||||
|
|
||||||
|
_langfuse_host = os.environ.get("LANGFUSE_HOST", "https://cloud.langfuse.com")
|
||||||
|
request_info += f"\n🪢 Langfuse Trace: {_langfuse_host}/trace/{trace_id}"
|
||||||
|
return request_info
|
||||||
|
|
||||||
async def response_taking_too_long(
|
async def response_taking_too_long(
|
||||||
self,
|
self,
|
||||||
start_time: Optional[float] = None,
|
start_time: Optional[float] = None,
|
||||||
|
@ -289,22 +321,18 @@ class ProxyLogging:
|
||||||
if messages is None:
|
if messages is None:
|
||||||
# if messages does not exist fallback to "input"
|
# if messages does not exist fallback to "input"
|
||||||
messages = request_data.get("input", None)
|
messages = request_data.get("input", None)
|
||||||
trace_id = request_data.get("metadata", {}).get(
|
|
||||||
"trace_id", None
|
# try casting messages to str and get the first 100 characters, else mark as None
|
||||||
) # get langfuse trace id
|
try:
|
||||||
if trace_id is not None:
|
|
||||||
messages = str(messages)
|
messages = str(messages)
|
||||||
messages = messages[:100]
|
messages = messages[:100]
|
||||||
messages = f"{messages}\nLangfuse Trace Id: {trace_id}"
|
except:
|
||||||
else:
|
messages = ""
|
||||||
# try casting messages to str and get the first 100 characters, else mark as None
|
|
||||||
try:
|
|
||||||
messages = str(messages)
|
|
||||||
messages = messages[:100]
|
|
||||||
except:
|
|
||||||
messages = None
|
|
||||||
|
|
||||||
request_info = f"\nRequest Model: `{model}`\nMessages: `{messages}`"
|
request_info = f"\nRequest Model: `{model}`\nMessages: `{messages}`"
|
||||||
|
if "langfuse" in litellm.success_callback:
|
||||||
|
request_info = self._add_langfuse_trace_id_to_alert(
|
||||||
|
request_info=request_info, request_data=request_data
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
request_info = ""
|
request_info = ""
|
||||||
|
|
||||||
|
@ -318,6 +346,7 @@ class ProxyLogging:
|
||||||
if (
|
if (
|
||||||
request_data is not None
|
request_data is not None
|
||||||
and request_data.get("litellm_status", "") != "success"
|
and request_data.get("litellm_status", "") != "success"
|
||||||
|
and request_data.get("litellm_status", "") != "fail"
|
||||||
):
|
):
|
||||||
if request_data.get("deployment", None) is not None and isinstance(
|
if request_data.get("deployment", None) is not None and isinstance(
|
||||||
request_data["deployment"], dict
|
request_data["deployment"], dict
|
||||||
|
@ -493,14 +522,19 @@ class ProxyLogging:
|
||||||
level: str - Low|Medium|High - if calls might fail (Medium) or are failing (High); Currently, no alerts would be 'Low'.
|
level: str - Low|Medium|High - if calls might fail (Medium) or are failing (High); Currently, no alerts would be 'Low'.
|
||||||
message: str - what is the alert about
|
message: str - what is the alert about
|
||||||
"""
|
"""
|
||||||
|
if self.alerting is None:
|
||||||
|
return
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
# Get the current timestamp
|
# Get the current timestamp
|
||||||
current_time = datetime.now().strftime("%H:%M:%S")
|
current_time = datetime.now().strftime("%H:%M:%S")
|
||||||
_proxy_base_url = os.getenv("PROXY_BASE_URL", "None")
|
_proxy_base_url = os.getenv("PROXY_BASE_URL", None)
|
||||||
formatted_message = f"Level: `{level}`\nTimestamp: `{current_time}`\n\nMessage: {message} \n\nProxy URL: `{_proxy_base_url}`"
|
formatted_message = (
|
||||||
if self.alerting is None:
|
f"Level: `{level}`\nTimestamp: `{current_time}`\n\nMessage: {message}"
|
||||||
return
|
)
|
||||||
|
if _proxy_base_url is not None:
|
||||||
|
formatted_message += f"\n\nProxy URL: `{_proxy_base_url}`"
|
||||||
|
|
||||||
for client in self.alerting:
|
for client in self.alerting:
|
||||||
if client == "slack":
|
if client == "slack":
|
||||||
|
@ -1157,6 +1191,8 @@ class PrismaClient:
|
||||||
response = await self.db.litellm_teamtable.find_many(
|
response = await self.db.litellm_teamtable.find_many(
|
||||||
where={"team_id": {"in": team_id_list}}
|
where={"team_id": {"in": team_id_list}}
|
||||||
)
|
)
|
||||||
|
elif query_type == "find_all" and team_id_list is None:
|
||||||
|
response = await self.db.litellm_teamtable.find_many(take=20)
|
||||||
return response
|
return response
|
||||||
elif table_name == "user_notification":
|
elif table_name == "user_notification":
|
||||||
if query_type == "find_unique":
|
if query_type == "find_unique":
|
||||||
|
|
|
@ -2872,7 +2872,27 @@ class Router:
|
||||||
f"get_available_deployment for model: {model}, Selected deployment: {self.print_deployment(deployment) or deployment[0]} for model: {model}"
|
f"get_available_deployment for model: {model}, Selected deployment: {self.print_deployment(deployment) or deployment[0]} for model: {model}"
|
||||||
)
|
)
|
||||||
return deployment or deployment[0]
|
return deployment or deployment[0]
|
||||||
|
############## Check if we can do a RPM/TPM based weighted pick #################
|
||||||
|
tpm = healthy_deployments[0].get("litellm_params").get("tpm", None)
|
||||||
|
if tpm is not None:
|
||||||
|
# use weight-random pick if rpms provided
|
||||||
|
tpms = [m["litellm_params"].get("tpm", 0) for m in healthy_deployments]
|
||||||
|
verbose_router_logger.debug(f"\ntpms {tpms}")
|
||||||
|
total_tpm = sum(tpms)
|
||||||
|
weights = [tpm / total_tpm for tpm in tpms]
|
||||||
|
verbose_router_logger.debug(f"\n weights {weights}")
|
||||||
|
# Perform weighted random pick
|
||||||
|
selected_index = random.choices(range(len(tpms)), weights=weights)[0]
|
||||||
|
verbose_router_logger.debug(f"\n selected index, {selected_index}")
|
||||||
|
deployment = healthy_deployments[selected_index]
|
||||||
|
verbose_router_logger.info(
|
||||||
|
f"get_available_deployment for model: {model}, Selected deployment: {self.print_deployment(deployment) or deployment[0]} for model: {model}"
|
||||||
|
)
|
||||||
|
return deployment or deployment[0]
|
||||||
|
|
||||||
|
############## No RPM/TPM passed, we do a random pick #################
|
||||||
|
item = random.choice(healthy_deployments)
|
||||||
|
return item or item[0]
|
||||||
if deployment is None:
|
if deployment is None:
|
||||||
verbose_router_logger.info(
|
verbose_router_logger.info(
|
||||||
f"get_available_deployment for model: {model}, No deployment available"
|
f"get_available_deployment for model: {model}, No deployment available"
|
||||||
|
|
|
@ -90,7 +90,7 @@ def load_vertex_ai_credentials():
|
||||||
|
|
||||||
# Create a temporary file
|
# Create a temporary file
|
||||||
with tempfile.NamedTemporaryFile(mode="w+", delete=False) as temp_file:
|
with tempfile.NamedTemporaryFile(mode="w+", delete=False) as temp_file:
|
||||||
# Write the updated content to the temporary file
|
# Write the updated content to the temporary files
|
||||||
json.dump(service_account_key_data, temp_file, indent=2)
|
json.dump(service_account_key_data, temp_file, indent=2)
|
||||||
|
|
||||||
# Export the temporary file as GOOGLE_APPLICATION_CREDENTIALS
|
# Export the temporary file as GOOGLE_APPLICATION_CREDENTIALS
|
||||||
|
|
|
@ -512,3 +512,76 @@ async def test_wildcard_openai_routing():
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pytest.fail(f"Error occurred: {e}")
|
pytest.fail(f"Error occurred: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Test async router get deployment (Simpl-shuffle)
|
||||||
|
"""
|
||||||
|
|
||||||
|
rpm_list = [[None, None], [6, 1440]]
|
||||||
|
tpm_list = [[None, None], [6, 1440]]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"rpm_list, tpm_list",
|
||||||
|
[(rpm, tpm) for rpm in rpm_list for tpm in tpm_list],
|
||||||
|
)
|
||||||
|
async def test_weighted_selection_router_async(rpm_list, tpm_list):
|
||||||
|
# this tests if load balancing works based on the provided rpms in the router
|
||||||
|
# it's a fast test, only tests get_available_deployment
|
||||||
|
# users can pass rpms as a litellm_param
|
||||||
|
try:
|
||||||
|
litellm.set_verbose = False
|
||||||
|
model_list = [
|
||||||
|
{
|
||||||
|
"model_name": "gpt-3.5-turbo",
|
||||||
|
"litellm_params": {
|
||||||
|
"model": "gpt-3.5-turbo-0613",
|
||||||
|
"api_key": os.getenv("OPENAI_API_KEY"),
|
||||||
|
"rpm": rpm_list[0],
|
||||||
|
"tpm": tpm_list[0],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model_name": "gpt-3.5-turbo",
|
||||||
|
"litellm_params": {
|
||||||
|
"model": "azure/chatgpt-v-2",
|
||||||
|
"api_key": os.getenv("AZURE_API_KEY"),
|
||||||
|
"api_base": os.getenv("AZURE_API_BASE"),
|
||||||
|
"api_version": os.getenv("AZURE_API_VERSION"),
|
||||||
|
"rpm": rpm_list[1],
|
||||||
|
"tpm": tpm_list[1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
router = Router(
|
||||||
|
model_list=model_list,
|
||||||
|
)
|
||||||
|
selection_counts = defaultdict(int)
|
||||||
|
|
||||||
|
# call get_available_deployment 1k times, it should pick azure/chatgpt-v-2 about 90% of the time
|
||||||
|
for _ in range(1000):
|
||||||
|
selected_model = await router.async_get_available_deployment(
|
||||||
|
"gpt-3.5-turbo"
|
||||||
|
)
|
||||||
|
selected_model_id = selected_model["litellm_params"]["model"]
|
||||||
|
selected_model_name = selected_model_id
|
||||||
|
selection_counts[selected_model_name] += 1
|
||||||
|
print(selection_counts)
|
||||||
|
|
||||||
|
total_requests = sum(selection_counts.values())
|
||||||
|
|
||||||
|
if rpm_list[0] is not None or tpm_list[0] is not None:
|
||||||
|
# Assert that 'azure/chatgpt-v-2' has about 90% of the total requests
|
||||||
|
assert (
|
||||||
|
selection_counts["azure/chatgpt-v-2"] / total_requests > 0.89
|
||||||
|
), f"Assertion failed: 'azure/chatgpt-v-2' does not have about 90% of the total requests in the weighted load balancer. Selection counts {selection_counts}"
|
||||||
|
else:
|
||||||
|
# Assert both are used
|
||||||
|
assert selection_counts["azure/chatgpt-v-2"] > 0
|
||||||
|
assert selection_counts["gpt-3.5-turbo-0613"] > 0
|
||||||
|
router.reset()
|
||||||
|
except Exception as e:
|
||||||
|
traceback.print_exc()
|
||||||
|
pytest.fail(f"Error occurred: {e}")
|
||||||
|
|
53
litellm/tests/test_simple_shuffle.py
Normal file
53
litellm/tests/test_simple_shuffle.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
# What is this?
|
||||||
|
## unit tests for 'simple-shuffle'
|
||||||
|
|
||||||
|
import sys, os, asyncio, time, random
|
||||||
|
from datetime import datetime
|
||||||
|
import traceback
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
import os
|
||||||
|
|
||||||
|
sys.path.insert(
|
||||||
|
0, os.path.abspath("../..")
|
||||||
|
) # Adds the parent directory to the system path
|
||||||
|
import pytest
|
||||||
|
from litellm import Router
|
||||||
|
|
||||||
|
"""
|
||||||
|
Test random shuffle
|
||||||
|
- async
|
||||||
|
- sync
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
async def test_simple_shuffle():
|
||||||
|
model_list = [
|
||||||
|
{
|
||||||
|
"model_name": "azure-model",
|
||||||
|
"litellm_params": {
|
||||||
|
"model": "azure/gpt-turbo",
|
||||||
|
"api_key": "os.environ/AZURE_FRANCE_API_KEY",
|
||||||
|
"api_base": "https://openai-france-1234.openai.azure.com",
|
||||||
|
"rpm": 1440,
|
||||||
|
},
|
||||||
|
"model_info": {"id": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model_name": "azure-model",
|
||||||
|
"litellm_params": {
|
||||||
|
"model": "azure/gpt-35-turbo",
|
||||||
|
"api_key": "os.environ/AZURE_EUROPE_API_KEY",
|
||||||
|
"api_base": "https://my-endpoint-europe-berri-992.openai.azure.com",
|
||||||
|
"rpm": 6,
|
||||||
|
},
|
||||||
|
"model_info": {"id": 2},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
router = Router(
|
||||||
|
model_list=model_list,
|
||||||
|
routing_strategy="usage-based-routing-v2",
|
||||||
|
set_verbose=False,
|
||||||
|
num_retries=3,
|
||||||
|
) # type: ignore
|
|
@ -5274,7 +5274,7 @@ def get_optional_params(
|
||||||
if tool_choice is not None:
|
if tool_choice is not None:
|
||||||
optional_params["tool_choice"] = tool_choice
|
optional_params["tool_choice"] = tool_choice
|
||||||
if response_format is not None:
|
if response_format is not None:
|
||||||
optional_params["response_format"] = tool_choice
|
optional_params["response_format"] = response_format
|
||||||
|
|
||||||
elif custom_llm_provider == "openrouter":
|
elif custom_llm_provider == "openrouter":
|
||||||
supported_params = get_supported_openai_params(
|
supported_params = get_supported_openai_params(
|
||||||
|
@ -5750,6 +5750,7 @@ def get_supported_openai_params(model: str, custom_llm_provider: str):
|
||||||
"frequency_penalty",
|
"frequency_penalty",
|
||||||
"logit_bias",
|
"logit_bias",
|
||||||
"user",
|
"user",
|
||||||
|
"response_format",
|
||||||
]
|
]
|
||||||
elif custom_llm_provider == "perplexity":
|
elif custom_llm_provider == "perplexity":
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -1535,6 +1535,13 @@
|
||||||
"litellm_provider": "openrouter",
|
"litellm_provider": "openrouter",
|
||||||
"mode": "chat"
|
"mode": "chat"
|
||||||
},
|
},
|
||||||
|
"openrouter/meta-llama/llama-3-70b-instruct": {
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"input_cost_per_token": 0.0000008,
|
||||||
|
"output_cost_per_token": 0.0000008,
|
||||||
|
"litellm_provider": "openrouter",
|
||||||
|
"mode": "chat"
|
||||||
|
},
|
||||||
"j2-ultra": {
|
"j2-ultra": {
|
||||||
"max_tokens": 8192,
|
"max_tokens": 8192,
|
||||||
"max_input_tokens": 8192,
|
"max_input_tokens": 8192,
|
||||||
|
|
|
@ -96,9 +96,9 @@ litellm_settings:
|
||||||
|
|
||||||
router_settings:
|
router_settings:
|
||||||
routing_strategy: usage-based-routing-v2
|
routing_strategy: usage-based-routing-v2
|
||||||
# redis_host: os.environ/REDIS_HOST
|
redis_host: os.environ/REDIS_HOST
|
||||||
# redis_password: os.environ/REDIS_PASSWORD
|
redis_password: os.environ/REDIS_PASSWORD
|
||||||
# redis_port: os.environ/REDIS_PORT
|
redis_port: os.environ/REDIS_PORT
|
||||||
enable_pre_call_checks: true
|
enable_pre_call_checks: true
|
||||||
|
|
||||||
general_settings:
|
general_settings:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "litellm"
|
name = "litellm"
|
||||||
version = "1.35.18"
|
version = "1.35.20"
|
||||||
description = "Library to easily interface with LLM API providers"
|
description = "Library to easily interface with LLM API providers"
|
||||||
authors = ["BerriAI"]
|
authors = ["BerriAI"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
@ -80,7 +80,7 @@ requires = ["poetry-core", "wheel"]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
[tool.commitizen]
|
[tool.commitizen]
|
||||||
version = "1.35.18"
|
version = "1.35.20"
|
||||||
version_files = [
|
version_files = [
|
||||||
"pyproject.toml:^version"
|
"pyproject.toml:^version"
|
||||||
]
|
]
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -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](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){return"static/css/dc347b0d22ffde5d.css"},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="/ui/_next/",i={272: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!=e){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)}else i[e]=0}},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](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){return"static/css/11285608926963e0.css"},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="/ui/_next/",i={272: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!=e){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)}else i[e]=0}},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))}();
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
||||||
<!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/ui/_next/static/chunks/webpack-75b5d58291566cf9.js" crossorigin=""/><script src="/ui/_next/static/chunks/fd9d1056-dafd44dfa2da140c.js" async="" crossorigin=""></script><script src="/ui/_next/static/chunks/69-e49705773ae41779.js" async="" crossorigin=""></script><script src="/ui/_next/static/chunks/main-app-9b4fb13a7db53edf.js" async="" crossorigin=""></script><title>LiteLLM Dashboard</title><meta name="description" content="LiteLLM Proxy Admin UI"/><link rel="icon" href="/ui/favicon.ico" type="image/x-icon" sizes="16x16"/><meta name="next-size-adjust"/><script src="/ui/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js" crossorigin="" noModule=""></script></head><body><script src="/ui/_next/static/chunks/webpack-75b5d58291566cf9.js" crossorigin="" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/ui/_next/static/media/c9a5bc6a7c948fb0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n2:HL[\"/ui/_next/static/css/dc347b0d22ffde5d.css\",\"style\",{\"crossOrigin\":\"\"}]\n0:\"$L3\"\n"])</script><script>self.__next_f.push([1,"4:I[47690,[],\"\"]\n6:I[77831,[],\"\"]\n7:I[65249,[\"968\",\"static/chunks/968-0cc23fee51b47e4e.js\",\"931\",\"static/chunks/app/page-6ba29bc4256320f4.js\"],\"\"]\n8:I[5613,[],\"\"]\n9:I[31778,[],\"\"]\nb:I[48955,[],\"\"]\nc:[]\n"])</script><script>self.__next_f.push([1,"3:[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/ui/_next/static/css/dc347b0d22ffde5d.css\",\"precedence\":\"next\",\"crossOrigin\":\"\"}]],[\"$\",\"$L4\",null,{\"buildId\":\"Vjlnu8AomhCFg4fkGtcUs\",\"assetPrefix\":\"/ui\",\"initialCanonicalUrl\":\"/\",\"initialTree\":[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"__PAGE__\",{},[\"$L5\",[\"$\",\"$L6\",null,{\"propsForComponent\":{\"params\":{}},\"Component\":\"$7\",\"isStaticGeneration\":true}],null]]},[null,[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"className\":\"__className_c23dc8\",\"children\":[\"$\",\"$L8\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"loading\":\"$undefined\",\"loadingStyles\":\"$undefined\",\"loadingScripts\":\"$undefined\",\"hasLoading\":false,\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L9\",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\":[],\"styles\":null}]}]}],null]],\"initialHead\":[false,\"$La\"],\"globalErrorComponent\":\"$b\",\"missingSlots\":\"$Wc\"}]]\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"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\":\"/ui/favicon.ico\",\"type\":\"image/x-icon\",\"sizes\":\"16x16\"}],[\"$\",\"meta\",\"5\",{\"name\":\"next-size-adjust\"}]]\n5:null\n"])</script><script>self.__next_f.push([1,""])</script></body></html>
|
<!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/ui/_next/static/chunks/webpack-df98554e08b2d9e3.js" crossorigin=""/><script src="/ui/_next/static/chunks/fd9d1056-dafd44dfa2da140c.js" async="" crossorigin=""></script><script src="/ui/_next/static/chunks/69-e49705773ae41779.js" async="" crossorigin=""></script><script src="/ui/_next/static/chunks/main-app-9b4fb13a7db53edf.js" async="" crossorigin=""></script><title>LiteLLM Dashboard</title><meta name="description" content="LiteLLM Proxy Admin UI"/><link rel="icon" href="/ui/favicon.ico" type="image/x-icon" sizes="16x16"/><meta name="next-size-adjust"/><script src="/ui/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js" crossorigin="" noModule=""></script></head><body><script src="/ui/_next/static/chunks/webpack-df98554e08b2d9e3.js" crossorigin="" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/ui/_next/static/media/c9a5bc6a7c948fb0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n2:HL[\"/ui/_next/static/css/11285608926963e0.css\",\"style\",{\"crossOrigin\":\"\"}]\n0:\"$L3\"\n"])</script><script>self.__next_f.push([1,"4:I[47690,[],\"\"]\n6:I[77831,[],\"\"]\n7:I[38919,[\"294\",\"static/chunks/294-843d8469c5bf2129.js\",\"931\",\"static/chunks/app/page-dd2e6236dd637c10.js\"],\"\"]\n8:I[5613,[],\"\"]\n9:I[31778,[],\"\"]\nb:I[48955,[],\"\"]\nc:[]\n"])</script><script>self.__next_f.push([1,"3:[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/ui/_next/static/css/11285608926963e0.css\",\"precedence\":\"next\",\"crossOrigin\":\"\"}]],[\"$\",\"$L4\",null,{\"buildId\":\"hJl7wGLdUQXe4Q17Ixjho\",\"assetPrefix\":\"/ui\",\"initialCanonicalUrl\":\"/\",\"initialTree\":[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"__PAGE__\",{},[\"$L5\",[\"$\",\"$L6\",null,{\"propsForComponent\":{\"params\":{}},\"Component\":\"$7\",\"isStaticGeneration\":true}],null]]},[null,[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"className\":\"__className_c23dc8\",\"children\":[\"$\",\"$L8\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"loading\":\"$undefined\",\"loadingStyles\":\"$undefined\",\"loadingScripts\":\"$undefined\",\"hasLoading\":false,\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L9\",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\":[],\"styles\":null}]}]}],null]],\"initialHead\":[false,\"$La\"],\"globalErrorComponent\":\"$b\",\"missingSlots\":\"$Wc\"}]]\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"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\":\"/ui/favicon.ico\",\"type\":\"image/x-icon\",\"sizes\":\"16x16\"}],[\"$\",\"meta\",\"5\",{\"name\":\"next-size-adjust\"}]]\n5:null\n"])</script><script>self.__next_f.push([1,""])</script></body></html>
|
|
@ -1,7 +1,7 @@
|
||||||
2:I[77831,[],""]
|
2:I[77831,[],""]
|
||||||
3:I[65249,["968","static/chunks/968-0cc23fee51b47e4e.js","931","static/chunks/app/page-6ba29bc4256320f4.js"],""]
|
3:I[38919,["294","static/chunks/294-843d8469c5bf2129.js","931","static/chunks/app/page-dd2e6236dd637c10.js"],""]
|
||||||
4:I[5613,[],""]
|
4:I[5613,[],""]
|
||||||
5:I[31778,[],""]
|
5:I[31778,[],""]
|
||||||
0:["Vjlnu8AomhCFg4fkGtcUs",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_c23dc8","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"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":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/dc347b0d22ffde5d.css","precedence":"next","crossOrigin":""}]],"$L6"]]]]
|
0:["hJl7wGLdUQXe4Q17Ixjho",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_c23dc8","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"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":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/11285608926963e0.css","precedence":"next","crossOrigin":""}]],"$L6"]]]]
|
||||||
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":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]]
|
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":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]]
|
||||||
1:null
|
1:null
|
||||||
|
|
|
@ -116,7 +116,6 @@ const CreateKey: React.FC<CreateKeyProps> = ({
|
||||||
wrapperCol={{ span: 16 }}
|
wrapperCol={{ span: 16 }}
|
||||||
labelAlign="left"
|
labelAlign="left"
|
||||||
>
|
>
|
||||||
{userRole === "App Owner" || userRole === "Admin" ? (
|
|
||||||
<>
|
<>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Key Name"
|
label="Key Name"
|
||||||
|
@ -124,7 +123,7 @@ const CreateKey: React.FC<CreateKeyProps> = ({
|
||||||
rules={[{ required: true, message: 'Please input a key name' }]}
|
rules={[{ required: true, message: 'Please input a key name' }]}
|
||||||
help="required"
|
help="required"
|
||||||
>
|
>
|
||||||
<Input />
|
<TextInput placeholder="" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Team ID"
|
label="Team ID"
|
||||||
|
@ -188,147 +187,6 @@ const CreateKey: React.FC<CreateKeyProps> = ({
|
||||||
|
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
|
||||||
className="mt-8"
|
|
||||||
label="Max Budget (USD)"
|
|
||||||
name="max_budget"
|
|
||||||
help={`Budget cannot exceed team max budget: $${team?.max_budget !== null && team?.max_budget !== undefined ? team?.max_budget : 'unlimited'}`}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
validator: async (_, value) => {
|
|
||||||
if (value && team && team.max_budget !== null && value > team.max_budget) {
|
|
||||||
throw new Error(`Budget cannot exceed team max budget: $${team.max_budget}`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber step={0.01} precision={2} width={200} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
className="mt-8"
|
|
||||||
label="Reset Budget"
|
|
||||||
name="budget_duration"
|
|
||||||
help={`Team Reset Budget: ${team?.budget_duration !== null && team?.budget_duration !== undefined ? team?.budget_duration : 'None'}`}
|
|
||||||
>
|
|
||||||
<Select defaultValue={null} placeholder="n/a">
|
|
||||||
<Select.Option value="24h">daily</Select.Option>
|
|
||||||
<Select.Option value="30d">monthly</Select.Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
className="mt-8"
|
|
||||||
label="Tokens per minute Limit (TPM)"
|
|
||||||
name="tpm_limit"
|
|
||||||
help={`TPM cannot exceed team TPM limit: ${team?.tpm_limit !== null && team?.tpm_limit !== undefined ? team?.tpm_limit : 'unlimited'}`}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
validator: async (_, value) => {
|
|
||||||
if (value && team && team.tpm_limit !== null && value > team.tpm_limit) {
|
|
||||||
throw new Error(`TPM limit cannot exceed team TPM limit: ${team.tpm_limit}`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber step={1} width={400} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
className="mt-8"
|
|
||||||
label="Requests per minute Limit (RPM)"
|
|
||||||
name="rpm_limit"
|
|
||||||
help={`RPM cannot exceed team RPM limit: ${team?.rpm_limit !== null && team?.rpm_limit !== undefined ? team?.rpm_limit : 'unlimited'}`}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
validator: async (_, value) => {
|
|
||||||
if (value && team && team.rpm_limit !== null && value > team.rpm_limit) {
|
|
||||||
throw new Error(`RPM limit cannot exceed team RPM limit: ${team.rpm_limit}`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber step={1} width={400} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label="Expire Key (eg: 30s, 30h, 30d)" name="duration" className="mt-8">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label="Metadata" name="metadata">
|
|
||||||
<Input.TextArea rows={4} placeholder="Enter metadata as JSON" />
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Form.Item
|
|
||||||
label="Key Name"
|
|
||||||
name="key_alias"
|
|
||||||
rules={[{ required: true, message: 'Please input a key name' }]}
|
|
||||||
help="required"
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label="Team ID"
|
|
||||||
name="team_id"
|
|
||||||
hidden={true}
|
|
||||||
initialValue={team ? team["team_id"] : null}
|
|
||||||
valuePropName="team_id"
|
|
||||||
className="mt-8"
|
|
||||||
>
|
|
||||||
<Input value={team ? team["team_alias"] : ""} disabled />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
label="Models"
|
|
||||||
name="models"
|
|
||||||
className="mb-12"
|
|
||||||
rules={[{ required: true, message: 'Please select a model' }]}
|
|
||||||
help="required"
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
mode="multiple"
|
|
||||||
placeholder="Select models"
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
onChange={(values) => {
|
|
||||||
const isAllTeamModelsSelected = values.includes("all-team-models");
|
|
||||||
|
|
||||||
if (isAllTeamModelsSelected) {
|
|
||||||
const newValues = ["all-team-models"];
|
|
||||||
form.setFieldsValue({ models: newValues });
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
|
|
||||||
>
|
|
||||||
<Option key="all-team-models" value="all-team-models">
|
|
||||||
All Team Models
|
|
||||||
</Option>
|
|
||||||
{team && team.models ? (
|
|
||||||
team.models.includes("all-proxy-models") ? (
|
|
||||||
userModels.map((model: string) => (
|
|
||||||
(
|
|
||||||
<Option key={model} value={model}>
|
|
||||||
{model}
|
|
||||||
</Option>
|
|
||||||
)
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
team.models.map((model: string) => (
|
|
||||||
<Option key={model} value={model}>
|
|
||||||
{model}
|
|
||||||
</Option>
|
|
||||||
))
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
userModels.map((model: string) => (
|
|
||||||
<Option key={model} value={model}>
|
|
||||||
{model}
|
|
||||||
</Option>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Accordion className="mt-20 mb-8" >
|
<Accordion className="mt-20 mb-8" >
|
||||||
<AccordionHeader>
|
<AccordionHeader>
|
||||||
<b>Optional Settings</b>
|
<b>Optional Settings</b>
|
||||||
|
@ -397,7 +255,7 @@ const CreateKey: React.FC<CreateKeyProps> = ({
|
||||||
<InputNumber step={1} width={400} />
|
<InputNumber step={1} width={400} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Expire Key (eg: 30s, 30h, 30d)" name="duration" className="mt-8">
|
<Form.Item label="Expire Key (eg: 30s, 30h, 30d)" name="duration" className="mt-8">
|
||||||
<Input />
|
<TextInput placeholder="" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Metadata" name="metadata">
|
<Form.Item label="Metadata" name="metadata">
|
||||||
<Input.TextArea rows={4} placeholder="Enter metadata as JSON" />
|
<Input.TextArea rows={4} placeholder="Enter metadata as JSON" />
|
||||||
|
@ -405,9 +263,8 @@ const CreateKey: React.FC<CreateKeyProps> = ({
|
||||||
|
|
||||||
</AccordionBody>
|
</AccordionBody>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)}
|
|
||||||
<div style={{ textAlign: "right", marginTop: "10px" }}>
|
<div style={{ textAlign: "right", marginTop: "10px" }}>
|
||||||
<Button2 htmlType="submit">Create Key</Button2>
|
<Button2 htmlType="submit">Create Key</Button2>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Button, Modal, Form, Input, message, Select, InputNumber } from "antd";
|
import { Button, Modal, Form, Input, message, Select, InputNumber } from "antd";
|
||||||
import { Button as Button2, Text } from "@tremor/react";
|
import { Button as Button2, Text, TextInput } from "@tremor/react";
|
||||||
import { userCreateCall, modelAvailableCall } from "./networking";
|
import { userCreateCall, modelAvailableCall } from "./networking";
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ const Createuser: React.FC<CreateuserProps> = ({ userID, accessToken, teams }) =
|
||||||
labelAlign="left"
|
labelAlign="left"
|
||||||
>
|
>
|
||||||
<Form.Item label="User Email" name="user_email">
|
<Form.Item label="User Email" name="user_email">
|
||||||
<Input placeholder="Enter User Email" />
|
<TextInput placeholder="" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Team ID" name="team_id">
|
<Form.Item label="Team ID" name="team_id">
|
||||||
<Select
|
<Select
|
||||||
|
|
|
@ -46,8 +46,8 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Layout style={{ minHeight: "100vh", maxWidth: "130px" }}>
|
<Layout style={{ minHeight: "100vh", maxWidth: "145px" }}>
|
||||||
<Sider width={130}>
|
<Sider width={145}>
|
||||||
<Menu
|
<Menu
|
||||||
mode="inline"
|
mode="inline"
|
||||||
defaultSelectedKeys={defaultSelectedKey ? defaultSelectedKey : ["1"]}
|
defaultSelectedKeys={defaultSelectedKey ? defaultSelectedKey : ["1"]}
|
||||||
|
@ -64,11 +64,7 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||||
</Text>
|
</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
|
||||||
<Menu.Item key="11" onClick={() => setPage("api_ref")}>
|
|
||||||
<Text>
|
|
||||||
API Reference
|
|
||||||
</Text>
|
|
||||||
</Menu.Item>
|
|
||||||
|
|
||||||
{
|
{
|
||||||
userRole == "Admin" ? (
|
userRole == "Admin" ? (
|
||||||
|
@ -79,15 +75,6 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
{userRole == "Admin" ? (
|
|
||||||
<Menu.Item key="6" onClick={() => setPage("teams")}>
|
|
||||||
<Text>
|
|
||||||
Teams
|
|
||||||
</Text>
|
|
||||||
</Menu.Item>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
userRole == "Admin" ? (
|
userRole == "Admin" ? (
|
||||||
<Menu.Item key="4" onClick={() => setPage("usage")}>
|
<Menu.Item key="4" onClick={() => setPage("usage")}>
|
||||||
|
@ -99,6 +86,16 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{userRole == "Admin" ? (
|
||||||
|
<Menu.Item key="6" onClick={() => setPage("teams")}>
|
||||||
|
<Text>
|
||||||
|
Teams
|
||||||
|
</Text>
|
||||||
|
</Menu.Item>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{userRole == "Admin" ? (
|
{userRole == "Admin" ? (
|
||||||
<Menu.Item key="5" onClick={() => setPage("users")}>
|
<Menu.Item key="5" onClick={() => setPage("users")}>
|
||||||
<Text>
|
<Text>
|
||||||
|
@ -111,7 +108,7 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||||
userRole == "Admin" ? (
|
userRole == "Admin" ? (
|
||||||
<Menu.Item key="8" onClick={() => setPage("settings")}>
|
<Menu.Item key="8" onClick={() => setPage("settings")}>
|
||||||
<Text>
|
<Text>
|
||||||
Integrations
|
Logging & Alerts
|
||||||
</Text>
|
</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
) : null
|
) : null
|
||||||
|
@ -134,6 +131,11 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||||
</Text>
|
</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
) : null}
|
) : null}
|
||||||
|
<Menu.Item key="11" onClick={() => setPage("api_ref")}>
|
||||||
|
<Text>
|
||||||
|
API Reference
|
||||||
|
</Text>
|
||||||
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
</Sider>
|
</Sider>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
|
@ -55,11 +55,11 @@ const Navbar: React.FC<NavbarProps> = ({
|
||||||
<div className="text-left my-2 absolute top-0 left-0">
|
<div className="text-left my-2 absolute top-0 left-0">
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<button className="text-gray-800 text-2xl py-1 rounded text-center">
|
<button className="text-gray-800 rounded text-center">
|
||||||
<img
|
<img
|
||||||
src={imageUrl}
|
src={imageUrl}
|
||||||
width={200}
|
width={160}
|
||||||
height={200}
|
height={160}
|
||||||
alt="LiteLLM Brand"
|
alt="LiteLLM Brand"
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -20,6 +20,7 @@ import {
|
||||||
TableHead,
|
TableHead,
|
||||||
TableHeaderCell,
|
TableHeaderCell,
|
||||||
TableRow,
|
TableRow,
|
||||||
|
TextInput,
|
||||||
Card,
|
Card,
|
||||||
Icon,
|
Icon,
|
||||||
Button,
|
Button,
|
||||||
|
@ -480,7 +481,7 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
|
||||||
name="team_alias"
|
name="team_alias"
|
||||||
rules={[{ required: true, message: 'Please input a team name' }]}
|
rules={[{ required: true, message: 'Please input a team name' }]}
|
||||||
>
|
>
|
||||||
<Input />
|
<TextInput placeholder="" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Models" name="models">
|
<Form.Item label="Models" name="models">
|
||||||
<Select2
|
<Select2
|
||||||
|
|
|
@ -295,6 +295,7 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
||||||
userRole={userRole}
|
userRole={userRole}
|
||||||
accessToken={accessToken}
|
accessToken={accessToken}
|
||||||
userSpend={null}
|
userSpend={null}
|
||||||
|
selectedTeam={null}
|
||||||
/>
|
/>
|
||||||
<TabGroup>
|
<TabGroup>
|
||||||
<TabList className="mt-2">
|
<TabList className="mt-2">
|
||||||
|
@ -391,6 +392,7 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
||||||
index="date"
|
index="date"
|
||||||
categories={uniqueTeamIds}
|
categories={uniqueTeamIds}
|
||||||
yAxisWidth={80}
|
yAxisWidth={80}
|
||||||
|
colors={["blue", "green", "yellow", "red", "purple"]}
|
||||||
|
|
||||||
stack={true}
|
stack={true}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -109,6 +109,10 @@ const ViewUserSpend: React.FC<ViewUserSpendProps> = ({ userID, userRole, accessT
|
||||||
if (modelsToDisplay && modelsToDisplay.includes("all-proxy-models")) {
|
if (modelsToDisplay && modelsToDisplay.includes("all-proxy-models")) {
|
||||||
console.log("user models:", userModels);
|
console.log("user models:", userModels);
|
||||||
modelsToDisplay = userModels;
|
modelsToDisplay = userModels;
|
||||||
|
} else if (modelsToDisplay && modelsToDisplay.includes("all-team-models")) {
|
||||||
|
modelsToDisplay = selectedTeam.models;
|
||||||
|
} else if (modelsToDisplay && modelsToDisplay.length === 0) {
|
||||||
|
modelsToDisplay = userModels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,7 +133,7 @@ const ViewUserSpend: React.FC<ViewUserSpendProps> = ({ userID, userRole, accessT
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-auto">
|
<div className="ml-auto">
|
||||||
<Accordion>
|
<Accordion>
|
||||||
<AccordionHeader>Models</AccordionHeader>
|
<AccordionHeader><Text>Team Models</Text></AccordionHeader>
|
||||||
<AccordionBody className="absolute right-0 z-10 bg-white p-2 shadow-lg max-w-xs">
|
<AccordionBody className="absolute right-0 z-10 bg-white p-2 shadow-lg max-w-xs">
|
||||||
<List>
|
<List>
|
||||||
{modelsToDisplay.map((model: string) => (
|
{modelsToDisplay.map((model: string) => (
|
||||||
|
|
|
@ -21,7 +21,8 @@ import {
|
||||||
SelectItem,
|
SelectItem,
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogPanel,
|
DialogPanel,
|
||||||
Icon
|
Icon,
|
||||||
|
TextInput,
|
||||||
} from "@tremor/react";
|
} from "@tremor/react";
|
||||||
import { userInfoCall, adminTopEndUsersCall } from "./networking";
|
import { userInfoCall, adminTopEndUsersCall } from "./networking";
|
||||||
import { Badge, BadgeDelta, Button } from "@tremor/react";
|
import { Badge, BadgeDelta, Button } from "@tremor/react";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue