mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-24 00:52:24 +00:00
feat(consus): add Consus Gateway as a native provider
Consus Gateway is OpenAI-compatible except it uses `x-api-key` instead of `Authorization: Bearer`. Replaces the prior workaround of `openai/` + `extra_headers` + a dummy api_key. `ConsusChatConfig` extends `OpenAIGPTConfig` and overrides only `validate_environment` and `_get_openai_compatible_provider_info`. Registers 15 IL2 / IL5+ITAR models with zero-cost placeholders. Tests: 11 unit + 3 mocked HTTP under `tests/test_litellm/llms/consus/`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
93706bfb9a
commit
cc0dfb01bd
16 changed files with 833 additions and 0 deletions
|
|
@ -237,6 +237,7 @@ azure_key: Optional[str] = None
|
|||
anthropic_key: Optional[str] = None
|
||||
replicate_key: Optional[str] = None
|
||||
bytez_key: Optional[str] = None
|
||||
consus_key: Optional[str] = None
|
||||
cohere_key: Optional[str] = None
|
||||
infinity_key: Optional[str] = None
|
||||
clarifai_key: Optional[str] = None
|
||||
|
|
@ -571,6 +572,7 @@ gemini_models: Set = set()
|
|||
xai_models: Set = set()
|
||||
zai_models: Set = set()
|
||||
deepseek_models: Set = set()
|
||||
consus_models: Set = set()
|
||||
runwayml_models: Set = set()
|
||||
azure_ai_models: Set = set()
|
||||
jina_ai_models: Set = set()
|
||||
|
|
@ -782,6 +784,8 @@ def add_known_models(model_cost_map: Optional[Dict] = None):
|
|||
fal_ai_models.add(key)
|
||||
elif value.get("litellm_provider") == "deepseek":
|
||||
deepseek_models.add(key)
|
||||
elif value.get("litellm_provider") == "consus":
|
||||
consus_models.add(key)
|
||||
elif value.get("litellm_provider") == "runwayml":
|
||||
runwayml_models.add(key)
|
||||
elif value.get("litellm_provider") == "meta_llama":
|
||||
|
|
@ -964,6 +968,7 @@ model_list = list(
|
|||
| zai_models
|
||||
| fal_ai_models
|
||||
| deepseek_models
|
||||
| consus_models
|
||||
| azure_ai_models
|
||||
| voyage_models
|
||||
| infinity_models
|
||||
|
|
@ -1057,6 +1062,7 @@ models_by_provider: dict = {
|
|||
"zai": zai_models,
|
||||
"fal_ai": fal_ai_models,
|
||||
"deepseek": deepseek_models,
|
||||
"consus": consus_models,
|
||||
"runwayml": runwayml_models,
|
||||
"mistral": mistral_chat_models,
|
||||
"azure_ai": azure_ai_models,
|
||||
|
|
@ -1826,6 +1832,9 @@ if TYPE_CHECKING:
|
|||
JinaAIEmbeddingConfig as JinaAIEmbeddingConfig,
|
||||
)
|
||||
from .llms.xai.chat.transformation import XAIChatConfig as XAIChatConfig
|
||||
from .llms.consus.chat.transformation import (
|
||||
ConsusChatConfig as ConsusChatConfig,
|
||||
)
|
||||
from .llms.zai.chat.transformation import ZAIChatConfig as ZAIChatConfig
|
||||
from .llms.aiml.chat.transformation import AIMLChatConfig as AIMLChatConfig
|
||||
from .llms.volcengine.chat.transformation import (
|
||||
|
|
|
|||
|
|
@ -264,6 +264,7 @@ LLM_CONFIG_NAMES = (
|
|||
"JinaAIEmbeddingConfig",
|
||||
"XAIChatConfig",
|
||||
"ZAIChatConfig",
|
||||
"ConsusChatConfig",
|
||||
"AIMLChatConfig",
|
||||
"VolcEngineChatConfig",
|
||||
"CodestralTextCompletionConfig",
|
||||
|
|
@ -1031,6 +1032,7 @@ _LLM_CONFIGS_IMPORT_MAP = {
|
|||
),
|
||||
"XAIChatConfig": (".llms.xai.chat.transformation", "XAIChatConfig"),
|
||||
"ZAIChatConfig": (".llms.zai.chat.transformation", "ZAIChatConfig"),
|
||||
"ConsusChatConfig": (".llms.consus.chat.transformation", "ConsusChatConfig"),
|
||||
"AIMLChatConfig": (".llms.aiml.chat.transformation", "AIMLChatConfig"),
|
||||
"VolcEngineChatConfig": (
|
||||
".llms.volcengine.chat.transformation",
|
||||
|
|
|
|||
|
|
@ -540,6 +540,7 @@ LITELLM_CHAT_PROVIDERS = [
|
|||
"openai",
|
||||
"openai_like",
|
||||
"bytez",
|
||||
"consus",
|
||||
"xai",
|
||||
"custom_openai",
|
||||
"text-completion-openai",
|
||||
|
|
@ -783,6 +784,7 @@ openai_compatible_endpoints: List = [
|
|||
"https://ai-gateway.vercel.sh/v1",
|
||||
"https://api.inference.wandb.ai/v1",
|
||||
"https://api.clarifai.com/v2/ext/openai/v1",
|
||||
"https://api.consus.io/v1",
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -802,6 +804,7 @@ openai_compatible_providers: List = [
|
|||
"perplexity",
|
||||
"xinference",
|
||||
"xai",
|
||||
"consus",
|
||||
"zai",
|
||||
"together_ai",
|
||||
"fireworks_ai",
|
||||
|
|
|
|||
|
|
@ -362,6 +362,9 @@ def get_llm_provider( # noqa: PLR0915
|
|||
elif endpoint == "https://api.inference.wandb.ai/v1":
|
||||
custom_llm_provider = "wandb"
|
||||
dynamic_api_key = get_secret_str("WANDB_API_KEY")
|
||||
elif endpoint == "https://api.consus.io/v1":
|
||||
custom_llm_provider = "consus"
|
||||
dynamic_api_key = get_secret_str("CONSUS_API_KEY")
|
||||
|
||||
if api_base is not None and not isinstance(api_base, str):
|
||||
raise Exception(
|
||||
|
|
@ -477,6 +480,8 @@ def get_llm_provider( # noqa: PLR0915
|
|||
# bytez models
|
||||
elif model.startswith("bytez/"):
|
||||
custom_llm_provider = "bytez"
|
||||
elif model.startswith("consus/"):
|
||||
custom_llm_provider = "consus"
|
||||
elif model.startswith("lemonade/"):
|
||||
custom_llm_provider = "lemonade"
|
||||
elif model.startswith("heroku/"):
|
||||
|
|
@ -793,6 +798,13 @@ def _get_openai_compatible_provider_info( # noqa: PLR0915
|
|||
) = litellm.XAIChatConfig()._get_openai_compatible_provider_info(
|
||||
api_base, api_key
|
||||
)
|
||||
elif custom_llm_provider == "consus":
|
||||
(
|
||||
api_base,
|
||||
dynamic_api_key,
|
||||
) = litellm.ConsusChatConfig()._get_openai_compatible_provider_info(
|
||||
api_base, api_key
|
||||
)
|
||||
elif custom_llm_provider == "zai":
|
||||
(
|
||||
api_base,
|
||||
|
|
|
|||
0
litellm/llms/consus/__init__.py
Normal file
0
litellm/llms/consus/__init__.py
Normal file
0
litellm/llms/consus/chat/__init__.py
Normal file
0
litellm/llms/consus/chat/__init__.py
Normal file
55
litellm/llms/consus/chat/transformation.py
Normal file
55
litellm/llms/consus/chat/transformation.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
"""
|
||||
Translates from OpenAI's `/v1/chat/completions` to Consus Gateway's
|
||||
`/v1/chat/completions`.
|
||||
|
||||
Consus Gateway is OpenAI-compatible in every respect except authentication:
|
||||
it requires the API key in an `x-api-key` header instead of
|
||||
`Authorization: Bearer <key>`.
|
||||
"""
|
||||
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
import litellm
|
||||
from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig
|
||||
from litellm.secret_managers.main import get_secret_str
|
||||
from litellm.types.llms.openai import AllMessageValues
|
||||
|
||||
CONSUS_API_BASE = "https://api.consus.io/v1"
|
||||
|
||||
|
||||
class ConsusChatConfig(OpenAIGPTConfig):
|
||||
@staticmethod
|
||||
def _resolve_api_key(api_key: Optional[str]) -> Optional[str]:
|
||||
return api_key or litellm.consus_key or get_secret_str("CONSUS_API_KEY")
|
||||
|
||||
def _get_openai_compatible_provider_info(
|
||||
self, api_base: Optional[str], api_key: Optional[str]
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
api_base = api_base or get_secret_str("CONSUS_API_BASE") or CONSUS_API_BASE
|
||||
dynamic_api_key = ConsusChatConfig._resolve_api_key(api_key)
|
||||
return api_base, dynamic_api_key
|
||||
|
||||
def validate_environment(
|
||||
self,
|
||||
headers: dict,
|
||||
model: str,
|
||||
messages: List[AllMessageValues],
|
||||
optional_params: dict,
|
||||
litellm_params: dict,
|
||||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
) -> dict:
|
||||
resolved_key = ConsusChatConfig._resolve_api_key(api_key)
|
||||
if not resolved_key:
|
||||
raise ValueError(
|
||||
"Missing Consus API key. Set the CONSUS_API_KEY environment "
|
||||
"variable, set litellm.consus_key, or pass api_key=... to "
|
||||
"completion()."
|
||||
)
|
||||
|
||||
headers["x-api-key"] = resolved_key
|
||||
|
||||
if "content-type" not in headers and "Content-Type" not in headers:
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
return headers
|
||||
|
|
@ -2299,6 +2299,37 @@ def completion( # type: ignore # noqa: PLR0915
|
|||
additional_args={"headers": headers},
|
||||
)
|
||||
raise e
|
||||
elif custom_llm_provider == "consus":
|
||||
## COMPLETION CALL
|
||||
try:
|
||||
response = base_llm_http_handler.completion(
|
||||
model=model,
|
||||
messages=messages,
|
||||
headers=headers,
|
||||
model_response=model_response,
|
||||
api_key=api_key,
|
||||
api_base=api_base,
|
||||
acompletion=acompletion,
|
||||
logging_obj=logging,
|
||||
optional_params=optional_params,
|
||||
litellm_params=litellm_params,
|
||||
shared_session=shared_session,
|
||||
timeout=timeout, # type: ignore
|
||||
client=client,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
encoding=_get_encoding(),
|
||||
stream=stream,
|
||||
provider_config=provider_config,
|
||||
)
|
||||
except Exception as e:
|
||||
## LOGGING - log the original exception returned
|
||||
logging.post_call(
|
||||
input=messages,
|
||||
api_key=api_key,
|
||||
original_response=str(e),
|
||||
additional_args={"headers": headers},
|
||||
)
|
||||
raise e
|
||||
elif custom_llm_provider == "groq":
|
||||
api_base = (
|
||||
api_base # for deepinfra/perplexity/anyscale/groq/friendliai we check in get_llm_provider and pass in the api base from there
|
||||
|
|
|
|||
|
|
@ -12417,6 +12417,242 @@
|
|||
"supports_tool_choice": true,
|
||||
"supports_function_calling": true
|
||||
},
|
||||
"consus/claude-3-7-sonnet:il5+itar": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-sonnet-4-5:il5+itar": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-sonnet-4-5:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-opus-4-6:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 1000000,
|
||||
"max_output_tokens": 128000,
|
||||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-sonnet-4-6:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 1000000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-opus-4-5:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-haiku-4-5:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-opus-4-1:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 32000,
|
||||
"max_tokens": 32000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-opus-4:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 32000,
|
||||
"max_tokens": 32000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-sonnet-4:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 1000000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/gemini-2-5-pro:il5": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 65535,
|
||||
"max_tokens": 65535,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/gemini-2-5-flash:il5": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 65535,
|
||||
"max_tokens": 65535,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/gpt-4.1:il5+itar": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 300000,
|
||||
"max_output_tokens": 32768,
|
||||
"max_tokens": 32768,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/gpt-4.1-mini:il5+itar": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 300000,
|
||||
"max_output_tokens": 32768,
|
||||
"max_tokens": 32768,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/gpt-5.1:il5+itar": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 272000,
|
||||
"max_output_tokens": 128000,
|
||||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"deepseek/deepseek-chat": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"cache_read_input_token_cost": 2.8e-08,
|
||||
|
|
|
|||
|
|
@ -3206,6 +3206,7 @@ class LlmProviders(str, Enum):
|
|||
JINA_AI = "jina_ai"
|
||||
XAI = "xai"
|
||||
ZAI = "zai"
|
||||
CONSUS = "consus"
|
||||
CUSTOM_OPENAI = "custom_openai"
|
||||
TEXT_COMPLETION_OPENAI = "text-completion-openai"
|
||||
COHERE = "cohere"
|
||||
|
|
|
|||
|
|
@ -8120,6 +8120,7 @@ class ProviderConfigManager:
|
|||
LlmProviders.DATABRICKS: (lambda: litellm.DatabricksConfig(), False),
|
||||
LlmProviders.XAI: (lambda: litellm.XAIChatConfig(), False),
|
||||
LlmProviders.ZAI: (lambda: litellm.ZAIChatConfig(), False),
|
||||
LlmProviders.CONSUS: (lambda: litellm.ConsusChatConfig(), False),
|
||||
LlmProviders.LAMBDA_AI: (lambda: litellm.LambdaAIChatConfig(), False),
|
||||
LlmProviders.LLAMA: (lambda: litellm.LlamaAPIConfig(), False),
|
||||
LlmProviders.TEXT_COMPLETION_OPENAI: (
|
||||
|
|
|
|||
|
|
@ -12422,6 +12422,242 @@
|
|||
"supports_tool_choice": true,
|
||||
"supports_function_calling": true
|
||||
},
|
||||
"consus/claude-3-7-sonnet:il5+itar": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-sonnet-4-5:il5+itar": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-sonnet-4-5:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-opus-4-6:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 1000000,
|
||||
"max_output_tokens": 128000,
|
||||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-sonnet-4-6:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 1000000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-opus-4-5:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-haiku-4-5:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-opus-4-1:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 32000,
|
||||
"max_tokens": 32000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-opus-4:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 32000,
|
||||
"max_tokens": 32000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/claude-sonnet-4:il2": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 1000000,
|
||||
"max_output_tokens": 64000,
|
||||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/gemini-2-5-pro:il5": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 65535,
|
||||
"max_tokens": 65535,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/gemini-2-5-flash:il5": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 65535,
|
||||
"max_tokens": 65535,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/gpt-4.1:il5+itar": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 300000,
|
||||
"max_output_tokens": 32768,
|
||||
"max_tokens": 32768,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/gpt-4.1-mini:il5+itar": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 300000,
|
||||
"max_output_tokens": 32768,
|
||||
"max_tokens": 32768,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"consus/gpt-5.1:il5+itar": {
|
||||
"litellm_provider": "consus",
|
||||
"max_input_tokens": 272000,
|
||||
"max_output_tokens": 128000,
|
||||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_function_calling": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"deepseek/deepseek-chat": {
|
||||
"cache_creation_input_token_cost": 0.0,
|
||||
"cache_read_input_token_cost": 2.8e-08,
|
||||
|
|
|
|||
0
tests/test_litellm/llms/consus/__init__.py
Normal file
0
tests/test_litellm/llms/consus/__init__.py
Normal file
0
tests/test_litellm/llms/consus/chat/__init__.py
Normal file
0
tests/test_litellm/llms/consus/chat/__init__.py
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, os.path.abspath("../../../../.."))
|
||||
|
||||
import litellm
|
||||
from litellm.llms.consus.chat.transformation import (
|
||||
CONSUS_API_BASE,
|
||||
ConsusChatConfig,
|
||||
)
|
||||
|
||||
TEST_MESSAGES = [{"role": "user", "content": "Hello"}]
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _clear_consus_env(monkeypatch):
|
||||
monkeypatch.delenv("CONSUS_API_KEY", raising=False)
|
||||
monkeypatch.delenv("CONSUS_API_BASE", raising=False)
|
||||
monkeypatch.setattr(litellm, "consus_key", None, raising=False)
|
||||
yield
|
||||
|
||||
|
||||
class TestConsusChatConfigAuth:
|
||||
def test_validate_environment_uses_x_api_key(self):
|
||||
config = ConsusChatConfig()
|
||||
headers = config.validate_environment(
|
||||
headers={},
|
||||
model="claude-sonnet-4-5:il2",
|
||||
messages=TEST_MESSAGES, # type: ignore
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
api_key="test-key",
|
||||
api_base=CONSUS_API_BASE,
|
||||
)
|
||||
|
||||
assert headers["x-api-key"] == "test-key"
|
||||
assert "Authorization" not in headers
|
||||
assert headers["Content-Type"] == "application/json"
|
||||
|
||||
def test_validate_environment_raises_when_no_key(self):
|
||||
config = ConsusChatConfig()
|
||||
with pytest.raises(ValueError, match="Missing Consus API key"):
|
||||
config.validate_environment(
|
||||
headers={},
|
||||
model="claude-sonnet-4-5:il2",
|
||||
messages=TEST_MESSAGES, # type: ignore
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
api_key=None,
|
||||
api_base=CONSUS_API_BASE,
|
||||
)
|
||||
|
||||
def test_validate_environment_resolves_from_env(self, monkeypatch):
|
||||
monkeypatch.setenv("CONSUS_API_KEY", "env-key")
|
||||
config = ConsusChatConfig()
|
||||
headers = config.validate_environment(
|
||||
headers={},
|
||||
model="claude-sonnet-4-5:il2",
|
||||
messages=TEST_MESSAGES, # type: ignore
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
api_key=None,
|
||||
api_base=CONSUS_API_BASE,
|
||||
)
|
||||
assert headers["x-api-key"] == "env-key"
|
||||
|
||||
def test_validate_environment_resolves_from_litellm_module(self, monkeypatch):
|
||||
monkeypatch.setattr(litellm, "consus_key", "module-key", raising=False)
|
||||
config = ConsusChatConfig()
|
||||
headers = config.validate_environment(
|
||||
headers={},
|
||||
model="claude-sonnet-4-5:il2",
|
||||
messages=TEST_MESSAGES, # type: ignore
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
api_key=None,
|
||||
api_base=CONSUS_API_BASE,
|
||||
)
|
||||
assert headers["x-api-key"] == "module-key"
|
||||
|
||||
def test_validate_environment_arg_takes_precedence(self, monkeypatch):
|
||||
monkeypatch.setenv("CONSUS_API_KEY", "env-key")
|
||||
monkeypatch.setattr(litellm, "consus_key", "module-key", raising=False)
|
||||
config = ConsusChatConfig()
|
||||
headers = config.validate_environment(
|
||||
headers={},
|
||||
model="claude-sonnet-4-5:il2",
|
||||
messages=TEST_MESSAGES, # type: ignore
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
api_key="arg-key",
|
||||
api_base=CONSUS_API_BASE,
|
||||
)
|
||||
assert headers["x-api-key"] == "arg-key"
|
||||
|
||||
|
||||
class TestConsusProviderInfo:
|
||||
def test_default_api_base(self):
|
||||
config = ConsusChatConfig()
|
||||
api_base, _ = config._get_openai_compatible_provider_info(None, "k")
|
||||
assert api_base == CONSUS_API_BASE
|
||||
assert api_base == "https://api.consus.io/v1"
|
||||
|
||||
def test_explicit_api_base_wins(self):
|
||||
config = ConsusChatConfig()
|
||||
api_base, _ = config._get_openai_compatible_provider_info(
|
||||
"https://other.example/v1", "k"
|
||||
)
|
||||
assert api_base == "https://other.example/v1"
|
||||
|
||||
def test_env_api_base_used_when_arg_none(self, monkeypatch):
|
||||
monkeypatch.setenv("CONSUS_API_BASE", "https://staging.consus.io/v1")
|
||||
config = ConsusChatConfig()
|
||||
api_base, _ = config._get_openai_compatible_provider_info(None, "k")
|
||||
assert api_base == "https://staging.consus.io/v1"
|
||||
|
||||
|
||||
class TestConsusModelRouting:
|
||||
def test_get_llm_provider_strips_prefix_keeps_colon(self, monkeypatch):
|
||||
monkeypatch.setenv("CONSUS_API_KEY", "test-key")
|
||||
model, provider, api_key, api_base = litellm.get_llm_provider(
|
||||
"consus/claude-sonnet-4-5:il5+itar"
|
||||
)
|
||||
assert provider == "consus"
|
||||
# The compliance suffix `:il5+itar` must be preserved end-to-end —
|
||||
# only the leading `consus/` prefix is stripped.
|
||||
assert model == "claude-sonnet-4-5:il5+itar"
|
||||
assert api_base == "https://api.consus.io/v1"
|
||||
assert api_key == "test-key"
|
||||
|
||||
def test_get_llm_provider_for_il2_models(self, monkeypatch):
|
||||
monkeypatch.setenv("CONSUS_API_KEY", "test-key")
|
||||
model, provider, _, _ = litellm.get_llm_provider("consus/claude-opus-4-6:il2")
|
||||
assert provider == "consus"
|
||||
assert model == "claude-opus-4-6:il2"
|
||||
|
||||
def test_get_llm_provider_for_gemini(self, monkeypatch):
|
||||
monkeypatch.setenv("CONSUS_API_KEY", "test-key")
|
||||
model, provider, _, _ = litellm.get_llm_provider("consus/gemini-2-5-pro:il5")
|
||||
assert provider == "consus"
|
||||
assert model == "gemini-2-5-pro:il5"
|
||||
104
tests/test_litellm/llms/consus/chat/test_consus_completion.py
Normal file
104
tests/test_litellm/llms/consus/chat/test_consus_completion.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
"""End-to-end (mocked HTTP) tests for the Consus provider.
|
||||
|
||||
Verifies that `litellm.completion(model="consus/...", ...)`:
|
||||
1. Hits `https://api.consus.io/v1/chat/completions`
|
||||
2. Sends the API key in `x-api-key` (NOT `Authorization`)
|
||||
3. Forwards the model name unprefixed (consus/X -> X) to the gateway
|
||||
4. Returns a parsed OpenAI-style response
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, os.path.abspath("../../../../.."))
|
||||
|
||||
import litellm
|
||||
from litellm.llms.consus.chat.transformation import CONSUS_API_BASE
|
||||
|
||||
TEST_API_KEY = "consus-test-key"
|
||||
TEST_MODEL_NAME = "claude-sonnet-4-5:il5+itar"
|
||||
TEST_MODEL = f"consus/{TEST_MODEL_NAME}"
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _clear_consus_env(monkeypatch):
|
||||
monkeypatch.delenv("CONSUS_API_KEY", raising=False)
|
||||
monkeypatch.delenv("CONSUS_API_BASE", raising=False)
|
||||
monkeypatch.setattr(litellm, "consus_key", None, raising=False)
|
||||
yield
|
||||
|
||||
|
||||
def _openai_style_response(content: str) -> dict:
|
||||
return {
|
||||
"id": "chatcmpl-consus-1",
|
||||
"object": "chat.completion",
|
||||
"created": 1234567890,
|
||||
"model": TEST_MODEL_NAME,
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {"role": "assistant", "content": content},
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
],
|
||||
"usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2},
|
||||
}
|
||||
|
||||
|
||||
class TestConsusCompletionMock:
|
||||
def test_request_uses_x_api_key_and_unprefixed_model(self, respx_mock):
|
||||
route = respx_mock.post(f"{CONSUS_API_BASE}/chat/completions").respond(
|
||||
json=_openai_style_response("hello from consus"),
|
||||
status_code=200,
|
||||
)
|
||||
|
||||
response = litellm.completion(
|
||||
model=TEST_MODEL,
|
||||
messages=[{"role": "user", "content": "ping"}],
|
||||
api_key=TEST_API_KEY,
|
||||
)
|
||||
|
||||
assert response.choices[0].message.content == "hello from consus" # type: ignore
|
||||
|
||||
assert route.called
|
||||
request = route.calls[0].request
|
||||
|
||||
assert request.headers["x-api-key"] == TEST_API_KEY
|
||||
assert "authorization" not in {k.lower() for k in request.headers.keys()}
|
||||
|
||||
sent = json.loads(request.content)
|
||||
# The `consus/` prefix must be stripped before forwarding —
|
||||
# but the `:compliance` suffix must be preserved.
|
||||
assert sent["model"] == TEST_MODEL_NAME
|
||||
|
||||
def test_request_uses_default_api_base_when_none_given(self, respx_mock):
|
||||
route = respx_mock.post(f"{CONSUS_API_BASE}/chat/completions").respond(
|
||||
json=_openai_style_response("ok"),
|
||||
status_code=200,
|
||||
)
|
||||
|
||||
litellm.completion(
|
||||
model="consus/gpt-4.1:il5+itar",
|
||||
messages=[{"role": "user", "content": "hi"}],
|
||||
api_key=TEST_API_KEY,
|
||||
)
|
||||
|
||||
assert route.called
|
||||
|
||||
def test_completion_uses_env_api_key(self, monkeypatch, respx_mock):
|
||||
monkeypatch.setenv("CONSUS_API_KEY", "env-resolved-key")
|
||||
route = respx_mock.post(f"{CONSUS_API_BASE}/chat/completions").respond(
|
||||
json=_openai_style_response("ok"),
|
||||
status_code=200,
|
||||
)
|
||||
|
||||
litellm.completion(
|
||||
model=TEST_MODEL,
|
||||
messages=[{"role": "user", "content": "hi"}],
|
||||
)
|
||||
|
||||
assert route.called
|
||||
assert route.calls[0].request.headers["x-api-key"] == "env-resolved-key"
|
||||
Loading…
Add table
Reference in a new issue