|
| 1 | +# Replay Consistency Test Framework |
| 2 | + |
| 3 | +tRPC-Agent supports InMemory, SQL, and Redis backends for Session/Memory storage. In production, developers often prototype with InMemory and then switch to SQL or Redis. If different backends produce inconsistent event order, state, memory, or summary data for the same agent trajectory, it leads to replay errors, context loss, long-term memory corruption, and summary overwrite issues. |
| 4 | + |
| 5 | +This framework provides a set of standardized input trajectories to drive multiple backends, automatically generates diff reports, and pinpoints the field path and values of each inconsistency. It serves both as a testing tool and a quality benchmark for backend implementations. |
| 6 | + |
| 7 | +## Architecture |
| 8 | + |
| 9 | +Core components: |
| 10 | + |
| 11 | +- **ReplayCase / ReplayStep**: JSONL files defining standardized input trajectories |
| 12 | +- **ReplayHarness**: Parses JSONL steps, drives two backends in parallel, collects raw results |
| 13 | +- **DiffEngine**: Four-dimension comparison (events / state / memory / summary), produces DiffReport |
| 14 | +- **Normalizer**: Truncates timestamps to second precision, reassigns stable IDs by content, excludes `is_final_response` |
| 15 | + |
| 16 | +Based on [tests/sessions/conftest.py](../../../tests/sessions/conftest.py) and [tests/sessions/test_replay_consistency.py](../../../tests/sessions/test_replay_consistency.py). |
| 17 | + |
| 18 | +## Replay Cases |
| 19 | + |
| 20 | +| # | Case Name | Type | Description | |
| 21 | +|---|---|---|---| |
| 22 | +| 1 | `single_turn` | Normal | Single user → agent exchange | |
| 23 | +| 2 | `multi_turn` | Normal | 3 rounds of alternating conversation | |
| 24 | +| 3 | `tool_call` | Normal | function_call + function_response | |
| 25 | +| 4 | `state_update` | Normal | Multiple state_delta writes and overwrites | |
| 26 | +| 5 | `memory_rw` | Normal | store_session + search_memory | |
| 27 | +| 6 | `summary_gen` | Normal | 22-turn conversation triggering summary | |
| 28 | +| 7 | `summary_truncate` | Known divergence | Two-layer validation: strict metadata + per-backend semantics | |
| 29 | +| 8 | `exception_recovery` | Injected | inject_skip_append to simulate write failure | |
| 30 | +| 9 | `injected_event_order` | Injected | inject_reorder_events to swap events | |
| 31 | +| 10 | `injected_summary_session` | Injected | inject_summary_session_id to alter summary ownership | |
| 32 | + |
| 33 | +## Normalization Strategy |
| 34 | + |
| 35 | +Before cross-backend comparison, non-business differences are removed: |
| 36 | + |
| 37 | +| Field | Treatment | |
| 38 | +|-------|-----------| |
| 39 | +| event.timestamp | Truncate to second precision (int) | |
| 40 | +| event.id | Reassign stable ID sorted by content | |
| 41 | +| state_delta | Unify JSON key ordering | |
| 42 | +| is_final_response | Excluded (computed property differs across serialization paths) | |
| 43 | + |
| 44 | +Three categories of differences are explicitly allowed and written to allowed_diff: |
| 45 | + |
| 46 | +1. Backend-generated `invocation_id` |
| 47 | +2. Backend-specific `save_key` format differences |
| 48 | +3. Event count differences after summary compression (InMemory stores compressed events in memory; SQL get_session re-reads all raw events from the event table) |
| 49 | + |
| 50 | +## Summary Comparison Strategy |
| 51 | + |
| 52 | +The comparison operates in two layers: |
| 53 | + |
| 54 | +1. **Summary metadata**: `session_id`, `summary_text`, `original_event_count`, `compressed_event_count` must be strictly consistent across backends — this is the core requirement for replay correctness |
| 55 | +2. **Per-backend independent validation**: summary text is non-empty, compression has taken effect (compressed < original), and new events appended after compression are preserved |
| 56 | + |
| 57 | +The exact boundary between summary text and retained events is allowed to differ due to backend storage model differences. |
| 58 | + |
| 59 | +## Backend Access |
| 60 | + |
| 61 | +| Mode | Backend A | Backend B | Trigger | |
| 62 | +|------|-----------|-----------|---------| |
| 63 | +| Lightweight (default) | InMemorySessionService | SqlSessionService(SQLite) | Always | |
| 64 | +| SQL integration | InMemorySessionService | SqlSessionService(MySQL) | TEST_MYSQL_URL | |
| 65 | +| Redis integration | InMemorySessionService | RedisSessionService | TEST_REDIS_URL | |
| 66 | + |
| 67 | +All three backends conform to the `SessionServiceABC` interface; adding a new backend only requires implementing that interface. |
0 commit comments