feat: add QuickSilver Pro as a JSON-configured OpenAI-compatible provider

Adds QuickSilver Pro (MachineFi Inc., https://quicksilverpro.io) as a
JSON-configured OpenAI-compatible provider, following the SCX.ai
provider pattern (PR #34752).

- litellm/llms/openai_like/providers.json: `quicksilverpro` entry with
  base_url https://api.quicksilverpro.io/v1, env vars
  QUICKSILVERPRO_API_KEY / QUICKSILVERPRO_API_BASE, param_mappings
  {max_completion_tokens -> max_tokens}, and supported endpoints
  /v1/chat/completions, /v1/responses, /v1/messages (Claude models).
- constants.py: appended to openai_compatible_endpoints and
  openai_compatible_providers.
- types/utils.py: LlmProviders.QUICKSILVERPRO.
- model_prices_and_context_window.json (+ backup, kept in sync): 8
  models with prices from https://quicksilverpro.io/pricing.json
  (catalog_version 40, updated 2026-09-30), USD per 1M tokens
  converted to USD per token:
  claude-opus-5-5 (1.60/8.00), claude-sonnet-5-5 (1.00/5.00),
  gpt-6.1-sol (2.00/10.00), deepseek-v4.1-flash (0.125/0.55),
  glm-5.3-flash (0.06/0.20), mimo-v2.6-pro (0.348/0.696),
  gemini-3.8-flash (0.6375/3.1875), qwen3.8-27b (0.34/2.04), with
  cache-read/cache-write prices and context windows from the same
  catalog. Where pricing.json lists no output cap (deepseek-v4.1-flash,
  mimo-v2.6-pro), the legacy max_tokens falls back to the context
  window.
- provider_endpoints_support.json (+ backup): chat_completions,
  messages and responses enabled; embeddings, image_generations,
  audio_transcriptions, audio_speech, moderations, batches, rerank and
  a2a disabled (no JSON-provider mechanism exists for images).
- proxy/public_endpoints/provider_create_fields.json: QUICKSILVERPRO
  form entry (required api_key, optional api_base with default
  placeholder), default model quicksilverpro/claude-sonnet-5-5.
- UI: provider enum + slug + logo + placeholder maps
  (provider_info_helpers.tsx), plate logo treatment
  (logoTreatments.ts), quicksilverpro.svg in public assets and the
  built proxy assets.
- tests/unit/llms/openai_like/test_quicksilverpro_provider.py
  (25 tests): registry/config, provider resolution + api_base
  override, param mapping, router config, dashboard registration, and
  per-model behaviour checks that charge/read each entry's own prices
  via cost_per_token + get_model_info instead of pinning upstream
  numbers. respx-mocked (no network) request tests cover all three
  declared endpoints: chat/completions (with max_completion_tokens ->
  max_tokens mapping asserted on the wire), /v1/responses, and
  /v1/messages.

Local validation: 290 passed (tests/unit/llms/openai_like/ +
tests/unit/test_model_prices_schema.py), 90 UI tests passed
(provider_info_helpers.test.tsx, logoTreatments.test.ts) with no type
errors.
This commit is contained in:
Raullen Chai 2026-10-02 17:56:43 -07:00
parent a292fd409f
commit 3ee6a70d04
14 changed files with 778 additions and 0 deletions

View file

@ -965,6 +965,7 @@ openai_compatible_endpoints: Final[list] = [
"https://api.scx.ai/v1",
"https://api.prisminference.com/v1",
"https://gigachat.devices.sberbank.ru/api/v1",
"https://api.quicksilverpro.io/v1",
]
@ -1040,6 +1041,7 @@ openai_compatible_providers: Final[list] = [
"scx-ai",
"prism",
"sail",
"quicksilverpro",
]
OPENAI_AUDIO_TRANSCRIPTION_PROVIDERS: Final = frozenset({"openai"} | frozenset(openai_compatible_providers))

View file

@ -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"]
},
"quicksilverpro": {
"base_url": "https://api.quicksilverpro.io/v1",
"api_key_env": "QUICKSILVERPRO_API_KEY",
"api_base_env": "QUICKSILVERPRO_API_BASE",
"param_mappings": {
"max_completion_tokens": "max_tokens"
},
"supported_endpoints": ["/v1/chat/completions", "/v1/responses", "/v1/messages"]
}
}

View file

@ -17209,6 +17209,144 @@
"/v1/images/generations"
]
},
"quicksilverpro/claude-opus-5-5": {
"cache_creation_input_token_cost": 2e-06,
"cache_read_input_token_cost": 8e-08,
"input_cost_per_token": 1.6e-06,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 8e-06,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": false,
"supports_tool_choice": true,
"supports_vision": true
},
"quicksilverpro/claude-sonnet-5-5": {
"cache_creation_input_token_cost": 1.25e-06,
"cache_read_input_token_cost": 1e-07,
"input_cost_per_token": 1e-06,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 5e-06,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": false,
"supports_tool_choice": true,
"supports_vision": true
},
"quicksilverpro/deepseek-v4.1-flash": {
"cache_read_input_token_cost": 1.25e-08,
"input_cost_per_token": 1.25e-07,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1048576,
"max_tokens": 1048576,
"mode": "chat",
"output_cost_per_token": 5.5e-07,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": false
},
"quicksilverpro/gemini-3.8-flash": {
"cache_read_input_token_cost": 6.375e-08,
"input_cost_per_token": 6.375e-07,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1048576,
"max_output_tokens": 65536,
"max_tokens": 65536,
"mode": "chat",
"output_cost_per_token": 3.1875e-06,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
"quicksilverpro/glm-5.3-flash": {
"cache_read_input_token_cost": 1.2e-08,
"input_cost_per_token": 6e-08,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1048576,
"max_output_tokens": 131072,
"max_tokens": 131072,
"mode": "chat",
"output_cost_per_token": 2e-07,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": false,
"supports_tool_choice": true,
"supports_vision": false
},
"quicksilverpro/gpt-6.1-sol": {
"cache_creation_input_token_cost": 2.5e-06,
"cache_read_input_token_cost": 1e-07,
"input_cost_per_token": 2e-06,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1048576,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 1e-05,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
"quicksilverpro/mimo-v2.6-pro": {
"cache_read_input_token_cost": 2.88e-09,
"input_cost_per_token": 3.48e-07,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1048576,
"max_tokens": 1048576,
"mode": "chat",
"output_cost_per_token": 6.96e-07,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
"quicksilverpro/qwen3.8-27b": {
"cache_creation_input_token_cost": 4.25e-07,
"cache_read_input_token_cost": 6.8e-08,
"input_cost_per_token": 3.4e-07,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1000000,
"max_output_tokens": 131072,
"max_tokens": 131072,
"mode": "chat",
"output_cost_per_token": 2.04e-06,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": false,
"supports_response_schema": false,
"supports_tool_choice": true,
"supports_vision": false
},
"qwencloud/deepseek-v4-flash": {
"cache_read_input_token_cost": 4e-08,
"input_cost_per_token": 2e-07,

View file

@ -1926,6 +1926,23 @@
"interactions": true
}
},
"quicksilverpro": {
"display_name": "QuickSilver Pro (`quicksilverpro`)",
"url": "https://quicksilverpro.io/docs",
"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,
"a2a": false
}
},
"darkbloom": {
"display_name": "Darkbloom (`darkbloom`)",
"url": "https://docs.litellm.ai/docs/providers/darkbloom",

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -2883,6 +2883,34 @@
],
"default_model_placeholder": "gpt-3.5-turbo"
},
{
"provider": "QUICKSILVERPRO",
"provider_display_name": "QuickSilver Pro",
"litellm_provider": "quicksilverpro",
"credential_fields": [
{
"key": "api_base",
"label": "API Base",
"placeholder": "https://api.quicksilverpro.io/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": "quicksilverpro/claude-sonnet-5-5"
},
{
"provider": "PRISM",
"provider_display_name": "Prism",

View file

@ -4158,6 +4158,7 @@ class LlmProviders(str, Enum):
DARKBLOOM = "darkbloom"
META = "meta"
SAIL = "sail"
QUICKSILVERPRO = "quicksilverpro"
LITELLM_AGENT = "litellm_agent"
CURSOR = "cursor"
BEDROCK_MANTLE = "bedrock_mantle"

View file

@ -17209,6 +17209,144 @@
"/v1/images/generations"
]
},
"quicksilverpro/claude-opus-5-5": {
"cache_creation_input_token_cost": 2e-06,
"cache_read_input_token_cost": 8e-08,
"input_cost_per_token": 1.6e-06,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 8e-06,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": false,
"supports_tool_choice": true,
"supports_vision": true
},
"quicksilverpro/claude-sonnet-5-5": {
"cache_creation_input_token_cost": 1.25e-06,
"cache_read_input_token_cost": 1e-07,
"input_cost_per_token": 1e-06,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1000000,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 5e-06,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": false,
"supports_tool_choice": true,
"supports_vision": true
},
"quicksilverpro/deepseek-v4.1-flash": {
"cache_read_input_token_cost": 1.25e-08,
"input_cost_per_token": 1.25e-07,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1048576,
"max_tokens": 1048576,
"mode": "chat",
"output_cost_per_token": 5.5e-07,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": false
},
"quicksilverpro/gemini-3.8-flash": {
"cache_read_input_token_cost": 6.375e-08,
"input_cost_per_token": 6.375e-07,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1048576,
"max_output_tokens": 65536,
"max_tokens": 65536,
"mode": "chat",
"output_cost_per_token": 3.1875e-06,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
"quicksilverpro/glm-5.3-flash": {
"cache_read_input_token_cost": 1.2e-08,
"input_cost_per_token": 6e-08,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1048576,
"max_output_tokens": 131072,
"max_tokens": 131072,
"mode": "chat",
"output_cost_per_token": 2e-07,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": false,
"supports_tool_choice": true,
"supports_vision": false
},
"quicksilverpro/gpt-6.1-sol": {
"cache_creation_input_token_cost": 2.5e-06,
"cache_read_input_token_cost": 1e-07,
"input_cost_per_token": 2e-06,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1048576,
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"output_cost_per_token": 1e-05,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
"quicksilverpro/mimo-v2.6-pro": {
"cache_read_input_token_cost": 2.88e-09,
"input_cost_per_token": 3.48e-07,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1048576,
"max_tokens": 1048576,
"mode": "chat",
"output_cost_per_token": 6.96e-07,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
"quicksilverpro/qwen3.8-27b": {
"cache_creation_input_token_cost": 4.25e-07,
"cache_read_input_token_cost": 6.8e-08,
"input_cost_per_token": 3.4e-07,
"litellm_provider": "quicksilverpro",
"max_input_tokens": 1000000,
"max_output_tokens": 131072,
"max_tokens": 131072,
"mode": "chat",
"output_cost_per_token": 2.04e-06,
"source": "https://quicksilverpro.io/pricing.json",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": false,
"supports_response_schema": false,
"supports_tool_choice": true,
"supports_vision": false
},
"qwencloud/deepseek-v4-flash": {
"cache_read_input_token_cost": 4e-08,
"input_cost_per_token": 2e-07,

View file

@ -2186,6 +2186,23 @@
"interactions": true
}
},
"quicksilverpro": {
"display_name": "QuickSilver Pro (`quicksilverpro`)",
"url": "https://quicksilverpro.io/docs",
"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,
"a2a": false
}
},
"darkbloom": {
"display_name": "Darkbloom (`darkbloom`)",
"url": "https://docs.litellm.ai/docs/providers/darkbloom",

View file

@ -0,0 +1,379 @@
"""
Tests for the QuickSilver Pro provider configuration and integration.
QuickSilver Pro (`quicksilverpro`) is a JSON-configured OpenAI-compatible
provider (https://quicksilverpro.io, operated by MachineFi Inc.). Its price /
context catalog is committed to the model cost map; these tests check
LiteLLM's own behaviour against the committed entries rather than pinning
upstream numbers.
"""
import json
from pathlib import Path
from typing import Final
import pytest
import respx
import litellm
from litellm.caching.llm_caching_handler import LLMClientCache
QSP_MODELS: Final = tuple(
sorted(name for name in litellm.model_cost if name.startswith("quicksilverpro/"))
)
class TestQuickSilverProProviderConfig:
def test_quicksilverpro_in_provider_list(self):
from litellm import LlmProviders
assert hasattr(LlmProviders, "QUICKSILVERPRO")
assert LlmProviders.QUICKSILVERPRO.value == "quicksilverpro"
assert "quicksilverpro" in litellm.provider_list
def test_quicksilverpro_json_config_exists(self):
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
assert JSONProviderRegistry.exists("quicksilverpro")
provider = JSONProviderRegistry.get("quicksilverpro")
assert provider is not None
assert provider.base_url == "https://api.quicksilverpro.io/v1"
assert provider.api_key_env == "QUICKSILVERPRO_API_KEY"
assert provider.api_base_env == "QUICKSILVERPRO_API_BASE"
assert provider.param_mappings.get("max_completion_tokens") == "max_tokens"
def test_quicksilverpro_supported_endpoints(self):
"""chat completions, Responses API and Anthropic /v1/messages are
declared; image generations have no JSON-provider mechanism and are
not."""
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
provider = JSONProviderRegistry.get("quicksilverpro")
assert provider is not None
assert provider.supported_endpoints == [
"/v1/chat/completions",
"/v1/responses",
"/v1/messages",
]
def test_quicksilverpro_in_openai_compatible_providers(self):
from litellm.constants import openai_compatible_providers
assert "quicksilverpro" in openai_compatible_providers
def test_quicksilverpro_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="quicksilverpro/claude-sonnet-5-5",
custom_llm_provider=None,
api_base=None,
api_key=None,
)
assert model == "claude-sonnet-5-5"
assert provider == "quicksilverpro"
assert api_base == "https://api.quicksilverpro.io/v1"
def test_quicksilverpro_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="quicksilverpro/claude-sonnet-5-5",
custom_llm_provider=None,
api_base="https://custom.quicksilverpro.example/v1",
api_key="sk-test",
)
assert provider == "quicksilverpro"
assert api_base == "https://custom.quicksilverpro.example/v1"
assert api_key == "sk-test"
def test_quicksilverpro_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="claude-sonnet-5-5",
custom_llm_provider=None,
api_base="https://api.quicksilverpro.io/v1",
api_key=None,
)
assert provider == "quicksilverpro"
assert api_base == "https://api.quicksilverpro.io/v1"
def test_quicksilverpro_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("quicksilverpro")
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="claude-sonnet-5-5",
drop_params=False,
)
assert optional_params["max_tokens"] == 256
assert "max_completion_tokens" not in optional_params
def test_quicksilverpro_router_config(self):
from litellm import Router
router = Router(
model_list=[
{
"model_name": "quicksilverpro-chat",
"litellm_params": {
"model": "quicksilverpro/claude-sonnet-5-5",
"api_key": "test-key",
},
}
]
)
assert len(router.model_list) == 1
assert router.model_list[0]["model_name"] == "quicksilverpro-chat"
class TestQuickSilverProModelMetadata:
@staticmethod
def _load(path_parts):
json_path = Path(__file__).parents[4].joinpath(*path_parts)
with open(json_path) as f:
return json.load(f)
def test_all_models_present_in_model_cost(self):
model_cost = self._load(("model_prices_and_context_window.json",))
for model in QSP_MODELS:
assert model in model_cost, f"{model} missing from model cost map"
assert model_cost[model]["litellm_provider"] == "quicksilverpro"
assert model_cost[model]["mode"] == "chat"
def test_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 QSP_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"
)
@pytest.mark.parametrize("model", QSP_MODELS)
def test_quicksilverpro_model_cost_and_capabilities(self, model: str):
"""Behaviour against the committed entries: cost_per_token must charge
each model's own input/output price, and get_model_info must expose a
coherent entry. Expected values are read from the loaded cost map, not
hard-coded."""
from litellm.cost_calculator import cost_per_token
prompt_cost, completion_cost = cost_per_token(
model=model,
prompt_tokens=1_000_000,
completion_tokens=1_000_000,
custom_llm_provider="quicksilverpro",
)
model_info = litellm.get_model_info(model)
assert prompt_cost == pytest.approx(model_info["input_cost_per_token"] * 1_000_000)
assert completion_cost == pytest.approx(model_info["output_cost_per_token"] * 1_000_000)
assert model_info["output_cost_per_token"] > 0
assert 0 < model_info["cache_read_input_token_cost"] < model_info["input_cost_per_token"]
# Entries without a provider-specified output cap come back with
# max_output_tokens=None; the legacy max_tokens then carries the cap.
max_output = model_info.get("max_output_tokens") or model_info["max_input_tokens"]
assert (
model_info["max_tokens"]
== max_output
<= model_info["max_input_tokens"]
)
assert model_info["litellm_provider"] == "quicksilverpro"
assert model_info["mode"] == "chat"
assert litellm.supports_vision(model) is model_info["supports_vision"]
def test_quicksilverpro_cost_map_is_queryable(self):
"""The entries must be consumable through litellm's own cost map, not
just present in the JSON file."""
from litellm import model_cost
for model in QSP_MODELS:
assert model in model_cost, model
assert model_cost[model]["litellm_provider"] == "quicksilverpro"
class TestQuickSilverProDashboardRegistration:
@staticmethod
def _provider_create_fields():
path = (
Path(litellm.__file__).parent
/ "proxy"
/ "public_endpoints"
/ "provider_create_fields.json"
)
with open(path) as f:
return json.load(f)
def test_quicksilverpro_is_selectable_in_the_add_model_form(self):
entries = [
e
for e in self._provider_create_fields()
if e["litellm_provider"] == "quicksilverpro"
]
assert (
len(entries) == 1
), "quicksilverpro must appear exactly once in provider_create_fields.json"
entry = entries[0]
assert entry["provider"] == "QUICKSILVERPRO"
assert entry["provider_display_name"] == "QuickSilver Pro"
assert entry["default_model_placeholder"].startswith("quicksilverpro/")
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
def test_quicksilverpro_supported_endpoints_matrix(self):
matrix = json.loads(
(
Path(litellm.__file__).parent
/ "provider_endpoints_support_backup.json"
).read_text()
)
endpoints = matrix["providers"]["quicksilverpro"]["endpoints"]
assert endpoints["chat_completions"] is True
assert endpoints["messages"] is True
assert endpoints["responses"] is True
assert endpoints["embeddings"] is False
assert endpoints["image_generations"] is False
def test_quicksilverpro_chat_completion_request():
"""chat/completions hits the provider's base URL with bearer auth and maps
max_completion_tokens -> max_tokens."""
with respx.mock() as upstream:
route: Final = upstream.post(
"https://api.quicksilverpro.io/v1/chat/completions"
).respond(
200,
json={
"id": "chatcmpl_quicksilverpro",
"object": "chat.completion",
"created": 1_789_550_000,
"model": "claude-sonnet-5-5",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "Hello from QuickSilver Pro"},
"finish_reason": "stop",
}
],
"usage": {"prompt_tokens": 4, "completion_tokens": 3, "total_tokens": 7},
},
)
response: Final = litellm.completion(
model="quicksilverpro/claude-sonnet-5-5",
messages=[{"role": "user", "content": "Say hello"}],
max_completion_tokens=128,
api_key="quicksilverpro-test-key",
)
request: Final = route.calls.last.request
body: Final = json.loads(request.content)
assert route.call_count == 1
assert str(request.url) == "https://api.quicksilverpro.io/v1/chat/completions"
assert request.headers["authorization"] == "Bearer quicksilverpro-test-key"
assert body["model"] == "claude-sonnet-5-5"
assert body["messages"] == [{"role": "user", "content": "Say hello"}]
assert body["max_tokens"] == 128
assert "max_completion_tokens" not in body
assert response.choices[0].message.content == "Hello from QuickSilver Pro"
def test_quicksilverpro_responses_request():
with respx.mock() as upstream:
route: Final = upstream.post(
"https://api.quicksilverpro.io/v1/responses"
).respond(
200,
json={
"id": "resp_quicksilverpro",
"object": "response",
"created_at": 1_789_550_000,
"model": "claude-sonnet-5-5",
"status": "completed",
"output": [
{
"id": "msg_quicksilverpro",
"type": "message",
"role": "assistant",
"status": "completed",
"content": [
{
"type": "output_text",
"text": "Hello from QuickSilver Pro",
"annotations": [],
}
],
}
],
"usage": {"input_tokens": 4, "output_tokens": 3, "total_tokens": 7},
},
)
response: Final = litellm.responses(
model="quicksilverpro/claude-sonnet-5-5",
input="Say hello",
api_key="quicksilverpro-test-key",
)
request: Final = route.calls.last.request
body: Final = json.loads(request.content)
assert route.call_count == 1
assert str(request.url) == "https://api.quicksilverpro.io/v1/responses"
assert request.headers["authorization"] == "Bearer quicksilverpro-test-key"
assert body["model"] == "claude-sonnet-5-5"
assert body["input"] == "Say hello"
assert response.output[0].content[0].text == "Hello from QuickSilver Pro"
@pytest.mark.asyncio
async def test_quicksilverpro_anthropic_messages_request(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(litellm, "disable_aiohttp_transport", True)
monkeypatch.setattr(litellm, "in_memory_llm_clients_cache", LLMClientCache())
with respx.mock() as upstream:
route: Final = upstream.post(
"https://api.quicksilverpro.io/v1/messages"
).respond(
200,
json={
"id": "msg_quicksilverpro",
"type": "message",
"role": "assistant",
"model": "claude-sonnet-5-5",
"content": [{"type": "text", "text": "Hello from QuickSilver Pro"}],
"stop_reason": "end_turn",
"stop_sequence": None,
"usage": {"input_tokens": 4, "output_tokens": 3},
},
)
response: Final = await litellm.anthropic.messages.acreate(
model="quicksilverpro/claude-sonnet-5-5",
messages=[{"role": "user", "content": "Say hello"}],
max_tokens=32,
api_key="quicksilverpro-test-key",
)
request: Final = route.calls.last.request
body: Final = json.loads(request.content)
assert route.call_count == 1
assert str(request.url) == "https://api.quicksilverpro.io/v1/messages"
assert request.headers["authorization"] == "Bearer quicksilverpro-test-key"
assert request.headers["anthropic-version"] == "2023-06-01"
assert body["model"] == "claude-sonnet-5-5"
assert body["messages"] == [{"role": "user", "content": "Say hello"}]
assert response["content"][0]["text"] == "Hello from QuickSilver Pro"

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -73,6 +73,17 @@ describe("provider_info_helpers", () => {
expect(fromEnumKey.logo).toBe(providerLogoMap[Providers.SCX_AI]);
});
it("should map quicksilverpro slug and QUICKSILVERPRO enum key to the QuickSilver Pro display name and logo", () => {
const fromSlug = getProviderLogoAndName("quicksilverpro");
expect(fromSlug.displayName).toBe(Providers.QUICKSILVERPRO);
expect(fromSlug.logo).toBe(providerLogoMap[Providers.QUICKSILVERPRO]);
expect(fromSlug.logo).toBeTruthy();
const fromEnumKey = getProviderLogoAndName("QUICKSILVERPRO");
expect(fromEnumKey.displayName).toBe(Providers.QUICKSILVERPRO);
expect(fromEnumKey.logo).toBe(providerLogoMap[Providers.QUICKSILVERPRO]);
});
it("should map bedrock_mantle slug to Bedrock Mantle display name and logo", () => {
const result = getProviderLogoAndName("bedrock_mantle");
expect(result.displayName).toBe(Providers.BedrockMantle);
@ -229,6 +240,10 @@ describe("provider_info_helpers", () => {
expect(getPlaceholder(Providers.SCX_AI)).toBe("scx-ai/GLM-5.2");
});
it("should return a quicksilverpro model placeholder for QUICKSILVERPRO provider", () => {
expect(getPlaceholder(Providers.QUICKSILVERPRO)).toBe("quicksilverpro/claude-sonnet-5-5");
});
it("should return an edenai model placeholder for EDENAI provider", () => {
expect(getPlaceholder(Providers.EDENAI)).toBe("edenai/openai/gpt-mini-latest");
});

View file

@ -47,6 +47,7 @@ import openrouterLogo from "../../public/assets/logos/openrouter.svg";
import oracleLogo from "../../public/assets/logos/oracle.svg";
import perplexityAiLogo from "../../public/assets/logos/perplexity-ai.svg";
import qwenLogo from "../../public/assets/logos/qwen.png";
import quicksilverproLogo from "../../public/assets/logos/quicksilverpro.svg";
import recraftLogo from "../../public/assets/logos/recraft.svg";
import replicateLogo from "../../public/assets/logos/replicate.svg";
import runwayLogo from "../../public/assets/logos/runway.png";
@ -155,6 +156,7 @@ export enum Providers {
PETALS = "Petals",
PG_VECTOR = "Pg Vector",
PREDIBASE = "Predibase",
QUICKSILVERPRO = "QuickSilver Pro",
Qwen_AI_Platform = "Qianwen AI Platform",
QwenCloud = "QwenCloud",
RECRAFT = "Recraft",
@ -274,6 +276,7 @@ export const provider_map: Record<string, string> = {
PETALS: "petals",
PG_VECTOR: "pg_vector",
PREDIBASE: "predibase",
QUICKSILVERPRO: "quicksilverpro",
Qwen_AI_Platform: "qwen_ai_platform",
QwenCloud: "qwencloud",
RECRAFT: "recraft",
@ -376,6 +379,7 @@ export const providerLogoMap: Partial<Record<Providers, string>> = {
[Providers.Openrouter]: openrouterLogo.src,
[Providers.Oracle]: oracleLogo.src,
[Providers.Perplexity]: perplexityAiLogo.src,
[Providers.QUICKSILVERPRO]: quicksilverproLogo.src,
[Providers.Qwen_AI_Platform]: qwenLogo.src,
[Providers.QwenCloud]: qwenLogo.src,
[Providers.RECRAFT]: recraftLogo.src,
@ -456,6 +460,7 @@ const providerPlaceholderMap: Partial<Record<Providers, string>> = {
[Providers.SageMaker]: "sagemaker/jumpstart-dft-meta-textgeneration-llama-2-7b",
[Providers.Sail]: "sail/openai/gpt-oss-120b",
[Providers.SCX_AI]: "scx-ai/GLM-5.2",
[Providers.QUICKSILVERPRO]: "quicksilverpro/claude-sonnet-5-5",
[Providers.Snowflake]: "snowflake/mistral-7b",
[Providers.Tencent]: "tencent/deepseek-v4-pro",
[Providers.Vertex_AI]: "gemini-pro",

View file

@ -39,6 +39,7 @@ const TREATMENT_BY_ASSET: Readonly<Record<string, LogoTreatment>> = {
"fireworks.svg": "plate",
"llm_guard.png": "plate",
"pangea.png": "plate",
"quicksilverpro.svg": "plate",
"repelloai.png": "plate",
"sambanova.svg": "plate",
"sentry.svg": "plate",