mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
Add digitalocean provider
This commit is contained in:
parent
f196434d55
commit
96dda40f45
17 changed files with 1396 additions and 796 deletions
|
|
@ -1154,7 +1154,7 @@ jobs:
|
|||
name: Install Helm
|
||||
command: |
|
||||
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
||||
|
||||
|
||||
# Install kind
|
||||
- run:
|
||||
name: Install Kind
|
||||
|
|
@ -1162,7 +1162,7 @@ jobs:
|
|||
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
|
||||
chmod +x ./kind
|
||||
sudo mv ./kind /usr/local/bin/kind
|
||||
|
||||
|
||||
# Install kubectl
|
||||
- run:
|
||||
name: Install kubectl
|
||||
|
|
@ -1170,19 +1170,19 @@ jobs:
|
|||
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
|
||||
chmod +x kubectl
|
||||
sudo mv kubectl /usr/local/bin/
|
||||
|
||||
|
||||
# Create kind cluster
|
||||
- run:
|
||||
name: Create Kind Cluster
|
||||
command: |
|
||||
kind create cluster --name litellm-test
|
||||
|
||||
|
||||
# Run helm lint
|
||||
- run:
|
||||
name: Run helm lint
|
||||
command: |
|
||||
helm lint ./deploy/charts/litellm-helm
|
||||
|
||||
|
||||
# Run helm tests
|
||||
- run:
|
||||
name: Run helm tests
|
||||
|
|
@ -1191,15 +1191,15 @@ jobs:
|
|||
# Wait for pod to be ready
|
||||
echo "Waiting 30 seconds for pod to be ready..."
|
||||
sleep 30
|
||||
|
||||
|
||||
# Print pod logs before running tests
|
||||
echo "Printing pod logs..."
|
||||
kubectl logs $(kubectl get pods -l app.kubernetes.io/name=litellm -o jsonpath="{.items[0].metadata.name}")
|
||||
|
||||
|
||||
# Run the helm tests
|
||||
helm test litellm --logs
|
||||
helm test litellm --logs
|
||||
|
||||
|
||||
# Cleanup
|
||||
- run:
|
||||
name: Cleanup
|
||||
|
|
@ -1309,7 +1309,7 @@ jobs:
|
|||
echo "=== Printing Full Container Startup Logs ==="
|
||||
docker logs my-app
|
||||
echo "=== End of Full Container Startup Logs ==="
|
||||
|
||||
|
||||
if docker logs my-app 2>&1 | grep -q "prisma schema out of sync with db. Consider running these sql_commands to sync the two"; then
|
||||
echo "Expected message found in logs. Test passed."
|
||||
else
|
||||
|
|
@ -1427,6 +1427,7 @@ jobs:
|
|||
-e LANGFUSE_PROJECT2_PUBLIC=$LANGFUSE_PROJECT2_PUBLIC \
|
||||
-e LANGFUSE_PROJECT1_SECRET=$LANGFUSE_PROJECT1_SECRET \
|
||||
-e LANGFUSE_PROJECT2_SECRET=$LANGFUSE_PROJECT2_SECRET \
|
||||
-e DIGITALOCEAN_API_KEY=$DIGITALOCEAN_API_KEY \
|
||||
--name my-app \
|
||||
-v $(pwd)/proxy_server_config.yaml:/app/config.yaml \
|
||||
my-app:latest \
|
||||
|
|
@ -1552,6 +1553,7 @@ jobs:
|
|||
-e LANGFUSE_PROJECT2_PUBLIC=$LANGFUSE_PROJECT2_PUBLIC \
|
||||
-e LANGFUSE_PROJECT1_SECRET=$LANGFUSE_PROJECT1_SECRET \
|
||||
-e LANGFUSE_PROJECT2_SECRET=$LANGFUSE_PROJECT2_SECRET \
|
||||
-e DIGITALOCEAN_API_KEY=$DIGITALOCEAN_API_KEY \
|
||||
--name my-app \
|
||||
-v $(pwd)/litellm/proxy/example_config_yaml/oai_misc_config.yaml:/app/config.yaml \
|
||||
my-app:latest \
|
||||
|
|
@ -2030,7 +2032,7 @@ jobs:
|
|||
no_output_timeout:
|
||||
120m
|
||||
# Clean up first container
|
||||
|
||||
|
||||
proxy_build_from_pip_tests:
|
||||
# Change from docker to machine executor
|
||||
machine:
|
||||
|
|
@ -2235,17 +2237,17 @@ jobs:
|
|||
curl -sSL https://rvm.io/mpapis.asc | gpg --import -
|
||||
curl -sSL https://rvm.io/pkuczynski.asc | gpg --import -
|
||||
}
|
||||
|
||||
|
||||
# Install Ruby version manager (RVM)
|
||||
curl -sSL https://get.rvm.io | bash -s stable
|
||||
|
||||
|
||||
# Source RVM from the correct location
|
||||
source $HOME/.rvm/scripts/rvm
|
||||
|
||||
|
||||
# Install Ruby 3.2.2
|
||||
rvm install 3.2.2
|
||||
rvm use 3.2.2 --default
|
||||
|
||||
|
||||
# Install latest Bundler
|
||||
gem install bundler
|
||||
|
||||
|
|
@ -2408,32 +2410,32 @@ jobs:
|
|||
python -m pip install toml
|
||||
# Get current version from pyproject.toml
|
||||
CURRENT_VERSION=$(python -c "import toml; print(toml.load('pyproject.toml')['tool']['poetry']['version'])")
|
||||
|
||||
|
||||
# Get last published version from PyPI
|
||||
LAST_VERSION=$(curl -s https://pypi.org/pypi/litellm-proxy-extras/json | python -c "import json, sys; print(json.load(sys.stdin)['info']['version'])")
|
||||
|
||||
|
||||
echo "Current version: $CURRENT_VERSION"
|
||||
echo "Last published version: $LAST_VERSION"
|
||||
|
||||
|
||||
# Compare versions using Python's packaging.version
|
||||
VERSION_COMPARE=$(python -c "from packaging import version; print(1 if version.parse('$CURRENT_VERSION') < version.parse('$LAST_VERSION') else 0)")
|
||||
|
||||
|
||||
echo "Version compare: $VERSION_COMPARE"
|
||||
if [ "$VERSION_COMPARE" = "1" ]; then
|
||||
echo "Error: Current version ($CURRENT_VERSION) is less than last published version ($LAST_VERSION)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# If versions are equal or current is greater, check contents
|
||||
pip download --no-deps litellm-proxy-extras==$LAST_VERSION -d /tmp
|
||||
|
||||
|
||||
echo "Contents of /tmp directory:"
|
||||
ls -la /tmp
|
||||
|
||||
|
||||
# Find the downloaded file (could be .whl or .tar.gz)
|
||||
DOWNLOADED_FILE=$(ls /tmp/litellm_proxy_extras-*)
|
||||
echo "Downloaded file: $DOWNLOADED_FILE"
|
||||
|
||||
|
||||
# Extract based on file extension
|
||||
if [[ "$DOWNLOADED_FILE" == *.whl ]]; then
|
||||
echo "Extracting wheel file..."
|
||||
|
|
@ -2444,10 +2446,10 @@ jobs:
|
|||
tar -xzf "$DOWNLOADED_FILE" -C /tmp
|
||||
EXTRACTED_DIR="/tmp/litellm_proxy_extras-$LAST_VERSION"
|
||||
fi
|
||||
|
||||
|
||||
echo "Contents of extracted package:"
|
||||
ls -R "$EXTRACTED_DIR"
|
||||
|
||||
|
||||
# Compare contents
|
||||
if ! diff -r "$EXTRACTED_DIR/litellm_proxy_extras" ./litellm_proxy_extras; then
|
||||
if [ "$CURRENT_VERSION" = "$LAST_VERSION" ]; then
|
||||
|
|
@ -2513,16 +2515,16 @@ jobs:
|
|||
export NVM_DIR="/opt/circleci/.nvm"
|
||||
source "$NVM_DIR/nvm.sh"
|
||||
source "$NVM_DIR/bash_completion"
|
||||
|
||||
|
||||
# Install and use Node version
|
||||
nvm install v18.17.0
|
||||
nvm use v18.17.0
|
||||
|
||||
|
||||
cd ui/litellm-dashboard
|
||||
|
||||
|
||||
# Install dependencies first
|
||||
npm install
|
||||
|
||||
|
||||
# Now source the build script
|
||||
source ./build_ui.sh
|
||||
- run:
|
||||
|
|
@ -2569,7 +2571,7 @@ jobs:
|
|||
name: Install Playwright Browsers
|
||||
command: |
|
||||
npx playwright install
|
||||
|
||||
|
||||
- run:
|
||||
name: Build Docker image
|
||||
command: docker build -t my-app:latest -f ./docker/Dockerfile.database .
|
||||
|
|
@ -2933,4 +2935,4 @@ workflows:
|
|||
- proxy_pass_through_endpoint_tests
|
||||
- check_code_and_doc_quality
|
||||
- publish_proxy_extras
|
||||
|
||||
|
||||
|
|
|
|||
13
README.md
13
README.md
|
|
@ -44,7 +44,7 @@ Support for more providers. Missing a provider or LLM Platform, raise a [feature
|
|||
# Usage ([**Docs**](https://docs.litellm.ai/docs/))
|
||||
|
||||
> [!IMPORTANT]
|
||||
> LiteLLM v1.0.0 now requires `openai>=1.0.0`. Migration guide [here](https://docs.litellm.ai/docs/migration)
|
||||
> LiteLLM v1.0.0 now requires `openai>=1.0.0`. Migration guide [here](https://docs.litellm.ai/docs/migration)
|
||||
> LiteLLM v1.40.14+ now requires `pydantic>=2.0.0`. No changes required.
|
||||
|
||||
<a target="_blank" href="https://colab.research.google.com/github/BerriAI/litellm/blob/main/cookbook/liteLLM_Getting_Started.ipynb">
|
||||
|
|
@ -129,7 +129,7 @@ print(response)
|
|||
|
||||
## Streaming ([Docs](https://docs.litellm.ai/docs/completion/stream))
|
||||
|
||||
liteLLM supports streaming the model response back, pass `stream=True` to get a streaming iterator in response.
|
||||
liteLLM supports streaming the model response back, pass `stream=True` to get a streaming iterator in response.
|
||||
Streaming is supported for all models (Bedrock, Huggingface, TogetherAI, Azure, OpenAI, etc.)
|
||||
|
||||
```python
|
||||
|
|
@ -227,7 +227,7 @@ $ litellm --model huggingface/bigcode/starcoder
|
|||
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 💡 [Use LiteLLM Proxy with Langchain (Python, JS), OpenAI SDK (Python, JS) Anthropic SDK, Mistral SDK, LlamaIndex, Instructor, Curl](https://docs.litellm.ai/docs/proxy/user_keys)
|
||||
> 💡 [Use LiteLLM Proxy with Langchain (Python, JS), OpenAI SDK (Python, JS) Anthropic SDK, Mistral SDK, LlamaIndex, Instructor, Curl](https://docs.litellm.ai/docs/proxy/user_keys)
|
||||
|
||||
```python
|
||||
import openai # openai v1.0.0+
|
||||
|
|
@ -259,7 +259,7 @@ echo 'LITELLM_MASTER_KEY="sk-1234"' > .env
|
|||
|
||||
# Add the litellm salt key - you cannot change this after adding a model
|
||||
# It is used to encrypt / decrypt your LLM API Key credentials
|
||||
# We recommend - https://1password.com/password-generator/
|
||||
# We recommend - https://1password.com/password-generator/
|
||||
# password generator to get a random hash for litellm salt key
|
||||
echo 'LITELLM_SALT_KEY="sk-1234"' > .env
|
||||
|
||||
|
|
@ -332,6 +332,7 @@ curl 'http://0.0.0.0:4000/key/generate' \
|
|||
| [xinference [Xorbits Inference]](https://docs.litellm.ai/docs/providers/xinference) | | | | | ✅ | |
|
||||
| [FriendliAI](https://docs.litellm.ai/docs/providers/friendliai) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [Galadriel](https://docs.litellm.ai/docs/providers/galadriel) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [DigitalOcean](https://docs.litellm.ai/docs/providers/digitalocean) | ✅ | ✅ | | | | |
|
||||
|
||||
[**Read the Docs**](https://docs.litellm.ai/docs/)
|
||||
|
||||
|
|
@ -344,7 +345,7 @@ For companies that need better security, user management and professional suppor
|
|||
|
||||
[Talk to founders](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat)
|
||||
|
||||
This covers:
|
||||
This covers:
|
||||
- ✅ **Features under the [LiteLLM Commercial License](https://docs.litellm.ai/docs/proxy/enterprise):**
|
||||
- ✅ **Feature Prioritization**
|
||||
- ✅ **Custom Integrations**
|
||||
|
|
@ -356,7 +357,7 @@ This covers:
|
|||
|
||||
LiteLLM follows the [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html).
|
||||
|
||||
We run:
|
||||
We run:
|
||||
- Ruff for [formatting and linting checks](https://github.com/BerriAI/litellm/blob/e19bb55e3b4c6a858b6e364302ebbf6633a51de5/.circleci/config.yml#L320)
|
||||
- Mypy + Pyright for typing [1](https://github.com/BerriAI/litellm/blob/e19bb55e3b4c6a858b6e364302ebbf6633a51de5/.circleci/config.yml#L90), [2](https://github.com/BerriAI/litellm/blob/e19bb55e3b4c6a858b6e364302ebbf6633a51de5/.pre-commit-config.yaml#L4)
|
||||
- Black for [formatting](https://github.com/BerriAI/litellm/blob/e19bb55e3b4c6a858b6e364302ebbf6633a51de5/.circleci/config.yml#L79)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@ COPY . /app
|
|||
WORKDIR /app
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
EXPOSE $PORT
|
||||
EXPOSE 3000
|
||||
|
||||
CMD litellm --host 0.0.0.0 --port $PORT --workers 10 --config config.yaml
|
||||
CMD litellm --host 0.0.0.0 --port 3000 --workers 10 --config config.yaml
|
||||
|
|
@ -245,7 +245,8 @@ const sidebars = {
|
|||
"providers/sambanova",
|
||||
"providers/custom_llm_server",
|
||||
"providers/petals",
|
||||
"providers/snowflake"
|
||||
"providers/snowflake",
|
||||
"providers/digitalocean",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -193,6 +193,7 @@ baseten_key: Optional[str] = None
|
|||
aleph_alpha_key: Optional[str] = None
|
||||
nlp_cloud_key: Optional[str] = None
|
||||
snowflake_key: Optional[str] = None
|
||||
digitalocean_api_key: Optional[str] = None
|
||||
common_cloud_provider_auth_params: dict = {
|
||||
"params": ["project", "region_name", "token"],
|
||||
"providers": ["vertex_ai", "bedrock", "watsonx", "azure", "vertex_ai_beta"],
|
||||
|
|
@ -431,6 +432,7 @@ galadriel_models: List = []
|
|||
sambanova_models: List = []
|
||||
assemblyai_models: List = []
|
||||
snowflake_models: List = []
|
||||
digitalocean_models: List = []
|
||||
|
||||
|
||||
def is_bedrock_pricing_only_model(key: str) -> bool:
|
||||
|
|
@ -588,6 +590,8 @@ def add_known_models():
|
|||
jina_ai_models.append(key)
|
||||
elif value.get("litellm_provider") == "snowflake":
|
||||
snowflake_models.append(key)
|
||||
elif value.get("litellm_provider") == "digitalocean":
|
||||
digitalocean_models.append(key)
|
||||
|
||||
|
||||
add_known_models()
|
||||
|
|
@ -664,6 +668,7 @@ model_list = (
|
|||
+ assemblyai_models
|
||||
+ jina_ai_models
|
||||
+ snowflake_models
|
||||
+ digitalocean_models
|
||||
)
|
||||
|
||||
model_list_set = set(model_list)
|
||||
|
|
@ -721,6 +726,7 @@ models_by_provider: dict = {
|
|||
"assemblyai": assemblyai_models,
|
||||
"jina_ai": jina_ai_models,
|
||||
"snowflake": snowflake_models,
|
||||
"digitalocean": digitalocean_models,
|
||||
}
|
||||
|
||||
# mapping for those models which have larger equivalents
|
||||
|
|
@ -963,7 +969,7 @@ from .llms.openai.chat.o_series_transformation import (
|
|||
)
|
||||
|
||||
from .llms.snowflake.chat.transformation import SnowflakeConfig
|
||||
|
||||
from .llms.digitalocean.chat.transformation import DigitalOceanConfig
|
||||
openaiOSeriesConfig = OpenAIOSeriesConfig()
|
||||
from .llms.openai.chat.gpt_transformation import (
|
||||
OpenAIGPTConfig,
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ LITELLM_CHAT_PROVIDERS = [
|
|||
"hosted_vllm",
|
||||
"lm_studio",
|
||||
"galadriel",
|
||||
"digitalocean"
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -321,6 +321,8 @@ def get_llm_provider( # noqa: PLR0915
|
|||
custom_llm_provider = "openai"
|
||||
elif model in litellm.empower_models:
|
||||
custom_llm_provider = "empower"
|
||||
elif model in litellm.digitalocean_models:
|
||||
custom_llm_provider = "digitalocean"
|
||||
elif model == "*":
|
||||
custom_llm_provider = "openai"
|
||||
if not custom_llm_provider:
|
||||
|
|
@ -580,6 +582,13 @@ def _get_openai_compatible_provider_info( # noqa: PLR0915
|
|||
or f"https://{get_secret('SNOWFLAKE_ACCOUNT_ID')}.snowflakecomputing.com/api/v2/cortex/inference:complete"
|
||||
) # type: ignore
|
||||
dynamic_api_key = api_key or get_secret_str("SNOWFLAKE_JWT")
|
||||
elif custom_llm_provider == "digitalocean":
|
||||
(
|
||||
api_base,
|
||||
dynamic_api_key,
|
||||
) = litellm.DigitalOceanConfig()._get_openai_compatible_provider_info(
|
||||
api_base, api_key
|
||||
)
|
||||
|
||||
if api_base is not None and not isinstance(api_base, str):
|
||||
raise Exception("api base needs to be a string. api_base={}".format(api_base))
|
||||
|
|
|
|||
191
litellm/llms/digitalocean/chat/transformation.py
Normal file
191
litellm/llms/digitalocean/chat/transformation.py
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
"""
|
||||
Translate from OpenAI's `/v1/chat/completions` to DigitalOcean AI Agent's `/v1/chat/completions`
|
||||
"""
|
||||
|
||||
from typing import List, Optional, Tuple, Union, Dict
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from litellm.secret_managers.main import get_secret_str
|
||||
from litellm.types.llms.openai import (
|
||||
AllMessageValues,
|
||||
ChatCompletionAssistantMessage,
|
||||
ChatCompletionToolParam,
|
||||
ChatCompletionToolParamFunctionChunk,
|
||||
)
|
||||
|
||||
from ...openai_like.chat.transformation import OpenAILikeChatConfig
|
||||
|
||||
|
||||
class DigitalOceanConfig(OpenAILikeChatConfig):
|
||||
|
||||
# DigitalOcean-specific parameters:
|
||||
stream_options: Optional[Dict] = None
|
||||
kb_filters: Optional[List[Dict]] = None
|
||||
filter_kb_content_by_query_metadata: Optional[bool] = None
|
||||
instruction_override: Optional[str] = None
|
||||
include_functions_info: Optional[bool] = None
|
||||
include_retrieval_info: Optional[bool] = None
|
||||
include_guardrails_info: Optional[bool] = None
|
||||
provide_citations: Optional[bool] = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
frequency_penalty: Optional[float] = None,
|
||||
function_call: Optional[Union[str, Dict]] = None,
|
||||
functions: Optional[List] = None,
|
||||
logit_bias: Optional[Dict] = None,
|
||||
max_tokens: Optional[int] = None,
|
||||
max_completion_tokens: Optional[int] = None, # Additional field; interchangeable with max_tokens per spec
|
||||
n: Optional[int] = None,
|
||||
presence_penalty: Optional[float] = None,
|
||||
retrieval_method: Optional[str] = None, # expected values: "rewrite", "step_back", "sub_queries", "none"
|
||||
stop: Optional[Union[str, List[str]]] = None,
|
||||
stream: Optional[bool] = None,
|
||||
temperature: Optional[float] = None,
|
||||
top_p: Optional[float] = None,
|
||||
response_format: Optional[Dict] = None,
|
||||
tools: Optional[List] = None,
|
||||
tool_choice: Optional[Union[str, Dict]] = None,
|
||||
) -> None:
|
||||
locals_ = locals().copy()
|
||||
for key, value in locals_.items():
|
||||
if key != "self" and value is not None:
|
||||
setattr(self.__class__, key, value)
|
||||
|
||||
@classmethod
|
||||
def get_config(cls):
|
||||
return super().get_config()
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> list:
|
||||
base_params = super().get_supported_openai_params(model)
|
||||
try:
|
||||
base_params.remove("max_retries")
|
||||
except ValueError:
|
||||
pass
|
||||
return base_params
|
||||
|
||||
def validate_environment(self,
|
||||
headers: dict,
|
||||
model: str,
|
||||
messages: List[AllMessageValues],
|
||||
optional_params: dict,
|
||||
litellm_params: dict,
|
||||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None):
|
||||
api_key = api_key or get_secret_str("DIGITALOCEAN_API_KEY")
|
||||
if api_key is None:
|
||||
raise ValueError("DigitalOcean API key not found")
|
||||
if headers is None:
|
||||
headers = {}
|
||||
headers["Authorization"] = f"Bearer {api_key}"
|
||||
headers["Content-Type"] = "application/json"
|
||||
return headers
|
||||
|
||||
|
||||
def get_complete_url(
|
||||
self,
|
||||
api_base: Optional[str],
|
||||
api_key: Optional[str],
|
||||
model: str,
|
||||
optional_params: dict,
|
||||
litellm_params: dict,
|
||||
stream: Optional[bool] = None,
|
||||
) -> str:
|
||||
|
||||
complete_url = f"{api_base}/api/v1/chat/completions"
|
||||
return complete_url
|
||||
|
||||
def _transform_messages(self, messages: List[AllMessageValues], model: str) -> List:
|
||||
for idx, message in enumerate(messages):
|
||||
"""
|
||||
1. Don't pass 'null' function_call assistant message to groq - https://github.com/BerriAI/litellm/issues/5839
|
||||
"""
|
||||
if isinstance(message, BaseModel):
|
||||
_message = message.model_dump()
|
||||
else:
|
||||
_message = message
|
||||
assistant_message = _message.get("role") == "assistant"
|
||||
if assistant_message:
|
||||
new_message = ChatCompletionAssistantMessage(role="assistant")
|
||||
for k, v in _message.items():
|
||||
if v is not None:
|
||||
new_message[k] = v # type: ignore
|
||||
messages[idx] = new_message
|
||||
|
||||
return messages
|
||||
|
||||
def _get_openai_compatible_provider_info(
|
||||
self, api_base: Optional[str], api_key: Optional[str]
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
api_base = (
|
||||
api_base
|
||||
or get_secret_str("DIGITALOCEAN_AGENT_ENDPOINT")
|
||||
or get_secret_str("DO_AGENT_ENDPOINT")
|
||||
) # type: ignore
|
||||
dynamic_api_key = api_key or get_secret_str("DO_API_KEY") or get_secret_str("DIGITALOCEAN_API_KEY")
|
||||
return api_base, dynamic_api_key
|
||||
|
||||
|
||||
|
||||
def _create_json_tool_call_for_response_format(
|
||||
self,
|
||||
json_schema: dict,
|
||||
):
|
||||
"""
|
||||
Handles creating a tool call for getting responses in JSON format.
|
||||
|
||||
Args:
|
||||
json_schema (Optional[dict]): The JSON schema the response should be in
|
||||
|
||||
Returns:
|
||||
AnthropicMessagesTool: The tool call to send to Anthropic API to get responses in JSON format
|
||||
"""
|
||||
return ChatCompletionToolParam(
|
||||
type="function",
|
||||
function=ChatCompletionToolParamFunctionChunk(
|
||||
name="json_tool_call",
|
||||
parameters=json_schema,
|
||||
),
|
||||
)
|
||||
|
||||
def map_openai_params(
|
||||
self,
|
||||
non_default_params: dict,
|
||||
optional_params: dict,
|
||||
model: str,
|
||||
drop_params: bool = False,
|
||||
replace_max_completion_tokens_with_max_tokens: bool = False, # groq supports max_completion_tokens
|
||||
) -> dict:
|
||||
_response_format = non_default_params.get("response_format")
|
||||
if _response_format is not None and isinstance(_response_format, dict):
|
||||
json_schema: Optional[dict] = None
|
||||
if "response_schema" in _response_format:
|
||||
json_schema = _response_format["response_schema"]
|
||||
elif "json_schema" in _response_format:
|
||||
json_schema = _response_format["json_schema"]["schema"]
|
||||
"""
|
||||
When using tools in this way: - https://docs.anthropic.com/en/docs/build-with-claude/tool-use#json-mode
|
||||
- You usually want to provide a single tool
|
||||
- You should set tool_choice (see Forcing tool use) to instruct the model to explicitly use that tool
|
||||
- Remember that the model will pass the input to the tool, so the name of the tool and description should be from the model’s perspective.
|
||||
"""
|
||||
if json_schema is not None:
|
||||
_tool_choice = {
|
||||
"type": "function",
|
||||
"function": {"name": "json_tool_call"},
|
||||
}
|
||||
_tool = self._create_json_tool_call_for_response_format(
|
||||
json_schema=json_schema,
|
||||
)
|
||||
optional_params["tools"] = [_tool]
|
||||
optional_params["tool_choice"] = _tool_choice
|
||||
optional_params["json_mode"] = True
|
||||
non_default_params.pop(
|
||||
"response_format", None
|
||||
) # only remove if it's a json_schema - handled via using groq's tool calling params.
|
||||
optional_params = super().map_openai_params(
|
||||
non_default_params, optional_params, model, drop_params
|
||||
)
|
||||
|
||||
return optional_params
|
||||
|
|
@ -3065,6 +3065,26 @@ def completion( # type: ignore # noqa: PLR0915
|
|||
additional_args={"headers": headers},
|
||||
)
|
||||
raise e
|
||||
elif custom_llm_provider == "digitalocean":
|
||||
|
||||
api_base = litellm.api_base or api_base
|
||||
response = base_llm_http_handler.completion(
|
||||
model=model,
|
||||
stream=stream,
|
||||
messages=messages,
|
||||
acompletion=acompletion,
|
||||
api_base=api_base,
|
||||
model_response=model_response,
|
||||
optional_params=optional_params,
|
||||
litellm_params=litellm_params,
|
||||
custom_llm_provider="digitalocean",
|
||||
timeout=timeout,
|
||||
headers=headers,
|
||||
encoding=encoding,
|
||||
api_key=api_key,
|
||||
logging_obj=logging, # model call logging done inside the class as we make need to modify I/O to fit aleph alpha's requirements
|
||||
client=client,
|
||||
)
|
||||
|
||||
elif custom_llm_provider == "custom":
|
||||
url = litellm.api_base or api_base or ""
|
||||
|
|
|
|||
|
|
@ -2102,6 +2102,7 @@ class LlmProviders(str, Enum):
|
|||
TOPAZ = "topaz"
|
||||
ASSEMBLYAI = "assemblyai"
|
||||
SNOWFLAKE = "snowflake"
|
||||
DIGITALOCEAN = "digitalocean"
|
||||
|
||||
|
||||
# Create a set of all provider values for quick lookup
|
||||
|
|
|
|||
|
|
@ -3705,6 +3705,17 @@ def get_optional_params( # noqa: PLR0915
|
|||
else False
|
||||
),
|
||||
)
|
||||
elif custom_llm_provider == "digitalocean":
|
||||
optional_params = litellm.DigitalOceanConfig().map_openai_params(
|
||||
non_default_params=non_default_params,
|
||||
optional_params=optional_params,
|
||||
model=model,
|
||||
drop_params=(
|
||||
drop_params
|
||||
if drop_params is not None and isinstance(drop_params, bool)
|
||||
else False
|
||||
),
|
||||
)
|
||||
elif custom_llm_provider == "openai":
|
||||
optional_params = litellm.OpenAIConfig().map_openai_params(
|
||||
non_default_params=non_default_params,
|
||||
|
|
@ -6569,6 +6580,8 @@ class ProviderConfigManager:
|
|||
return litellm.LiteLLMProxyChatConfig()
|
||||
elif litellm.LlmProviders.OPENAI == provider:
|
||||
return litellm.OpenAIGPTConfig()
|
||||
elif litellm.LlmProviders.DIGITALOCEAN == provider:
|
||||
return litellm.DigitalOceanConfig()
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -18,7 +18,7 @@ model_list:
|
|||
api_version: "2023-05-15"
|
||||
api_key: os.environ/AZURE_API_KEY # The `os.environ/` prefix tells litellm to read this from the env. See https://docs.litellm.ai/docs/simple_proxy#load-api-keys-from-vault
|
||||
- model_name: gpt-3.5-turbo-large
|
||||
litellm_params:
|
||||
litellm_params:
|
||||
model: "gpt-3.5-turbo-1106"
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
rpm: 480
|
||||
|
|
@ -36,9 +36,9 @@ model_list:
|
|||
- model_name: sagemaker-completion-model
|
||||
litellm_params:
|
||||
model: sagemaker/berri-benchmarking-Llama-2-70b-chat-hf-4
|
||||
input_cost_per_second: 0.000420
|
||||
input_cost_per_second: 0.000420
|
||||
- model_name: text-embedding-ada-002
|
||||
litellm_params:
|
||||
litellm_params:
|
||||
model: azure/azure-embedding-model
|
||||
api_key: os.environ/AZURE_API_KEY
|
||||
api_base: https://openai-gpt-4-test-v-1.openai.azure.com/
|
||||
|
|
@ -106,7 +106,7 @@ model_list:
|
|||
litellm_params:
|
||||
model: openai/*
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
|
||||
|
||||
|
||||
# provider specific wildcard routing
|
||||
- model_name: "anthropic/*"
|
||||
|
|
@ -137,18 +137,23 @@ model_list:
|
|||
model: openai/my-fake-model
|
||||
api_key: my-fake-key
|
||||
api_base: https://exampleopenaiendpoint-production.up.railway.appxxxx/
|
||||
- model_name: "digitalocean/*"
|
||||
litellm_params:
|
||||
model: "digitalocean/*"
|
||||
api_key: os.environ/DIGITALOCEAN_API_KEY
|
||||
api_base: os.environ/DIGITALOCEAN_AGENT_ENDPOINT
|
||||
|
||||
|
||||
litellm_settings:
|
||||
# set_verbose: True # Uncomment this if you want to see verbose logs; not recommended in production
|
||||
drop_params: True
|
||||
# max_budget: 100
|
||||
# max_budget: 100
|
||||
# budget_duration: 30d
|
||||
num_retries: 5
|
||||
request_timeout: 600
|
||||
telemetry: False
|
||||
context_window_fallbacks: [{"gpt-3.5-turbo": ["gpt-3.5-turbo-large"]}]
|
||||
default_team_settings:
|
||||
default_team_settings:
|
||||
- team_id: team-1
|
||||
success_callback: ["langfuse"]
|
||||
failure_callback: ["langfuse"]
|
||||
|
|
@ -180,14 +185,14 @@ files_settings:
|
|||
api_key: os.environ/OPENAI_API_KEY
|
||||
|
||||
router_settings:
|
||||
routing_strategy: usage-based-routing-v2
|
||||
routing_strategy: usage-based-routing-v2
|
||||
redis_host: os.environ/REDIS_HOST
|
||||
redis_password: os.environ/REDIS_PASSWORD
|
||||
redis_port: os.environ/REDIS_PORT
|
||||
enable_pre_call_checks: true
|
||||
model_group_alias: {"my-special-fake-model-alias-name": "fake-openai-endpoint-3"}
|
||||
model_group_alias: {"my-special-fake-model-alias-name": "fake-openai-endpoint-3"}
|
||||
|
||||
general_settings:
|
||||
general_settings:
|
||||
master_key: sk-1234 # [OPTIONAL] Use to enforce auth on proxy. See - https://docs.litellm.ai/docs/proxy/virtual_keys
|
||||
store_model_in_db: True
|
||||
proxy_budget_rescheduler_min_time: 60
|
||||
|
|
@ -200,7 +205,7 @@ general_settings:
|
|||
- path: "/v1/rerank" # route you want to add to LiteLLM Proxy Server
|
||||
target: "https://api.cohere.com/v1/rerank" # URL this route should forward requests to
|
||||
headers: # headers to forward to this URL
|
||||
content-type: application/json # (Optional) Extra Headers to pass to this endpoint
|
||||
content-type: application/json # (Optional) Extra Headers to pass to this endpoint
|
||||
accept: application/json
|
||||
forward_headers: True
|
||||
|
||||
|
|
@ -208,4 +213,4 @@ general_settings:
|
|||
# settings for using redis caching
|
||||
# REDIS_HOST: redis-16337.c322.us-east-1-2.ec2.cloud.redislabs.com
|
||||
# REDIS_PORT: "16337"
|
||||
# REDIS_PASSWORD:
|
||||
# REDIS_PASSWORD:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
import os
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
sys.path.insert(
|
||||
0, os.path.abspath("../../../../..")
|
||||
) # Adds the parent directory to the system path
|
||||
|
||||
from litellm.llms.digitalocean.chat.transformation import DigitalOceanConfig
|
||||
|
||||
DO_ENDPOINT_PATH = "/api/v1/chat/completions"
|
||||
DO_BASE_URL = "https://api.digitalocean.com"
|
||||
|
||||
@pytest.fixture
|
||||
def config():
|
||||
return DigitalOceanConfig()
|
||||
|
||||
def test_validate_environment_sets_headers(monkeypatch, config):
|
||||
monkeypatch.setenv("DIGITALOCEAN_API_KEY", "test-key")
|
||||
headers = {}
|
||||
result = config.validate_environment(
|
||||
headers=headers,
|
||||
model="digitalocean/test-model",
|
||||
messages=[],
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
api_key=None,
|
||||
api_base=None,
|
||||
)
|
||||
assert result["Authorization"] == "Bearer test-key"
|
||||
assert result["Content-Type"] == "application/json"
|
||||
|
||||
def test_get_complete_url(config):
|
||||
url = config.get_complete_url(
|
||||
api_base=DO_BASE_URL,
|
||||
api_key="test-key",
|
||||
model="digitalocean/test-model",
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
stream=False,
|
||||
)
|
||||
assert url == f"{DO_BASE_URL}{DO_ENDPOINT_PATH}"
|
||||
|
||||
def test_transform_messages_handles_dicts_only(config):
|
||||
messages = [
|
||||
{"role": "assistant", "content": "Hello!"},
|
||||
{"role": "user", "content": "Hi!"},
|
||||
]
|
||||
out = config._transform_messages(messages, model="digitalocean/test-model")
|
||||
assert out[0]["role"] == "assistant"
|
||||
assert out[0]["content"] == "Hello!"
|
||||
assert out[1]["role"] == "user"
|
||||
assert out[1]["content"] == "Hi!"
|
||||
|
||||
def test_get_openai_compatible_provider_info_env(monkeypatch, config):
|
||||
monkeypatch.setenv("DIGITALOCEAN_AGENT_ENDPOINT", DO_BASE_URL)
|
||||
monkeypatch.setenv("DIGITALOCEAN_API_KEY", "env-key")
|
||||
api_base, api_key = config._get_openai_compatible_provider_info(None, None)
|
||||
assert api_base == DO_BASE_URL
|
||||
assert api_key == "env-key"
|
||||
229
ui/litellm-dashboard/out/assets/logos/digitalocean.svg
Normal file
229
ui/litellm-dashboard/out/assets/logos/digitalocean.svg
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="68.544662mm"
|
||||
height="68.542419mm"
|
||||
viewBox="0 0 68.544665 68.542419"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
|
||||
sodipodi:docname="DigitalOcean 2016 Icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.90583672"
|
||||
inkscape:cx="219.68639"
|
||||
inkscape:cy="198.71131"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g11760" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath838">
|
||||
<path
|
||||
d="M 0,792 H 612 V 0 H 0 Z"
|
||||
id="path836" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath858">
|
||||
<path
|
||||
d="M 0,792 H 612 V 0 H 0 Z"
|
||||
id="path856" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath255">
|
||||
<path
|
||||
d="M 425.197,24.946 H 559.275 V 161.574 H 425.197 Z"
|
||||
id="path253"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath2816">
|
||||
<path
|
||||
d="M 0,0 H 396 V 612 H 0 Z"
|
||||
id="path2814"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath2794">
|
||||
<path
|
||||
d="M 0,0 H 396 V 612 H 0 Z"
|
||||
id="path2792"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath2778">
|
||||
<path
|
||||
d="M 0,0 H 396 V 612 H 0 Z"
|
||||
id="path2776"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath2748">
|
||||
<path
|
||||
d="M 0,0 H 396 V 612 H 0 Z"
|
||||
id="path2746"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath2710">
|
||||
<path
|
||||
d="M 0,0 H 396 V 612 H 0 Z"
|
||||
id="path2708"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath2664">
|
||||
<path
|
||||
d="M 0,0 H 396 V 612 H 0 Z"
|
||||
id="path2662"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath6055">
|
||||
<path
|
||||
d="m 170.008,153.156 v 18.559 h -5.93 v -18.559 h 5.93"
|
||||
id="path6053"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath6035">
|
||||
<path
|
||||
d="M 30,0 V 292 H 177 V 0 Z"
|
||||
id="path6033"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath6025">
|
||||
<path
|
||||
d="m 170.008,153.156 v 18.559 h -5.93 v -18.559 h 5.93"
|
||||
id="path6023"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath81">
|
||||
<path
|
||||
d="M 45.36,756.5 H 90.60001 V 806 H 45.36 Z"
|
||||
id="path79"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath71">
|
||||
<path
|
||||
d="M 45,755.12 H 90.84 V 806 H 45 Z"
|
||||
id="path69"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath7101">
|
||||
<path
|
||||
d="M 0,0 H 612 V 828 H 0 Z"
|
||||
id="path7099"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath9113">
|
||||
<path
|
||||
d="M 0,531 H 719.972 V 0 H 0 Z"
|
||||
id="path9111"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath11764">
|
||||
<path
|
||||
d="M 0,792 H 612 V 0 H 0 Z"
|
||||
id="path11762"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-23.73095,-45.75945)">
|
||||
<g
|
||||
id="g830"
|
||||
inkscape:label="PSA-Style-Guide"
|
||||
transform="matrix(0.35277777,0,0,-0.35277777,-34.820531,253.68695)">
|
||||
<g
|
||||
style="fill:none"
|
||||
id="g2484"
|
||||
transform="matrix(4.7902363,0,0,-4.7902363,168.73293,576.70634)">
|
||||
<g
|
||||
id="g11760"
|
||||
clip-path="url(#clipPath11764)"
|
||||
transform="matrix(0.92105933,0,0,-0.92105933,-111.34849,650.43322)">
|
||||
<g
|
||||
id="g11766"
|
||||
transform="matrix(1.5371265,0,0,1.5371265,142.2855,665.02011)">
|
||||
<path
|
||||
d="m 0,0 v 5.547 c 5.888,0 10.439,5.828 8.192,12.017 -0.833,2.292 -2.661,4.121 -4.954,4.953 -6.189,2.246 -12.014,-2.305 -12.015,-8.191 0,0 0,-0.002 -0.001,-0.002 h -5.547 c 0,9.38 9.062,16.683 18.89,13.614 4.295,-1.34 7.708,-4.753 9.049,-9.048 C 16.683,9.061 9.379,0 0,0"
|
||||
style="fill:#0069ff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path11768"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
id="g11770"
|
||||
transform="matrix(1.5371265,0,0,1.5371265,142.30502,673.52534)">
|
||||
<path
|
||||
d="m 0,0 h -5.531 v 5.53 c 0,0 0,10e-4 10e-4,10e-4 h 5.529 L 0,5.53 Z"
|
||||
style="fill:#0069ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="path11772"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
id="g11774"
|
||||
transform="matrix(1.5371265,0,0,1.5371265,133.80133,666.99332)">
|
||||
<path
|
||||
d="M 0,0 H -4.249 L -4.25,0.001 V 4.25 H 0.001 V 0.001 Z"
|
||||
style="fill:#0069ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="path11776"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
id="g11778"
|
||||
transform="matrix(1.5371265,0,0,1.5371265,127.27377,673.52534)">
|
||||
<path
|
||||
d="m 0,0 h -3.562 c -0.001,0 -0.002,0.001 -0.002,0.001 v 3.56 c 0,0 10e-4,0.003 0.002,0.003 h 3.56 C -0.001,3.564 0,3.563 0,3.563 Z"
|
||||
style="fill:#0069ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
id="path11780"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7 KiB |
|
|
@ -327,7 +327,27 @@ const PROVIDER_CREDENTIAL_FIELDS: Record<Providers, ProviderCredentialField[]> =
|
|||
label: "API Key",
|
||||
type: "password",
|
||||
required: true
|
||||
}]
|
||||
}],
|
||||
[Providers.DigitalOcean]: [
|
||||
{
|
||||
key: "endpoint",
|
||||
label: "DigitalOcean Endpoint",
|
||||
placeholder: "https://...",
|
||||
required: true
|
||||
},
|
||||
|
||||
{
|
||||
key: "base_model",
|
||||
label: "Base Model",
|
||||
placeholder: "digitalocean/mistral-nemo"
|
||||
},
|
||||
{
|
||||
key: "api_key",
|
||||
label: "DigitalOcean API Key",
|
||||
type: "password",
|
||||
required: true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({
|
||||
|
|
@ -335,7 +355,7 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({
|
|||
uploadProps
|
||||
}) => {
|
||||
const selectedProviderEnum = Providers[selectedProvider as keyof typeof Providers] as Providers;
|
||||
|
||||
|
||||
// Simply use the fields as defined in PROVIDER_CREDENTIAL_FIELDS
|
||||
const allFields = React.useMemo(() => {
|
||||
return PROVIDER_CREDENTIAL_FIELDS[selectedProviderEnum] || [];
|
||||
|
|
@ -353,7 +373,7 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({
|
|||
className={field.key === "vertex_credentials" ? "mb-0" : undefined}
|
||||
>
|
||||
{field.type === "select" ? (
|
||||
<Select
|
||||
<Select
|
||||
placeholder={field.placeholder}
|
||||
defaultValue={field.defaultValue}
|
||||
>
|
||||
|
|
@ -368,9 +388,9 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({
|
|||
<Button2 icon={<UploadOutlined />}>Click to Upload</Button2>
|
||||
</Upload>
|
||||
) : (
|
||||
<TextInput
|
||||
placeholder={field.placeholder}
|
||||
type={field.type === "password" ? "password" : "text"}
|
||||
<TextInput
|
||||
placeholder={field.placeholder}
|
||||
type={field.type === "password" ? "password" : "text"}
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
|
|
|||
|
|
@ -25,10 +25,9 @@ export enum Providers {
|
|||
Perplexity = "Perplexity",
|
||||
TogetherAI = "TogetherAI",
|
||||
Openrouter = "Openrouter",
|
||||
FireworksAI = "Fireworks AI"
|
||||
|
||||
FireworksAI = "Fireworks AI",
|
||||
DigitalOcean = "DigitalOcean"
|
||||
}
|
||||
|
||||
export const provider_map: Record<string, string> = {
|
||||
OpenAI: "openai",
|
||||
OpenAI_Text: "text-completion-openai",
|
||||
|
|
@ -53,7 +52,8 @@ export const provider_map: Record<string, string> = {
|
|||
Perplexity: "perplexity",
|
||||
TogetherAI: "togetherai",
|
||||
Openrouter: "openrouter",
|
||||
FireworksAI: "fireworks_ai"
|
||||
FireworksAI: "fireworks_ai",
|
||||
DigitalOcean: "digitalocean"
|
||||
};
|
||||
|
||||
const asset_logos_folder = '/ui/assets/logos/';
|
||||
|
|
@ -82,7 +82,8 @@ export const providerLogoMap: Record<string, string> = {
|
|||
[Providers.Sambanova]: `${asset_logos_folder}sambanova.svg`,
|
||||
[Providers.TogetherAI]: `${asset_logos_folder}togetherai.svg`,
|
||||
[Providers.Vertex_AI]: `${asset_logos_folder}google.svg`,
|
||||
[Providers.xAI]: `${asset_logos_folder}xai.svg`
|
||||
[Providers.xAI]: `${asset_logos_folder}xai.svg`,
|
||||
[Providers.DigitalOcean]: `${asset_logos_folder}digitalocean.svg`
|
||||
};
|
||||
|
||||
export const getProviderLogoAndName = (providerValue: string): { logo: string, displayName: string } => {
|
||||
|
|
@ -136,9 +137,9 @@ export const getPlaceholder = (selectedProvider: string): string => {
|
|||
console.log(`Provider key: ${providerKey}`);
|
||||
let custom_llm_provider = provider_map[providerKey];
|
||||
console.log(`Provider mapped to: ${custom_llm_provider}`);
|
||||
|
||||
|
||||
let providerModels: Array<string> = [];
|
||||
|
||||
|
||||
if (providerKey && typeof modelMap === "object") {
|
||||
Object.entries(modelMap).forEach(([key, value]) => {
|
||||
if (
|
||||
|
|
@ -151,7 +152,7 @@ export const getPlaceholder = (selectedProvider: string): string => {
|
|||
providerModels.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Special case for cohere_chat
|
||||
// we need both cohere_chat and cohere models to show on dropdown
|
||||
if (providerKey == Providers.Cohere) {
|
||||
|
|
@ -168,6 +169,6 @@ export const getPlaceholder = (selectedProvider: string): string => {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return providerModels;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue