Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion exa_py/monitors/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ class GroundingEntry(BaseModel):

class SearchMonitorRunOutput(BaseModel):
results: Optional[List[Dict[str, Any]]] = None
content: Optional[Union[str, Dict[str, Any]]] = None
content: Optional[Union[str, Dict[str, Any], List[Any]]] = None
grounding: Optional[List[GroundingEntry]] = None

model_config = {"populate_by_name": True}
Expand Down
23 changes: 22 additions & 1 deletion tests/unit/test_search_monitors.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def test_trigger_returns_typed_response(self, monitors_client, mock_client):

class TestSearchMonitorRunOutputContent:
"""Runs configured with an outputSchema return ``output.content`` as a
structured object, so the field must accept both ``str`` and ``dict``."""
structured object/list, so the field must accept ``str``, ``dict``, and ``list``."""

def _make_run_with_content(self, content) -> dict:
run = _make_run("run_1")
Expand All @@ -225,6 +225,17 @@ def test_get_run_with_string_content(self, monitors_client, mock_client):
assert run.output is not None
assert run.output.content == "plain text"

def test_get_run_with_structured_list_content(self, monitors_client, mock_client):
# Issue #208: schema output can be a list of objects; typing as str-only breaks list().
structured_list = [
{"title": "First hit", "url": "https://example.com/1"},
{"title": "Second hit", "url": "https://example.com/2"},
]
mock_client.request.return_value = self._make_run_with_content(structured_list)
run = monitors_client.runs.get("sm_123", "run_1")
assert run.output is not None
assert run.output.content == structured_list

def test_list_runs_with_structured_content(self, monitors_client, mock_client):
structured = {"summary": "AI breakthroughs"}
mock_client.request.return_value = {
Expand All @@ -234,3 +245,13 @@ def test_list_runs_with_structured_content(self, monitors_client, mock_client):
}
runs = monitors_client.runs.list("sm_123")
assert runs.data[0].output.content == structured

def test_list_runs_with_structured_list_content(self, monitors_client, mock_client):
structured_list = [{"id": "anthropic", "score": 0.99}]
mock_client.request.return_value = {
"data": [self._make_run_with_content(structured_list)],
"hasMore": False,
"nextCursor": None,
}
runs = monitors_client.runs.list("sm_123")
assert runs.data[0].output.content == structured_list