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
5 changes: 5 additions & 0 deletions docs/replay_consistency_design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Session / Memory / Summary Replay Consistency

框架以 JSONL 记录标准操作,用固定 id 和顺序驱动后端。默认比较内存与 SQLite;环境变量可切轻量模式或加入 Redis。时间统一为占位符,memory 结果排序;事件、state 和工具数据仍严格比较。`allowed_diff` 必须声明原因。

Summary 同时比较文本及存储关系。summary event 携带 session id、递增 version 与 supersedes id,摘要丢失、旧版覆盖和归属错误不会被掩盖。active 与 historical events 分开校验。报告给出 session、event index 或 summary id、路径及两端值;测试还会对 10 条轨迹逐一注入差异。
33 changes: 33 additions & 0 deletions session_memory_summary_diff_report.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"schema_version": 1,
"reference_backend": "in_memory",
"compared_backends": [
"sqlite",
"redis"
],
"generated_by": "tests/sessions/test_replay_consistency.py",
"allowed_diff_source": "tests/sessions/replay_cases/session_memory_summary.jsonl",
"duration_seconds": 1.7,
"compared_sections": [
"events",
"state",
"memory",
"summary"
],
"normalization": [
"timestamp precision",
"memory result ordering"
],
"cases": [
{"case_id":"single_turn","session_id":"replay-single_turn","status":"match","differences":[]},
{"case_id":"multi_turn","session_id":"replay-multi_turn","status":"match","differences":[]},
{"case_id":"tool_round_trip","session_id":"replay-tool_round_trip","status":"match","differences":[]},
{"case_id":"state_overwrite","session_id":"replay-state_overwrite","status":"match","differences":[]},
{"case_id":"memory_preference","session_id":"replay-memory_preference","status":"match","differences":[]},
{"case_id":"memory_fact_update","session_id":"replay-memory_fact_update","status":"match","differences":[]},
{"case_id":"summary_create","session_id":"replay-summary_create","status":"match","differences":[]},
{"case_id":"summary_update","session_id":"replay-summary_update","status":"match","differences":[]},
{"case_id":"summary_truncation","session_id":"replay-summary_truncation","status":"match","differences":[]},
{"case_id":"retry_recovery","session_id":"replay-retry_recovery","status":"match","differences":[]}
]
}
10 changes: 10 additions & 0 deletions tests/sessions/replay_cases/session_memory_summary.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{"case_id":"single_turn","description":"single user and assistant turn","operations":[{"type":"event","author":"user","role":"user","text":"Hello agent"},{"type":"event","author":"assistant","role":"model","text":"Hello user"}],"allowed_diff":[{"path":"events[*].timestamp","reason":"Backend clocks and SQL timestamp precision differ; event order and payload remain exact."}]}
{"case_id":"multi_turn","description":"three consecutive conversation turns","operations":[{"type":"event","author":"user","role":"user","text":"My name is Lin"},{"type":"event","author":"assistant","role":"model","text":"Nice to meet you, Lin"},{"type":"event","author":"user","role":"user","text":"What is my name?"},{"type":"event","author":"assistant","role":"model","text":"Your name is Lin"},{"type":"event","author":"user","role":"user","text":"Thanks"},{"type":"event","author":"assistant","role":"model","text":"You are welcome"}],"allowed_diff":[{"path":"events[*].timestamp","reason":"Backend clocks and SQL timestamp precision differ; event order and payload remain exact."}]}
{"case_id":"tool_round_trip","description":"function call and response survive persistence","operations":[{"type":"event","author":"user","role":"user","text":"Weather in Shenzhen?"},{"type":"function_call","author":"assistant","role":"model","name":"weather","args":{"city":"Shenzhen"}},{"type":"function_response","author":"tool","role":"user","name":"weather","response":{"temperature":31,"unit":"C"}},{"type":"event","author":"assistant","role":"model","text":"It is 31 C in Shenzhen"}],"allowed_diff":[{"path":"events[*].timestamp","reason":"Backend clocks and SQL timestamp precision differ; tool names and values remain exact."}]}
{"case_id":"state_overwrite","description":"session, user and app state updates and overwrites","operations":[{"type":"state","author":"assistant","text":"state one","state_delta":{"theme":"light","user:language":"zh","app:region":"cn"}},{"type":"state","author":"assistant","text":"state two","state_delta":{"theme":"dark","counter":1}},{"type":"state","author":"assistant","text":"state three","state_delta":{"counter":2,"user:language":"en"}}],"allowed_diff":[{"path":"events[*].timestamp","reason":"Backend clocks and SQL timestamp precision differ; final state is compared exactly."}]}
{"case_id":"memory_preference","description":"write and retrieve a user preference","operations":[{"type":"event","author":"user","role":"user","text":"I prefer jasmine tea"},{"type":"event","author":"assistant","role":"model","text":"I will remember your tea preference"},{"type":"memory_store"},{"type":"memory_search","label":"preference","query":"jasmine"}],"allowed_diff":[{"path":"memory.*[*].timestamp","reason":"Memory services format timestamps from backend-specific precision; content and author remain exact."}]}
{"case_id":"memory_fact_update","description":"memory rewrite is idempotent and searchable","operations":[{"type":"event","author":"user","role":"user","text":"Project Atlas launches Friday","event_id":"memory-fact"},{"type":"memory_store"},{"type":"memory_store"},{"type":"memory_search","label":"fact","query":"Atlas","limit":5}],"allowed_diff":[{"path":"memory.*[*].timestamp","reason":"Memory services format timestamps from backend-specific precision; duplicate count and payload remain exact."}]}
{"case_id":"summary_create","description":"create and read a first summary","operations":[{"type":"event","author":"user","role":"user","text":"First detail"},{"type":"event","author":"assistant","role":"model","text":"First answer"},{"type":"event","author":"user","role":"user","text":"Recent question"},{"type":"summary","text":"The user discussed the first detail.","keep_recent":1}],"allowed_diff":[{"path":"summary.*.updated_at","reason":"Summary update timestamps use backend clocks; identity, ownership, version and content remain exact."},{"path":"historical_events[*].timestamp","reason":"Historical event timestamps have backend-specific storage precision."}]}
{"case_id":"summary_update","description":"new summary supersedes the previous version","operations":[{"type":"event","author":"user","role":"user","text":"Old topic"},{"type":"event","author":"assistant","role":"model","text":"Old answer"},{"type":"summary","text":"Old topic was discussed.","keep_recent":1},{"type":"event","author":"user","role":"user","text":"New topic"},{"type":"event","author":"assistant","role":"model","text":"New answer"},{"type":"summary","text":"Old and new topics were discussed.","keep_recent":1}],"allowed_diff":[{"path":"summary.*.updated_at","reason":"Summary update timestamps use backend clocks; overwrite links and versions remain exact."},{"path":"historical_events[*].timestamp","reason":"Historical event timestamps have backend-specific storage precision."}]}
{"case_id":"summary_truncation","description":"summary plus retained and new events reconstruct context","operations":[{"type":"event","author":"user","role":"user","text":"Archived question one"},{"type":"event","author":"assistant","role":"model","text":"Archived answer one"},{"type":"event","author":"user","role":"user","text":"Retained question"},{"type":"summary","text":"One archived question was answered.","keep_recent":1},{"type":"event","author":"assistant","role":"model","text":"Answer after summary"}],"allowed_diff":[{"path":"summary.*.updated_at","reason":"Summary update timestamps use backend clocks; ownership and active position remain exact."},{"path":"historical_events[*].timestamp","reason":"Historical event timestamps have backend-specific storage precision."}]}
{"case_id":"retry_recovery","description":"failed and duplicate writes do not create dirty state or events","operations":[{"type":"event","author":"user","role":"user","text":"Submit once","event_id":"retry-stable-id"},{"type":"retry_event","author":"user","role":"user","text":"Submit once","event_id":"retry-stable-id"},{"type":"fail_before_commit","state_delta":{"dirty":true}},{"type":"state","author":"assistant","text":"Committed","state_delta":{"status":"committed"}}],"allowed_diff":[{"path":"events[*].timestamp","reason":"Backend clocks and SQL timestamp precision differ; retry cardinality and committed state remain exact."}]}
Loading
Loading