From 09b91accbc30277aa018f52eb737d1424b9356d8 Mon Sep 17 00:00:00 2001 From: Lee Penkman Date: Wed, 1 Jul 2026 19:15:24 +1200 Subject: [PATCH] test: cover app.nz provider params --- litellm/llms/openai_like/dynamic_config.py | 4 +- provider_endpoints_support.json | 18 ++++++ .../llms/openai_like/test_json_providers.py | 63 ++++++++----------- 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/litellm/llms/openai_like/dynamic_config.py b/litellm/llms/openai_like/dynamic_config.py index fcff1fbcf4e..5b64e051d52 100644 --- a/litellm/llms/openai_like/dynamic_config.py +++ b/litellm/llms/openai_like/dynamic_config.py @@ -94,9 +94,7 @@ def create_config_class(provider: SimpleProviderConfig): from litellm.utils import supports_function_calling supported_params = super().get_supported_openai_params(model=model) - supported_params = list( - dict.fromkeys([*supported_params, *provider.extra_supported_params]) - ) + supported_params = list(dict.fromkeys([*supported_params, *provider.extra_supported_params])) _supports_fc = supports_function_calling(model=model, custom_llm_provider=provider.slug) diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index b137ec59a1f..1436a189059 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -177,6 +177,24 @@ "interactions": true } }, + "app_nz": { + "display_name": "app.nz (`app_nz`)", + "url": "https://docs.litellm.ai/docs/providers/app_nz", + "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": true, + "interactions": true + } + }, "apertis": { "display_name": "Apertis (`apertis`)", "endpoints": { 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 c8743e1809d..0053879ca32 100644 --- a/tests/test_litellm/llms/openai_like/test_json_providers.py +++ b/tests/test_litellm/llms/openai_like/test_json_providers.py @@ -52,9 +52,7 @@ class TestJSONProviderLoader: assert api_base == "https://api.publicai.co/v1" # Test with custom base - api_base, api_key = config._get_openai_compatible_provider_info( - "https://custom.api.com", "test-key" - ) + api_base, api_key = config._get_openai_compatible_provider_info("https://custom.api.com", "test-key") assert api_base == "https://custom.api.com" assert api_key == "test-key" @@ -70,9 +68,7 @@ class TestJSONProviderLoader: # Test parameter mapping optional_params = {} non_default_params = {"max_completion_tokens": 100, "temperature": 0.7} - result = config.map_openai_params( - non_default_params, optional_params, "gpt-4", False - ) + result = config.map_openai_params(non_default_params, optional_params, "gpt-4", False) # max_completion_tokens should be mapped to max_tokens assert "max_tokens" in result @@ -98,6 +94,19 @@ class TestJSONProviderLoader: assert isinstance(supported, list) assert len(supported) > 0 + def test_extra_supported_params(self): + """Test JSON providers can extend supported OpenAI params""" + from litellm.llms.openai_like.dynamic_config import create_config_class + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + provider = JSONProviderRegistry.get("app_nz") + config_class = create_config_class(provider) + config = config_class() + + supported = config.get_supported_openai_params("app/auto") + + assert "reasoning_effort" in supported + def test_tool_params_excluded_when_function_calling_not_supported(self): """Test that tool-related params are excluded for models that don't support function calling. Regression test for https://github.com/BerriAI/litellm/issues/21125 @@ -121,9 +130,9 @@ class TestJSONProviderLoader: "parallel_tool_calls", ] for param in tool_params: - assert ( - param not in supported - ), f"'{param}' should not be in supported params when function calling is not supported" + assert param not in supported, ( + f"'{param}' should not be in supported params when function calling is not supported" + ) # Non-tool params should still be present assert "temperature" in supported @@ -168,9 +177,7 @@ class TestJSONProviderLoader: from litellm import LlmProviders from litellm.utils import ProviderConfigManager - config = ProviderConfigManager.get_provider_chat_config( - model="gpt-4", provider=LlmProviders.PUBLICAI - ) + config = ProviderConfigManager.get_provider_chat_config(model="gpt-4", provider=LlmProviders.PUBLICAI) assert config is not None assert config.custom_llm_provider == "publicai" @@ -218,9 +225,7 @@ class TestPinstripes: api_base, api_key = config._get_openai_compatible_provider_info(None, None) assert api_base == "https://pinstripes.io/v1" - api_base, api_key = config._get_openai_compatible_provider_info( - "https://custom.pinstripes.io/v1", "test-key" - ) + api_base, api_key = config._get_openai_compatible_provider_info("https://custom.pinstripes.io/v1", "test-key") assert api_base == "https://custom.pinstripes.io/v1" assert api_key == "test-key" @@ -235,9 +240,7 @@ class TestPinstripes: optional_params = {} non_default_params = {"max_completion_tokens": 100, "temperature": 0.7} - result = config.map_openai_params( - non_default_params, optional_params, "ps/glm-4.5-air", False - ) + result = config.map_openai_params(non_default_params, optional_params, "ps/glm-4.5-air", False) assert "max_tokens" in result assert result["max_tokens"] == 100 @@ -282,9 +285,7 @@ class TestDarkbloom: api_base, api_key = config._get_openai_compatible_provider_info(None, None) assert api_base == "https://api.darkbloom.dev/v1" - api_base, api_key = config._get_openai_compatible_provider_info( - "https://custom.darkbloom.dev/v1", "test-key" - ) + api_base, api_key = config._get_openai_compatible_provider_info("https://custom.darkbloom.dev/v1", "test-key") assert api_base == "https://custom.darkbloom.dev/v1" assert api_key == "test-key" @@ -311,17 +312,13 @@ class TestDarkbloom: from litellm import LlmProviders from litellm.utils import ProviderConfigManager - config = ProviderConfigManager.get_provider_chat_config( - model="gemma-4-26b", provider=LlmProviders.DARKBLOOM - ) + config = ProviderConfigManager.get_provider_chat_config(model="gemma-4-26b", provider=LlmProviders.DARKBLOOM) assert config is not None assert config.custom_llm_provider == "darkbloom" def test_darkbloom_model_cost_map(self): - with open( - os.path.join(workspace_path, "model_prices_and_context_window.json") - ) as f: + with open(os.path.join(workspace_path, "model_prices_and_context_window.json")) as f: model_cost = json.load(f) expected_models = { @@ -373,9 +370,7 @@ class TestPublicAIIntegration: content = response.choices[0].message.content.lower() assert len(content) > 0 - print( - f"✓ PublicAI completion successful: {response.choices[0].message.content}" - ) + print(f"✓ PublicAI completion successful: {response.choices[0].message.content}") except Exception as e: if pytest: @@ -403,9 +398,7 @@ class TestPublicAIIntegration: chunks = [] for chunk in response: assert chunk is not None - if hasattr(chunk.choices[0], "delta") and hasattr( - chunk.choices[0].delta, "content" - ): + if hasattr(chunk.choices[0], "delta") and hasattr(chunk.choices[0].delta, "content"): if chunk.choices[0].delta.content: chunks.append(chunk.choices[0].delta.content) @@ -461,9 +454,7 @@ class TestPublicAIIntegration: # Send message with content as list (should be converted to string) response = litellm.completion( model="publicai/swiss-ai/apertus-8b-instruct", - messages=[ - {"role": "user", "content": [{"type": "text", "text": "Say hello"}]} - ], + messages=[{"role": "user", "content": [{"type": "text", "text": "Say hello"}]}], max_tokens=10, )