rename provider to Gradient AI

This commit is contained in:
Muhammad Sannan Nasir 2025-07-03 05:11:26 +05:00
parent b6b056d719
commit 35706ec95d
18 changed files with 89 additions and 91 deletions

View file

@ -336,7 +336,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) | ✅ | ✅ | | | | |
| [GradientAI](https://docs.litellm.ai/docs/providers/gradient_ai) | ✅ | ✅ | | | | |
| [Novita AI](https://novita.ai/models/llm?utm_source=github_litellm&utm_medium=github_readme&utm_campaign=github_link) | ✅ | ✅ | ✅ | ✅ | | |
| [Featherless AI](https://docs.litellm.ai/docs/providers/featherless_ai) | ✅ | ✅ | ✅ | ✅ | | |
| [Nebius AI Studio](https://docs.litellm.ai/docs/providers/nebius) | ✅ | ✅ | ✅ | ✅ | ✅ | |
@ -345,7 +345,7 @@ curl 'http://0.0.0.0:4000/key/generate' \
## Contributing
Interested in contributing? Contributions to LiteLLM Python SDK, Proxy Server, and LLM integrations are both accepted and highly encouraged!
Interested in contributing? Contributions to LiteLLM Python SDK, Proxy Server, and LLM integrations are both accepted and highly encouraged!
**Quick start:** `git clone` → `make install-dev` → `make format` → `make lint` → `make test-unit`

View file

@ -1,12 +1,12 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# DigitalOcean GenAI
# GradientAI GradientAI
https://digitalocean.com/products/genai
LiteLLM provides native support for DigitalOcean GenAI models.
To use a DigitalOcean model, specify it as `digitalocean/<model-name>` in your LiteLLM requests.
LiteLLM provides native support for GradientAI GenAI models.
To use a GradientAI model, specify it as `gradient_ai/<model-name>` in your LiteLLM requests.
## API Key & Endpoint
@ -15,8 +15,8 @@ Set your credentials and endpoint as environment variables:
```python
import os
os.environ['DIGITALOCEAN_API_KEY'] = "your-api-key"
os.environ['DIGITALOCEAN_AGENT_ENDPOINT'] = "https://api.digitalocean.com/api/v1/chat" # default endpoint
os.environ['GRADIENT_AI_API_KEY'] = "your-api-key"
os.environ['GRADIENT_AI_AGENT_ENDPOINT'] = "https://api.gradient_ai.com/api/v1/chat" # default endpoint
```
## Sample Usage
@ -25,9 +25,9 @@ os.environ['DIGITALOCEAN_AGENT_ENDPOINT'] = "https://api.digitalocean.com/api/v1
from litellm import completion
import os
os.environ['DIGITALOCEAN_API_KEY'] = "your-api-key"
os.environ['GRADIENT_AI_API_KEY'] = "your-api-key"
response = completion(
model="digitalocean/model-name",
model="gradient_ai/model-name",
messages=[
{"role": "user", "content": "Hello, how are you?"}
],
@ -41,9 +41,9 @@ print(response.choices[0].message.content)
from litellm import completion
import os
os.environ['DIGITALOCEAN_API_KEY'] = "your-api-key"
os.environ['GRADIENT_AI_API_KEY'] = "your-api-key"
response = completion(
model="digitalocean/model-name",
model="gradient_ai/model-name",
messages=[
{"role": "user", "content": "Write a story about a robot learning to love"}
],

View file

@ -1,9 +1,9 @@
# ✨ Secret Detection/Redaction (Enterprise-only)
❓ Use this to REDACT API Keys, Secrets sent in requests to an LLM.
❓ Use this to REDACT API Keys, Secrets sent in requests to an LLM.
Example if you want to redact the value of `OPENAI_API_KEY` in the following request
#### Incoming Request
#### Incoming Request
```json
{
@ -31,13 +31,13 @@ Example if you want to redact the value of `OPENAI_API_KEY` in the following req
**Usage**
**Step 1** Add this to your config.yaml
**Step 1** Add this to your config.yaml
```yaml
guardrails:
- guardrail_name: "my-custom-name"
litellm_params:
guardrail: "hide-secrets" # supported values: "aporia", "lakera", ..
guardrail: "hide-secrets" # supported values: "aporia", "lakera", ..
mode: "pre_call"
```
@ -125,7 +125,7 @@ guardrails:
**2. Start proxy**
Run with `--detailed_debug` for more detailed logs. Use in dev only.
Run with `--detailed_debug` for more detailed logs. Use in dev only.
```bash
litellm --config /path/to/config.yaml --detailed_debug
@ -157,7 +157,7 @@ Look for this in your logs, to confirm your changes worked as expected.
No secrets detected on input.
```
### Default Config Used
### Default Config Used
```
_default_detect_secrets_config = {
@ -259,8 +259,8 @@ _default_detect_secrets_config = {
"path": _custom_plugins_path + "/defined_networking_api_token.py",
},
{
"name": "DigitaloceanDetector",
"path": _custom_plugins_path + "/digitalocean.py",
"name": "GradientAIDetector",
"path": _custom_plugins_path + "/gradient_ai.py",
},
{
"name": "DopplerApiTokenDetector",

View file

@ -74,12 +74,12 @@ const sidebars = {
"tutorials/claude_responses_api",
]
},
],
// But you can create a sidebar manually
tutorialSidebar: [
{ type: "doc", id: "index" }, // NEW
{
type: "category",
label: "LiteLLM Proxy Server",
@ -203,7 +203,7 @@ const sidebars = {
"proxy/dynamic_logging"
],
},
{
type: "category",
label: "Secret Managers",
@ -439,7 +439,7 @@ const sidebars = {
"providers/custom_llm_server",
"providers/petals",
"providers/snowflake",
"providers/digitalocean",
"providers/gradient_ai",
"providers/featherless_ai",
"providers/nebius"
],
@ -474,7 +474,7 @@ const sidebars = {
]
},
{
type: "category",
label: "Routing, Loadbalancing & Fallbacks",
@ -505,7 +505,7 @@ const sidebars = {
},
],
},
{
type: "category",
label: "Load Testing",

View file

@ -123,8 +123,8 @@ _default_detect_secrets_config = {
"path": _custom_plugins_path + "/defined_networking_api_token.py",
},
{
"name": "DigitaloceanDetector",
"path": _custom_plugins_path + "/digitalocean.py",
"name": "GradientAItector",
"path": _custom_plugins_path + "/gradient_aipy",
},
{
"name": "DopplerApiTokenDetector",

View file

@ -1,5 +1,5 @@
"""
This plugin searches for DigitalOcean tokens.
This plugin searches for GradientAIokens.
"""
import re
@ -7,12 +7,12 @@ import re
from detect_secrets.plugins.base import RegexBasedDetector
class DigitaloceanDetector(RegexBasedDetector):
"""Scans for various DigitalOcean Tokens."""
class GradientAItector(RegexBasedDetector):
"""Scans for various GradientAI Tokens."""
@property
def secret_type(self) -> str:
return "DigitalOcean Token"
return "GradientAI Token"
@property
def denylist(self) -> list[re.Pattern]:

View file

@ -203,7 +203,7 @@ aleph_alpha_key: Optional[str] = None
nlp_cloud_key: Optional[str] = None
novita_api_key: Optional[str] = None
snowflake_key: Optional[str] = None
digitalocean_api_key: Optional[str] = None
gradient_ai_api_key: Optional[str] = None
nebius_key: Optional[str] = None
common_cloud_provider_auth_params: dict = {
"params": ["project", "region_name", "token"],
@ -474,7 +474,7 @@ sambanova_models: List = []
novita_models: List = []
assemblyai_models: List = []
snowflake_models: List = []
digitalocean_models: List = []
gradient_ai_models: List = []
llama_models: List = []
nscale_models: List = []
nebius_models: List = []
@ -650,8 +650,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)
elif value.get("litellm_provider") == "gradient_ai":
gradient_ai_models.append(key)
elif value.get("litellm_provider") == "featherless_ai":
featherless_ai_models.append(key)
elif value.get("litellm_provider") == "deepgram":
@ -736,7 +736,7 @@ model_list = (
+ assemblyai_models
+ jina_ai_models
+ snowflake_models
+ digitalocean_models
+ gradient_ai_models
+ llama_models
+ featherless_ai_models
+ nscale_models
@ -802,7 +802,7 @@ models_by_provider: dict = {
"assemblyai": assemblyai_models,
"jina_ai": jina_ai_models,
"snowflake": snowflake_models,
"digitalocean": digitalocean_models,
"gradient_ai": gradient_ai_models,
"meta_llama": llama_models,
"nscale": nscale_models,
"featherless_ai": featherless_ai_models,
@ -1059,7 +1059,7 @@ from .llms.openai.chat.o_series_transformation import (
)
from .llms.snowflake.chat.transformation import SnowflakeConfig
from .llms.digitalocean.chat.transformation import DigitalOceanConfig
from .llms.gradient_ai.chat.transformation import GradientAIConfig
openaiOSeriesConfig = OpenAIOSeriesConfig()
from .llms.openai.chat.gpt_transformation import (
OpenAIGPTConfig,

View file

@ -255,7 +255,7 @@ LITELLM_CHAT_PROVIDERS = [
"llamafile",
"lm_studio",
"galadriel",
"digitalocean",
"gradient_ai",
"novita",
"meta_llama",
"featherless_ai",

View file

@ -336,8 +336,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 in litellm.gradient_ai_models:
custom_llm_provider = "gradient_ai"
elif model == "*":
custom_llm_provider = "openai"
if not custom_llm_provider:
@ -638,11 +638,11 @@ 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":
elif custom_llm_provider == "gradient_ai":
(
api_base,
dynamic_api_key,
) = litellm.DigitalOceanConfig()._get_openai_compatible_provider_info(
) = litellm.GradientAIConfig()._get_openai_compatible_provider_info(
api_base, api_key
)
elif custom_llm_provider == "featherless_ai":

View file

@ -10,9 +10,8 @@ from litellm.types.llms.openai import (
from ...openai_like.chat.transformation import OpenAILikeChatConfig
class DigitalOceanConfig(OpenAILikeChatConfig):
class GradientAIConfig(OpenAILikeChatConfig):
# DigitalOcean-specific parameters:
stream_options: Optional[Dict] = None
kb_filters: Optional[List[Dict]] = None
filter_kb_content_by_query_metadata: Optional[bool] = None
@ -66,9 +65,9 @@ class DigitalOceanConfig(OpenAILikeChatConfig):
litellm_params: dict,
api_key: Optional[str] = None,
api_base: Optional[str] = None):
api_key = api_key or get_secret_str("DIGITALOCEAN_API_KEY")
api_key = api_key or get_secret_str("GRADIENT_AI_API_KEY")
if api_key is None:
raise ValueError("DigitalOcean API key not found")
raise ValueError("GradientAI API key not found")
if headers is None:
headers = {}
headers["Authorization"] = f"Bearer {api_key}"
@ -94,10 +93,9 @@ class DigitalOceanConfig(OpenAILikeChatConfig):
) -> Tuple[Optional[str], Optional[str]]:
api_base = (
api_base
or get_secret_str("DIGITALOCEAN_AGENT_ENDPOINT")
or get_secret_str("DO_AGENT_ENDPOINT")
or get_secret_str("GRADIENT_AI_AGENT_ENDPOINT")
) # type: ignore
dynamic_api_key = api_key or get_secret_str("DO_API_KEY") or get_secret_str("DIGITALOCEAN_API_KEY")
dynamic_api_key = api_key or get_secret_str("GRADIENT_AI_API_KEY")
return api_base, dynamic_api_key

View file

@ -3193,7 +3193,7 @@ def completion( # type: ignore # noqa: PLR0915
additional_args={"headers": headers},
)
raise e
elif custom_llm_provider == "digitalocean":
elif custom_llm_provider == "gradient_ai":
api_base = litellm.api_base or api_base
response = base_llm_http_handler.completion(
@ -3205,7 +3205,7 @@ def completion( # type: ignore # noqa: PLR0915
model_response=model_response,
optional_params=optional_params,
litellm_params=litellm_params,
custom_llm_provider="digitalocean",
custom_llm_provider="gradient_ai",
timeout=timeout,
headers=headers,
encoding=encoding,

View file

@ -1620,7 +1620,7 @@ class ImageResponse(OpenAIImageResponse, BaseLiteLLMOpenAIResponseObject):
usage: Optional[ImageUsage] = None # type: ignore
"""
Users might use litellm with older python versions, we don't want this to break for them.
Users might use litellm with older python versions, we don't want this to break for them.
Happens when their OpenAIImageResponse has the old OpenAI usage class.
"""
@ -2291,7 +2291,7 @@ class LlmProviders(str, Enum):
TOPAZ = "topaz"
ASSEMBLYAI = "assemblyai"
SNOWFLAKE = "snowflake"
DIGITALOCEAN = "digitalocean"
GRADIENT_AI = "gradient_ai"
LLAMA = "meta_llama"
NSCALE = "nscale"

View file

@ -3770,8 +3770,8 @@ def get_optional_params( # noqa: PLR0915
else False
),
)
elif custom_llm_provider == "digitalocean":
optional_params = litellm.DigitalOceanConfig().map_openai_params(
elif custom_llm_provider == "gradient_ai":
optional_params = litellm.GradientAIConfig().map_openai_params(
non_default_params=non_default_params,
optional_params=optional_params,
model=model,
@ -6795,8 +6795,8 @@ class ProviderConfigManager:
return litellm.LiteLLMProxyChatConfig()
elif litellm.LlmProviders.OPENAI == provider:
return litellm.OpenAIGPTConfig()
elif litellm.LlmProviders.DIGITALOCEAN == provider:
return litellm.DigitalOceanConfig()
elif litellm.LlmProviders.GRADIENT_AI == provider:
return litellm.GradientAIConfig()
elif litellm.LlmProviders.NSCALE == provider:
return litellm.NscaleConfig()
return None

View file

@ -15238,42 +15238,42 @@
"litellm_provider": "snowflake",
"mode": "chat"
},
"digitalocean/deepseek-r1-distill-llama-70b": {
"gradient_ai/deepseek-r1-distill-llama-70b": {
"input_cost_per_token": 99e-08,
"output_cost_per_token": 99e-089,
"litellm_provider": "digitalocean",
"litellm_provider": "gradient_ai",
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions"],
"supported_modalities": ["text"]
},
"digitalocean/llama-3.1-8b": {
"gradient_ai/llama-3.1-8b": {
"input_cost_per_token": 2e-07,
"output_cost_per_token": 2e-07,
"litellm_provider": "digitalocean",
"litellm_provider": "gradient_ai",
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions"],
"supported_modalities": ["text"]
},
"digitalocean/llama-3.1-70b": {
"gradient_ai/llama-3.1-70b": {
"input_cost_per_token": 7e-07,
"output_cost_per_token": 7e-07,
"litellm_provider": "digitalocean",
"litellm_provider": "gradient_ai",
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions"],
"supported_modalities": ["text"]
},
"digitalocean/llama-3.3-70b": {
"gradient_ai/llama-3.3-70b": {
"input_cost_per_token": 65e-08,
"output_cost_per_token": 65e-08,
"litellm_provider": "digitalocean",
"litellm_provider": "gradient_ai",
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions"],
"supported_modalities": ["text"]
},
"digitalocean/mistral-nemo": {
"gradient_ai/mistral-nemo": {
"input_cost_per_token": 3e-07,
"output_cost_per_token": 3e-07,
"litellm_provider": "digitalocean",
"litellm_provider": "gradient_ai",
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions"],
"supported_modalities": ["text"]

View file

@ -137,11 +137,11 @@ model_list:
model: openai/my-fake-model
api_key: my-fake-key
api_base: https://exampleopenaiendpoint-production.up.railway.appxxxx/
- model_name: "digitalocean/*"
- model_name: "gradient_ai/*"
litellm_params:
model: "digitalocean/*"
api_key: os.environ/DIGITALOCEAN_API_KEY
api_base: os.environ/DIGITALOCEAN_AGENT_ENDPOINT
model: "gradient_ai/*"
api_key: os.environ/_API_KEY
api_base: os.environ/GRADIENT_AI_AGENT_ENDPOINT
litellm_settings:

View file

@ -6,21 +6,21 @@ sys.path.insert(
0, os.path.abspath("../../../../..")
) # Adds the parent directory to the system path
from litellm.llms.digitalocean.chat.transformation import DigitalOceanConfig
from litellm.llms.gradient_ai.chat.transformation import GradientAIConfig
DO_ENDPOINT_PATH = "/api/v1/chat/completions"
DO_BASE_URL = "https://api.digitalocean.com"
DO_BASE_URL = "https://api.gradient_ai.com"
@pytest.fixture
def config():
return DigitalOceanConfig()
return GradientAIConfig()
def test_validate_environment_sets_headers(monkeypatch, config):
monkeypatch.setenv("DIGITALOCEAN_API_KEY", "test-key")
monkeypatch.setenv("GRADIENT_AI_API_KEY", "test-key")
headers = {}
result = config.validate_environment(
headers=headers,
model="digitalocean/test-model",
model="gradient_ai/test-model",
messages=[],
optional_params={},
litellm_params={},
@ -34,7 +34,7 @@ def test_get_complete_url(config):
url = config.get_complete_url(
api_base=DO_BASE_URL,
api_key="test-key",
model="digitalocean/test-model",
model="gradient_ai/test-model",
optional_params={},
litellm_params={},
stream=False,
@ -46,15 +46,15 @@ def test_transform_messages_handles_dicts_only(config):
{"role": "assistant", "content": "Hello!"},
{"role": "user", "content": "Hi!"},
]
out = config._transform_messages(messages, model="digitalocean/test-model")
out = config._transform_messages(messages, model="gradient_ai/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")
monkeypatch.setenv("GRADIENT_AI_AGENT_ENDPOINT", DO_BASE_URL)
monkeypatch.setenv("GRADIENT_AI_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"

View file

@ -351,10 +351,10 @@ const PROVIDER_CREDENTIAL_FIELDS: Record<Providers, ProviderCredentialField[]> =
type: "password",
required: true
}],
[Providers.DigitalOcean]: [
[Providers.GradientAI]: [
{
key: "endpoint",
label: "DigitalOcean Endpoint",
label: "GradientAI Endpoint",
placeholder: "https://...",
required: true
},
@ -362,11 +362,11 @@ const PROVIDER_CREDENTIAL_FIELDS: Record<Providers, ProviderCredentialField[]> =
{
key: "base_model",
label: "Base Model",
placeholder: "digitalocean/mistral-nemo"
placeholder: "gradient_ai/mistral-nemo"
},
{
key: "api_key",
label: "DigitalOcean API Key",
label: "GradientAI API Key",
type: "password",
required: true
}
@ -420,7 +420,7 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({
onChange(info: any) {
console.log("Upload onChange triggered in ProviderSpecificFields");
console.log("Current form values:", form.getFieldsValue());
if (info.file.status !== "uploading") {
console.log(info.file, info.fileList);
}
@ -450,14 +450,14 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({
))}
</Select>
) : field.type === "upload" ? (
<Upload
<Upload
{...handleUpload}
onChange={(info) => {
// First call the original onChange
if (uploadProps?.onChange) {
uploadProps.onChange(info);
}
// Check the field value after a short delay
setTimeout(() => {
const value = form.getFieldValue(field.key);

View file

@ -26,7 +26,7 @@ export enum Providers {
TogetherAI = "TogetherAI",
Openrouter = "Openrouter",
FireworksAI = "Fireworks AI",
DigitalOcean = "DigitalOcean",
GradientAI = "GradientAI",
Triton = "Triton",
Deepgram = "Deepgram",
ElevenLabs = "ElevenLabs"
@ -56,7 +56,7 @@ export const provider_map: Record<string, string> = {
TogetherAI: "together_ai",
Openrouter: "openrouter",
FireworksAI: "fireworks_ai",
DigitalOcean: "digitalocean",
GradientAI: "gradient_ai",
Triton: "triton",
Deepgram: "deepgram",
ElevenLabs: "elevenlabs"
@ -89,7 +89,7 @@ export const providerLogoMap: Record<string, string> = {
[Providers.TogetherAI]: `${asset_logos_folder}togetherai.svg`,
[Providers.Vertex_AI]: `${asset_logos_folder}google.svg`,
[Providers.xAI]: `${asset_logos_folder}xai.svg`,
[Providers.DigitalOcean]: `${asset_logos_folder}digitalocean.svg`,
[Providers.GradientAI]: `${asset_logos_folder}gradientai.svg`,
[Providers.Triton]: `${asset_logos_folder}nvidia_triton.png`,
[Providers.Deepgram]: `${asset_logos_folder}deepgram.png`,
[Providers.ElevenLabs]: `${asset_logos_folder}elevenlabs.png`