f3d80b4628
Auto Release / auto-release (push) Failing after 1s
CI / lint (push) Failing after 0s
CI / screenshot-quality (push) Failing after 3s
CI / pr-policy (push) Has been skipped
CI / test (3.11) (push) Failing after 0s
CI / test (3.12) (push) Failing after 0s
CI / test (3.13) (push) Failing after 3s
CI / coverage (push) Failing after 1s
Legibility / legibility (push) Failing after 3s
560 lines
21 KiB
Python
560 lines
21 KiB
Python
"""Tests for compact SQLite trace record storage."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sqlite3
|
|
from copy import deepcopy
|
|
|
|
from claude_tap.compact_trace import (
|
|
BLOB_KIND_JSON,
|
|
BLOB_REF_MARKER,
|
|
json_blob_payload,
|
|
load_compact_trace,
|
|
make_blob_ref,
|
|
)
|
|
from claude_tap.trace_store import (
|
|
COMPACT_RECORD_MARKER,
|
|
TraceStore,
|
|
get_trace_store,
|
|
)
|
|
|
|
|
|
def _large_codex_record(index: int, *, instructions: str, tools: list[dict]) -> dict:
|
|
return {
|
|
"timestamp": f"2026-05-30T04:00:{index:02d}+00:00",
|
|
"turn": index,
|
|
"request": {
|
|
"method": "WEBSOCKET",
|
|
"path": "/v1/responses",
|
|
"body": {
|
|
"model": "gpt-5.5",
|
|
"instructions": instructions,
|
|
"tools": tools,
|
|
"input": [
|
|
{
|
|
"role": "user",
|
|
"content": [{"type": "input_text", "text": f"round {index}"}],
|
|
}
|
|
],
|
|
"previous_response_id": f"resp_{index - 1}" if index > 1 else None,
|
|
},
|
|
},
|
|
"response": {
|
|
"status": 101,
|
|
"body": {
|
|
"id": f"resp_{index}",
|
|
"model": "gpt-5.5",
|
|
"instructions": instructions,
|
|
"tools": tools,
|
|
"output": [
|
|
{
|
|
"type": "function_call",
|
|
"call_id": f"call_{index}",
|
|
"name": "shell",
|
|
"arguments": json.dumps({"cmd": f"printf round-{index}"}),
|
|
},
|
|
{
|
|
"type": "message",
|
|
"content": [{"type": "output_text", "text": f"done {index}"}],
|
|
},
|
|
],
|
|
"usage": {"input_tokens": 100 + index, "output_tokens": 10},
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
def _large_message_item(index: int) -> dict:
|
|
return {
|
|
"role": "user" if index % 2 else "assistant",
|
|
"content": [
|
|
{
|
|
"type": "input_text",
|
|
"text": f"shared history item {index} " + ("long repeated agent context payload " * 120),
|
|
}
|
|
],
|
|
}
|
|
|
|
|
|
def _messages_record(index: int, messages: list[dict]) -> dict:
|
|
return {
|
|
"timestamp": f"2026-06-11T08:00:{index:02d}+00:00",
|
|
"turn": index,
|
|
"request": {
|
|
"method": "POST",
|
|
"path": "/v1/messages",
|
|
"body": {
|
|
"model": "claude-sonnet-4-6",
|
|
"messages": deepcopy(messages),
|
|
},
|
|
},
|
|
"response": {
|
|
"status": 200,
|
|
"body": {
|
|
"content": [{"type": "text", "text": f"ok {index}"}],
|
|
"usage": {"input_tokens": 1000 + index, "output_tokens": 12},
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
def _responses_input_record(index: int, input_items: list[dict]) -> dict:
|
|
return {
|
|
"timestamp": f"2026-06-11T08:01:{index:02d}+00:00",
|
|
"turn": index,
|
|
"request": {
|
|
"method": "POST",
|
|
"path": "/v1/responses",
|
|
"body": {
|
|
"model": "gpt-5.5",
|
|
"input": deepcopy(input_items),
|
|
},
|
|
},
|
|
"response": {
|
|
"status": 200,
|
|
"body": {
|
|
"output": [{"type": "message", "content": [{"type": "output_text", "text": f"done {index}"}]}],
|
|
"usage": {"input_tokens": 900 + index, "output_tokens": 9},
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
def _raw_record_payloads(db_path) -> list[dict]:
|
|
conn = sqlite3.connect(db_path)
|
|
rows = conn.execute("SELECT payload_json FROM records ORDER BY record_index").fetchall()
|
|
return [json.loads(row[0]) for row in rows]
|
|
|
|
|
|
def test_trace_store_compacts_repeated_instructions_and_tools(trace_db) -> None:
|
|
store = get_trace_store()
|
|
session_id = store.create_session(client="codex", proxy_mode="reverse")
|
|
instructions = "shared system instructions\n" * 800
|
|
tools = [
|
|
{
|
|
"type": "function",
|
|
"name": "shell",
|
|
"description": "shared shell tool description " * 300,
|
|
"parameters": {"type": "object", "properties": {"cmd": {"type": "string"}}},
|
|
}
|
|
]
|
|
records = [_large_codex_record(index, instructions=instructions, tools=tools) for index in range(1, 4)]
|
|
|
|
for record in records:
|
|
store.append_record(session_id, deepcopy(record))
|
|
|
|
assert store.load_records(session_id) == records
|
|
exported = [json.loads(line) for line in store.export_jsonl(session_id).splitlines()]
|
|
assert exported == records
|
|
|
|
raw_payloads = _raw_record_payloads(trace_db)
|
|
assert all(COMPACT_RECORD_MARKER in payload for payload in raw_payloads)
|
|
assert all(BLOB_REF_MARKER in json.dumps(payload) for payload in raw_payloads)
|
|
assert instructions not in json.dumps(raw_payloads, ensure_ascii=False)
|
|
|
|
conn = sqlite3.connect(trace_db)
|
|
blob_count = conn.execute("SELECT COUNT(*) FROM record_blobs").fetchone()[0]
|
|
# instructions and tools are shared across request/response and all records.
|
|
assert blob_count == 2
|
|
|
|
|
|
def test_compact_blobs_follow_session_lifecycle(trace_db) -> None:
|
|
store = get_trace_store()
|
|
first_session = store.create_session(client="codex", proxy_mode="reverse")
|
|
second_session = store.create_session(client="codex", proxy_mode="reverse")
|
|
instructions = "shared but session-scoped instructions\n" * 800
|
|
tools = [
|
|
{
|
|
"type": "function",
|
|
"name": "shell",
|
|
"description": "shared shell tool description " * 300,
|
|
"parameters": {"type": "object", "properties": {"cmd": {"type": "string"}}},
|
|
}
|
|
]
|
|
record = _large_codex_record(1, instructions=instructions, tools=tools)
|
|
|
|
store.append_record(first_session, deepcopy(record))
|
|
store.append_record(second_session, deepcopy(record))
|
|
|
|
conn = store._connect()
|
|
assert conn.execute("SELECT COUNT(*) FROM record_blobs WHERE session_id = ?", (first_session,)).fetchone()[0] == 2
|
|
assert conn.execute("SELECT COUNT(*) FROM record_blobs WHERE session_id = ?", (second_session,)).fetchone()[0] == 2
|
|
|
|
conn.execute("DELETE FROM sessions WHERE id = ?", (first_session,))
|
|
conn.commit()
|
|
|
|
assert conn.execute("SELECT COUNT(*) FROM record_blobs WHERE session_id = ?", (first_session,)).fetchone()[0] == 0
|
|
assert conn.execute("SELECT COUNT(*) FROM record_blobs WHERE session_id = ?", (second_session,)).fetchone()[0] == 2
|
|
assert store.load_records(second_session) == [record]
|
|
|
|
|
|
def test_trace_store_reads_legacy_full_payload_rows(trace_db) -> None:
|
|
store = get_trace_store()
|
|
session_id = store.create_session(client="codex", proxy_mode="reverse")
|
|
legacy_record = {
|
|
"timestamp": "2026-05-30T04:01:00+00:00",
|
|
"turn": 1,
|
|
"request": {"body": {"model": "gpt-5.5", "input": "legacy full row"}},
|
|
"response": {"body": {"output": [{"type": "message", "content": "ok"}]}},
|
|
}
|
|
conn = sqlite3.connect(trace_db)
|
|
conn.execute(
|
|
"""
|
|
INSERT INTO records (session_id, record_index, turn, timestamp, payload_json)
|
|
VALUES (?, 1, 1, ?, ?)
|
|
""",
|
|
(
|
|
session_id,
|
|
legacy_record["timestamp"],
|
|
json.dumps(legacy_record, ensure_ascii=False, separators=(",", ":")),
|
|
),
|
|
)
|
|
conn.commit()
|
|
|
|
assert store.load_records(session_id) == [legacy_record]
|
|
assert json.loads(store.export_jsonl(session_id)) == legacy_record
|
|
|
|
|
|
def test_trace_store_reads_legacy_compact_rows_without_refs(trace_db) -> None:
|
|
store = get_trace_store()
|
|
session_id = store.create_session(client="codex", proxy_mode="reverse")
|
|
instructions = "legacy shared instructions " * 200
|
|
tools = [
|
|
{
|
|
"type": "function",
|
|
"name": "legacy_shell",
|
|
"description": "legacy shared tool schema " * 120,
|
|
"parameters": {"type": "object", "properties": {"cmd": {"type": "string"}}},
|
|
}
|
|
]
|
|
fake_user_blob_ref = {
|
|
BLOB_REF_MARKER: {
|
|
"version": 1,
|
|
"kind": "json",
|
|
"hash": "sha256:user-controlled-marker-shape",
|
|
"bytes": 42,
|
|
}
|
|
}
|
|
legacy_record = _large_codex_record(1, instructions=instructions, tools=tools)
|
|
legacy_record["response"]["body"]["output"].append(
|
|
{
|
|
"type": "message",
|
|
"content": [
|
|
{
|
|
"type": "output_text",
|
|
"text": "preserve marker-shaped user payload",
|
|
"metadata": fake_user_blob_ref,
|
|
}
|
|
],
|
|
}
|
|
)
|
|
compact_payload = deepcopy(legacy_record)
|
|
conn = sqlite3.connect(trace_db)
|
|
for value in (instructions, tools):
|
|
payload_json, size_bytes, hash_value = json_blob_payload(value)
|
|
conn.execute(
|
|
"""
|
|
INSERT OR IGNORE INTO record_blobs (session_id, hash, kind, payload_json, size_bytes, created_at)
|
|
VALUES (?, ?, ?, ?, ?, '2026-06-11T08:02:00+00:00')
|
|
""",
|
|
(session_id, hash_value, BLOB_KIND_JSON, payload_json, size_bytes),
|
|
)
|
|
ref = make_blob_ref(hash_value, size_bytes)
|
|
for section in ("request", "response"):
|
|
target = compact_payload[section]["body"]
|
|
if target.get("instructions") == value:
|
|
target["instructions"] = ref
|
|
if target.get("tools") == value:
|
|
target["tools"] = ref
|
|
legacy_compact_payload = {
|
|
COMPACT_RECORD_MARKER: {
|
|
"version": 1,
|
|
"encoding": "json-blob-ref",
|
|
},
|
|
"record": compact_payload,
|
|
}
|
|
conn.execute(
|
|
"""
|
|
INSERT INTO records (session_id, record_index, turn, timestamp, payload_json)
|
|
VALUES (?, 1, 1, ?, ?)
|
|
""",
|
|
(
|
|
session_id,
|
|
legacy_record["timestamp"],
|
|
json.dumps(legacy_compact_payload, ensure_ascii=False, separators=(",", ":")),
|
|
),
|
|
)
|
|
conn.commit()
|
|
|
|
assert store.load_records(session_id) == [legacy_record]
|
|
assert [json.loads(line) for line in store.export_jsonl(session_id).splitlines()] == [legacy_record]
|
|
assert load_compact_trace(store.export_compact(session_id)) == [legacy_record]
|
|
|
|
|
|
def test_trace_store_migrates_v3_database_and_keeps_full_rows_readable(tmp_path) -> None:
|
|
db_path = tmp_path / "v3.sqlite3"
|
|
legacy_record = {
|
|
"timestamp": "2026-05-30T04:02:00+00:00",
|
|
"turn": 1,
|
|
"request": {"body": {"model": "gpt-5.5", "input": "v3 row"}},
|
|
"response": {"body": {"output": [{"type": "message", "content": "ok"}]}},
|
|
}
|
|
conn = sqlite3.connect(db_path)
|
|
conn.executescript(
|
|
"""
|
|
CREATE TABLE sessions (
|
|
id TEXT PRIMARY KEY,
|
|
started_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
date_key TEXT NOT NULL,
|
|
client TEXT NOT NULL DEFAULT '',
|
|
proxy_mode TEXT NOT NULL DEFAULT '',
|
|
status TEXT NOT NULL DEFAULT 'active',
|
|
record_count INTEGER NOT NULL DEFAULT 0,
|
|
summary_json TEXT,
|
|
legacy_source_key TEXT NOT NULL DEFAULT '',
|
|
legacy_rel_path TEXT
|
|
);
|
|
CREATE TABLE records (
|
|
session_id TEXT NOT NULL,
|
|
record_index INTEGER NOT NULL,
|
|
turn INTEGER,
|
|
timestamp TEXT,
|
|
payload_json TEXT NOT NULL,
|
|
PRIMARY KEY (session_id, record_index)
|
|
);
|
|
CREATE TABLE proxy_logs (
|
|
session_id TEXT NOT NULL,
|
|
line_no INTEGER NOT NULL,
|
|
logged_at TEXT,
|
|
level TEXT,
|
|
message TEXT NOT NULL,
|
|
PRIMARY KEY (session_id, line_no)
|
|
);
|
|
CREATE TABLE migration_state (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL
|
|
);
|
|
PRAGMA user_version = 3;
|
|
"""
|
|
)
|
|
conn.execute(
|
|
"""
|
|
INSERT INTO sessions (id, started_at, updated_at, date_key, client, proxy_mode, status, record_count)
|
|
VALUES ('legacy-session', '2026-05-30T04:02:00+00:00', '2026-05-30T04:02:00+00:00', '2026-05-30', 'codex', 'reverse', 'complete', 1)
|
|
"""
|
|
)
|
|
conn.execute(
|
|
"""
|
|
INSERT INTO records (session_id, record_index, turn, timestamp, payload_json)
|
|
VALUES ('legacy-session', 1, 1, '2026-05-30T04:02:00+00:00', ?)
|
|
""",
|
|
(json.dumps(legacy_record, ensure_ascii=False, separators=(",", ":")),),
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
store = TraceStore(db_path)
|
|
assert store.load_records("legacy-session") == [legacy_record]
|
|
with sqlite3.connect(db_path) as migrated:
|
|
assert migrated.execute("PRAGMA user_version").fetchone()[0] == 4
|
|
assert migrated.execute("SELECT COUNT(*) FROM record_blobs").fetchone()[0] == 0
|
|
|
|
|
|
def test_compact_storage_reduces_large_trace_payload_and_preserves_roundtrip(trace_db) -> None:
|
|
store = get_trace_store()
|
|
session_id = store.create_session(client="codex", proxy_mode="reverse")
|
|
instructions = "repeatable instructions block " * 2000
|
|
tools = [
|
|
{
|
|
"type": "function",
|
|
"name": f"tool_{tool_index}",
|
|
"description": "repeatable tool schema " * 500,
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {"value": {"type": "string"}},
|
|
"required": ["value"],
|
|
},
|
|
}
|
|
for tool_index in range(4)
|
|
]
|
|
records = [_large_codex_record(index, instructions=instructions, tools=tools) for index in range(1, 81)]
|
|
raw_jsonl = "".join(json.dumps(record, ensure_ascii=False, separators=(",", ":")) + "\n" for record in records)
|
|
|
|
for record in records:
|
|
store.append_record(session_id, deepcopy(record))
|
|
|
|
conn = sqlite3.connect(trace_db)
|
|
stored_payload_bytes = conn.execute("SELECT SUM(LENGTH(payload_json)) FROM records").fetchone()[0]
|
|
blob_payload_bytes = conn.execute("SELECT SUM(size_bytes) FROM record_blobs").fetchone()[0]
|
|
compact_total = stored_payload_bytes + blob_payload_bytes
|
|
|
|
assert store.load_records(session_id) == records
|
|
assert [json.loads(line) for line in store.export_jsonl(session_id).splitlines()] == records
|
|
assert compact_total < len(raw_jsonl.encode("utf-8")) * 0.15
|
|
assert conn.execute("SELECT COUNT(*) FROM record_blobs").fetchone()[0] == 2
|
|
|
|
|
|
def test_trace_store_compacts_repeated_messages_without_prefix_dependency(trace_db) -> None:
|
|
store = get_trace_store()
|
|
session_id = store.create_session(client="claude", proxy_mode="reverse")
|
|
first = _large_message_item(1)
|
|
second = _large_message_item(2)
|
|
third = _large_message_item(3)
|
|
fourth = _large_message_item(4)
|
|
fifth = _large_message_item(5)
|
|
records = [
|
|
_messages_record(1, [first, second]),
|
|
_messages_record(2, [third, first, second]),
|
|
# This is intentionally not an append-only prefix of the previous request.
|
|
_messages_record(3, [fourth, second, first, third]),
|
|
_messages_record(4, [first, fourth, second, third]),
|
|
_messages_record(5, [fifth, first, fourth, second, third]),
|
|
]
|
|
raw_jsonl = "".join(json.dumps(record, ensure_ascii=False, separators=(",", ":")) + "\n" for record in records)
|
|
|
|
for record in records:
|
|
store.append_record(session_id, deepcopy(record))
|
|
|
|
assert store.load_records(session_id) == records
|
|
assert store.load_records(session_id, limit=1, offset=1) == [records[1]]
|
|
assert [json.loads(line) for line in store.export_jsonl(session_id).splitlines()] == records
|
|
|
|
raw_payloads = _raw_record_payloads(trace_db)
|
|
encoded_payloads = json.dumps(raw_payloads, ensure_ascii=False)
|
|
assert all(COMPACT_RECORD_MARKER in payload for payload in raw_payloads)
|
|
assert BLOB_REF_MARKER in encoded_payloads
|
|
assert "shared history item 1" not in encoded_payloads
|
|
|
|
conn = sqlite3.connect(trace_db)
|
|
stored_payload_bytes = conn.execute("SELECT SUM(LENGTH(payload_json)) FROM records").fetchone()[0]
|
|
blob_payload_bytes = conn.execute("SELECT SUM(size_bytes) FROM record_blobs").fetchone()[0]
|
|
compact_total = stored_payload_bytes + blob_payload_bytes
|
|
|
|
assert compact_total < len(raw_jsonl.encode("utf-8")) * 0.45
|
|
assert conn.execute("SELECT COUNT(*) FROM record_blobs").fetchone()[0] == 5
|
|
|
|
|
|
def test_trace_store_compacts_repeated_responses_input_items(trace_db) -> None:
|
|
store = get_trace_store()
|
|
session_id = store.create_session(client="codex", proxy_mode="reverse")
|
|
shared_context = _large_message_item(1)
|
|
tool_result = {
|
|
"type": "function_call_output",
|
|
"call_id": "call_shared",
|
|
"output": "large tool result " * 400,
|
|
}
|
|
records = [
|
|
_responses_input_record(1, [shared_context, tool_result]),
|
|
_responses_input_record(2, [shared_context, tool_result, _large_message_item(2)]),
|
|
]
|
|
|
|
for record in records:
|
|
store.append_record(session_id, deepcopy(record))
|
|
|
|
assert store.load_records(session_id) == records
|
|
raw_payloads = _raw_record_payloads(trace_db)
|
|
encoded_payloads = json.dumps(raw_payloads, ensure_ascii=False)
|
|
assert BLOB_REF_MARKER in encoded_payloads
|
|
assert "large tool result" not in encoded_payloads
|
|
|
|
conn = sqlite3.connect(trace_db)
|
|
assert conn.execute("SELECT COUNT(*) FROM record_blobs").fetchone()[0] == 3
|
|
|
|
|
|
def test_compact_records_preserve_user_blob_ref_shaped_payloads(trace_db) -> None:
|
|
store = get_trace_store()
|
|
session_id = store.create_session(client="codex", proxy_mode="reverse")
|
|
fake_user_blob_ref = {
|
|
BLOB_REF_MARKER: {
|
|
"version": 1,
|
|
"kind": "json",
|
|
"hash": "sha256:user-controlled-marker-shape",
|
|
"bytes": 42,
|
|
}
|
|
}
|
|
shared_context = _large_message_item(1)
|
|
records = [
|
|
_responses_input_record(
|
|
1,
|
|
[
|
|
shared_context,
|
|
{
|
|
"type": "function_call_output",
|
|
"call_id": "call_marker",
|
|
"output": fake_user_blob_ref,
|
|
},
|
|
],
|
|
),
|
|
_responses_input_record(
|
|
2,
|
|
[
|
|
shared_context,
|
|
{
|
|
"role": "user",
|
|
"content": [
|
|
{
|
|
"type": "input_text",
|
|
"text": "second turn keeps a user supplied marker-shaped object",
|
|
"metadata": fake_user_blob_ref,
|
|
}
|
|
],
|
|
},
|
|
],
|
|
),
|
|
]
|
|
|
|
for record in records:
|
|
store.append_record(session_id, deepcopy(record))
|
|
|
|
raw_payloads = _raw_record_payloads(trace_db)
|
|
assert any(COMPACT_RECORD_MARKER in payload for payload in raw_payloads)
|
|
assert store.load_records(session_id) == records
|
|
assert [json.loads(line) for line in store.export_jsonl(session_id).splitlines()] == records
|
|
assert load_compact_trace(store.export_compact(session_id)) == records
|
|
|
|
|
|
def test_trace_store_keeps_small_messages_inline(trace_db) -> None:
|
|
store = get_trace_store()
|
|
session_id = store.create_session(client="claude", proxy_mode="reverse")
|
|
record = _messages_record(1, [{"role": "user", "content": "hi"}])
|
|
|
|
store.append_record(session_id, deepcopy(record))
|
|
|
|
assert store.load_records(session_id) == [record]
|
|
raw_payloads = _raw_record_payloads(trace_db)
|
|
assert COMPACT_RECORD_MARKER not in raw_payloads[0]
|
|
assert BLOB_REF_MARKER not in json.dumps(raw_payloads, ensure_ascii=False)
|
|
|
|
conn = sqlite3.connect(trace_db)
|
|
assert conn.execute("SELECT COUNT(*) FROM record_blobs").fetchone()[0] == 0
|
|
|
|
|
|
def test_trace_store_message_item_compaction_scales_to_long_reordered_histories(trace_db) -> None:
|
|
store = get_trace_store()
|
|
session_id = store.create_session(client="codex", proxy_mode="reverse")
|
|
shared_items = [_large_message_item(index) for index in range(1, 21)]
|
|
records = []
|
|
for turn in range(1, 61):
|
|
history = [shared_items[(turn + step * 7) % len(shared_items)] for step in range(14)]
|
|
if turn % 3 == 0:
|
|
history = list(reversed(history))
|
|
records.append(_responses_input_record(turn, history))
|
|
raw_jsonl = "".join(json.dumps(record, ensure_ascii=False, separators=(",", ":")) + "\n" for record in records)
|
|
|
|
for record in records:
|
|
store.append_record(session_id, deepcopy(record))
|
|
|
|
conn = sqlite3.connect(trace_db)
|
|
stored_payload_bytes = conn.execute("SELECT SUM(LENGTH(payload_json)) FROM records").fetchone()[0]
|
|
blob_payload_bytes = conn.execute("SELECT SUM(size_bytes) FROM record_blobs").fetchone()[0]
|
|
compact_total = stored_payload_bytes + blob_payload_bytes
|
|
|
|
assert store.load_records(session_id) == records
|
|
assert store.load_records(session_id, limit=3, offset=41) == records[41:44]
|
|
assert [json.loads(line) for line in store.export_jsonl(session_id).splitlines()] == records
|
|
assert load_compact_trace(store.export_compact(session_id)) == records
|
|
assert compact_total < len(raw_jsonl.encode("utf-8")) * 0.2
|
|
assert conn.execute("SELECT COUNT(*) FROM record_blobs").fetchone()[0] == len(shared_items)
|