From 2e8521f5a36f3315a4cb8fa4d5c14c67e3808a9f Mon Sep 17 00:00:00 2001 From: wassel alazhar Date: Sat, 3 Oct 2026 19:45:14 +0200 Subject: [PATCH 1/6] feat(providers): add Umans AI as an OpenAI-compatible provider --- litellm/constants.py | 2 + litellm/llms/openai_like/providers.json | 6 + .../provider_endpoints_support_backup.json | 18 ++ .../provider_create_fields.json | 28 +++ litellm/types/utils.py | 1 + provider_endpoints_support.json | 18 ++ .../openai_like/test_umans_ai_provider.py | 187 ++++++++++++++++++ 7 files changed, 260 insertions(+) create mode 100644 tests/unit/llms/openai_like/test_umans_ai_provider.py diff --git a/litellm/constants.py b/litellm/constants.py index d1daf2ee2d1..70b4edd4066 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -1060,6 +1060,7 @@ openai_compatible_endpoints: Final[list[str]] = [ "https://api.scx.ai/v1", "https://api.prisminference.com/v1", "https://api.reka.ai/v1", + "https://api.code.umans.ai/v1", "https://gigachat.devices.sberbank.ru/api/v1", "https://api.openinfer.ai/v1", ] @@ -1141,6 +1142,7 @@ openai_compatible_providers: Final[list[str]] = [ "prism", "sail", "reka", + "umans-ai", ] OPENAI_AUDIO_TRANSCRIPTION_PROVIDERS: Final = frozenset({"openai"} | frozenset(openai_compatible_providers)) diff --git a/litellm/llms/openai_like/providers.json b/litellm/llms/openai_like/providers.json index 36337fddbdc..0f4932345b2 100644 --- a/litellm/llms/openai_like/providers.json +++ b/litellm/llms/openai_like/providers.json @@ -249,5 +249,11 @@ "/v1/responses", "/v1/messages" ] + }, + "umans-ai": { + "base_url": "https://api.code.umans.ai/v1", + "api_key_env": "UMANS_AI_API_KEY", + "api_base_env": "UMANS_AI_API_BASE", + "supported_endpoints": ["/v1/chat/completions", "/v1/responses", "/v1/messages"] } } diff --git a/litellm/provider_endpoints_support_backup.json b/litellm/provider_endpoints_support_backup.json index f95b553c2b4..9b8833359f6 100644 --- a/litellm/provider_endpoints_support_backup.json +++ b/litellm/provider_endpoints_support_backup.json @@ -2334,6 +2334,24 @@ "interactions": true } }, + "umans-ai": { + "display_name": "Umans AI (`umans-ai`)", + "url": "https://docs.litellm.ai/docs/providers/umans_ai", + "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, + "interactions": false + } + }, "v0": { "display_name": "V0 (`v0`)", "url": "https://docs.litellm.ai/docs/providers/v0", diff --git a/litellm/proxy/public_endpoints/provider_create_fields.json b/litellm/proxy/public_endpoints/provider_create_fields.json index 19ee465c7dc..774c387bdb2 100644 --- a/litellm/proxy/public_endpoints/provider_create_fields.json +++ b/litellm/proxy/public_endpoints/provider_create_fields.json @@ -3667,6 +3667,34 @@ ], "default_model_placeholder": "typesafe/jev-latest" }, + { + "provider": "UMANS_AI", + "provider_display_name": "Umans AI", + "litellm_provider": "umans-ai", + "credential_fields": [ + { + "key": "api_base", + "label": "API Base", + "placeholder": "https://api.code.umans.ai/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": "umans-ai/umans-deepseek-v4-flash-0731" + }, { "provider": "V0", "provider_display_name": "V0", diff --git a/litellm/types/utils.py b/litellm/types/utils.py index cc7ef851111..9a96fcd23ec 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -4586,6 +4586,7 @@ class LlmProviders(str, Enum): META = "meta" SAIL = "sail" REKA = "reka" + UMANS_AI = "umans-ai" LITELLM_AGENT = "litellm_agent" CURSOR = "cursor" BEDROCK_MANTLE = "bedrock_mantle" diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index d65da3f41fd..e795156b622 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -2704,6 +2704,24 @@ "interactions": true } }, + "umans-ai": { + "display_name": "Umans AI (`umans-ai`)", + "url": "https://docs.litellm.ai/docs/providers/umans_ai", + "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, + "interactions": false + } + }, "v0": { "display_name": "V0 (`v0`)", "url": "https://docs.litellm.ai/docs/providers/v0", diff --git a/tests/unit/llms/openai_like/test_umans_ai_provider.py b/tests/unit/llms/openai_like/test_umans_ai_provider.py new file mode 100644 index 00000000000..ce0387a485d --- /dev/null +++ b/tests/unit/llms/openai_like/test_umans_ai_provider.py @@ -0,0 +1,187 @@ +import json +from pathlib import Path +from typing import Final + +import pytest +import respx + +import litellm +from litellm.caching.llm_caching_handler import LLMClientCache + + +def test_umans_ai_provider_resolution(monkeypatch: pytest.MonkeyPatch): + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + monkeypatch.setenv("UMANS_AI_API_KEY", "umans-test-key") + + model, provider, api_key, api_base = get_llm_provider( + model="umans-ai/umans-deepseek-v4-flash-0731", + custom_llm_provider=None, + api_base=None, + api_key=None, + ) + + assert model == "umans-deepseek-v4-flash-0731" + assert provider == "umans-ai" + assert api_key == "umans-test-key" + assert api_base == "https://api.code.umans.ai/v1" + + +def test_umans_ai_provider_keeps_explicit_credentials(monkeypatch: pytest.MonkeyPatch): + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + monkeypatch.setenv("UMANS_AI_API_KEY", "umans-env-key") + + _, provider, api_key, api_base = get_llm_provider( + model="umans-ai/umans-deepseek-v4-flash-0731", + custom_llm_provider=None, + api_base="https://umans.internal.example/v1", + api_key="umans-explicit-key", + ) + + assert provider == "umans-ai" + assert api_key == "umans-explicit-key" + assert api_base == "https://umans.internal.example/v1" + + +def test_umans_ai_is_available_in_add_model_form(): + fields_path = Path(litellm.__file__).parent / "proxy" / "public_endpoints" / "provider_create_fields.json" + providers = json.loads(fields_path.read_text()) + umans = next(provider for provider in providers if provider["litellm_provider"] == "umans-ai") + + assert umans["provider"] == "UMANS_AI" + assert umans["provider_display_name"] == "Umans AI" + assert umans["default_model_placeholder"] == "umans-ai/umans-deepseek-v4-flash-0731" + assert {field["key"]: field["required"] for field in umans["credential_fields"]} == { + "api_base": False, + "api_key": True, + } + + +def test_umans_ai_supported_endpoints(): + matrix_path = Path(litellm.__file__).parent / "provider_endpoints_support_backup.json" + providers = json.loads(matrix_path.read_text())["providers"] + + assert providers["umans-ai"]["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, + "interactions": False, + } + + +def test_umans_ai_chat_completion_request(): + with respx.mock() as upstream: + route: Final = upstream.post("https://api.code.umans.ai/v1/chat/completions").respond( + 200, + json={ + "id": "chatcmpl_umans", + "object": "chat.completion", + "created": 1_789_550_000, + "model": "umans-deepseek-v4-flash-0731", + "choices": [ + { + "index": 0, + "message": {"role": "assistant", "content": "Hello from Umans AI"}, + "finish_reason": "stop", + } + ], + "usage": {"prompt_tokens": 4, "completion_tokens": 3, "total_tokens": 7}, + }, + ) + response: Final = litellm.completion( + model="umans-ai/umans-deepseek-v4-flash-0731", + messages=[{"role": "user", "content": "Say hello"}], + api_key="umans-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.code.umans.ai/v1/chat/completions" + assert request.headers["authorization"] == "Bearer umans-test-key" + assert body["model"] == "umans-deepseek-v4-flash-0731" + assert body["messages"] == [{"role": "user", "content": "Say hello"}] + assert response.choices[0].message.content == "Hello from Umans AI" + + +def test_umans_ai_responses_request(): + with respx.mock() as upstream: + route: Final = upstream.post("https://api.code.umans.ai/v1/responses").respond( + 200, + json={ + "id": "resp_umans", + "object": "response", + "created_at": 1_789_550_000, + "model": "umans-deepseek-v4-flash-0731", + "status": "completed", + "output": [ + { + "id": "msg_umans", + "type": "message", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "Hello from Umans AI", "annotations": []}], + } + ], + "usage": {"input_tokens": 4, "output_tokens": 3, "total_tokens": 7}, + }, + ) + response: Final = litellm.responses( + model="umans-ai/umans-deepseek-v4-flash-0731", + input="Say hello", + api_key="umans-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.code.umans.ai/v1/responses" + assert request.headers["authorization"] == "Bearer umans-test-key" + assert body["model"] == "umans-deepseek-v4-flash-0731" + assert body["input"] == "Say hello" + assert response.output[0].content[0].text == "Hello from Umans AI" + + +@pytest.mark.asyncio +async def test_umans_ai_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.code.umans.ai/v1/messages").respond( + 200, + json={ + "id": "msg_umans", + "type": "message", + "role": "assistant", + "model": "umans-deepseek-v4-flash-0731", + "content": [{"type": "text", "text": "Hello from Umans AI"}], + "stop_reason": "end_turn", + "stop_sequence": None, + "usage": {"input_tokens": 4, "output_tokens": 3}, + }, + ) + response: Final = await litellm.anthropic.messages.acreate( + model="umans-ai/umans-deepseek-v4-flash-0731", + messages=[{"role": "user", "content": "Say hello"}], + max_tokens=32, + api_key="umans-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.code.umans.ai/v1/messages" + assert request.headers["authorization"] == "Bearer umans-test-key" + assert request.headers["anthropic-version"] == "2023-06-01" + assert body["model"] == "umans-deepseek-v4-flash-0731" + assert body["messages"] == [{"role": "user", "content": "Say hello"}] + assert response["content"][0]["text"] == "Hello from Umans AI" From 3334148d084364b72b136b71393bdb032499ec83 Mon Sep 17 00:00:00 2001 From: wassel alazhar Date: Sat, 3 Oct 2026 20:29:14 +0200 Subject: [PATCH 2/6] feat(providers): price Umans AI models and cover streaming paths Adds cost map entries (main and bundled backup) for the six public Umans AI models at the rates on app.umans.ai/pricing, so proxy spend and budgets count Umans usage. Adds streaming tests for chat completions, Responses and Anthropic Messages. --- ...odel_prices_and_context_window_backup.json | 143 +++++++++++++ model_prices_and_context_window.json | 143 +++++++++++++ .../openai_like/test_umans_ai_provider.py | 192 ++++++++++++++++++ 3 files changed, 478 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 9a7d88b8eda..9efdcb77f62 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -78652,5 +78652,148 @@ "prompt_cache_min_tokens": 512, "source": "https://platform.claude.com/docs/en/about-claude/pricing#claude-platform-on-aws-pricing", "supports_web_search": true + }, + "umans-ai/umans-deepseek-v4-flash-0731": { + "cache_read_input_token_cost": 2.8e-08, + "input_cost_per_token": 1.4e-07, + "litellm_provider": "umans-ai", + "max_input_tokens": 1048576, + "max_output_tokens": 393215, + "max_tokens": 393215, + "mode": "chat", + "output_cost_per_token": 2.8e-07, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false + }, + "umans-ai/umans-deepseek-v4.1-flash": { + "cache_read_input_token_cost": 2.8e-08, + "input_cost_per_token": 1.5e-07, + "litellm_provider": "umans-ai", + "max_input_tokens": 1048576, + "max_output_tokens": 393215, + "max_tokens": 393215, + "mode": "chat", + "output_cost_per_token": 6e-07, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "umans-ai/umans-glm-5.3-flash": { + "cache_read_input_token_cost": 3e-08, + "input_cost_per_token": 1.5e-07, + "litellm_provider": "umans-ai", + "max_input_tokens": 1048576, + "max_output_tokens": 131071, + "max_tokens": 131071, + "mode": "chat", + "output_cost_per_token": 5e-07, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "umans-ai/umans-kimi-k3": { + "cache_read_input_token_cost": 3e-07, + "input_cost_per_token": 3e-06, + "litellm_provider": "umans-ai", + "max_input_tokens": 1048576, + "max_output_tokens": 131072, + "max_tokens": 131072, + "mode": "chat", + "output_cost_per_token": 1.5e-05, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "umans-ai/umans-flash": { + "cache_read_input_token_cost": 5e-08, + "input_cost_per_token": 1.5e-07, + "litellm_provider": "umans-ai", + "max_input_tokens": 262144, + "max_output_tokens": 32768, + "max_tokens": 32768, + "mode": "chat", + "output_cost_per_token": 1e-06, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "umans-ai/umans-coder": { + "cache_read_input_token_cost": 3e-08, + "input_cost_per_token": 1.5e-07, + "litellm_provider": "umans-ai", + "max_input_tokens": 1048576, + "max_output_tokens": 131071, + "max_tokens": 131071, + "mode": "chat", + "output_cost_per_token": 5e-07, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true } } diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 9a7d88b8eda..9efdcb77f62 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -78652,5 +78652,148 @@ "prompt_cache_min_tokens": 512, "source": "https://platform.claude.com/docs/en/about-claude/pricing#claude-platform-on-aws-pricing", "supports_web_search": true + }, + "umans-ai/umans-deepseek-v4-flash-0731": { + "cache_read_input_token_cost": 2.8e-08, + "input_cost_per_token": 1.4e-07, + "litellm_provider": "umans-ai", + "max_input_tokens": 1048576, + "max_output_tokens": 393215, + "max_tokens": 393215, + "mode": "chat", + "output_cost_per_token": 2.8e-07, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false + }, + "umans-ai/umans-deepseek-v4.1-flash": { + "cache_read_input_token_cost": 2.8e-08, + "input_cost_per_token": 1.5e-07, + "litellm_provider": "umans-ai", + "max_input_tokens": 1048576, + "max_output_tokens": 393215, + "max_tokens": 393215, + "mode": "chat", + "output_cost_per_token": 6e-07, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "umans-ai/umans-glm-5.3-flash": { + "cache_read_input_token_cost": 3e-08, + "input_cost_per_token": 1.5e-07, + "litellm_provider": "umans-ai", + "max_input_tokens": 1048576, + "max_output_tokens": 131071, + "max_tokens": 131071, + "mode": "chat", + "output_cost_per_token": 5e-07, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "umans-ai/umans-kimi-k3": { + "cache_read_input_token_cost": 3e-07, + "input_cost_per_token": 3e-06, + "litellm_provider": "umans-ai", + "max_input_tokens": 1048576, + "max_output_tokens": 131072, + "max_tokens": 131072, + "mode": "chat", + "output_cost_per_token": 1.5e-05, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "umans-ai/umans-flash": { + "cache_read_input_token_cost": 5e-08, + "input_cost_per_token": 1.5e-07, + "litellm_provider": "umans-ai", + "max_input_tokens": 262144, + "max_output_tokens": 32768, + "max_tokens": 32768, + "mode": "chat", + "output_cost_per_token": 1e-06, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "umans-ai/umans-coder": { + "cache_read_input_token_cost": 3e-08, + "input_cost_per_token": 1.5e-07, + "litellm_provider": "umans-ai", + "max_input_tokens": 1048576, + "max_output_tokens": 131071, + "max_tokens": 131071, + "mode": "chat", + "output_cost_per_token": 5e-07, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true } } diff --git a/tests/unit/llms/openai_like/test_umans_ai_provider.py b/tests/unit/llms/openai_like/test_umans_ai_provider.py index ce0387a485d..c3b0199822c 100644 --- a/tests/unit/llms/openai_like/test_umans_ai_provider.py +++ b/tests/unit/llms/openai_like/test_umans_ai_provider.py @@ -185,3 +185,195 @@ async def test_umans_ai_anthropic_messages_request(monkeypatch: pytest.MonkeyPat assert body["model"] == "umans-deepseek-v4-flash-0731" assert body["messages"] == [{"role": "user", "content": "Say hello"}] assert response["content"][0]["text"] == "Hello from Umans AI" + + +UMANS_PRICING: Final = ( + # model, then USD per token for input, output and cache read (app.umans.ai/pricing) + ("umans-ai/umans-deepseek-v4-flash-0731", 1.4e-07, 2.8e-07, 2.8e-08), + ("umans-ai/umans-deepseek-v4.1-flash", 1.5e-07, 6e-07, 2.8e-08), + ("umans-ai/umans-glm-5.3-flash", 1.5e-07, 5e-07, 3e-08), + ("umans-ai/umans-kimi-k3", 3e-06, 1.5e-05, 3e-07), + ("umans-ai/umans-flash", 1.5e-07, 1e-06, 5e-08), + ("umans-ai/umans-coder", 1.5e-07, 5e-07, 3e-08), +) + + +def _load_cost_map(filename: str = "model_prices_and_context_window.json") -> dict: + with open(Path(__file__).parents[4] / filename) as f: + return json.load(f) + + +@pytest.mark.parametrize(("model", "input_cost", "output_cost", "cache_read_cost"), UMANS_PRICING) +def test_umans_ai_models_are_priced(model: str, input_cost: float, output_cost: float, cache_read_cost: float): + entry: Final = _load_cost_map()[model] + + assert entry["litellm_provider"] == "umans-ai" + assert entry["mode"] == "chat" + assert entry["input_cost_per_token"] == input_cost + assert entry["output_cost_per_token"] == output_cost + assert entry["cache_read_input_token_cost"] == cache_read_cost + assert _load_cost_map("litellm/model_prices_and_context_window_backup.json")[model] == entry + + +def test_umans_ai_usage_is_billed(): + prompt_cost, completion_cost = litellm.cost_per_token( + model="umans-ai/umans-deepseek-v4-flash-0731", + prompt_tokens=1_000_000, + completion_tokens=1_000_000, + ) + + assert prompt_cost == pytest.approx(0.14) + assert completion_cost == pytest.approx(0.28) + + +def _sse(*events: dict) -> bytes: + return b"".join(f"data: {json.dumps(event)}\n\n".encode() for event in events) + b"data: [DONE]\n\n" + + +def _typed_sse(*events: dict) -> bytes: + return b"".join(f"event: {event['type']}\ndata: {json.dumps(event)}\n\n".encode() for event in events) + + +def test_umans_ai_chat_completion_streaming_request(): + chunk: Final = { + "id": "chatcmpl_umans", + "object": "chat.completion.chunk", + "created": 1_789_550_000, + "model": "umans-deepseek-v4-flash-0731", + } + stream_body: Final = _sse( + { + **chunk, + "choices": [{"index": 0, "delta": {"role": "assistant", "content": "Hello from "}, "finish_reason": None}], + }, + {**chunk, "choices": [{"index": 0, "delta": {"content": "Umans AI"}, "finish_reason": None}]}, + {**chunk, "choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}]}, + ) + with respx.mock() as upstream: + route: Final = upstream.post("https://api.code.umans.ai/v1/chat/completions").respond( + 200, content=stream_body, headers={"content-type": "text/event-stream"} + ) + chunks: Final = list( + litellm.completion( + model="umans-ai/umans-deepseek-v4-flash-0731", + messages=[{"role": "user", "content": "Say hello"}], + api_key="umans-test-key", + stream=True, + ) + ) + + body: Final = json.loads(route.calls.last.request.content) + assert route.call_count == 1 + assert str(route.calls.last.request.url) == "https://api.code.umans.ai/v1/chat/completions" + assert body["stream"] is True + assert "".join(chunk.choices[0].delta.content or "" for chunk in chunks) == "Hello from Umans AI" + + +def test_umans_ai_responses_streaming_request(): + response: Final = { + "id": "resp_umans", + "object": "response", + "created_at": 1_789_550_000, + "model": "umans-deepseek-v4-flash-0731", + "status": "in_progress", + "output": [], + } + message: Final = { + "id": "msg_umans", + "type": "message", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "Hello from Umans AI", "annotations": []}], + } + stream_body: Final = _typed_sse( + {"type": "response.created", "sequence_number": 0, "response": response}, + { + "type": "response.output_text.delta", + "sequence_number": 1, + "item_id": "msg_umans", + "output_index": 0, + "content_index": 0, + "delta": "Hello from Umans AI", + }, + { + "type": "response.completed", + "sequence_number": 2, + "response": { + **response, + "status": "completed", + "output": [message], + "usage": {"input_tokens": 4, "output_tokens": 3, "total_tokens": 7}, + }, + }, + ) + with respx.mock() as upstream: + route: Final = upstream.post("https://api.code.umans.ai/v1/responses").respond( + 200, content=stream_body, headers={"content-type": "text/event-stream"} + ) + events: Final = list( + litellm.responses( + model="umans-ai/umans-deepseek-v4-flash-0731", + input="Say hello", + api_key="umans-test-key", + stream=True, + ) + ) + + body: Final = json.loads(route.calls.last.request.content) + assert route.call_count == 1 + assert str(route.calls.last.request.url) == "https://api.code.umans.ai/v1/responses" + assert body["stream"] is True + assert "".join(event.delta for event in events if event.type == "response.output_text.delta") == ( + "Hello from Umans AI" + ) + assert events[-1].type == "response.completed" + + +@pytest.mark.asyncio +async def test_umans_ai_anthropic_messages_streaming_request(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(litellm, "disable_aiohttp_transport", True) + monkeypatch.setattr(litellm, "in_memory_llm_clients_cache", LLMClientCache()) + stream_body: Final = _typed_sse( + { + "type": "message_start", + "message": { + "id": "msg_umans", + "type": "message", + "role": "assistant", + "model": "umans-deepseek-v4-flash-0731", + "content": [], + "stop_reason": None, + "stop_sequence": None, + "usage": {"input_tokens": 4, "output_tokens": 0}, + }, + }, + {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}, + {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "Hello from Umans AI"}}, + {"type": "content_block_stop", "index": 0}, + { + "type": "message_delta", + "delta": {"stop_reason": "end_turn", "stop_sequence": None}, + "usage": {"output_tokens": 3}, + }, + {"type": "message_stop"}, + ) + with respx.mock() as upstream: + route: Final = upstream.post("https://api.code.umans.ai/v1/messages").respond( + 200, content=stream_body, headers={"content-type": "text/event-stream"} + ) + stream = await litellm.anthropic.messages.acreate( + model="umans-ai/umans-deepseek-v4-flash-0731", + messages=[{"role": "user", "content": "Say hello"}], + max_tokens=32, + api_key="umans-test-key", + stream=True, + ) + streamed: Final = b"".join([chunk async for chunk in stream]).decode() + + body: Final = json.loads(route.calls.last.request.content) + assert route.call_count == 1 + assert str(route.calls.last.request.url) == "https://api.code.umans.ai/v1/messages" + assert body["stream"] is True + assert "event: message_start" in streamed + assert "Hello from Umans AI" in streamed + assert "event: message_stop" in streamed From 5e63c92879135e0508196f6046a13e4f96918029 Mon Sep 17 00:00:00 2001 From: wassel alazhar Date: Sat, 3 Oct 2026 22:13:35 +0200 Subject: [PATCH 3/6] feat(providers): price Umans AI GLM 5.3 Adds the umans-ai/umans-glm-5.3 cost map entry (main and bundled backup) at the rates on app.umans.ai/pricing and covers it in the pricing test. --- ...odel_prices_and_context_window_backup.json | 23 +++++++++++++++++++ model_prices_and_context_window.json | 23 +++++++++++++++++++ .../openai_like/test_umans_ai_provider.py | 1 + 3 files changed, 47 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 9efdcb77f62..06def5f73cd 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -78700,6 +78700,29 @@ "supports_tool_choice": true, "supports_vision": true }, + "umans-ai/umans-glm-5.3": { + "cache_read_input_token_cost": 2.6e-07, + "input_cost_per_token": 1.4e-06, + "litellm_provider": "umans-ai", + "max_input_tokens": 1048576, + "max_output_tokens": 131071, + "max_tokens": 131071, + "mode": "chat", + "output_cost_per_token": 4.4e-06, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false + }, "umans-ai/umans-glm-5.3-flash": { "cache_read_input_token_cost": 3e-08, "input_cost_per_token": 1.5e-07, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 9efdcb77f62..06def5f73cd 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -78700,6 +78700,29 @@ "supports_tool_choice": true, "supports_vision": true }, + "umans-ai/umans-glm-5.3": { + "cache_read_input_token_cost": 2.6e-07, + "input_cost_per_token": 1.4e-06, + "litellm_provider": "umans-ai", + "max_input_tokens": 1048576, + "max_output_tokens": 131071, + "max_tokens": 131071, + "mode": "chat", + "output_cost_per_token": 4.4e-06, + "source": "https://app.umans.ai/pricing", + "supported_modalities": [ + "text" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": false + }, "umans-ai/umans-glm-5.3-flash": { "cache_read_input_token_cost": 3e-08, "input_cost_per_token": 1.5e-07, diff --git a/tests/unit/llms/openai_like/test_umans_ai_provider.py b/tests/unit/llms/openai_like/test_umans_ai_provider.py index c3b0199822c..c31b9cb55e8 100644 --- a/tests/unit/llms/openai_like/test_umans_ai_provider.py +++ b/tests/unit/llms/openai_like/test_umans_ai_provider.py @@ -191,6 +191,7 @@ UMANS_PRICING: Final = ( # model, then USD per token for input, output and cache read (app.umans.ai/pricing) ("umans-ai/umans-deepseek-v4-flash-0731", 1.4e-07, 2.8e-07, 2.8e-08), ("umans-ai/umans-deepseek-v4.1-flash", 1.5e-07, 6e-07, 2.8e-08), + ("umans-ai/umans-glm-5.3", 1.4e-06, 4.4e-06, 2.6e-07), ("umans-ai/umans-glm-5.3-flash", 1.5e-07, 5e-07, 3e-08), ("umans-ai/umans-kimi-k3", 3e-06, 1.5e-05, 3e-07), ("umans-ai/umans-flash", 1.5e-07, 1e-06, 5e-08), From fee295c2a45b9d45b26493221a52004910b24d9f Mon Sep 17 00:00:00 2001 From: wassel alazhar Date: Sat, 3 Oct 2026 22:39:00 +0200 Subject: [PATCH 4/6] test(providers): assert Umans AI cost map invariants instead of pinned prices Replaces literal vendor prices with checks LiteLLM owns: main and backup agree, every entry is a priced chat model, the Add Model default model is priced, and billing (cache reads included) uses the cost map rates. Marks test locals Final and types the SSE helpers as read-only mappings. --- .../openai_like/test_umans_ai_provider.py | 83 ++++++++++--------- 1 file changed, 46 insertions(+), 37 deletions(-) diff --git a/tests/unit/llms/openai_like/test_umans_ai_provider.py b/tests/unit/llms/openai_like/test_umans_ai_provider.py index c31b9cb55e8..8a233e778a0 100644 --- a/tests/unit/llms/openai_like/test_umans_ai_provider.py +++ b/tests/unit/llms/openai_like/test_umans_ai_provider.py @@ -1,5 +1,7 @@ import json +from collections.abc import Mapping from pathlib import Path +from types import MappingProxyType from typing import Final import pytest @@ -45,9 +47,9 @@ def test_umans_ai_provider_keeps_explicit_credentials(monkeypatch: pytest.Monkey def test_umans_ai_is_available_in_add_model_form(): - fields_path = Path(litellm.__file__).parent / "proxy" / "public_endpoints" / "provider_create_fields.json" - providers = json.loads(fields_path.read_text()) - umans = next(provider for provider in providers if provider["litellm_provider"] == "umans-ai") + fields_path: Final = Path(litellm.__file__).parent / "proxy" / "public_endpoints" / "provider_create_fields.json" + providers: Final = json.loads(fields_path.read_text()) + umans: Final = next(provider for provider in providers if provider["litellm_provider"] == "umans-ai") assert umans["provider"] == "UMANS_AI" assert umans["provider_display_name"] == "Umans AI" @@ -59,8 +61,8 @@ def test_umans_ai_is_available_in_add_model_form(): def test_umans_ai_supported_endpoints(): - matrix_path = Path(litellm.__file__).parent / "provider_endpoints_support_backup.json" - providers = json.loads(matrix_path.read_text())["providers"] + matrix_path: Final = Path(litellm.__file__).parent / "provider_endpoints_support_backup.json" + providers: Final = json.loads(matrix_path.read_text())["providers"] assert providers["umans-ai"]["endpoints"] == { "chat_completions": True, @@ -187,51 +189,58 @@ async def test_umans_ai_anthropic_messages_request(monkeypatch: pytest.MonkeyPat assert response["content"][0]["text"] == "Hello from Umans AI" -UMANS_PRICING: Final = ( - # model, then USD per token for input, output and cache read (app.umans.ai/pricing) - ("umans-ai/umans-deepseek-v4-flash-0731", 1.4e-07, 2.8e-07, 2.8e-08), - ("umans-ai/umans-deepseek-v4.1-flash", 1.5e-07, 6e-07, 2.8e-08), - ("umans-ai/umans-glm-5.3", 1.4e-06, 4.4e-06, 2.6e-07), - ("umans-ai/umans-glm-5.3-flash", 1.5e-07, 5e-07, 3e-08), - ("umans-ai/umans-kimi-k3", 3e-06, 1.5e-05, 3e-07), - ("umans-ai/umans-flash", 1.5e-07, 1e-06, 5e-08), - ("umans-ai/umans-coder", 1.5e-07, 5e-07, 3e-08), -) +COST_FIELDS: Final = ("input_cost_per_token", "output_cost_per_token", "cache_read_input_token_cost") -def _load_cost_map(filename: str = "model_prices_and_context_window.json") -> dict: - with open(Path(__file__).parents[4] / filename) as f: - return json.load(f) +def _umans_cost_entries(filename: str = "model_prices_and_context_window.json") -> Mapping[str, Mapping[str, object]]: + cost_map: Final = json.loads((Path(__file__).parents[4] / filename).read_text()) + return MappingProxyType({model: entry for model, entry in cost_map.items() if model.startswith("umans-ai/")}) -@pytest.mark.parametrize(("model", "input_cost", "output_cost", "cache_read_cost"), UMANS_PRICING) -def test_umans_ai_models_are_priced(model: str, input_cost: float, output_cost: float, cache_read_cost: float): - entry: Final = _load_cost_map()[model] +def test_umans_ai_cost_map_entries_match_backup_and_are_priced(): + entries: Final = _umans_cost_entries() - assert entry["litellm_provider"] == "umans-ai" - assert entry["mode"] == "chat" - assert entry["input_cost_per_token"] == input_cost - assert entry["output_cost_per_token"] == output_cost - assert entry["cache_read_input_token_cost"] == cache_read_cost - assert _load_cost_map("litellm/model_prices_and_context_window_backup.json")[model] == entry + assert entries + assert entries == _umans_cost_entries("litellm/model_prices_and_context_window_backup.json") + for entry in entries.values(): + assert entry["litellm_provider"] == "umans-ai" + assert entry["mode"] == "chat" + assert entry["max_tokens"] == entry["max_output_tokens"] + assert all(isinstance(cost, float) and cost > 0 for cost in (entry[field] for field in COST_FIELDS)) -def test_umans_ai_usage_is_billed(): - prompt_cost, completion_cost = litellm.cost_per_token( - model="umans-ai/umans-deepseek-v4-flash-0731", - prompt_tokens=1_000_000, - completion_tokens=1_000_000, +def test_umans_ai_default_model_is_priced(): + fields_path: Final = Path(litellm.__file__).parent / "proxy" / "public_endpoints" / "provider_create_fields.json" + umans: Final = next( + provider for provider in json.loads(fields_path.read_text()) if provider["litellm_provider"] == "umans-ai" ) - assert prompt_cost == pytest.approx(0.14) - assert completion_cost == pytest.approx(0.28) + assert umans["default_model_placeholder"] in _umans_cost_entries() -def _sse(*events: dict) -> bytes: +def test_umans_ai_usage_is_billed_at_cost_map_rates(): + model: Final = "umans-ai/umans-deepseek-v4-flash-0731" + entry: Final = _umans_cost_entries()[model] + input_cost: Final = entry["input_cost_per_token"] + output_cost: Final = entry["output_cost_per_token"] + cache_read_cost: Final = entry["cache_read_input_token_cost"] + assert isinstance(input_cost, float) + assert isinstance(output_cost, float) + assert isinstance(cache_read_cost, float) + + prompt_cost, completion_cost = litellm.cost_per_token( + model=model, prompt_tokens=1_000, completion_tokens=2_000, cache_read_input_tokens=800 + ) + + assert prompt_cost == pytest.approx(200 * input_cost + 800 * cache_read_cost) + assert completion_cost == pytest.approx(2_000 * output_cost) + + +def _sse(*events: Mapping[str, object]) -> bytes: return b"".join(f"data: {json.dumps(event)}\n\n".encode() for event in events) + b"data: [DONE]\n\n" -def _typed_sse(*events: dict) -> bytes: +def _typed_sse(*events: Mapping[str, object]) -> bytes: return b"".join(f"event: {event['type']}\ndata: {json.dumps(event)}\n\n".encode() for event in events) @@ -362,7 +371,7 @@ async def test_umans_ai_anthropic_messages_streaming_request(monkeypatch: pytest route: Final = upstream.post("https://api.code.umans.ai/v1/messages").respond( 200, content=stream_body, headers={"content-type": "text/event-stream"} ) - stream = await litellm.anthropic.messages.acreate( + stream: Final = await litellm.anthropic.messages.acreate( model="umans-ai/umans-deepseek-v4-flash-0731", messages=[{"role": "user", "content": "Say hello"}], max_tokens=32, From cbeba59c8e91a0e7b380349d76d9e8b81db8b552 Mon Sep 17 00:00:00 2001 From: wassel alazhar Date: Wed, 4 Nov 2026 00:40:46 +0100 Subject: [PATCH 5/6] feat(providers): drop Umans AI's retired ids, price coder and flash at DeepSeek V4.1 Flash Umans AI retired umans-deepseek-v4-flash-0731 and umans-glm-5.3-flash on 2026-11-01 (successor: umans-deepseek-v4.1-flash), so their cost map entries go. umans-coder and umans-flash now serve DeepSeek V4.1 Flash 1017 and bill at its rates ($0.15 / $0.60 / $0.028 per 1M input / output / cache read). umans-coder takes its live card's output cap (393215, the API rejects 393216 and above); umans-flash keeps its 262144 window and 32768 output. The Add Model default and the tests move to umans-ai/umans-deepseek-v4.1-flash. --- ...odel_prices_and_context_window_backup.json | 59 ++----------------- .../provider_create_fields.json | 2 +- model_prices_and_context_window.json | 59 ++----------------- .../openai_like/test_umans_ai_provider.py | 40 ++++++------- 4 files changed, 33 insertions(+), 127 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index bc29db7d8f6..607738e6ddb 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -83134,29 +83134,6 @@ "supports_function_calling": true, "source": "https://tokenfactory.nebius.com/api/public/models_info" }, - "umans-ai/umans-deepseek-v4-flash-0731": { - "cache_read_input_token_cost": 2.8e-08, - "input_cost_per_token": 1.4e-07, - "litellm_provider": "umans-ai", - "max_input_tokens": 1048576, - "max_output_tokens": 393215, - "max_tokens": 393215, - "mode": "chat", - "output_cost_per_token": 2.8e-07, - "source": "https://app.umans.ai/pricing", - "supported_modalities": [ - "text" - ], - "supported_output_modalities": [ - "text" - ], - "supports_function_calling": true, - "supports_prompt_caching": true, - "supports_reasoning": true, - "supports_response_schema": true, - "supports_tool_choice": true, - "supports_vision": false - }, "umans-ai/umans-deepseek-v4.1-flash": { "cache_read_input_token_cost": 2.8e-08, "input_cost_per_token": 1.5e-07, @@ -83204,30 +83181,6 @@ "supports_tool_choice": true, "supports_vision": false }, - "umans-ai/umans-glm-5.3-flash": { - "cache_read_input_token_cost": 3e-08, - "input_cost_per_token": 1.5e-07, - "litellm_provider": "umans-ai", - "max_input_tokens": 1048576, - "max_output_tokens": 131071, - "max_tokens": 131071, - "mode": "chat", - "output_cost_per_token": 5e-07, - "source": "https://app.umans.ai/pricing", - "supported_modalities": [ - "text", - "image" - ], - "supported_output_modalities": [ - "text" - ], - "supports_function_calling": true, - "supports_prompt_caching": true, - "supports_reasoning": true, - "supports_response_schema": true, - "supports_tool_choice": true, - "supports_vision": true - }, "umans-ai/umans-kimi-k3": { "cache_read_input_token_cost": 3e-07, "input_cost_per_token": 3e-06, @@ -83253,14 +83206,14 @@ "supports_vision": true }, "umans-ai/umans-flash": { - "cache_read_input_token_cost": 5e-08, + "cache_read_input_token_cost": 2.8e-08, "input_cost_per_token": 1.5e-07, "litellm_provider": "umans-ai", "max_input_tokens": 262144, "max_output_tokens": 32768, "max_tokens": 32768, "mode": "chat", - "output_cost_per_token": 1e-06, + "output_cost_per_token": 6e-07, "source": "https://app.umans.ai/pricing", "supported_modalities": [ "text", @@ -83277,14 +83230,14 @@ "supports_vision": true }, "umans-ai/umans-coder": { - "cache_read_input_token_cost": 3e-08, + "cache_read_input_token_cost": 2.8e-08, "input_cost_per_token": 1.5e-07, "litellm_provider": "umans-ai", "max_input_tokens": 1048576, - "max_output_tokens": 131071, - "max_tokens": 131071, + "max_output_tokens": 393215, + "max_tokens": 393215, "mode": "chat", - "output_cost_per_token": 5e-07, + "output_cost_per_token": 6e-07, "source": "https://app.umans.ai/pricing", "supported_modalities": [ "text", diff --git a/litellm/proxy/public_endpoints/provider_create_fields.json b/litellm/proxy/public_endpoints/provider_create_fields.json index 6c99bf41311..eeaf6deb606 100644 --- a/litellm/proxy/public_endpoints/provider_create_fields.json +++ b/litellm/proxy/public_endpoints/provider_create_fields.json @@ -3973,7 +3973,7 @@ "default_value": null } ], - "default_model_placeholder": "umans-ai/umans-deepseek-v4-flash-0731" + "default_model_placeholder": "umans-ai/umans-deepseek-v4.1-flash" }, { "provider": "V0", diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index bc29db7d8f6..607738e6ddb 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -83134,29 +83134,6 @@ "supports_function_calling": true, "source": "https://tokenfactory.nebius.com/api/public/models_info" }, - "umans-ai/umans-deepseek-v4-flash-0731": { - "cache_read_input_token_cost": 2.8e-08, - "input_cost_per_token": 1.4e-07, - "litellm_provider": "umans-ai", - "max_input_tokens": 1048576, - "max_output_tokens": 393215, - "max_tokens": 393215, - "mode": "chat", - "output_cost_per_token": 2.8e-07, - "source": "https://app.umans.ai/pricing", - "supported_modalities": [ - "text" - ], - "supported_output_modalities": [ - "text" - ], - "supports_function_calling": true, - "supports_prompt_caching": true, - "supports_reasoning": true, - "supports_response_schema": true, - "supports_tool_choice": true, - "supports_vision": false - }, "umans-ai/umans-deepseek-v4.1-flash": { "cache_read_input_token_cost": 2.8e-08, "input_cost_per_token": 1.5e-07, @@ -83204,30 +83181,6 @@ "supports_tool_choice": true, "supports_vision": false }, - "umans-ai/umans-glm-5.3-flash": { - "cache_read_input_token_cost": 3e-08, - "input_cost_per_token": 1.5e-07, - "litellm_provider": "umans-ai", - "max_input_tokens": 1048576, - "max_output_tokens": 131071, - "max_tokens": 131071, - "mode": "chat", - "output_cost_per_token": 5e-07, - "source": "https://app.umans.ai/pricing", - "supported_modalities": [ - "text", - "image" - ], - "supported_output_modalities": [ - "text" - ], - "supports_function_calling": true, - "supports_prompt_caching": true, - "supports_reasoning": true, - "supports_response_schema": true, - "supports_tool_choice": true, - "supports_vision": true - }, "umans-ai/umans-kimi-k3": { "cache_read_input_token_cost": 3e-07, "input_cost_per_token": 3e-06, @@ -83253,14 +83206,14 @@ "supports_vision": true }, "umans-ai/umans-flash": { - "cache_read_input_token_cost": 5e-08, + "cache_read_input_token_cost": 2.8e-08, "input_cost_per_token": 1.5e-07, "litellm_provider": "umans-ai", "max_input_tokens": 262144, "max_output_tokens": 32768, "max_tokens": 32768, "mode": "chat", - "output_cost_per_token": 1e-06, + "output_cost_per_token": 6e-07, "source": "https://app.umans.ai/pricing", "supported_modalities": [ "text", @@ -83277,14 +83230,14 @@ "supports_vision": true }, "umans-ai/umans-coder": { - "cache_read_input_token_cost": 3e-08, + "cache_read_input_token_cost": 2.8e-08, "input_cost_per_token": 1.5e-07, "litellm_provider": "umans-ai", "max_input_tokens": 1048576, - "max_output_tokens": 131071, - "max_tokens": 131071, + "max_output_tokens": 393215, + "max_tokens": 393215, "mode": "chat", - "output_cost_per_token": 5e-07, + "output_cost_per_token": 6e-07, "source": "https://app.umans.ai/pricing", "supported_modalities": [ "text", diff --git a/tests/unit/llms/openai_like/test_umans_ai_provider.py b/tests/unit/llms/openai_like/test_umans_ai_provider.py index 8a233e778a0..0f5b86a09c0 100644 --- a/tests/unit/llms/openai_like/test_umans_ai_provider.py +++ b/tests/unit/llms/openai_like/test_umans_ai_provider.py @@ -17,13 +17,13 @@ def test_umans_ai_provider_resolution(monkeypatch: pytest.MonkeyPatch): monkeypatch.setenv("UMANS_AI_API_KEY", "umans-test-key") model, provider, api_key, api_base = get_llm_provider( - model="umans-ai/umans-deepseek-v4-flash-0731", + model="umans-ai/umans-deepseek-v4.1-flash", custom_llm_provider=None, api_base=None, api_key=None, ) - assert model == "umans-deepseek-v4-flash-0731" + assert model == "umans-deepseek-v4.1-flash" assert provider == "umans-ai" assert api_key == "umans-test-key" assert api_base == "https://api.code.umans.ai/v1" @@ -35,7 +35,7 @@ def test_umans_ai_provider_keeps_explicit_credentials(monkeypatch: pytest.Monkey monkeypatch.setenv("UMANS_AI_API_KEY", "umans-env-key") _, provider, api_key, api_base = get_llm_provider( - model="umans-ai/umans-deepseek-v4-flash-0731", + model="umans-ai/umans-deepseek-v4.1-flash", custom_llm_provider=None, api_base="https://umans.internal.example/v1", api_key="umans-explicit-key", @@ -53,7 +53,7 @@ def test_umans_ai_is_available_in_add_model_form(): assert umans["provider"] == "UMANS_AI" assert umans["provider_display_name"] == "Umans AI" - assert umans["default_model_placeholder"] == "umans-ai/umans-deepseek-v4-flash-0731" + assert umans["default_model_placeholder"] == "umans-ai/umans-deepseek-v4.1-flash" assert {field["key"]: field["required"] for field in umans["credential_fields"]} == { "api_base": False, "api_key": True, @@ -88,7 +88,7 @@ def test_umans_ai_chat_completion_request(): "id": "chatcmpl_umans", "object": "chat.completion", "created": 1_789_550_000, - "model": "umans-deepseek-v4-flash-0731", + "model": "umans-deepseek-v4.1-flash", "choices": [ { "index": 0, @@ -100,7 +100,7 @@ def test_umans_ai_chat_completion_request(): }, ) response: Final = litellm.completion( - model="umans-ai/umans-deepseek-v4-flash-0731", + model="umans-ai/umans-deepseek-v4.1-flash", messages=[{"role": "user", "content": "Say hello"}], api_key="umans-test-key", ) @@ -110,7 +110,7 @@ def test_umans_ai_chat_completion_request(): assert route.call_count == 1 assert str(request.url) == "https://api.code.umans.ai/v1/chat/completions" assert request.headers["authorization"] == "Bearer umans-test-key" - assert body["model"] == "umans-deepseek-v4-flash-0731" + assert body["model"] == "umans-deepseek-v4.1-flash" assert body["messages"] == [{"role": "user", "content": "Say hello"}] assert response.choices[0].message.content == "Hello from Umans AI" @@ -123,7 +123,7 @@ def test_umans_ai_responses_request(): "id": "resp_umans", "object": "response", "created_at": 1_789_550_000, - "model": "umans-deepseek-v4-flash-0731", + "model": "umans-deepseek-v4.1-flash", "status": "completed", "output": [ { @@ -138,7 +138,7 @@ def test_umans_ai_responses_request(): }, ) response: Final = litellm.responses( - model="umans-ai/umans-deepseek-v4-flash-0731", + model="umans-ai/umans-deepseek-v4.1-flash", input="Say hello", api_key="umans-test-key", ) @@ -148,7 +148,7 @@ def test_umans_ai_responses_request(): assert route.call_count == 1 assert str(request.url) == "https://api.code.umans.ai/v1/responses" assert request.headers["authorization"] == "Bearer umans-test-key" - assert body["model"] == "umans-deepseek-v4-flash-0731" + assert body["model"] == "umans-deepseek-v4.1-flash" assert body["input"] == "Say hello" assert response.output[0].content[0].text == "Hello from Umans AI" @@ -164,7 +164,7 @@ async def test_umans_ai_anthropic_messages_request(monkeypatch: pytest.MonkeyPat "id": "msg_umans", "type": "message", "role": "assistant", - "model": "umans-deepseek-v4-flash-0731", + "model": "umans-deepseek-v4.1-flash", "content": [{"type": "text", "text": "Hello from Umans AI"}], "stop_reason": "end_turn", "stop_sequence": None, @@ -172,7 +172,7 @@ async def test_umans_ai_anthropic_messages_request(monkeypatch: pytest.MonkeyPat }, ) response: Final = await litellm.anthropic.messages.acreate( - model="umans-ai/umans-deepseek-v4-flash-0731", + model="umans-ai/umans-deepseek-v4.1-flash", messages=[{"role": "user", "content": "Say hello"}], max_tokens=32, api_key="umans-test-key", @@ -184,7 +184,7 @@ async def test_umans_ai_anthropic_messages_request(monkeypatch: pytest.MonkeyPat assert str(request.url) == "https://api.code.umans.ai/v1/messages" assert request.headers["authorization"] == "Bearer umans-test-key" assert request.headers["anthropic-version"] == "2023-06-01" - assert body["model"] == "umans-deepseek-v4-flash-0731" + assert body["model"] == "umans-deepseek-v4.1-flash" assert body["messages"] == [{"role": "user", "content": "Say hello"}] assert response["content"][0]["text"] == "Hello from Umans AI" @@ -219,7 +219,7 @@ def test_umans_ai_default_model_is_priced(): def test_umans_ai_usage_is_billed_at_cost_map_rates(): - model: Final = "umans-ai/umans-deepseek-v4-flash-0731" + model: Final = "umans-ai/umans-deepseek-v4.1-flash" entry: Final = _umans_cost_entries()[model] input_cost: Final = entry["input_cost_per_token"] output_cost: Final = entry["output_cost_per_token"] @@ -249,7 +249,7 @@ def test_umans_ai_chat_completion_streaming_request(): "id": "chatcmpl_umans", "object": "chat.completion.chunk", "created": 1_789_550_000, - "model": "umans-deepseek-v4-flash-0731", + "model": "umans-deepseek-v4.1-flash", } stream_body: Final = _sse( { @@ -265,7 +265,7 @@ def test_umans_ai_chat_completion_streaming_request(): ) chunks: Final = list( litellm.completion( - model="umans-ai/umans-deepseek-v4-flash-0731", + model="umans-ai/umans-deepseek-v4.1-flash", messages=[{"role": "user", "content": "Say hello"}], api_key="umans-test-key", stream=True, @@ -284,7 +284,7 @@ def test_umans_ai_responses_streaming_request(): "id": "resp_umans", "object": "response", "created_at": 1_789_550_000, - "model": "umans-deepseek-v4-flash-0731", + "model": "umans-deepseek-v4.1-flash", "status": "in_progress", "output": [], } @@ -322,7 +322,7 @@ def test_umans_ai_responses_streaming_request(): ) events: Final = list( litellm.responses( - model="umans-ai/umans-deepseek-v4-flash-0731", + model="umans-ai/umans-deepseek-v4.1-flash", input="Say hello", api_key="umans-test-key", stream=True, @@ -350,7 +350,7 @@ async def test_umans_ai_anthropic_messages_streaming_request(monkeypatch: pytest "id": "msg_umans", "type": "message", "role": "assistant", - "model": "umans-deepseek-v4-flash-0731", + "model": "umans-deepseek-v4.1-flash", "content": [], "stop_reason": None, "stop_sequence": None, @@ -372,7 +372,7 @@ async def test_umans_ai_anthropic_messages_streaming_request(monkeypatch: pytest 200, content=stream_body, headers={"content-type": "text/event-stream"} ) stream: Final = await litellm.anthropic.messages.acreate( - model="umans-ai/umans-deepseek-v4-flash-0731", + model="umans-ai/umans-deepseek-v4.1-flash", messages=[{"role": "user", "content": "Say hello"}], max_tokens=32, api_key="umans-test-key", From f05a5777754932a78d026505fad55a653cb4c961 Mon Sep 17 00:00:00 2001 From: wassel alazhar Date: Wed, 4 Nov 2026 00:43:54 +0100 Subject: [PATCH 6/6] feat(providers): name Umans AI's DeepSeek V4.1 Flash entry by its dated 1017 id umans-deepseek-v4.1-flash is Umans AI's floating id, which follows future DeepSeek V4.1 Flash updates. umans-deepseek-v4.1-flash-1017 pins the DeepSeek-V4.1-Flash-1017 weights that these limits and prices describe, so the cost map entry, the Add Model default and the tests use the dated id. --- ...odel_prices_and_context_window_backup.json | 2 +- .../provider_create_fields.json | 2 +- model_prices_and_context_window.json | 2 +- .../openai_like/test_umans_ai_provider.py | 40 +++++++++---------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 607738e6ddb..0cc1a1c6cc3 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -83134,7 +83134,7 @@ "supports_function_calling": true, "source": "https://tokenfactory.nebius.com/api/public/models_info" }, - "umans-ai/umans-deepseek-v4.1-flash": { + "umans-ai/umans-deepseek-v4.1-flash-1017": { "cache_read_input_token_cost": 2.8e-08, "input_cost_per_token": 1.5e-07, "litellm_provider": "umans-ai", diff --git a/litellm/proxy/public_endpoints/provider_create_fields.json b/litellm/proxy/public_endpoints/provider_create_fields.json index eeaf6deb606..499e0bdcccb 100644 --- a/litellm/proxy/public_endpoints/provider_create_fields.json +++ b/litellm/proxy/public_endpoints/provider_create_fields.json @@ -3973,7 +3973,7 @@ "default_value": null } ], - "default_model_placeholder": "umans-ai/umans-deepseek-v4.1-flash" + "default_model_placeholder": "umans-ai/umans-deepseek-v4.1-flash-1017" }, { "provider": "V0", diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 607738e6ddb..0cc1a1c6cc3 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -83134,7 +83134,7 @@ "supports_function_calling": true, "source": "https://tokenfactory.nebius.com/api/public/models_info" }, - "umans-ai/umans-deepseek-v4.1-flash": { + "umans-ai/umans-deepseek-v4.1-flash-1017": { "cache_read_input_token_cost": 2.8e-08, "input_cost_per_token": 1.5e-07, "litellm_provider": "umans-ai", diff --git a/tests/unit/llms/openai_like/test_umans_ai_provider.py b/tests/unit/llms/openai_like/test_umans_ai_provider.py index 0f5b86a09c0..0e45eb833b3 100644 --- a/tests/unit/llms/openai_like/test_umans_ai_provider.py +++ b/tests/unit/llms/openai_like/test_umans_ai_provider.py @@ -17,13 +17,13 @@ def test_umans_ai_provider_resolution(monkeypatch: pytest.MonkeyPatch): monkeypatch.setenv("UMANS_AI_API_KEY", "umans-test-key") model, provider, api_key, api_base = get_llm_provider( - model="umans-ai/umans-deepseek-v4.1-flash", + model="umans-ai/umans-deepseek-v4.1-flash-1017", custom_llm_provider=None, api_base=None, api_key=None, ) - assert model == "umans-deepseek-v4.1-flash" + assert model == "umans-deepseek-v4.1-flash-1017" assert provider == "umans-ai" assert api_key == "umans-test-key" assert api_base == "https://api.code.umans.ai/v1" @@ -35,7 +35,7 @@ def test_umans_ai_provider_keeps_explicit_credentials(monkeypatch: pytest.Monkey monkeypatch.setenv("UMANS_AI_API_KEY", "umans-env-key") _, provider, api_key, api_base = get_llm_provider( - model="umans-ai/umans-deepseek-v4.1-flash", + model="umans-ai/umans-deepseek-v4.1-flash-1017", custom_llm_provider=None, api_base="https://umans.internal.example/v1", api_key="umans-explicit-key", @@ -53,7 +53,7 @@ def test_umans_ai_is_available_in_add_model_form(): assert umans["provider"] == "UMANS_AI" assert umans["provider_display_name"] == "Umans AI" - assert umans["default_model_placeholder"] == "umans-ai/umans-deepseek-v4.1-flash" + assert umans["default_model_placeholder"] == "umans-ai/umans-deepseek-v4.1-flash-1017" assert {field["key"]: field["required"] for field in umans["credential_fields"]} == { "api_base": False, "api_key": True, @@ -88,7 +88,7 @@ def test_umans_ai_chat_completion_request(): "id": "chatcmpl_umans", "object": "chat.completion", "created": 1_789_550_000, - "model": "umans-deepseek-v4.1-flash", + "model": "umans-deepseek-v4.1-flash-1017", "choices": [ { "index": 0, @@ -100,7 +100,7 @@ def test_umans_ai_chat_completion_request(): }, ) response: Final = litellm.completion( - model="umans-ai/umans-deepseek-v4.1-flash", + model="umans-ai/umans-deepseek-v4.1-flash-1017", messages=[{"role": "user", "content": "Say hello"}], api_key="umans-test-key", ) @@ -110,7 +110,7 @@ def test_umans_ai_chat_completion_request(): assert route.call_count == 1 assert str(request.url) == "https://api.code.umans.ai/v1/chat/completions" assert request.headers["authorization"] == "Bearer umans-test-key" - assert body["model"] == "umans-deepseek-v4.1-flash" + assert body["model"] == "umans-deepseek-v4.1-flash-1017" assert body["messages"] == [{"role": "user", "content": "Say hello"}] assert response.choices[0].message.content == "Hello from Umans AI" @@ -123,7 +123,7 @@ def test_umans_ai_responses_request(): "id": "resp_umans", "object": "response", "created_at": 1_789_550_000, - "model": "umans-deepseek-v4.1-flash", + "model": "umans-deepseek-v4.1-flash-1017", "status": "completed", "output": [ { @@ -138,7 +138,7 @@ def test_umans_ai_responses_request(): }, ) response: Final = litellm.responses( - model="umans-ai/umans-deepseek-v4.1-flash", + model="umans-ai/umans-deepseek-v4.1-flash-1017", input="Say hello", api_key="umans-test-key", ) @@ -148,7 +148,7 @@ def test_umans_ai_responses_request(): assert route.call_count == 1 assert str(request.url) == "https://api.code.umans.ai/v1/responses" assert request.headers["authorization"] == "Bearer umans-test-key" - assert body["model"] == "umans-deepseek-v4.1-flash" + assert body["model"] == "umans-deepseek-v4.1-flash-1017" assert body["input"] == "Say hello" assert response.output[0].content[0].text == "Hello from Umans AI" @@ -164,7 +164,7 @@ async def test_umans_ai_anthropic_messages_request(monkeypatch: pytest.MonkeyPat "id": "msg_umans", "type": "message", "role": "assistant", - "model": "umans-deepseek-v4.1-flash", + "model": "umans-deepseek-v4.1-flash-1017", "content": [{"type": "text", "text": "Hello from Umans AI"}], "stop_reason": "end_turn", "stop_sequence": None, @@ -172,7 +172,7 @@ async def test_umans_ai_anthropic_messages_request(monkeypatch: pytest.MonkeyPat }, ) response: Final = await litellm.anthropic.messages.acreate( - model="umans-ai/umans-deepseek-v4.1-flash", + model="umans-ai/umans-deepseek-v4.1-flash-1017", messages=[{"role": "user", "content": "Say hello"}], max_tokens=32, api_key="umans-test-key", @@ -184,7 +184,7 @@ async def test_umans_ai_anthropic_messages_request(monkeypatch: pytest.MonkeyPat assert str(request.url) == "https://api.code.umans.ai/v1/messages" assert request.headers["authorization"] == "Bearer umans-test-key" assert request.headers["anthropic-version"] == "2023-06-01" - assert body["model"] == "umans-deepseek-v4.1-flash" + assert body["model"] == "umans-deepseek-v4.1-flash-1017" assert body["messages"] == [{"role": "user", "content": "Say hello"}] assert response["content"][0]["text"] == "Hello from Umans AI" @@ -219,7 +219,7 @@ def test_umans_ai_default_model_is_priced(): def test_umans_ai_usage_is_billed_at_cost_map_rates(): - model: Final = "umans-ai/umans-deepseek-v4.1-flash" + model: Final = "umans-ai/umans-deepseek-v4.1-flash-1017" entry: Final = _umans_cost_entries()[model] input_cost: Final = entry["input_cost_per_token"] output_cost: Final = entry["output_cost_per_token"] @@ -249,7 +249,7 @@ def test_umans_ai_chat_completion_streaming_request(): "id": "chatcmpl_umans", "object": "chat.completion.chunk", "created": 1_789_550_000, - "model": "umans-deepseek-v4.1-flash", + "model": "umans-deepseek-v4.1-flash-1017", } stream_body: Final = _sse( { @@ -265,7 +265,7 @@ def test_umans_ai_chat_completion_streaming_request(): ) chunks: Final = list( litellm.completion( - model="umans-ai/umans-deepseek-v4.1-flash", + model="umans-ai/umans-deepseek-v4.1-flash-1017", messages=[{"role": "user", "content": "Say hello"}], api_key="umans-test-key", stream=True, @@ -284,7 +284,7 @@ def test_umans_ai_responses_streaming_request(): "id": "resp_umans", "object": "response", "created_at": 1_789_550_000, - "model": "umans-deepseek-v4.1-flash", + "model": "umans-deepseek-v4.1-flash-1017", "status": "in_progress", "output": [], } @@ -322,7 +322,7 @@ def test_umans_ai_responses_streaming_request(): ) events: Final = list( litellm.responses( - model="umans-ai/umans-deepseek-v4.1-flash", + model="umans-ai/umans-deepseek-v4.1-flash-1017", input="Say hello", api_key="umans-test-key", stream=True, @@ -350,7 +350,7 @@ async def test_umans_ai_anthropic_messages_streaming_request(monkeypatch: pytest "id": "msg_umans", "type": "message", "role": "assistant", - "model": "umans-deepseek-v4.1-flash", + "model": "umans-deepseek-v4.1-flash-1017", "content": [], "stop_reason": None, "stop_sequence": None, @@ -372,7 +372,7 @@ async def test_umans_ai_anthropic_messages_streaming_request(monkeypatch: pytest 200, content=stream_body, headers={"content-type": "text/event-stream"} ) stream: Final = await litellm.anthropic.messages.acreate( - model="umans-ai/umans-deepseek-v4.1-flash", + model="umans-ai/umans-deepseek-v4.1-flash-1017", messages=[{"role": "user", "content": "Say hello"}], max_tokens=32, api_key="umans-test-key",