mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
Merge b6a0f610ed into 4ece6c9fb8
This commit is contained in:
commit
af89052431
9 changed files with 437 additions and 0 deletions
|
|
@ -33,6 +33,7 @@ from collections.abc import Mapping, Sequence
|
|||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
cast,
|
||||
Dict,
|
||||
Final,
|
||||
get_args,
|
||||
|
|
@ -688,6 +689,7 @@ moonshot_models: Set = set()
|
|||
publicai_models: Set = set()
|
||||
darkbloom_models: Set = set()
|
||||
v0_models: Set = set()
|
||||
vispark_models: Set = set()
|
||||
morph_models: Set = set()
|
||||
lambda_ai_models: Set = set()
|
||||
inception_models: Set = set()
|
||||
|
|
@ -954,6 +956,9 @@ def _populate_provider_model_sets(model_cost_map: Dict) -> None:
|
|||
darkbloom_models.add(key)
|
||||
elif value.get("litellm_provider") == "v0":
|
||||
v0_models.add(key)
|
||||
elif value.get("litellm_provider") == "vispark":
|
||||
# cast keeps the basedpyright reportUnknownArgumentType budget flat (keys are model-name strings)
|
||||
vispark_models.add(cast(str, key)) # cast-ok: model keys are strings
|
||||
elif value.get("litellm_provider") == "morph":
|
||||
morph_models.add(key)
|
||||
elif value.get("litellm_provider") == "lambda_ai":
|
||||
|
|
@ -1116,6 +1121,7 @@ model_list = list(
|
|||
| publicai_models
|
||||
| darkbloom_models
|
||||
| v0_models
|
||||
| vispark_models
|
||||
| morph_models
|
||||
| lambda_ai_models
|
||||
| inception_models
|
||||
|
|
@ -1228,6 +1234,7 @@ def _build_models_by_provider() -> dict:
|
|||
"publicai": publicai_models,
|
||||
"darkbloom": darkbloom_models,
|
||||
"v0": v0_models,
|
||||
"vispark": vispark_models,
|
||||
"morph": morph_models,
|
||||
"lambda_ai": lambda_ai_models,
|
||||
"inception": inception_models,
|
||||
|
|
|
|||
|
|
@ -964,6 +964,7 @@ openai_compatible_endpoints: Final[list] = [
|
|||
"https://api.cognition.ai/v1",
|
||||
"https://api.cortecs.ai/v1",
|
||||
"https://api.scx.ai/v1",
|
||||
"https://api.lab.vispark.in/v1",
|
||||
"https://api.prisminference.com/v1",
|
||||
"https://gigachat.devices.sberbank.ru/api/v1",
|
||||
]
|
||||
|
|
@ -1035,6 +1036,7 @@ openai_compatible_providers: Final[list] = [
|
|||
"ragflow",
|
||||
"pinstripes", # Pinstripes - JSON-configured provider
|
||||
"darkbloom",
|
||||
"vispark", # Vispark Lab - JSON-configured provider
|
||||
"meta", # Meta Model API (Muse Spark) - JSON-configured provider
|
||||
"cognition",
|
||||
"cortecs",
|
||||
|
|
@ -1066,6 +1068,7 @@ openai_text_completion_compatible_providers: Final[list] = [ # providers that s
|
|||
"poe",
|
||||
"chutes",
|
||||
"v0",
|
||||
"vispark",
|
||||
"lambda_ai",
|
||||
"hyperbolic",
|
||||
"wandb",
|
||||
|
|
|
|||
|
|
@ -218,5 +218,14 @@
|
|||
"api_key_env": "SAIL_API_KEY",
|
||||
"api_base_env": "SAIL_API_BASE",
|
||||
"supported_endpoints": ["/v1/chat/completions", "/v1/responses", "/v1/messages"]
|
||||
},
|
||||
"vispark": {
|
||||
"base_url": "https://api.lab.vispark.in/v1",
|
||||
"api_key_env": "VISPARK_LAB_API_KEY",
|
||||
"api_base_env": "VISPARK_LAB_API_BASE",
|
||||
"param_mappings": {
|
||||
"max_completion_tokens": "max_tokens"
|
||||
},
|
||||
"supported_endpoints": ["/v1/chat/completions", "/v1/completions"]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47917,6 +47917,102 @@
|
|||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"vispark/vision-small": {
|
||||
"input_cost_per_token": 1.05e-06,
|
||||
"litellm_provider": "vispark",
|
||||
"max_input_tokens": 1000000,
|
||||
"max_output_tokens": 65536,
|
||||
"max_tokens": 65536,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 3.16e-06,
|
||||
"source": "https://lab.vispark.in/#vision",
|
||||
"supported_endpoints": [
|
||||
"/v1/chat/completions",
|
||||
"/v1/completions"
|
||||
],
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image",
|
||||
"audio",
|
||||
"video"
|
||||
],
|
||||
"supported_output_modalities": [
|
||||
"text"
|
||||
],
|
||||
"supports_function_calling": true,
|
||||
"supports_native_streaming": true,
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"vispark/vision-medium": {
|
||||
"input_cost_per_token": 4.21e-06,
|
||||
"litellm_provider": "vispark",
|
||||
"max_input_tokens": 1000000,
|
||||
"max_output_tokens": 65536,
|
||||
"max_tokens": 65536,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1.263e-05,
|
||||
"source": "https://lab.vispark.in/#vision",
|
||||
"supported_endpoints": [
|
||||
"/v1/chat/completions",
|
||||
"/v1/completions"
|
||||
],
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image",
|
||||
"audio",
|
||||
"video"
|
||||
],
|
||||
"supported_output_modalities": [
|
||||
"text"
|
||||
],
|
||||
"supports_function_calling": true,
|
||||
"supports_native_streaming": true,
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"vispark/vision-large": {
|
||||
"input_cost_per_token": 7.37e-06,
|
||||
"litellm_provider": "vispark",
|
||||
"max_input_tokens": 1000000,
|
||||
"max_output_tokens": 65536,
|
||||
"max_tokens": 65536,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.211e-05,
|
||||
"source": "https://lab.vispark.in/#vision",
|
||||
"supported_endpoints": [
|
||||
"/v1/chat/completions",
|
||||
"/v1/completions"
|
||||
],
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image",
|
||||
"audio",
|
||||
"video"
|
||||
],
|
||||
"supported_output_modalities": [
|
||||
"text"
|
||||
],
|
||||
"supports_function_calling": true,
|
||||
"supports_native_streaming": true,
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"vercel_ai_gateway/alibaba/qwen-3-14b": {
|
||||
"input_cost_per_token": 8e-08,
|
||||
"litellm_provider": "vercel_ai_gateway",
|
||||
|
|
|
|||
|
|
@ -3712,5 +3712,33 @@
|
|||
}
|
||||
],
|
||||
"default_model_placeholder": "cursor/claude-4-sonnet"
|
||||
},
|
||||
{
|
||||
"provider": "VISPARK",
|
||||
"provider_display_name": "Vispark",
|
||||
"litellm_provider": "vispark",
|
||||
"credential_fields": [
|
||||
{
|
||||
"key": "api_base",
|
||||
"label": "API Base",
|
||||
"placeholder": "https://api.lab.vispark.in/v1",
|
||||
"tooltip": null,
|
||||
"required": false,
|
||||
"field_type": "text",
|
||||
"options": null,
|
||||
"default_value": null
|
||||
},
|
||||
{
|
||||
"key": "api_key",
|
||||
"label": "API Key",
|
||||
"placeholder": null,
|
||||
"tooltip": null,
|
||||
"required": true,
|
||||
"field_type": "password",
|
||||
"options": null,
|
||||
"default_value": null
|
||||
}
|
||||
],
|
||||
"default_model_placeholder": "vispark/vision-medium"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -4069,6 +4069,7 @@ class LlmProviders(str, Enum):
|
|||
MOONSHOT = "moonshot"
|
||||
PUBLICAI = "publicai"
|
||||
V0 = "v0"
|
||||
VISPARK = "vispark"
|
||||
MORPH = "morph"
|
||||
LAMBDA_AI = "lambda_ai"
|
||||
INCEPTION = "inception"
|
||||
|
|
|
|||
|
|
@ -47917,6 +47917,102 @@
|
|||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"vispark/vision-small": {
|
||||
"input_cost_per_token": 1.05e-06,
|
||||
"litellm_provider": "vispark",
|
||||
"max_input_tokens": 1000000,
|
||||
"max_output_tokens": 65536,
|
||||
"max_tokens": 65536,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 3.16e-06,
|
||||
"source": "https://lab.vispark.in/#vision",
|
||||
"supported_endpoints": [
|
||||
"/v1/chat/completions",
|
||||
"/v1/completions"
|
||||
],
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image",
|
||||
"audio",
|
||||
"video"
|
||||
],
|
||||
"supported_output_modalities": [
|
||||
"text"
|
||||
],
|
||||
"supports_function_calling": true,
|
||||
"supports_native_streaming": true,
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"vispark/vision-medium": {
|
||||
"input_cost_per_token": 4.21e-06,
|
||||
"litellm_provider": "vispark",
|
||||
"max_input_tokens": 1000000,
|
||||
"max_output_tokens": 65536,
|
||||
"max_tokens": 65536,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1.263e-05,
|
||||
"source": "https://lab.vispark.in/#vision",
|
||||
"supported_endpoints": [
|
||||
"/v1/chat/completions",
|
||||
"/v1/completions"
|
||||
],
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image",
|
||||
"audio",
|
||||
"video"
|
||||
],
|
||||
"supported_output_modalities": [
|
||||
"text"
|
||||
],
|
||||
"supports_function_calling": true,
|
||||
"supports_native_streaming": true,
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"vispark/vision-large": {
|
||||
"input_cost_per_token": 7.37e-06,
|
||||
"litellm_provider": "vispark",
|
||||
"max_input_tokens": 1000000,
|
||||
"max_output_tokens": 65536,
|
||||
"max_tokens": 65536,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.211e-05,
|
||||
"source": "https://lab.vispark.in/#vision",
|
||||
"supported_endpoints": [
|
||||
"/v1/chat/completions",
|
||||
"/v1/completions"
|
||||
],
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image",
|
||||
"audio",
|
||||
"video"
|
||||
],
|
||||
"supported_output_modalities": [
|
||||
"text"
|
||||
],
|
||||
"supports_function_calling": true,
|
||||
"supports_native_streaming": true,
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"vercel_ai_gateway/alibaba/qwen-3-14b": {
|
||||
"input_cost_per_token": 8e-08,
|
||||
"litellm_provider": "vercel_ai_gateway",
|
||||
|
|
|
|||
|
|
@ -2641,6 +2641,23 @@
|
|||
"interactions": true
|
||||
}
|
||||
},
|
||||
"vispark": {
|
||||
"display_name": "Vispark (`vispark`)",
|
||||
"url": "https://lab.vispark.in/#vision",
|
||||
"endpoints": {
|
||||
"chat_completions": true,
|
||||
"messages": false,
|
||||
"responses": false,
|
||||
"embeddings": false,
|
||||
"image_generations": false,
|
||||
"audio_transcriptions": false,
|
||||
"audio_speech": false,
|
||||
"moderations": false,
|
||||
"batches": false,
|
||||
"rerank": false,
|
||||
"a2a": false
|
||||
}
|
||||
},
|
||||
"vercel_ai_gateway": {
|
||||
"display_name": "Vercel AI Gateway (`vercel_ai_gateway`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/vercel_ai_gateway",
|
||||
|
|
|
|||
180
tests/unit/llms/openai_like/test_vispark_provider.py
Normal file
180
tests/unit/llms/openai_like/test_vispark_provider.py
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
"""
|
||||
Tests for Vispark provider configuration and integration.
|
||||
"""
|
||||
|
||||
import litellm
|
||||
|
||||
|
||||
class TestVisparkProviderConfig:
|
||||
def test_vispark_in_provider_list(self):
|
||||
from litellm import LlmProviders
|
||||
|
||||
assert hasattr(LlmProviders, "VISPARK")
|
||||
assert LlmProviders.VISPARK.value == "vispark"
|
||||
assert "vispark" in litellm.provider_list
|
||||
|
||||
def test_vispark_json_config_exists(self):
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
||||
|
||||
assert JSONProviderRegistry.exists("vispark")
|
||||
|
||||
vispark = JSONProviderRegistry.get("vispark")
|
||||
assert vispark is not None
|
||||
assert vispark.base_url == "https://api.lab.vispark.in/v1"
|
||||
assert vispark.api_key_env == "VISPARK_LAB_API_KEY"
|
||||
assert vispark.api_base_env == "VISPARK_LAB_API_BASE"
|
||||
assert vispark.param_mappings.get("max_completion_tokens") == "max_tokens"
|
||||
|
||||
def test_vispark_in_openai_compatible_providers(self):
|
||||
from litellm.constants import (
|
||||
openai_compatible_providers,
|
||||
openai_text_completion_compatible_providers,
|
||||
)
|
||||
|
||||
assert "vispark" in openai_compatible_providers
|
||||
assert "vispark" in openai_text_completion_compatible_providers
|
||||
|
||||
def test_vispark_provider_resolution(self):
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
||||
|
||||
model, provider, api_key, api_base = get_llm_provider(
|
||||
model="vispark/vision-medium",
|
||||
custom_llm_provider=None,
|
||||
api_base=None,
|
||||
api_key=None,
|
||||
)
|
||||
|
||||
assert model == "vision-medium"
|
||||
assert provider == "vispark"
|
||||
assert api_base == "https://api.lab.vispark.in/v1"
|
||||
|
||||
def test_vispark_api_base_override(self):
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
||||
|
||||
model, provider, api_key, api_base = get_llm_provider(
|
||||
model="vispark/vision-medium",
|
||||
custom_llm_provider=None,
|
||||
api_base="https://custom.vispark.in/v1",
|
||||
api_key="sk-test",
|
||||
)
|
||||
|
||||
assert provider == "vispark"
|
||||
assert api_base == "https://custom.vispark.in/v1"
|
||||
assert api_key == "sk-test"
|
||||
|
||||
def test_vispark_url_autodetection(self):
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
||||
|
||||
model, provider, api_key, api_base = get_llm_provider(
|
||||
model="vision-medium",
|
||||
custom_llm_provider=None,
|
||||
api_base="https://api.lab.vispark.in/v1",
|
||||
api_key=None,
|
||||
)
|
||||
assert provider == "vispark"
|
||||
assert api_base == "https://api.lab.vispark.in/v1"
|
||||
|
||||
def test_vispark_max_completion_tokens_mapped(self):
|
||||
from litellm.llms.openai_like.dynamic_config import create_config_class
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
||||
|
||||
provider = JSONProviderRegistry.get("vispark")
|
||||
assert provider is not None
|
||||
config = create_config_class(provider)()
|
||||
|
||||
optional_params = config.map_openai_params(
|
||||
non_default_params={"max_completion_tokens": 256},
|
||||
optional_params={},
|
||||
model="vision-medium",
|
||||
drop_params=False,
|
||||
)
|
||||
assert optional_params["max_tokens"] == 256
|
||||
assert "max_completion_tokens" not in optional_params
|
||||
|
||||
def test_vispark_router_config(self):
|
||||
from litellm import Router
|
||||
|
||||
router = Router(
|
||||
model_list=[
|
||||
{
|
||||
"model_name": "vispark-chat",
|
||||
"litellm_params": {
|
||||
"model": "vispark/vision-medium",
|
||||
"api_key": "test-key",
|
||||
},
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
assert len(router.model_list) == 1
|
||||
assert router.model_list[0]["model_name"] == "vispark-chat"
|
||||
|
||||
|
||||
class TestVisparkModelMetadata:
|
||||
VISPARK_MODELS = (
|
||||
"vispark/vision-small",
|
||||
"vispark/vision-medium",
|
||||
"vispark/vision-large",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _load(path_parts):
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
json_path = Path(__file__).parents[4].joinpath(*path_parts)
|
||||
with open(json_path) as f:
|
||||
return json.load(f)
|
||||
|
||||
def test_vispark_models_registered_with_correct_metadata(self):
|
||||
model_cost = self._load(("model_prices_and_context_window.json",))
|
||||
for model in self.VISPARK_MODELS:
|
||||
info = model_cost.get(model)
|
||||
assert info is not None, f"{model} missing from model_prices_and_context_window.json"
|
||||
assert info["litellm_provider"] == "vispark"
|
||||
assert info["mode"] == "chat"
|
||||
assert info["input_cost_per_token"] > 0
|
||||
assert info["output_cost_per_token"] > 0
|
||||
assert info["supports_function_calling"] is True
|
||||
assert info["supports_tool_choice"] is True
|
||||
assert info["supports_reasoning"] is True
|
||||
assert info["supports_response_schema"] is True
|
||||
assert info["supports_vision"] is True
|
||||
|
||||
assert info["max_output_tokens"] == 65536
|
||||
assert info["max_tokens"] == info["max_output_tokens"]
|
||||
assert info["max_input_tokens"] == 1000000
|
||||
|
||||
def test_vispark_models_synced_to_backup(self):
|
||||
model_cost = self._load(("model_prices_and_context_window.json",))
|
||||
backup = self._load(("litellm", "model_prices_and_context_window_backup.json"))
|
||||
for model in self.VISPARK_MODELS:
|
||||
assert model in backup, f"{model} missing from backup json"
|
||||
assert backup[model] == model_cost[model], f"{model} differs between root and backup json"
|
||||
|
||||
|
||||
class TestVisparkDashboardRegistration:
|
||||
@staticmethod
|
||||
def _provider_create_fields():
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import litellm
|
||||
|
||||
path = Path(litellm.__file__).parent / "proxy" / "public_endpoints" / "provider_create_fields.json"
|
||||
with open(path) as f:
|
||||
return json.load(f)
|
||||
|
||||
def test_vispark_is_selectable_in_the_add_model_form(self):
|
||||
entries = [e for e in self._provider_create_fields() if e["litellm_provider"] == "vispark"]
|
||||
assert len(entries) == 1, "vispark must appear exactly once in provider_create_fields.json"
|
||||
|
||||
entry = entries[0]
|
||||
assert entry["provider"] == "VISPARK"
|
||||
assert entry["provider_display_name"] == "Vispark"
|
||||
assert entry["default_model_placeholder"].startswith("vispark/")
|
||||
|
||||
fields = {f["key"]: f for f in entry["credential_fields"]}
|
||||
assert fields["api_key"]["required"] is True
|
||||
assert fields["api_key"]["field_type"] == "password"
|
||||
assert fields["api_base"]["required"] is False
|
||||
Loading…
Add table
Reference in a new issue