"""Summarize model request latency and context size without reading prompt bodies.""" import argparse from collections import Counter import json import math from pathlib import Path import statistics def summarize(path: Path) -> dict: requests = {} for line in path.read_text(encoding="utf-8").splitlines(): try: event = json.loads(line) except ValueError: continue number = event.get("request") if not isinstance(number, int): continue row = requests.setdefault(number, {"request": number}) kind = event.get("event") if kind == "request": row.update(start=event["seconds"], requestBytes=event["bytes"]) elif kind == "context": row.update({key: event.get(key) for key in ("inputItems", "tools", "toolOutputBytes")}) elif kind == "usage": row["inputTokens"] = event["inputTokens"] elif kind == "retry": row["retries"] = row.get("retries", 0) + 1 elif kind in {"error", "http_error", "cancelled", "context_rejected"}: row["outcome"] = event.get("errorType") or kind elif kind == "end": row["outcome"] = event.get("terminal") or "incomplete_stream" if "start" in row: elapsed = round(event["seconds"] - row["start"], 3) if kind == "headers": row["headerSeconds"] = elapsed elif kind == "first_body": row["firstBodySeconds"] = elapsed elif kind in {"end", "error", "http_error", "cancelled"}: row["totalSeconds"] = elapsed rows = [{key: value for key, value in row.items() if key != "start"} for row in requests.values()] complete = [row for row in rows if row.get("outcome") == "response.completed"] def distribution(key): values = sorted(row[key] for row in complete if isinstance(row.get(key), (int, float))) if not values: return {"measured": 0} return {"measured": len(values), "min": values[0], "median": statistics.median(values), "p95": values[math.ceil(len(values) * 0.95) - 1], "max": values[-1]} overlapping = [] ordered = sorted((row for row in requests.values() if "start" in row), key=lambda row: row["start"]) for index, row in enumerate(ordered): end = row["start"] + row.get("totalSeconds", 0) for later in ordered[index + 1:]: if later["start"] >= end: break overlapping.append([row["request"], later["request"]]) return {"schema": "freecad-web-model-transport-analysis/v1", "source": str(path), "requests": len(rows), "outcomes": dict(Counter(row.get("outcome", "pending") for row in rows)), "successfulRequests": len(complete), "successfulLatency": {key: distribution(key) for key in ("headerSeconds", "firstBodySeconds", "totalSeconds", "requestBytes", "inputTokens")}, "overlappingRequestPairs": overlapping, "rows": rows, "scope": "Log observations only. Bytes are not tokens; completed model responses are not completed business features. Overlap and BrokenPipe alone do not establish the cancellation cause."} def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("log", type=Path) parser.add_argument("--output", type=Path) args = parser.parse_args() report = summarize(args.log) if args.output: args.output.parent.mkdir(parents=True, exist_ok=True) args.output.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") print(json.dumps({key: value for key, value in report.items() if key != "rows"}, ensure_ascii=False)) if __name__ == "__main__": main()