Spaces:
Configuration error
Configuration error
| """Relationship context compaction tests.""" | |
| import json | |
| from pycatan.ai.agent_state import AgentState | |
| from pycatan.ai.config import AIConfig | |
| from pycatan.ai.llm_client import LLMResponse | |
| from pycatan.ai.memory_compactor import MemoryCompactor | |
| from pycatan.ai.schemas import ( | |
| ResponseType, | |
| SchemaVersion, | |
| get_schema_description, | |
| get_schema_for_response_type, | |
| ) | |
| class _FakeLLMClient: | |
| def generate(self, *args, **kwargs): | |
| return LLMResponse( | |
| success=True, | |
| content=json.dumps({ | |
| "compacted_memory": "Prefer ore expansion; Shon may be racing city upgrades.", | |
| "recent_notes_to_keep": ["Recent note A", "Recent note B"], | |
| "relationship_updates": [ | |
| "Shon refused a fair trade, making future promises less credible.", | |
| ], | |
| "discarded_as_irrelevant": [], | |
| }), | |
| ) | |
| class _BrokenJSONLLMClient: | |
| def generate(self, *args, **kwargs): | |
| return LLMResponse( | |
| success=True, | |
| content="I cannot provide that as JSON.", | |
| model="fake-model", | |
| ) | |
| class _FailingLLMClient: | |
| def generate(self, *args, **kwargs): | |
| return LLMResponse( | |
| success=False, | |
| error="provider rejected response_format", | |
| model="fake-model", | |
| ) | |
| def test_player_response_schemas_do_not_ask_for_relationship_update(): | |
| for version in [SchemaVersion.V1, SchemaVersion.V2]: | |
| for response_type in [ResponseType.ACTIVE_TURN, ResponseType.OBSERVING]: | |
| schema = get_schema_for_response_type(response_type, version) | |
| assert "relationship_update" not in schema["properties"] | |
| assert "relationship_update" not in schema["propertyOrdering"] | |
| assert "relationship_update" not in get_schema_description(response_type, version) | |
| def test_memory_compactor_extracts_relationship_updates(): | |
| agent = AgentState(player_name="Hadar", player_id=0, player_color="Red") | |
| agent.memory_history = [ | |
| {"note": "Shon refused my fair trade after promising to help."}, | |
| {"note": "Recent note A"}, | |
| {"note": "Recent note B"}, | |
| ] | |
| result = MemoryCompactor(AIConfig()).compact( | |
| agent=agent, | |
| game_state={ | |
| "meta": {"curr": "Hadar", "phase": "NORMAL_PLAY"}, | |
| "H": [], | |
| "N": [], | |
| "state": {"bld": [], "rds": []}, | |
| "players": {"Hadar": {"vp": 0, "res": {}}, "Shon": {"vp": 0, "res": {}}}, | |
| }, | |
| chat_history=[ | |
| {"from": "Shon", "message": "I'll help next time, promise."}, | |
| ], | |
| llm_client=_FakeLLMClient(), | |
| ) | |
| assert result is not None | |
| assert result["relationship_updates"] == [ | |
| "Shon refused a fair trade, making future promises less credible.", | |
| ] | |
| assert "relationship_updates" in result["prompt"]["output_requirements"]["schema"] | |
| assert "existing_relationship_updates" in result["prompt"]["memory_input"] | |
| def test_memory_compactor_filters_repeated_relationship_updates(): | |
| compactor = MemoryCompactor(AIConfig()) | |
| updates = compactor._clean_relationship_updates( | |
| [ | |
| "Shon refused a fair trade.", | |
| "Shon refused a fair trade.", | |
| "Hadar backed my warning.", | |
| ], | |
| [{"note": "Shon refused a fair trade."}], | |
| ) | |
| assert updates == ["Hadar backed my warning."] | |
| def test_memory_compactor_falls_back_when_model_returns_unparseable_json(): | |
| agent = AgentState(player_name="Hadar", player_id=0, player_color="Red") | |
| agent.memory_history = [ | |
| {"note": "Need brick and sheep to build the winning settlement at node 23."}, | |
| {"note": "Ziv is blocking trades that help Hadar win."}, | |
| {"note": "Recent note A"}, | |
| {"note": "Recent note B"}, | |
| ] | |
| result = MemoryCompactor(AIConfig()).compact( | |
| agent=agent, | |
| game_state={ | |
| "meta": {"curr": "Hadar", "phase": "NORMAL_PLAY"}, | |
| "H": [], | |
| "N": [], | |
| "state": {"bld": [], "rds": []}, | |
| "players": {"Hadar": {"vp": 4, "res": {}}}, | |
| }, | |
| chat_history=[], | |
| llm_client=_BrokenJSONLLMClient(), | |
| ) | |
| assert result is not None | |
| assert result["fallback_used"] is True | |
| assert result["fallback_reason"] == "unparseable_response" | |
| assert "winning settlement" in result["compacted_memory"] | |
| assert result["recent_entries"] == agent.memory_history[-2:] | |
| def test_memory_compactor_falls_back_when_llm_call_fails(): | |
| agent = AgentState(player_name="Ziv", player_id=1, player_color="Blue") | |
| agent.memory_history = [ | |
| {"note": "Hadar is at 4 VP and must not receive brick."}, | |
| {"note": "Need wood and brick for my own road."}, | |
| {"note": "Recent note A"}, | |
| {"note": "Recent note B"}, | |
| ] | |
| result = MemoryCompactor(AIConfig()).compact( | |
| agent=agent, | |
| game_state={ | |
| "meta": {"curr": "Ziv", "phase": "NORMAL_PLAY"}, | |
| "H": [], | |
| "N": [], | |
| "state": {"bld": [], "rds": []}, | |
| "players": {"Ziv": {"vp": 3, "res": {}}}, | |
| }, | |
| chat_history=[{"from": "Hadar", "message": "Can anyone trade brick?"}], | |
| llm_client=_FailingLLMClient(), | |
| ) | |
| assert result is not None | |
| assert result["fallback_used"] is True | |
| assert result["fallback_reason"] == "llm_error: provider rejected response_format" | |
| assert "must not receive brick" in result["compacted_memory"] | |