feat(providers): add Bitdeer AI as a JSON-configured provider

Registers Bitdeer's OpenAI-compatible inference API (api-inference.bitdeer.ai)
following the existing JSON-configured provider pattern: providers.json entry,
LlmProviders enum member, openai_compatible_providers list entry, and the
api_base auto-detection branch in get_llm_provider_logic.py.

Adds pricing for 6 models to model_prices_and_context_window.json
(Kimi-K3, Kimi-K2.6, GLM-5.2, Qwen3.5-397B-A17B, DeepSeek-V4-Pro,
DeepSeek-V4-Flash), sourced from bitdeer.ai's published pricing page.
This commit is contained in:
ruijie 2026-08-13 00:16:12 +08:00
parent f64479e74d
commit 192052d2cc
6 changed files with 229 additions and 0 deletions

View file

@ -754,6 +754,7 @@ openai_compatible_endpoints: Final[list] = [
"https://api.libertai.io/v1",
"https://pinstripes.io/v1",
"https://api.meta.ai/v1",
"https://api-inference.bitdeer.ai/v1",
]
@ -821,6 +822,7 @@ openai_compatible_providers: Final[list] = [
"pinstripes", # Pinstripes - JSON-configured provider
"darkbloom",
"meta", # Meta Model API (Muse Spark) - JSON-configured provider
"bitdeer", # Bitdeer AI - JSON-configured provider
]
openai_text_completion_compatible_providers: Final[list] = [ # providers that support `/v1/completions`
"together_ai",

View file

@ -325,6 +325,9 @@ def get_llm_provider(
elif endpoint == "https://llm.chutes.ai/v1/":
custom_llm_provider = "chutes"
dynamic_api_key = get_secret_str("CHUTES_API_KEY")
elif endpoint == "https://api-inference.bitdeer.ai/v1":
custom_llm_provider = "bitdeer"
dynamic_api_key = get_secret_str("BITDEER_API_KEY")
elif endpoint == "https://api.v0.dev/v1":
custom_llm_provider = "v0"
dynamic_api_key = get_secret_str("V0_API_KEY")

View file

@ -183,5 +183,12 @@
"max_completion_tokens": "max_tokens"
},
"supported_endpoints": ["/v1/chat/completions", "/v1/responses", "/v1/embeddings"]
},
"bitdeer": {
"base_url": "https://api-inference.bitdeer.ai/v1",
"api_key_env": "BITDEER_API_KEY",
"param_mappings": {
"max_completion_tokens": "max_tokens"
}
}
}

View file

@ -3710,6 +3710,7 @@ class LlmProviders(str, Enum):
XIAOMI_MIMO = "xiaomi_mimo"
TENSORMESH = "tensormesh"
LIBERTAI = "libertai"
BITDEER = "bitdeer"
PINSTRIPES = "pinstripes"
DARKBLOOM = "darkbloom"
META = "meta"

View file

@ -47176,6 +47176,99 @@
],
"supports_audio_output": true
},
"bitdeer/moonshotai/Kimi-K3": {
"max_tokens": 1048576,
"max_input_tokens": 1048576,
"max_output_tokens": 131072,
"input_cost_per_token": 3e-06,
"output_cost_per_token": 1.5e-05,
"cache_read_input_token_cost": 3e-07,
"litellm_provider": "bitdeer",
"mode": "chat",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_reasoning": true,
"supports_vision": true,
"supports_prompt_caching": true,
"source": "https://www.bitdeer.ai/en/pricing/ai-models"
},
"bitdeer/zai-org/GLM-5.2": {
"max_tokens": 1000000,
"max_input_tokens": 1000000,
"max_output_tokens": 131072,
"input_cost_per_token": 1.4e-06,
"output_cost_per_token": 4.4e-06,
"cache_read_input_token_cost": 2.6e-07,
"litellm_provider": "bitdeer",
"mode": "chat",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_reasoning": true,
"supports_prompt_caching": true,
"source": "https://www.bitdeer.ai/en/pricing/ai-models"
},
"bitdeer/moonshotai/Kimi-K2.6": {
"max_tokens": 262144,
"max_input_tokens": 262144,
"max_output_tokens": 262144,
"input_cost_per_token": 9.5e-07,
"output_cost_per_token": 4e-06,
"cache_read_input_token_cost": 2e-07,
"litellm_provider": "bitdeer",
"mode": "chat",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_reasoning": true,
"supports_vision": true,
"supports_prompt_caching": true,
"source": "https://www.bitdeer.ai/en/pricing/ai-models"
},
"bitdeer/Qwen/Qwen3.5-397B-A17B": {
"max_tokens": 262144,
"max_input_tokens": 262144,
"max_output_tokens": 65536,
"input_cost_per_token": 6e-07,
"output_cost_per_token": 3.55e-06,
"cache_read_input_token_cost": 2e-07,
"litellm_provider": "bitdeer",
"mode": "chat",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_reasoning": true,
"supports_vision": true,
"supports_prompt_caching": true,
"source": "https://www.bitdeer.ai/en/pricing/ai-models"
},
"bitdeer/deepseek-ai/DeepSeek-V4-Pro": {
"max_tokens": 1000000,
"max_input_tokens": 1000000,
"max_output_tokens": 384000,
"input_cost_per_token": 1.168e-06,
"output_cost_per_token": 2.336e-06,
"cache_read_input_token_cost": 1e-07,
"litellm_provider": "bitdeer",
"mode": "chat",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_reasoning": true,
"supports_prompt_caching": true,
"source": "https://www.bitdeer.ai/en/pricing/ai-models"
},
"bitdeer/deepseek-ai/DeepSeek-V4-Flash": {
"max_tokens": 1000000,
"max_input_tokens": 1000000,
"max_output_tokens": 384000,
"input_cost_per_token": 4e-08,
"output_cost_per_token": 8e-08,
"cache_read_input_token_cost": 1e-08,
"litellm_provider": "bitdeer",
"mode": "chat",
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_reasoning": true,
"supports_prompt_caching": true,
"source": "https://www.bitdeer.ai/en/pricing/ai-models"
},
"fallback_generalizations": {
"rules": [
{

View file

@ -0,0 +1,123 @@
"""
Tests for Bitdeer AI provider configuration and integration.
"""
import json
import os
import litellm
workspace_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../.."))
class TestBitdeerProviderConfig:
"""Test Bitdeer AI provider configuration"""
def test_bitdeer_in_provider_list(self):
"""Test that bitdeer is in the provider list"""
from litellm import LlmProviders
assert hasattr(LlmProviders, "BITDEER")
assert LlmProviders.BITDEER.value == "bitdeer"
assert "bitdeer" in litellm.provider_list
def test_bitdeer_json_config_exists(self):
"""Test that bitdeer is configured in providers.json"""
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
assert JSONProviderRegistry.exists("bitdeer")
bitdeer = JSONProviderRegistry.get("bitdeer")
assert bitdeer is not None
assert bitdeer.base_url == "https://api-inference.bitdeer.ai/v1"
assert bitdeer.api_key_env == "BITDEER_API_KEY"
assert bitdeer.param_mappings.get("max_completion_tokens") == "max_tokens"
def test_bitdeer_in_openai_compatible_providers(self):
"""Test that bitdeer is in the openai_compatible_providers list"""
from litellm.constants import openai_compatible_providers
assert "bitdeer" in openai_compatible_providers
def test_bitdeer_provider_resolution(self):
"""Test that provider resolution finds bitdeer and returns the default base URL"""
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
model, provider, api_key, api_base = get_llm_provider(
model="bitdeer/moonshotai/Kimi-K3",
custom_llm_provider=None,
api_base=None,
api_key=None,
)
assert model == "moonshotai/Kimi-K3"
assert provider == "bitdeer"
assert api_base == "https://api-inference.bitdeer.ai/v1"
def test_bitdeer_api_base_override(self):
"""Test that an explicit api_base / api_key overrides the default"""
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
model, provider, api_key, api_base = get_llm_provider(
model="bitdeer/moonshotai/Kimi-K3",
custom_llm_provider=None,
api_base="https://custom.bitdeer.ai/v1",
api_key="sk-test",
)
assert provider == "bitdeer"
assert api_base == "https://custom.bitdeer.ai/v1"
assert api_key == "sk-test"
def test_bitdeer_url_autodetection(self):
"""Test that api_base=api-inference.bitdeer.ai/v1 auto-sets custom_llm_provider=bitdeer"""
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
model, provider, api_key, api_base = get_llm_provider(
model="moonshotai/Kimi-K3",
custom_llm_provider=None,
api_base="https://api-inference.bitdeer.ai/v1",
api_key=None,
)
assert provider == "bitdeer"
assert api_base == "https://api-inference.bitdeer.ai/v1"
def test_bitdeer_router_config(self):
"""Test that bitdeer can be used in Router configuration"""
from litellm import Router
router = Router(
model_list=[
{
"model_name": "bitdeer-kimi-k3",
"litellm_params": {
"model": "bitdeer/moonshotai/Kimi-K3",
"api_key": "test-key",
},
}
]
)
assert len(router.model_list) == 1
assert router.model_list[0]["model_name"] == "bitdeer-kimi-k3"
def test_bitdeer_model_cost_map(self):
"""Test that bitdeer model pricing is registered correctly"""
with open(
os.path.join(workspace_path, "model_prices_and_context_window.json")
) as f:
model_cost = json.load(f)
expected_models = {
"bitdeer/moonshotai/Kimi-K3": (3e-06, 1.5e-05),
"bitdeer/zai-org/GLM-5.2": (1.4e-06, 4.4e-06),
"bitdeer/moonshotai/Kimi-K2.6": (9.5e-07, 4e-06),
"bitdeer/Qwen/Qwen3.5-397B-A17B": (6e-07, 3.55e-06),
"bitdeer/deepseek-ai/DeepSeek-V4-Pro": (1.168e-06, 2.336e-06),
"bitdeer/deepseek-ai/DeepSeek-V4-Flash": (4e-08, 8e-08),
}
for model, (input_cost, output_cost) in expected_models.items():
assert model in model_cost
assert model_cost[model]["litellm_provider"] == "bitdeer"
assert model_cost[model]["input_cost_per_token"] == input_cost
assert model_cost[model]["output_cost_per_token"] == output_cost