5353 "$switch_model" ,
5454 "$retry_context" ,
5555 "$post_accept_edit" ,
56+ "$sessionend" ,
5657 "$blur" ,
5758 "$return" ,
5859 "$scroll_regression" ,
@@ -106,6 +107,32 @@ def event(self, event_type: EventType, **metadata: object) -> None:
106107 metadata = merged if merged else None ,
107108 )
108109
110+ def edit (self , before : str , after : str , ** metadata : object ) -> None :
111+ """Record that the user modified the output before using it.
112+
113+ Send the raw before/after text. The backend computes edit distance,
114+ diff classification, and derived metrics.
115+ """
116+ self .event ("$edit" , before = before , after = after , ** metadata )
117+
118+ def accept (self , ** metadata : object ) -> None :
119+ """User used the output as-is."""
120+ self .event ("$accept" , ** metadata )
121+
122+ def copy (self , ** metadata : object ) -> None :
123+ """User copied the output."""
124+ self .event ("$copy" , ** metadata )
125+
126+ def regenerate (self , ** metadata : object ) -> None :
127+ """User requested a new output. Fire BEFORE creating the next generation."""
128+ self .event ("$regenerate" , ** metadata )
129+
130+ def share (self , channel : str | None = None , ** metadata : object ) -> None :
131+ """User shared the output."""
132+ if channel :
133+ metadata ["channel" ] = channel
134+ self .event ("$share" , ** metadata )
135+
109136
110137class Feature :
111138 """Scoped handle for an AI feature. Carries defaults so you don't
@@ -271,6 +298,14 @@ def track(
271298 generation_id : str | None = None ,
272299 metadata : dict | None = None ,
273300 timestamp : datetime | None = None ,
301+ model : str | None = None ,
302+ provider : str | None = None ,
303+ input_tokens : int | None = None ,
304+ output_tokens : int | None = None ,
305+ total_tokens : int | None = None ,
306+ duration_ms : int | None = None ,
307+ ttft_ms : int | None = None ,
308+ cost : float | None = None ,
274309 ) -> str | None :
275310 """Enqueue a single event. Returns the event UUID or None if dropped."""
276311 if self .disabled :
@@ -294,6 +329,23 @@ def track(
294329 if generation_id :
295330 msg ["generation_id" ] = generation_id
296331
332+ if model :
333+ msg ["model" ] = model
334+ if provider :
335+ msg ["provider" ] = provider
336+ if input_tokens is not None :
337+ msg ["input_tokens" ] = input_tokens
338+ if output_tokens is not None :
339+ msg ["output_tokens" ] = output_tokens
340+ if total_tokens is not None :
341+ msg ["total_tokens" ] = total_tokens
342+ if duration_ms is not None :
343+ msg ["duration_ms" ] = duration_ms
344+ if ttft_ms is not None :
345+ msg ["ttft_ms" ] = ttft_ms
346+ if cost is not None :
347+ msg ["cost" ] = cost
348+
297349 props = {"$lib" : "litmus-python" , "$lib_version" : VERSION }
298350 if metadata :
299351 props .update (metadata )
@@ -328,10 +380,25 @@ def generation(
328380 prompt_id : str | None = None ,
329381 prompt_version : str | None = None ,
330382 model : str | None = None ,
383+ provider : str | None = None ,
384+ input_tokens : int | None = None ,
385+ output_tokens : int | None = None ,
386+ total_tokens : int | None = None ,
387+ duration_ms : int | None = None ,
388+ ttft_ms : int | None = None ,
389+ cost : float | None = None ,
331390 metadata : dict | None = None ,
391+ generation_id : str | None = None ,
332392 ) -> Generation :
333- """Create a generation and return a handle for recording signals."""
334- generation_id = str (uuid4 ())
393+ """Create a generation and return a handle for recording signals.
394+
395+ Pass ``generation_id`` when the caller has already minted an id
396+ (e.g. to correlate with an OTel/OpenRouter trace broadcast). When
397+ omitted, a fresh UUID4 is used. The id is emitted on the
398+ ``$generation`` event, so downstream joins work either way.
399+ """
400+ if generation_id is None :
401+ generation_id = str (uuid4 ())
335402 defaults = {
336403 "user_id" : user_id ,
337404 "prompt_id" : prompt_id ,
@@ -348,6 +415,14 @@ def generation(
348415 prompt_id = prompt_id ,
349416 prompt_version = prompt_version ,
350417 metadata = metadata ,
418+ model = model ,
419+ provider = provider ,
420+ input_tokens = input_tokens ,
421+ output_tokens = output_tokens ,
422+ total_tokens = total_tokens ,
423+ duration_ms = duration_ms ,
424+ ttft_ms = ttft_ms ,
425+ cost = cost ,
351426 )
352427
353428 return Generation (self , session_id , generation_id , defaults )
0 commit comments