mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-05 02:41:56 +00:00
fix: guard asyncio.create_task in RubrikLogger.__init__ against missing event loop
asyncio.create_task() raises RuntimeError when called outside a running event loop. Wrap the call in a try/except RuntimeError so that RubrikLogger can be instantiated in synchronous contexts (e.g. during startup, testing) without crashing. The periodic_flush background task simply won't start in those cases; it starts normally when the constructor is called inside an event loop. Add a test that verifies instantiation outside an event loop does not raise (does not patch asyncio.create_task). Co-authored-by: Sameer Kankute <Sameerlite@users.noreply.github.com>
This commit is contained in:
parent
8333cfd2f6
commit
021a14918b
2 changed files with 26 additions and 17 deletions
|
|
@ -121,7 +121,13 @@ class RubrikLogger(CustomGuardrail, CustomBatchLogger):
|
|||
if self.key:
|
||||
self._headers["Authorization"] = f"Bearer {self.key}"
|
||||
|
||||
asyncio.create_task(self.periodic_flush())
|
||||
try:
|
||||
asyncio.create_task(self.periodic_flush())
|
||||
except RuntimeError:
|
||||
verbose_logger.debug(
|
||||
"Rubrik: no running event loop at init time; "
|
||||
"periodic_flush will not run automatically."
|
||||
)
|
||||
|
||||
async def aclose(self):
|
||||
"""Close the dedicated tool blocking HTTP client."""
|
||||
|
|
|
|||
|
|
@ -59,9 +59,7 @@ class TestInitialization:
|
|||
|
||||
def test_init_with_constructor_params(self):
|
||||
with patch("asyncio.create_task", Mock()):
|
||||
handler = RubrikLogger(
|
||||
api_key="ctor-key", api_base="http://ctor-host:9090"
|
||||
)
|
||||
handler = RubrikLogger(api_key="ctor-key", api_base="http://ctor-host:9090")
|
||||
assert handler.key == "ctor-key"
|
||||
assert (
|
||||
handler.tool_blocking_endpoint
|
||||
|
|
@ -148,14 +146,23 @@ class TestInitialization:
|
|||
):
|
||||
assert RubrikLogger().batch_size == 256
|
||||
|
||||
def test_init_outside_event_loop_does_not_raise(self):
|
||||
"""Instantiation without a running event loop must not raise RuntimeError."""
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{"RUBRIK_WEBHOOK_URL": "http://localhost:8080", "RUBRIK_API_KEY": "k"},
|
||||
):
|
||||
# Do NOT patch asyncio.create_task — the real call should be
|
||||
# guarded and fall back gracefully when there is no event loop.
|
||||
handler = RubrikLogger()
|
||||
assert handler.tool_blocking_endpoint.startswith("http://localhost:8080")
|
||||
|
||||
def test_headers_with_api_key(self, handler):
|
||||
assert handler._headers["Authorization"] == "Bearer test-api-key"
|
||||
assert handler._headers["Content-Type"] == "application/json"
|
||||
|
||||
def test_headers_without_api_key(self):
|
||||
with patch.dict(
|
||||
os.environ, {"RUBRIK_WEBHOOK_URL": "http://host"}, clear=True
|
||||
):
|
||||
with patch.dict(os.environ, {"RUBRIK_WEBHOOK_URL": "http://host"}, clear=True):
|
||||
with patch("asyncio.create_task", Mock()):
|
||||
h = RubrikLogger()
|
||||
assert "Authorization" not in h._headers
|
||||
|
|
@ -352,9 +359,7 @@ def _echo_service():
|
|||
@pytest.mark.asyncio
|
||||
class TestApplyGuardrail:
|
||||
async def test_skips_requests(self, handler):
|
||||
inputs = make_inputs_with_tools(
|
||||
[make_tool_call_dict("call_1", "test_tool")]
|
||||
)
|
||||
inputs = make_inputs_with_tools([make_tool_call_dict("call_1", "test_tool")])
|
||||
result = await handler.apply_guardrail(
|
||||
inputs=inputs, request_data={}, input_type="request"
|
||||
)
|
||||
|
|
@ -465,9 +470,7 @@ class TestApplyGuardrail:
|
|||
|
||||
async def test_blocking_service_payload_format(self, handler):
|
||||
tc1 = make_tool_call_dict("call_1", "get_weather", '{"location": "SF"}')
|
||||
tc2 = make_tool_call_dict(
|
||||
"call_2", "send_email", '{"to": "user@example.com"}'
|
||||
)
|
||||
tc2 = make_tool_call_dict("call_2", "send_email", '{"to": "user@example.com"}')
|
||||
inputs = make_inputs_with_tools([tc1, tc2])
|
||||
|
||||
captured_payload: Dict[str, Any] = {}
|
||||
|
|
@ -606,9 +609,7 @@ class TestApplyGuardrailAnthropicFormat:
|
|||
inputs = make_inputs_with_tools([tc])
|
||||
|
||||
mock_client = AsyncMock()
|
||||
mock_client.post = AsyncMock(
|
||||
side_effect=httpx.TimeoutException("Timeout")
|
||||
)
|
||||
mock_client.post = AsyncMock(side_effect=httpx.TimeoutException("Timeout"))
|
||||
handler.tool_blocking_client = mock_client
|
||||
|
||||
result = await handler.apply_guardrail(
|
||||
|
|
@ -729,5 +730,7 @@ class TestResolveModel:
|
|||
|
||||
response = Mock()
|
||||
response.model = ""
|
||||
result = RubrikLogger._resolve_model({"response": response}, {"model": "fallback"})
|
||||
result = RubrikLogger._resolve_model(
|
||||
{"response": response}, {"model": "fallback"}
|
||||
)
|
||||
assert result == "unknown"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue