From 404609ccc41d306bc220034d098c9d9a1943bcab Mon Sep 17 00:00:00 2001 From: jaberjaber23 Date: Sun, 23 Aug 2026 04:42:54 +0300 Subject: [PATCH] feat(runinfra): add Ornith 1.5 35B and declare image input on the two vision models Ornith 1.5 35B A3B went public on RunInfra after this PR was opened, so the cost map advertised five of our six conversational models and LiteLLM users could not route to the sixth at all. Prices are read from the production row and match the published rates: $0.10/M input, $0.40/M output, $0.01/M cached. Also declares supports_vision on the two models that accept image input, Qwen3.8 27B and Ornith. Both accept image_url and input_image content parts on the live gateway; the other four are text-only and stay unflagged. Without it a user filtering LiteLLM for vision-capable providers sees neither. Every value traces to a primary source: the production model row for prices and capabilities, and the public model page named in each entry's `source`. No capability is copied from a sibling entry. The per-model test table gains Ornith, plus a vision table that asserts the flag on exactly the two proven models and its ABSENCE on the other four, so a flag copied onto a text-only model fails rather than passing quietly. Verified by flipping Ornith's flag to false and watching test_runinfra_model_cost_map fail, then restoring it. Both price JSONs stay byte-identical, confirmed on the staged content. tests/test_litellm/llms/openai_like/test_json_providers.py: 24 passed, 4 skipped. Co-Authored-By: Claude Opus 5 (1M context) --- ...odel_prices_and_context_window_backup.json | 24 ++++++++++++++++++- model_prices_and_context_window.json | 24 ++++++++++++++++++- .../llms/openai_like/test_json_providers.py | 13 ++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 2b2c5483d45..8f182b515e6 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -49944,7 +49944,8 @@ "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, - "supports_tool_choice": true + "supports_tool_choice": true, + "supports_vision": true }, "runinfra/deepseek-ai/DeepSeek-V4-Flash-0731": { "cache_read_input_token_cost": 1e-08, @@ -50004,6 +50005,27 @@ "supports_response_schema": true, "supports_tool_choice": true }, + "runinfra/ornith-ai/Ornith-1.5-35B-A3B": { + "cache_read_input_token_cost": 1e-08, + "input_cost_per_token": 1e-07, + "litellm_provider": "runinfra", + "max_input_tokens": 262144, + "max_output_tokens": 32768, + "max_tokens": 32768, + "mode": "chat", + "output_cost_per_token": 4e-07, + "source": "https://runinfra.ai/inference-api/ornith-1-5-35b", + "supported_endpoints": [ + "/v1/chat/completions" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, "xai/grok-4.20-0309-non-reasoning": { "cache_read_input_token_cost": 2e-07, "input_cost_per_token": 1.25e-06, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 2b2c5483d45..8f182b515e6 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -49944,7 +49944,8 @@ "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, - "supports_tool_choice": true + "supports_tool_choice": true, + "supports_vision": true }, "runinfra/deepseek-ai/DeepSeek-V4-Flash-0731": { "cache_read_input_token_cost": 1e-08, @@ -50004,6 +50005,27 @@ "supports_response_schema": true, "supports_tool_choice": true }, + "runinfra/ornith-ai/Ornith-1.5-35B-A3B": { + "cache_read_input_token_cost": 1e-08, + "input_cost_per_token": 1e-07, + "litellm_provider": "runinfra", + "max_input_tokens": 262144, + "max_output_tokens": 32768, + "max_tokens": 32768, + "mode": "chat", + "output_cost_per_token": 4e-07, + "source": "https://runinfra.ai/inference-api/ornith-1-5-35b", + "supported_endpoints": [ + "/v1/chat/completions" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, "xai/grok-4.20-0309-non-reasoning": { "cache_read_input_token_cost": 2e-07, "input_cost_per_token": 1.25e-06, diff --git a/tests/test_litellm/llms/openai_like/test_json_providers.py b/tests/test_litellm/llms/openai_like/test_json_providers.py index bf27307d8fc..1dbdd079ebb 100644 --- a/tests/test_litellm/llms/openai_like/test_json_providers.py +++ b/tests/test_litellm/llms/openai_like/test_json_providers.py @@ -427,6 +427,7 @@ class TestRuninfra: ), "runinfra/Inferact/Qwen3.8-2.4T-A95B-NVFP4": (2e-06, 6e-06, 2e-07), "runinfra/Qwen/Qwen3.8-27B": (1e-07, 4e-07, 1e-08), + "runinfra/ornith-ai/Ornith-1.5-35B-A3B": (1e-07, 4e-07, 1e-08), } for model, (input_cost, output_cost, cache_read_cost) in expected_models.items(): assert model in model_cost @@ -449,6 +450,18 @@ class TestRuninfra: assert model_cost["runinfra/Qwen/Qwen3.8-27B"]["max_input_tokens"] == 262144 assert model_cost["runinfra/Inferact/Qwen3.8-2.4T-A95B-NVFP4"]["supports_response_schema"] is True assert model_cost["runinfra/Qwen/Qwen3.8-27B"]["supports_response_schema"] is True + assert model_cost["runinfra/ornith-ai/Ornith-1.5-35B-A3B"]["max_input_tokens"] == 262144 + # Image input is proven on exactly these two and declared on no other, + # so a copied flag on a text-only model reddens here. + for model, expected_vision in ( + ("runinfra/Qwen/Qwen3.8-27B", True), + ("runinfra/ornith-ai/Ornith-1.5-35B-A3B", True), + ("runinfra/deepseek-ai/DeepSeek-V4-Flash-0731", False), + ("runinfra/deepseek-ai/DeepSeek-V4-Pro-0813", False), + ("runinfra/nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16", False), + ("runinfra/Inferact/Qwen3.8-2.4T-A95B-NVFP4", False), + ): + assert model_cost[model].get("supports_vision", False) is expected_vision class TestPublicAIIntegration: