Spaces:
Paused
Paused
| import json | |
| import os | |
| import sys | |
| from unittest.mock import patch, MagicMock | |
| import pytest | |
| sys.path.insert( | |
| 0, os.path.abspath("../..") | |
| ) # Adds the parent directory to the system path | |
| import litellm | |
| from litellm import completion | |
| from litellm.utils import get_optional_params | |
| class TestPerplexityReasoning: | |
| """ | |
| Test suite for Perplexity Sonar reasoning models with reasoning_effort parameter | |
| """ | |
| def test_perplexity_reasoning_effort_parameter_mapping(self, model, reasoning_effort): | |
| """ | |
| Test that reasoning_effort parameter is correctly mapped for Perplexity Sonar reasoning models | |
| """ | |
| # Set up local model cost map | |
| os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" | |
| litellm.model_cost = litellm.get_model_cost_map(url="") | |
| # Get provider and optional params | |
| _, provider, _, _ = litellm.get_llm_provider(model=model) | |
| optional_params = get_optional_params( | |
| model=model, | |
| custom_llm_provider=provider, | |
| reasoning_effort=reasoning_effort, | |
| ) | |
| # Verify that reasoning_effort is preserved in optional_params for Perplexity | |
| assert "reasoning_effort" in optional_params | |
| assert optional_params["reasoning_effort"] == reasoning_effort | |
| def test_perplexity_reasoning_effort_mock_completion(self, model): | |
| """ | |
| Test that reasoning_effort is correctly passed in actual completion call (mocked) | |
| """ | |
| from openai import OpenAI | |
| from openai.types.chat.chat_completion import ChatCompletion | |
| litellm.set_verbose = True | |
| # Mock successful response with reasoning content | |
| response_object = { | |
| "id": "cmpl-test", | |
| "object": "chat.completion", | |
| "created": 1677652288, | |
| "model": model.split("/")[1], | |
| "choices": [ | |
| { | |
| "index": 0, | |
| "message": { | |
| "role": "assistant", | |
| "content": "This is a test response from the reasoning model.", | |
| "reasoning_content": "Let me think about this step by step...", | |
| }, | |
| "finish_reason": "stop", | |
| } | |
| ], | |
| "usage": { | |
| "prompt_tokens": 9, | |
| "completion_tokens": 20, | |
| "total_tokens": 29, | |
| "completion_tokens_details": { | |
| "reasoning_tokens": 15 | |
| } | |
| }, | |
| } | |
| pydantic_obj = ChatCompletion(**response_object) | |
| def _return_pydantic_obj(*args, **kwargs): | |
| new_response = MagicMock() | |
| new_response.headers = {"content-type": "application/json"} | |
| new_response.parse.return_value = pydantic_obj | |
| return new_response | |
| openai_client = OpenAI(api_key="fake-api-key") | |
| with patch.object( | |
| openai_client.chat.completions.with_raw_response, "create", side_effect=_return_pydantic_obj | |
| ) as mock_client: | |
| response = completion( | |
| model=model, | |
| messages=[{"role": "user", "content": "Hello, please think about this carefully."}], | |
| reasoning_effort="high", | |
| client=openai_client, | |
| ) | |
| # Verify the call was made | |
| assert mock_client.called | |
| # Get the request data from the mock call | |
| call_args = mock_client.call_args | |
| request_data = call_args.kwargs | |
| # Verify reasoning_effort was included in the request | |
| assert "reasoning_effort" in request_data | |
| assert request_data["reasoning_effort"] == "high" | |
| # Verify response structure | |
| assert response.choices[0].message.content is not None | |
| assert response.choices[0].message.content == "This is a test response from the reasoning model." | |
| def test_perplexity_reasoning_models_support_reasoning(self): | |
| """ | |
| Test that Perplexity Sonar reasoning models are correctly identified as supporting reasoning | |
| """ | |
| from litellm.utils import supports_reasoning | |
| # Set up local model cost map | |
| os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" | |
| litellm.model_cost = litellm.get_model_cost_map(url="") | |
| reasoning_models = [ | |
| "perplexity/sonar-reasoning", | |
| "perplexity/sonar-reasoning-pro", | |
| ] | |
| for model in reasoning_models: | |
| assert supports_reasoning(model, None), f"{model} should support reasoning" | |
| def test_perplexity_non_reasoning_models_dont_support_reasoning(self): | |
| """ | |
| Test that non-reasoning Perplexity models don't support reasoning | |
| """ | |
| from litellm.utils import supports_reasoning | |
| # Set up local model cost map | |
| os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" | |
| litellm.model_cost = litellm.get_model_cost_map(url="") | |
| non_reasoning_models = [ | |
| "perplexity/sonar", | |
| "perplexity/sonar-pro", | |
| "perplexity/llama-3.1-sonar-large-128k-chat", | |
| "perplexity/mistral-7b-instruct", | |
| ] | |
| for model in non_reasoning_models: | |
| # These models should not support reasoning (should return False or raise exception) | |
| try: | |
| result = supports_reasoning(model, None) | |
| # If it doesn't raise an exception, it should return False | |
| assert result is False, f"{model} should not support reasoning" | |
| except Exception: | |
| # If it raises an exception, that's also acceptable behavior | |
| pass | |
| def test_perplexity_reasoning_api_base_configuration(self, model, expected_api_base): | |
| """ | |
| Test that Perplexity reasoning models use the correct API base | |
| """ | |
| from litellm.llms.perplexity.chat.transformation import PerplexityChatConfig | |
| config = PerplexityChatConfig() | |
| api_base, _ = config._get_openai_compatible_provider_info( | |
| api_base=None, api_key="test-key" | |
| ) | |
| assert api_base == expected_api_base | |
| def test_perplexity_reasoning_effort_in_supported_params(self): | |
| """ | |
| Test that reasoning_effort is in the list of supported parameters for Perplexity | |
| """ | |
| from litellm.llms.perplexity.chat.transformation import PerplexityChatConfig | |
| config = PerplexityChatConfig() | |
| supported_params = config.get_supported_openai_params(model="perplexity/sonar-reasoning") | |
| assert "reasoning_effort" in supported_params |