-
Notifications
You must be signed in to change notification settings - Fork 1
Enhance DSPy Inference Robustness #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
db98bb2
832afaf
fb470cd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| import asyncio | ||
| from unittest.mock import AsyncMock, patch | ||
| from tests.test_template import TestTemplate | ||
| from utils.llm.dspy_inference import DSPYInference | ||
| from litellm.exceptions import RateLimitError, ServiceUnavailableError, Timeout | ||
| import dspy | ||
|
|
||
| class MockSignature(dspy.Signature): | ||
| """A mock DSPy signature for testing purposes.""" | ||
| input = dspy.InputField() | ||
| output = dspy.OutputField() | ||
|
|
||
| class TestDSPYInferenceRobustness(TestTemplate): | ||
| """Tests for DSPYInference robustness features (retry, fallback, timeout).""" | ||
|
|
||
| def test_retry_on_rate_limit(self): | ||
| """ | ||
| Tests that the DSPYInference class correctly retries operations when encountering | ||
| a RateLimitError from the LLM provider. | ||
| """ | ||
| async def _test(): | ||
| with patch("common.global_config.global_config.llm_api_key", return_value="fake-key"), \ | ||
| patch("common.global_config.global_config.default_llm.default_request_timeout", 1): | ||
|
|
||
| inference = DSPYInference(pred_signature=MockSignature, observe=False) | ||
|
|
||
| error = RateLimitError("Rate limit", llm_provider="openai", model="gpt-4") | ||
|
|
||
| mock_method = AsyncMock(side_effect=[ | ||
| error, | ||
| dspy.Prediction(output="Success") | ||
| ]) | ||
| inference.inference_module_async = mock_method | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: Test bypasses the actual retry logic by mocking Mock Context Used: Context from Prompt To Fix With AIThis is a comment left during a code review.
Path: tests/utils/llm/test_dspy_inference_robustness.py
Line: 33:33
Comment:
**logic:** Test bypasses the actual retry logic by mocking `inference_module_async` directly. The `@retry` decorator on `_run_inference` is never executed since you're replacing the method it calls.
Mock `_run_inference` instead to test the actual retry behavior:
```
mock_method = AsyncMock(side_effect=[
error,
dspy.Prediction(output="Success")
])
inference._run_inference = mock_method
```
**Context Used:** Context from `dashboard` - On how to write tests ([source](https://app.greptile.com/review/custom-context?memory=d6a5ba7a-9b07-47ff-8835-a1b5ee599b6f))
How can I resolve this? If you propose a fix, please make it concise. |
||
|
|
||
| result = await inference.run(input="test") | ||
|
|
||
| assert result.output == "Success" | ||
| assert mock_method.call_count == 2 | ||
|
|
||
| asyncio.run(_test()) | ||
|
|
||
| def test_retry_on_timeout(self): | ||
| """ | ||
| Tests that the DSPYInference class correctly retries operations when encountering | ||
| a Timeout error. | ||
| """ | ||
| async def _test(): | ||
| with patch("common.global_config.global_config.llm_api_key", return_value="fake-key"), \ | ||
| patch("common.global_config.global_config.default_llm.default_request_timeout", 1): | ||
|
|
||
| inference = DSPYInference(pred_signature=MockSignature, observe=False) | ||
|
|
||
| error = Timeout("Timeout", llm_provider="openai", model="gpt-4") | ||
|
|
||
| mock_method = AsyncMock(side_effect=[ | ||
| error, | ||
| dspy.Prediction(output="Success") | ||
| ]) | ||
| inference.inference_module_async = mock_method | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: Same issue - test bypasses retry logic. Mock Context Used: Context from Prompt To Fix With AIThis is a comment left during a code review.
Path: tests/utils/llm/test_dspy_inference_robustness.py
Line: 59:59
Comment:
**logic:** Same issue - test bypasses retry logic. Mock `_run_inference` instead of `inference_module_async` to test actual retry behavior with exponential backoff.
**Context Used:** Context from `dashboard` - On how to write tests ([source](https://app.greptile.com/review/custom-context?memory=d6a5ba7a-9b07-47ff-8835-a1b5ee599b6f))
How can I resolve this? If you propose a fix, please make it concise. |
||
|
|
||
| result = await inference.run(input="test") | ||
|
|
||
| assert result.output == "Success" | ||
| assert mock_method.call_count == 2 | ||
|
|
||
| asyncio.run(_test()) | ||
|
|
||
| def test_fallback_logic(self): | ||
| """ | ||
| Tests the model fallback mechanism. | ||
| """ | ||
| async def _test(): | ||
| with patch("common.global_config.global_config.llm_api_key", return_value="fake-key"): | ||
|
|
||
| # Setup with fallback | ||
| inference = DSPYInference( | ||
| pred_signature=MockSignature, | ||
| observe=False, | ||
| model_name="primary-model", | ||
| fallback_model="fallback-model" | ||
| ) | ||
|
|
||
| async def side_effect(*args, **kwargs): | ||
| lm = kwargs.get('lm') | ||
| if lm.model == "primary-model": | ||
| raise ServiceUnavailableError("Down", llm_provider="openai", model="primary-model") | ||
| elif lm.model == "fallback-model": | ||
| return dspy.Prediction(output="Fallback Success") | ||
| else: | ||
| raise ValueError(f"Unknown model: {lm.model}") | ||
|
|
||
| inference.inference_module_async = AsyncMock(side_effect=side_effect) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: Mocking Context Used: Context from Prompt To Fix With AIThis is a comment left during a code review.
Path: tests/utils/llm/test_dspy_inference_robustness.py
Line: 92:92
Comment:
**logic:** Mocking `inference_module_async` bypasses both retry and fallback logic. Mock `_run_inference` to properly test the retry-then-fallback flow.
**Context Used:** Context from `dashboard` - On how to write tests ([source](https://app.greptile.com/review/custom-context?memory=d6a5ba7a-9b07-47ff-8835-a1b5ee599b6f))
How can I resolve this? If you propose a fix, please make it concise. |
||
|
|
||
| result = await inference.run(input="test") | ||
|
|
||
| assert result.output == "Fallback Success" | ||
|
|
||
| # Primary model should have been retried max_attempts times (default 3) | ||
| # Fallback model called once | ||
| # Total 4 | ||
| assert inference.inference_module_async.call_count == 4 | ||
|
|
||
| asyncio.run(_test()) | ||
|
|
||
| def test_fallback_failure(self): | ||
| """ | ||
| Tests the scenario where both the primary and fallback models fail. | ||
| """ | ||
| async def _test(): | ||
| with patch("common.global_config.global_config.llm_api_key", return_value="fake-key"): | ||
|
|
||
| # Setup with fallback where fallback also fails | ||
| inference = DSPYInference( | ||
| pred_signature=MockSignature, | ||
| observe=False, | ||
| model_name="primary-model", | ||
| fallback_model="fallback-model" | ||
| ) | ||
|
|
||
| async def side_effect(*args, **kwargs): | ||
| lm = kwargs.get('lm') | ||
| if lm.model == "primary-model": | ||
| raise ServiceUnavailableError("Down", llm_provider="openai", model="primary-model") | ||
| elif lm.model == "fallback-model": | ||
| raise ServiceUnavailableError("Also Down", llm_provider="openai", model="fallback-model") | ||
| else: | ||
| raise ValueError(f"Unknown model: {lm.model}") | ||
|
|
||
| inference.inference_module_async = AsyncMock(side_effect=side_effect) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: Same mocking issue. Test should mock Context Used: Context from Prompt To Fix With AIThis is a comment left during a code review.
Path: tests/utils/llm/test_dspy_inference_robustness.py
Line: 129:129
Comment:
**logic:** Same mocking issue. Test should mock `_run_inference` to verify the actual retry and fallback behavior.
**Context Used:** Context from `dashboard` - On how to write tests ([source](https://app.greptile.com/review/custom-context?memory=d6a5ba7a-9b07-47ff-8835-a1b5ee599b6f))
How can I resolve this? If you propose a fix, please make it concise. |
||
|
|
||
| # Execute and expect exception | ||
| try: | ||
| await inference.run(input="test") | ||
| assert False, "Should have raised ServiceUnavailableError" | ||
| except ServiceUnavailableError: | ||
| pass | ||
|
|
||
| # Primary called 3 times, Fallback called 3 times | ||
| # Total 6 | ||
| assert inference.inference_module_async.call_count == 6 | ||
|
|
||
| asyncio.run(_test()) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax: Missing
__init__.pyfiles intests/utils/andtests/utils/llm/directories. Per.cursor/rules/tests.mdc, new test directories require__init__.pyfiles so Python can recognize them.Create:
tests/utils/__init__.pytests/utils/llm/__init__.pyContext Used: Context from
dashboard- On how to write tests (source)Prompt To Fix With AI