mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
feat(providers): add Pareto Inference as OpenAI-compatible provider
Registers pareto through the JSON provider registry, so pareto/z-ai/glm-5.3-flash resolves to https://api.paretoinference.com/v1 with PARETO_API_KEY and an optional PARETO_API_BASE override. Adds LlmProviders.PARETO, the constants.py provider and endpoint lists so an api_base pointing at Pareto also resolves to the provider, the provider endpoints matrix, the Add Model provider fields, per-token pricing for pareto/z-ai/glm-5.3-flash in both cost maps, and tests.
This commit is contained in:
parent
eacb670b6e
commit
f67d164484
9 changed files with 200 additions and 0 deletions
|
|
@ -1095,6 +1095,7 @@ openai_compatible_endpoints: Final[list[str]] = [
|
|||
"https://api.prisminference.com/v1",
|
||||
"https://api.reka.ai/v1",
|
||||
"https://inference.runanywhere.ai/v1",
|
||||
"https://api.paretoinference.com/v1",
|
||||
"https://gigachat.devices.sberbank.ru/api/v1",
|
||||
"https://api.openinfer.ai/v1",
|
||||
"https://vanchin.streamlake.ai/api/gateway/v1/endpoints",
|
||||
|
|
@ -1183,6 +1184,7 @@ openai_compatible_providers: Final[list[str]] = [
|
|||
"reka",
|
||||
"streamlake",
|
||||
"wally",
|
||||
"pareto",
|
||||
]
|
||||
|
||||
OPENAI_AUDIO_TRANSCRIPTION_PROVIDERS: Final = frozenset({"openai"} | frozenset(openai_compatible_providers))
|
||||
|
|
|
|||
|
|
@ -314,5 +314,10 @@
|
|||
"supports_function_calling": true,
|
||||
"supports_reasoning": true
|
||||
}
|
||||
},
|
||||
"pareto": {
|
||||
"base_url": "https://api.paretoinference.com/v1",
|
||||
"api_key_env": "PARETO_API_KEY",
|
||||
"api_base_env": "PARETO_API_BASE"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64400,6 +64400,14 @@
|
|||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"pareto/z-ai/glm-5.3-flash": {
|
||||
"input_cost_per_token": 3e-08,
|
||||
"output_cost_per_token": 1e-07,
|
||||
"cache_read_input_token_cost": 6e-09,
|
||||
"litellm_provider": "pareto",
|
||||
"mode": "chat",
|
||||
"source": "https://docs.paretoinference.com/pricing"
|
||||
},
|
||||
"xai/grok-4.20-0309-non-reasoning": {
|
||||
"cache_read_input_token_cost": 2e-07,
|
||||
"cache_read_input_token_cost_batches": 1.6e-07,
|
||||
|
|
|
|||
|
|
@ -3189,6 +3189,23 @@
|
|||
"generateContent": false
|
||||
}
|
||||
},
|
||||
"pareto": {
|
||||
"display_name": "Pareto Inference (`pareto`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/pareto",
|
||||
"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
|
||||
}
|
||||
},
|
||||
"perplexity": {
|
||||
"display_name": "Perplexity AI (`perplexity`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/perplexity",
|
||||
|
|
|
|||
|
|
@ -3269,6 +3269,34 @@
|
|||
],
|
||||
"default_model_placeholder": "gpt-3.5-turbo"
|
||||
},
|
||||
{
|
||||
"provider": "PARETO",
|
||||
"provider_display_name": "Pareto Inference",
|
||||
"litellm_provider": "pareto",
|
||||
"credential_fields": [
|
||||
{
|
||||
"key": "api_base",
|
||||
"label": "API Base",
|
||||
"placeholder": "https://api.paretoinference.com/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": "pareto/z-ai/glm-5.3-flash"
|
||||
},
|
||||
{
|
||||
"provider": "Perplexity",
|
||||
"provider_display_name": "Perplexity",
|
||||
|
|
|
|||
|
|
@ -4785,6 +4785,7 @@ class LlmProviders(str, Enum):
|
|||
INFERENCEAPIS = "inferenceapis"
|
||||
HOSTINGER_ROUTER = "hostinger_router"
|
||||
ZEROGPU = "zerogpu"
|
||||
PARETO = "pareto"
|
||||
DARKBLOOM = "darkbloom"
|
||||
META = "meta"
|
||||
SAIL = "sail"
|
||||
|
|
|
|||
|
|
@ -64400,6 +64400,14 @@
|
|||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"pareto/z-ai/glm-5.3-flash": {
|
||||
"input_cost_per_token": 3e-08,
|
||||
"output_cost_per_token": 1e-07,
|
||||
"cache_read_input_token_cost": 6e-09,
|
||||
"litellm_provider": "pareto",
|
||||
"mode": "chat",
|
||||
"source": "https://docs.paretoinference.com/pricing"
|
||||
},
|
||||
"xai/grok-4.20-0309-non-reasoning": {
|
||||
"cache_read_input_token_cost": 2e-07,
|
||||
"cache_read_input_token_cost_batches": 1.6e-07,
|
||||
|
|
|
|||
|
|
@ -3189,6 +3189,23 @@
|
|||
"generateContent": false
|
||||
}
|
||||
},
|
||||
"pareto": {
|
||||
"display_name": "Pareto Inference (`pareto`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/pareto",
|
||||
"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
|
||||
}
|
||||
},
|
||||
"perplexity": {
|
||||
"display_name": "Perplexity AI (`perplexity`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/perplexity",
|
||||
|
|
|
|||
114
tests/unit/llms/openai_like/test_pareto_provider.py
Normal file
114
tests/unit/llms/openai_like/test_pareto_provider.py
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import json
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
import pytest
|
||||
import respx
|
||||
|
||||
import litellm
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry, SimpleProviderConfig
|
||||
|
||||
MODEL: Final = "z-ai/glm-5.3-flash"
|
||||
REPO_ROOT: Final = Path(__file__).parents[4]
|
||||
COST_MAPS: Final = (
|
||||
REPO_ROOT / "model_prices_and_context_window.json",
|
||||
REPO_ROOT / "litellm" / "model_prices_and_context_window_backup.json",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pareto() -> SimpleProviderConfig:
|
||||
config: Final = JSONProviderRegistry.get("pareto")
|
||||
assert config is not None
|
||||
return config
|
||||
|
||||
|
||||
def test_prefixed_model_resolves_to_pareto_and_keeps_the_vendor_namespace(
|
||||
pareto: SimpleProviderConfig, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.delenv("PARETO_API_BASE", raising=False)
|
||||
|
||||
model, provider, _, api_base = get_llm_provider(model=f"pareto/{MODEL}")
|
||||
|
||||
assert provider == litellm.LlmProviders.PARETO.value
|
||||
assert model == MODEL
|
||||
assert api_base == pareto.base_url
|
||||
|
||||
|
||||
def test_api_key_and_base_are_read_from_pareto_env_vars(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
||||
monkeypatch.setenv("PARETO_API_KEY", "sk-pareto-env")
|
||||
monkeypatch.setenv("PARETO_API_BASE", "https://pareto.internal.example/v1")
|
||||
|
||||
_, provider, api_key, api_base = get_llm_provider(model=f"pareto/{MODEL}")
|
||||
|
||||
assert provider == "pareto"
|
||||
assert api_key == "sk-pareto-env"
|
||||
assert api_base == "https://pareto.internal.example/v1"
|
||||
|
||||
|
||||
def test_default_api_base_autodetects_pareto(pareto: SimpleProviderConfig, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("PARETO_API_KEY", "sk-pareto-env")
|
||||
|
||||
model, provider, api_key, _ = get_llm_provider(model=MODEL, api_base=pareto.base_url)
|
||||
|
||||
assert provider == "pareto"
|
||||
assert model == MODEL
|
||||
assert api_key == "sk-pareto-env"
|
||||
|
||||
|
||||
@respx.mock
|
||||
def test_completion_posts_to_pareto_chat_completions_with_bearer_key(
|
||||
pareto: SimpleProviderConfig, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.delenv("PARETO_API_BASE", raising=False)
|
||||
monkeypatch.setenv("PARETO_API_KEY", "sk-pareto-env")
|
||||
route: Final = respx.post(f"{pareto.base_url}/chat/completions").respond(
|
||||
json={
|
||||
"id": "chatcmpl-pareto",
|
||||
"object": "chat.completion",
|
||||
"created": 1,
|
||||
"model": MODEL,
|
||||
"choices": [{"index": 0, "message": {"role": "assistant", "content": "hi"}, "finish_reason": "stop"}],
|
||||
"usage": {"prompt_tokens": 5, "completion_tokens": 1, "total_tokens": 6},
|
||||
}
|
||||
)
|
||||
|
||||
response: Final = litellm.completion(
|
||||
model=f"pareto/{MODEL}",
|
||||
messages=[{"role": "user", "content": "hello"}],
|
||||
)
|
||||
|
||||
assert route.call_count == 1
|
||||
request: Final = route.calls.last.request
|
||||
assert request.headers["authorization"] == "Bearer sk-pareto-env"
|
||||
assert json.loads(request.content)["model"] == MODEL
|
||||
assert response.choices[0].message.content == "hi"
|
||||
assert response.usage.total_tokens == 6
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def local_model_cost_map(monkeypatch: pytest.MonkeyPatch):
|
||||
"""Resolve pricing against the in-repo cost map, not the remote one fetched at import."""
|
||||
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
|
||||
monkeypatch.setattr(litellm, "model_cost", litellm.get_model_cost_map(url=""))
|
||||
litellm.get_model_info.cache_clear()
|
||||
yield
|
||||
litellm.get_model_info.cache_clear()
|
||||
|
||||
|
||||
def test_cost_maps_carry_the_same_pareto_entry() -> None:
|
||||
main, backup = (json.loads(path.read_text())[f"pareto/{MODEL}"] for path in COST_MAPS)
|
||||
|
||||
assert main == backup
|
||||
assert main["litellm_provider"] == "pareto"
|
||||
|
||||
|
||||
def test_published_per_token_prices_are_used_for_cost(local_model_cost_map: None) -> None:
|
||||
prompt_cost, completion_cost = litellm.cost_per_token(
|
||||
model=f"pareto/{MODEL}", prompt_tokens=1_000_000, completion_tokens=1_000_000
|
||||
)
|
||||
|
||||
assert prompt_cost == pytest.approx(0.03)
|
||||
assert completion_cost == pytest.approx(0.10)
|
||||
Loading…
Add table
Reference in a new issue