mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
feat: add Nanobridge as OpenAI-compatible provider
This commit is contained in:
parent
d45e9e4d56
commit
e2bc9d9ec3
6 changed files with 197 additions and 19 deletions
93
docs/my-website/docs/providers/nanobridge.md
Normal file
93
docs/my-website/docs/providers/nanobridge.md
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# Nanobridge
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Provider** | `nanobridge` |
|
||||
| **API key env** | `NANOBRIDGE_API_KEY` |
|
||||
| **Base override env** | `NANOBRIDGE_API_BASE` (optional) |
|
||||
|
||||
Nanobridge is an OpenAI-compatible API gateway for Tencent Hunyuan model routes (DeepSeek, GLM, MiniMax, etc.). It also exposes an Anthropic Messages-compatible base for Claude Code and similar agents.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
export NANOBRIDGE_API_KEY="your-api-key"
|
||||
```
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
response = litellm.completion(
|
||||
model="nanobridge/deepseek-v4-flash",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
)
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
## Regional endpoints
|
||||
|
||||
Three regions share the same API key and billing. Pick the closest node for lower latency.
|
||||
|
||||
| Region | OpenAI base (`/v1`) | Anthropic base |
|
||||
|--------|---------------------|----------------|
|
||||
| Germany (default) | `https://api.nanobridge.net/v1` | `https://api.nanobridge.net/anthropic` |
|
||||
| Singapore | `https://api-sg.nanobridge.net/v1` | `https://api-sg.nanobridge.net/anthropic` |
|
||||
| United States | `https://api-us.nanobridge.net/v1` | `https://api-us.nanobridge.net/anthropic` |
|
||||
|
||||
Override the default Germany URL:
|
||||
|
||||
```bash
|
||||
export NANOBRIDGE_API_BASE="https://api-sg.nanobridge.net/v1"
|
||||
```
|
||||
|
||||
## Supported models
|
||||
|
||||
Use `nanobridge/<model_id>` in LiteLLM:
|
||||
|
||||
| Model ID | Notes |
|
||||
|----------|--------|
|
||||
| `deepseek-v4-flash` | Recommended default |
|
||||
| `deepseek-v4-pro` | |
|
||||
| `deepseek-v3.2` | |
|
||||
| `glm-5.1` | Alias: `glm-5-1` |
|
||||
| `minimax-m2.7` | Alias: `minimax-m-2-7` |
|
||||
|
||||
## API paths
|
||||
|
||||
- Chat: `POST /chat/completions` or `POST /v1/chat/completions`
|
||||
- Models: `GET /v1/models`
|
||||
- Anthropic: `POST /v1/messages` on the Anthropic base
|
||||
- Streaming: `stream: true` (SSE)
|
||||
- Auth: `Authorization: Bearer <API_KEY>`
|
||||
|
||||
## LiteLLM Proxy
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: nanobridge-deepseek-flash
|
||||
litellm_params:
|
||||
model: nanobridge/deepseek-v4-flash
|
||||
api_key: os.environ/NANOBRIDGE_API_KEY
|
||||
# api_base: os.environ/NANOBRIDGE_API_BASE
|
||||
```
|
||||
|
||||
## OpenAI Python SDK
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
import os
|
||||
|
||||
client = OpenAI(
|
||||
api_key=os.environ["NANOBRIDGE_API_KEY"],
|
||||
base_url="https://api.nanobridge.net/v1",
|
||||
)
|
||||
client.chat.completions.create(
|
||||
model="deepseek-v4-flash",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
)
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
- [Nanobridge API documentation](https://platform.nanobridge.net/#/api-docs)
|
||||
- [Platform console](https://platform.nanobridge.net)
|
||||
|
|
@ -774,6 +774,7 @@ openai_compatible_endpoints: List = [
|
|||
"https://serverless.tensormesh.ai/v1",
|
||||
"https://api.stima.tech/v1",
|
||||
"https://nano-gpt.com/api/v1",
|
||||
"https://api.nanobridge.net/v1",
|
||||
"https://api.poe.com/v1",
|
||||
"https://llm.chutes.ai/v1/",
|
||||
"https://api.v0.dev/v1",
|
||||
|
|
@ -824,6 +825,7 @@ openai_compatible_providers: List = [
|
|||
"tensormesh", # Tensormesh - JSON-configured provider
|
||||
"apertis", # Apertis - JSON-configured provider
|
||||
"nano-gpt", # Nano-GPT - JSON-configured provider
|
||||
"nanobridge", # Nanobridge - JSON-configured provider
|
||||
"poe", # Poe - JSON-configured provider
|
||||
"chutes", # Chutes - JSON-configured provider
|
||||
"featherless_ai",
|
||||
|
|
@ -860,6 +862,7 @@ openai_text_completion_compatible_providers: List = (
|
|||
"tensormesh",
|
||||
"apertis",
|
||||
"nano-gpt",
|
||||
"nanobridge",
|
||||
"poe",
|
||||
"chutes",
|
||||
"v0",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
{
|
||||
"nanobridge": {
|
||||
"base_url": "https://api.nanobridge.net/v1",
|
||||
"api_key_env": "NANOBRIDGE_API_KEY",
|
||||
"api_base_env": "NANOBRIDGE_API_BASE"
|
||||
},
|
||||
"publicai": {
|
||||
"base_url": "https://api.publicai.co/v1",
|
||||
"api_key_env": "PUBLICAI_API_KEY",
|
||||
|
|
|
|||
|
|
@ -3369,6 +3369,7 @@ class LlmProviders(str, Enum):
|
|||
SYNTHETIC = "synthetic"
|
||||
APERTIS = "apertis"
|
||||
NANOGPT = "nano-gpt"
|
||||
NANOBRIDGE = "nanobridge"
|
||||
POE = "poe"
|
||||
CHUTES = "chutes"
|
||||
XIAOMI_MIMO = "xiaomi_mimo"
|
||||
|
|
|
|||
|
|
@ -488,9 +488,7 @@
|
|||
"audio_speech": false,
|
||||
"moderations": false,
|
||||
"batches": false,
|
||||
"rerank": false,
|
||||
"a2a": false,
|
||||
"interactions": false
|
||||
"rerank": false
|
||||
}
|
||||
},
|
||||
"chutes": {
|
||||
|
|
@ -1592,6 +1590,24 @@
|
|||
"interactions": true
|
||||
}
|
||||
},
|
||||
"nanobridge": {
|
||||
"display_name": "Nanobridge (`nanobridge`)",
|
||||
"url": "https://platform.nanobridge.net/#/api-docs",
|
||||
"endpoints": {
|
||||
"chat_completions": true,
|
||||
"messages": true,
|
||||
"responses": false,
|
||||
"embeddings": false,
|
||||
"image_generations": false,
|
||||
"audio_transcriptions": false,
|
||||
"audio_speech": false,
|
||||
"moderations": false,
|
||||
"batches": false,
|
||||
"rerank": false,
|
||||
"a2a": false,
|
||||
"interactions": false
|
||||
}
|
||||
},
|
||||
"nvidia_nim": {
|
||||
"display_name": "Nvidia NIM (`nvidia_nim`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/nvidia_nim",
|
||||
|
|
@ -2664,22 +2680,6 @@
|
|||
"batches": false,
|
||||
"rerank": false
|
||||
}
|
||||
},
|
||||
"charity_engine": {
|
||||
"display_name": "Charity Engine (`charity_engine`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/charity_engine",
|
||||
"endpoints": {
|
||||
"chat_completions": true,
|
||||
"messages": true,
|
||||
"responses": true,
|
||||
"embeddings": false,
|
||||
"image_generations": false,
|
||||
"audio_transcriptions": false,
|
||||
"audio_speech": false,
|
||||
"moderations": false,
|
||||
"batches": false,
|
||||
"rerank": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"endpoints": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
"""
|
||||
Unit tests for Nanobridge JSON-configured OpenAI-compatible provider.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
import pytest
|
||||
except ImportError:
|
||||
pytest = None
|
||||
|
||||
workspace_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../.."))
|
||||
sys.path.insert(0, workspace_path)
|
||||
|
||||
import litellm
|
||||
from litellm import LlmProviders
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
||||
|
||||
|
||||
class TestNanobridgeProvider:
|
||||
def test_nanobridge_in_llm_providers_enum(self):
|
||||
assert hasattr(LlmProviders, "NANOBRIDGE")
|
||||
assert LlmProviders.NANOBRIDGE.value == "nanobridge"
|
||||
|
||||
def test_nanobridge_json_config_exists(self):
|
||||
assert JSONProviderRegistry.exists("nanobridge")
|
||||
cfg = JSONProviderRegistry.get("nanobridge")
|
||||
assert cfg is not None
|
||||
assert cfg.base_url == "https://api.nanobridge.net/v1"
|
||||
assert cfg.api_key_env == "NANOBRIDGE_API_KEY"
|
||||
assert cfg.api_base_env == "NANOBRIDGE_API_BASE"
|
||||
|
||||
def test_nanobridge_provider_resolution(self):
|
||||
model, provider, _api_key, api_base = get_llm_provider(
|
||||
model="nanobridge/deepseek-v4-flash",
|
||||
custom_llm_provider=None,
|
||||
api_base=None,
|
||||
api_key=None,
|
||||
)
|
||||
assert model == "deepseek-v4-flash"
|
||||
assert provider == "nanobridge"
|
||||
assert api_base == "https://api.nanobridge.net/v1"
|
||||
|
||||
def test_nanobridge_api_base_env_override(self, monkeypatch):
|
||||
monkeypatch.setenv("NANOBRIDGE_API_BASE", "https://api-sg.nanobridge.net/v1")
|
||||
_model, _provider, _api_key, api_base = get_llm_provider(
|
||||
model="nanobridge/deepseek-v4-flash",
|
||||
custom_llm_provider=None,
|
||||
api_base=None,
|
||||
api_key=None,
|
||||
)
|
||||
assert api_base == "https://api-sg.nanobridge.net/v1"
|
||||
|
||||
def test_nanobridge_completion_mocked(self):
|
||||
response = litellm.completion(
|
||||
model="nanobridge/deepseek-v4-flash",
|
||||
messages=[{"role": "user", "content": "ping"}],
|
||||
mock_response="PONG",
|
||||
)
|
||||
assert response.choices[0].message.content == "PONG"
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("NANOBRIDGE_API_KEY"),
|
||||
reason="NANOBRIDGE_API_KEY not set",
|
||||
)
|
||||
class TestNanobridgeLive:
|
||||
def test_nanobridge_live_chat_completions(self):
|
||||
response = litellm.completion(
|
||||
model="nanobridge/deepseek-v4-flash",
|
||||
messages=[{"role": "user", "content": "Reply with exactly: ok"}],
|
||||
max_tokens=16,
|
||||
)
|
||||
assert response.choices[0].message.content
|
||||
Loading…
Add table
Reference in a new issue