77
88
99class TraceStore (Protocol ):
10- def write_event (self , event : Any ) -> None :
10+ """Storage contract used by the local viewer and trace sinks.
11+
12+ Implementations may store raw trace_event records, legacy run snapshots,
13+ or both. Read methods return reconstructed run dictionaries and raw event
14+ records so the viewer API does not depend on the physical storage backend.
15+ """
16+
17+ def append_event (self , event : Any ) -> None :
18+ ...
19+
20+ def append_events (self , events : list [Any ]) -> None :
21+ ...
22+
23+ def append_record (self , record : dict [str , Any ]) -> None :
24+ ...
25+
26+ def append_records (self , records : list [dict [str , Any ]]) -> None :
27+ ...
28+
29+ def import_jsonl (self , content : str ) -> int :
30+ ...
31+
32+ def list_records (self ) -> list [dict [str , Any ]]:
1133 ...
1234
1335 def list_runs (self ) -> list [dict [str , Any ]]:
@@ -21,13 +43,38 @@ def list_events(self, trace_id: str) -> list[dict[str, Any]]:
2143
2244
2345class JsonlTraceStore :
46+ """TraceStore implementation backed by a local JSONL file."""
47+
2448 def __init__ (self , path : str | Path = ".aisuite/events.jsonl" ):
2549 self .path = Path (path )
2650
27- def write_event (self , event : Any ) -> None :
51+ def append_event (self , event : Any ) -> None :
52+ self .append_record (event .to_dict ())
53+
54+ def append_events (self , events : list [Any ]) -> None :
55+ self .append_records ([event .to_dict () for event in events ])
56+
57+ def append_record (self , record : dict [str , Any ]) -> None :
58+ self .append_records ([record ])
59+
60+ def append_records (self , records : list [dict [str , Any ]]) -> None :
61+ if not records :
62+ return
2863 self .path .parent .mkdir (parents = True , exist_ok = True )
2964 with self .path .open ("a" , encoding = "utf-8" ) as handle :
30- handle .write (json .dumps (event .to_dict ()) + "\n " )
65+ for record in records :
66+ handle .write (json .dumps (record ) + "\n " )
67+
68+ def write_event (self , event : Any ) -> None :
69+ self .append_event (event )
70+
71+ def write_record (self , record : dict [str , Any ]) -> None :
72+ self .append_record (record )
73+
74+ def import_jsonl (self , content : str ) -> int :
75+ records = parse_jsonl_records (content )
76+ self .append_records (records )
77+ return len (records )
3178
3279 def list_records (self ) -> list [dict [str , Any ]]:
3380 if not self .path .exists ():
@@ -63,6 +110,64 @@ def list_events(self, trace_id: str) -> list[dict[str, Any]]:
63110 ]
64111
65112
113+ class InMemoryTraceStore :
114+ """TraceStore implementation backed by an in-memory record list."""
115+
116+ def __init__ (self , records : Optional [list [dict [str , Any ]]] = None ):
117+ self .records = list (records or [])
118+
119+ def append_event (self , event : Any ) -> None :
120+ self .append_record (event .to_dict ())
121+
122+ def append_events (self , events : list [Any ]) -> None :
123+ self .append_records ([event .to_dict () for event in events ])
124+
125+ def append_record (self , record : dict [str , Any ]) -> None :
126+ self .append_records ([record ])
127+
128+ def append_records (self , records : list [dict [str , Any ]]) -> None :
129+ self .records .extend (copy .deepcopy (records ))
130+
131+ def import_jsonl (self , content : str ) -> int :
132+ records = parse_jsonl_records (content )
133+ self .append_records (records )
134+ return len (records )
135+
136+ def list_records (self ) -> list [dict [str , Any ]]:
137+ return copy .deepcopy (self .records )
138+
139+ def list_runs (self ) -> list [dict [str , Any ]]:
140+ return reconstruct_runs (self .list_records ())
141+
142+ def get_run (self , trace_id : str ) -> Optional [dict [str , Any ]]:
143+ for run in self .list_runs ():
144+ if run .get ("trace_id" ) == trace_id :
145+ return run
146+ return None
147+
148+ def list_events (self , trace_id : str ) -> list [dict [str , Any ]]:
149+ return [
150+ record
151+ for record in self .list_records ()
152+ if record .get ("record_type" ) == "trace_event"
153+ and record .get ("trace_id" ) == trace_id
154+ ]
155+
156+
157+ def parse_jsonl_records (content : str ) -> list [dict [str , Any ]]:
158+ records = []
159+ for line in content .splitlines ():
160+ line = line .strip ()
161+ if not line :
162+ continue
163+ try :
164+ record = json .loads (line )
165+ except json .JSONDecodeError :
166+ continue
167+ records .append (record )
168+ return records
169+
170+
66171def reconstruct_runs (records : list [dict [str , Any ]]) -> list [dict [str , Any ]]:
67172 snapshots = []
68173 events_by_trace_id : dict [str , list [dict [str , Any ]]] = {}
0 commit comments