fleet-memory/hindsight-clients/python/tests/test_reflect_response_parsing.py
Nicolò Boschi f9a8a8e01e
fix: resolve based_on schema/serialization issues in reflect API (#348)
* fix: add default values to OpenAPI schema for default_factory fields

This commit fixes the OpenAPI schema to include default values for fields
using default_factory, which improves schema accuracy and client generation.

Changes:
1. Added FieldWithDefault() helper to inject default values into OpenAPI schema
2. Updated 14 fields using default_factory to include defaults in schema:
   - ReflectBasedOn.{memories, mental_models, directives}
   - ReflectTrace.{tool_calls, llm_calls}
   - All tags fields
   - All trigger fields
   - All include fields

3. Regenerated OpenAPI spec with proper defaults

4. Added tests to verify API returns correct format with empty banks

Note: This fixes the schema but doesn't change the v0.3.0 -> v0.4.0 breaking
change where based_on went from list to object. Clients should handle both
formats for backward compatibility.

* fix: remove client imports from API test

The test was failing in CI because it imported the client library
which isn't installed in the API test environment.

Changed to test only API JSON response format, not client parsing.
This is more appropriate for an API test anyway.

* test: add client tests for ReflectResponse parsing

Added comprehensive tests in hindsight-clients/python/tests to verify:
- v0.4.0+ format with empty based_on object
- v0.4.0+ format with null based_on
- v0.4.0+ format with populated facts
- v0.3.0 format (list) correctly fails validation
- Missing based_on field handling

These tests document the v0.3.0 -> v0.4.0 breaking change where
based_on changed from list to object.
2026-02-12 11:26:12 +01:00

125 lines
4 KiB
Python

"""
Test ReflectResponse parsing for different API versions.
This tests the client's ability to parse reflect responses from:
- v0.3.0 API (based_on as list)
- v0.4.0+ API (based_on as object)
"""
import pytest
from hindsight_client_api.models.reflect_response import ReflectResponse
from hindsight_client_api.models.reflect_based_on import ReflectBasedOn
def test_parse_v4_format_with_empty_based_on():
"""Test parsing v0.4.0+ format with empty based_on object."""
response_data = {
"text": "I don't have any information about that.",
"based_on": {
"memories": [],
"mental_models": [],
"directives": []
}
}
response = ReflectResponse.from_dict(response_data)
assert response is not None
assert response.text == "I don't have any information about that."
assert response.based_on is not None
assert isinstance(response.based_on, ReflectBasedOn)
assert response.based_on.memories == []
assert response.based_on.mental_models == []
assert response.based_on.directives == []
def test_parse_v4_format_with_null_based_on():
"""Test parsing v0.4.0+ format with null based_on (include.facts not set)."""
response_data = {
"text": "Hello!",
"based_on": None
}
response = ReflectResponse.from_dict(response_data)
assert response is not None
assert response.text == "Hello!"
assert response.based_on is None
def test_parse_v4_format_with_populated_based_on():
"""Test parsing v0.4.0+ format with actual facts."""
response_data = {
"text": "Based on my knowledge, AI is transformative.",
"based_on": {
"memories": [
{
"id": "mem-123",
"text": "AI is used in healthcare",
"type": "world",
"context": None,
"occurred_start": None,
"occurred_end": None
}
],
"mental_models": [
{
"id": "mm-456",
"text": "AI transforms industries",
"context": "technology trends"
}
],
"directives": [
{
"id": "dir-789",
"name": "Be concise",
"content": "Keep responses brief"
}
]
}
}
response = ReflectResponse.from_dict(response_data)
assert response is not None
assert response.text == "Based on my knowledge, AI is transformative."
assert response.based_on is not None
assert len(response.based_on.memories) == 1
assert response.based_on.memories[0].id == "mem-123"
assert len(response.based_on.mental_models) == 1
assert response.based_on.mental_models[0].id == "mm-456"
assert len(response.based_on.directives) == 1
assert response.based_on.directives[0].id == "dir-789"
def test_parse_v3_format_with_empty_list_fails():
"""
Test that v0.3.0 format (based_on as list) fails validation.
This is a BREAKING CHANGE from v0.3.0 to v0.4.0.
Clients using v0.4.x SDK cannot parse v0.3.0 API responses.
Users must either:
- Upgrade API to v0.4.0+
- Use v0.3.0 client with v0.3.0 API
"""
response_data = {
"text": "No information available.",
"based_on": [] # v0.3.0 format - incompatible with v0.4.0+ client
}
with pytest.raises(Exception) as exc_info:
ReflectResponse.from_dict(response_data)
# Should fail with validation error
assert "ValidationError" in str(type(exc_info.value).__name__) or "validation" in str(exc_info.value).lower()
def test_parse_missing_based_on_field():
"""Test parsing response when based_on field is omitted entirely."""
response_data = {
"text": "Hello!"
# based_on field not present
}
response = ReflectResponse.from_dict(response_data)
assert response is not None
assert response.text == "Hello!"
assert response.based_on is None