diff --git a/AppImage/scripts/ai_providers/openai_provider.py b/AppImage/scripts/ai_providers/openai_provider.py index 01a33e8a..8ff3dedf 100644 --- a/AppImage/scripts/ai_providers/openai_provider.py +++ b/AppImage/scripts/ai_providers/openai_provider.py @@ -24,6 +24,9 @@ class OpenAIProvider(AIProvider): """ NAME = "openai" + # The Monitor gives any rewrite 25 s; a local model behind an + # OpenAI-compatible endpoint may need all of it. + REQUEST_TIMEOUT_SECONDS = 25 REQUIRES_API_KEY = True DEFAULT_API_URL = "https://api.openai.com/v1/chat/completions" DEFAULT_MODELS_URL = "https://api.openai.com/v1/models" @@ -226,8 +229,8 @@ class OpenAIProvider(AIProvider): } api_url = self._get_api_url() - result = self._make_request(api_url, payload, headers) - + result = self._make_request(api_url, payload, headers, timeout=self.REQUEST_TIMEOUT_SECONDS) + try: return result['choices'][0]['message']['content'].strip() except (KeyError, IndexError) as e: diff --git a/AppImage/scripts/tests/test_openai_compatible_ai_models.py b/AppImage/scripts/tests/test_openai_compatible_ai_models.py index 96bceffa..1c8441a8 100644 --- a/AppImage/scripts/tests/test_openai_compatible_ai_models.py +++ b/AppImage/scripts/tests/test_openai_compatible_ai_models.py @@ -20,8 +20,9 @@ class CapturingOpenAIProvider(OpenAIProvider): super().__init__(*args, **kwargs) self.captured_payload = None - def _make_request(self, url, payload, headers): + def _make_request(self, url, payload, headers, timeout=15): self.captured_payload = payload + self.captured_timeout = timeout return {"choices": [{"message": {"content": "ok"}}]} @@ -119,6 +120,14 @@ class OpenAICompatibleModelTests(unittest.TestCase): self.assertNotIn("reasoning_effort", provider.captured_payload) self.assertNotIn("max_completion_tokens", provider.captured_payload) + def test_a_request_waits_as_long_as_the_monitor_allows_a_rewrite(self): + # A local model behind an OpenAI-compatible endpoint is slow; the + # request must not give up before the Monitor's own limit. + provider = CapturingOpenAIProvider(api_key="token", model="gemma-3-1b-it-Q4_K_M", + base_url="http://192.0.2.22:8080/v1") + provider.generate("system", "user", max_tokens=100) + self.assertEqual(provider.captured_timeout, notification_manager.AI_REWRITE_TIMEOUT_SECONDS) + def test_official_openai_reasoning_model_still_uses_reasoning_contract(self): provider = CapturingOpenAIProvider( api_key="token",