Files
FreeCADWasmDev/tools/analyze_model_transport.py
wangdequan 9a7c4f0335 Save FreeCAD Web workspace sources and user validation evidence
IFC native binding remains incomplete; preserve accurate test results.
2026-09-15 22:01:58 +08:00

80 lines
3.7 KiB
Python

"""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()