Skip to content

Commit 56b84e1

Browse files
DineshThumma9xuanyang15
authored andcommitted
fix(cli): detailed error message on sse stream specifying stacktrack (error type on client side as well)
Merge #4248 Co-authored-by: Xuan Yang <xygoogle@google.com> PiperOrigin-RevId: 940627393
1 parent c91fc75 commit 56b84e1

2 files changed

Lines changed: 50 additions & 11 deletions

File tree

src/google/adk/cli/api_server.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import os
2727
import re
2828
import sys
29+
import time
2930
import traceback
3031
import typing
3132
from typing import Any
@@ -1603,7 +1604,18 @@ async def event_generator():
16031604
yield f"data: {sse_event}\n\n"
16041605
except Exception as e:
16051606
logger.exception("Error in event_generator: %s", e)
1606-
yield f"data: {json.dumps({'error': str(e)})}\n\n"
1607+
error_details = {
1608+
"error_type": type(e).__name__,
1609+
"error_message": str(e),
1610+
"timestamp": time.time(),
1611+
}
1612+
if logger.isEnabledFor(logging.DEBUG):
1613+
error_details["stacktrace"] = traceback.format_exc()
1614+
1615+
yield (
1616+
"data:"
1617+
f" {json.dumps({'error': f'{type(e).__name__}: {e}', 'error_details': error_details})}\n\n"
1618+
)
16071619

16081620
# Returns a streaming response with the proper media type for SSE
16091621
return StreamingResponse(

tests/unittests/cli/test_fast_api.py

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1645,7 +1645,7 @@ async def run_async_with_artifact_delta(
16451645
def test_agent_run_sse_yields_error_object_on_exception(
16461646
test_app, create_test_session, monkeypatch
16471647
):
1648-
"""Test /run_sse streams an error object if streaming raises."""
1648+
"""Test /run_sse streams structured error details on exception."""
16491649
info = create_test_session
16501650

16511651
async def run_async_raises(self, **kwargs):
@@ -1662,15 +1662,42 @@ async def run_async_raises(self, **kwargs):
16621662
"streaming": True,
16631663
}
16641664

1665-
response = test_app.post("/run_sse", json=payload)
1666-
assert response.status_code == 200
1667-
1668-
sse_events = [
1669-
json.loads(line.removeprefix("data: "))
1670-
for line in response.text.splitlines()
1671-
if line.startswith("data: ")
1672-
]
1673-
assert sse_events == [{"error": "boom"}]
1665+
# 1. Test without DEBUG enabled
1666+
with patch(
1667+
"google.adk.cli.api_server.logger.isEnabledFor", return_value=False
1668+
):
1669+
response = test_app.post("/run_sse", json=payload)
1670+
assert response.status_code == 200
1671+
sse_events = [
1672+
json.loads(line.removeprefix("data: "))
1673+
for line in response.text.splitlines()
1674+
if line.startswith("data: ")
1675+
]
1676+
assert len(sse_events) == 1
1677+
error_event = sse_events[0]
1678+
assert error_event["error"] == "ValueError: boom"
1679+
assert "error_details" in error_event
1680+
assert error_event["error_details"]["error_type"] == "ValueError"
1681+
assert error_event["error_details"]["error_message"] == "boom"
1682+
assert "stacktrace" not in error_event["error_details"]
1683+
assert "timestamp" in error_event["error_details"]
1684+
1685+
# 2. Test with DEBUG enabled
1686+
with patch(
1687+
"google.adk.cli.api_server.logger.isEnabledFor", return_value=True
1688+
):
1689+
response = test_app.post("/run_sse", json=payload)
1690+
assert response.status_code == 200
1691+
sse_events = [
1692+
json.loads(line.removeprefix("data: "))
1693+
for line in response.text.splitlines()
1694+
if line.startswith("data: ")
1695+
]
1696+
assert len(sse_events) == 1
1697+
error_event = sse_events[0]
1698+
assert error_event["error"] == "ValueError: boom"
1699+
assert "stacktrace" in error_event["error_details"]
1700+
assert "ValueError: boom" in error_event["error_details"]["stacktrace"]
16741701

16751702

16761703
def test_list_artifact_names(test_app, create_test_session):

0 commit comments

Comments
 (0)