Skip to content

Commit ffe1d6a

Browse files
authored
Merge pull request #26 from asamal4/agenteval-fix-streaming-errorhandling
fix: streaming error handling
2 parents 17f7728 + 7459e96 commit ffe1d6a

File tree

4 files changed

+70
-10
lines changed

4 files changed

+70
-10
lines changed

lsc_agent_eval/src/lsc_agent_eval/core/utils/api_client.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""HTTP client for agent API communication."""
22

3+
import json
34
import logging
45
import os
56
from typing import Any, Optional
@@ -120,17 +121,42 @@ def streaming_query_agent(
120121
json=api_input,
121122
timeout=timeout,
122123
) as response:
123-
response.raise_for_status()
124+
# Potential change lsc-stack to provide SSE error message
125+
if response.status_code != 200:
126+
error_content = response.read().decode("utf-8")
127+
try:
128+
error_data = json.loads(error_content)
129+
if isinstance(error_data, dict) and "detail" in error_data:
130+
detail = error_data["detail"]
131+
if isinstance(detail, dict):
132+
response_msg = detail.get("response", "")
133+
cause_msg = detail.get("cause", "")
134+
error_msg = (
135+
f"{response_msg} - {cause_msg}"
136+
if cause_msg
137+
else response_msg
138+
)
139+
else:
140+
error_msg = str(detail)
141+
else:
142+
error_msg = error_content
143+
except (json.JSONDecodeError, KeyError, TypeError):
144+
error_msg = error_content
145+
146+
raise httpx.HTTPStatusError(
147+
message=f"Agent API error: {response.status_code} - {error_msg}",
148+
request=response.request,
149+
response=response,
150+
)
151+
124152
return parse_streaming_response(response)
125153

126154
except httpx.TimeoutException as e:
127155
raise AgentAPIError(
128156
f"Agent streaming query timeout after {timeout} seconds"
129157
) from e
130158
except httpx.HTTPStatusError as e:
131-
raise AgentAPIError(
132-
f"Agent API error: {e.response.status_code} - {e.response.text}"
133-
) from e
159+
raise AgentAPIError(str(e)) from e
134160
except ValueError as e:
135161
raise AgentAPIError(f"Streaming response validation error: {e}") from e
136162
except Exception as e:

lsc_agent_eval/src/lsc_agent_eval/core/utils/streaming_parser.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ def parse_streaming_response(response: httpx.Response) -> dict[str, Any]:
3333
if event == "start" and "conversation_id" in event_data:
3434
conversation_id = event_data["conversation_id"].strip()
3535
logger.debug("Found conversation_id: %s", conversation_id)
36+
elif event == "error" and "token" in event_data:
37+
error_message = event_data["token"]
38+
logger.error("Received error event from streaming API: %s", error_message)
39+
raise ValueError(f"Streaming API error: {error_message}")
3640
elif event == "turn_complete" and "token" in event_data:
3741
final_response = event_data["token"].strip()
3842
logger.debug("Found final response (%d characters)", len(final_response))

lsc_agent_eval/tests/core/utils/test_api_client.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Tests for agent API client."""
22

3+
import json
34
from unittest.mock import MagicMock, Mock, mock_open, patch
45

56
import httpx
@@ -218,6 +219,7 @@ def test_client_setup_exception(self):
218219
def test_streaming_query_agent_success(self):
219220
"""Test successful streaming agent query."""
220221
mock_response = Mock()
222+
mock_response.status_code = 200
221223
mock_response.raise_for_status.return_value = None
222224

223225
mock_stream_response = MagicMock()
@@ -265,6 +267,7 @@ def test_streaming_query_agent_success(self):
265267
def test_streaming_query_agent_parser_error(self):
266268
"""Test streaming agent query when parser raises ValueError."""
267269
mock_response = Mock()
270+
mock_response.status_code = 200
268271
mock_response.raise_for_status.return_value = None
269272

270273
mock_stream_response = MagicMock()
@@ -312,19 +315,32 @@ def test_streaming_query_agent_timeout(self):
312315

313316
def test_streaming_query_agent_http_error(self):
314317
"""Test streaming agent query with HTTP error."""
318+
error_response = {
319+
"detail": {
320+
"response": "Access denied",
321+
"cause": "You do not have permission to access this conversation",
322+
}
323+
}
324+
error_json = json.dumps(error_response)
325+
315326
mock_response = Mock()
316-
mock_response.status_code = 500
317-
mock_response.text = "Internal Server Error"
327+
mock_response.status_code = 403
328+
mock_response.read.return_value = error_json.encode("utf-8")
329+
330+
mock_stream_response = MagicMock()
331+
mock_stream_response.__enter__.return_value = mock_response
332+
mock_stream_response.__exit__.return_value = None
318333

319334
mock_client = Mock()
320-
mock_client.stream.side_effect = httpx.HTTPStatusError(
321-
"Server error", request=Mock(), response=mock_response
322-
)
335+
mock_client.stream.return_value = mock_stream_response
323336

324337
with patch("httpx.Client", return_value=mock_client):
325338
client = AgentHttpClient("http://localhost:8080")
326339

327340
api_input = {"query": "Test query", "provider": "openai", "model": "gpt-4"}
328341

329-
with pytest.raises(AgentAPIError, match="Agent API error: 500"):
342+
with pytest.raises(
343+
AgentAPIError,
344+
match="Agent API error: 403 - Access denied - You do not have permission to access this conversation",
345+
):
330346
client.streaming_query_agent(api_input)

lsc_agent_eval/tests/core/utils/test_streaming_parser.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,20 @@ def test_error_conditions(self):
6363
with pytest.raises(ValueError, match="No Conversation ID found"):
6464
parse_streaming_response(mock_response)
6565

66+
def test_error_event_handling(self):
67+
"""Test error event handling in streaming response."""
68+
mock_response = Mock()
69+
mock_response.iter_lines.return_value = [
70+
'data: {"event": "start", "data": {"conversation_id": "conv-error"}}',
71+
'data: {"event": "error", "data": {"id": 1, "token": "Unable to connect to LLama Stack backend: Connection timed out"}}',
72+
]
73+
74+
with pytest.raises(
75+
ValueError,
76+
match="Streaming API error: Unable to connect to LLama Stack backend: Connection timed out",
77+
):
78+
parse_streaming_response(mock_response)
79+
6680
def test_tool_call_parsing(self):
6781
"""Test tool call parsing functionality."""
6882
# Valid tool call

0 commit comments

Comments
 (0)