Initial commit: grok-free-register-oss
Open-source Grok free registration CLI, xai_enroller auth pipeline, local auth service, tests and docs.
This commit is contained in:
419
tools/runtime_log_analyzer.py
Normal file
419
tools/runtime_log_analyzer.py
Normal file
@@ -0,0 +1,419 @@
|
||||
"""
|
||||
Parse real runtime monitor logs from both CSP and legacy state-machine runs.
|
||||
|
||||
The analyzer is intentionally read-only. It does not tune parameters or modify
|
||||
runtime state; it only turns production logs into comparable stage rates.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
import json
|
||||
import re
|
||||
from typing import Iterable
|
||||
|
||||
|
||||
_CSP_RE = re.compile(
|
||||
r"^\[\*\] T:(?P<t>\d+) Q:(?P<q>\d+) phys:(?P<phys>\d+) "
|
||||
r"(?:p_send:(?P<p_send>\d+) )?t_slot:(?P<t_slot>\d+) q_slot:(?P<q_slot>\d+) q_pend:(?P<q_pend>\d+)"
|
||||
r"(?P<rest>.*)rate:(?P<rate>[0-9.]+)/min #(?P<ok>\d+)"
|
||||
)
|
||||
|
||||
_STATE_RE = re.compile(
|
||||
r"^\[\*\] slots:(?P<slots>\d+)/(?P<max_slots>\d+) act:(?P<active>\d+) "
|
||||
r"cpu:(?P<cpu>[0-9.]+)% avg:(?P<cpu_avg>[0-9.]+)%/(?P<cpu_target>[0-9.]+) "
|
||||
r"mem:(?P<mem>\d+)M T:(?P<t>\d+) Q:(?P<q>\d+) "
|
||||
r"sent:(?P<q_sent>\d+) got:(?P<q_ret>\d+)\((?P<q_hit>[0-9.]+)%\) "
|
||||
r"rate:(?P<rate>[0-9.]+)/min #(?P<ok>\d+)"
|
||||
)
|
||||
|
||||
_SOLVER_TIMELINE_PREFIX = "[solver_timeline] "
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MonitorRow:
|
||||
kind: str
|
||||
t: int
|
||||
q: int
|
||||
ok: int
|
||||
rate: float
|
||||
phys: int | None = None
|
||||
p_send: int | None = None
|
||||
t_slot: int | None = None
|
||||
q_slot: int | None = None
|
||||
q_pend: int | None = None
|
||||
p_batch: float | None = None
|
||||
t_prog: int | None = None
|
||||
q_inflight: int | None = None
|
||||
t_prod: int | None = None
|
||||
q_sent: int | None = None
|
||||
q_ret: int | None = None
|
||||
q_adm: int | None = None
|
||||
pair: int | None = None
|
||||
fail: int | None = None
|
||||
s_phys_wait: float | None = None
|
||||
s_phys_hold: float | None = None
|
||||
p_phys_wait: float | None = None
|
||||
p_phys_hold: float | None = None
|
||||
c_phys_wait: float | None = None
|
||||
c_phys_hold: float | None = None
|
||||
p_email_create: float | None = None
|
||||
p_page_prepare: float | None = None
|
||||
p_send_stage: float | None = None
|
||||
c_page_acquire: float | None = None
|
||||
c_verify: float | None = None
|
||||
c_register: float | None = None
|
||||
c_hot_hits: int | None = None
|
||||
c_hot_misses: int | None = None
|
||||
solver_goto: float | None = None
|
||||
solver_inject: float | None = None
|
||||
solver_initial: float | None = None
|
||||
solver_click: float | None = None
|
||||
solver_wait: float | None = None
|
||||
solver_reuse: float | None = None
|
||||
solver_visible: float | None = None
|
||||
slots: int | None = None
|
||||
max_slots: int | None = None
|
||||
active: int | None = None
|
||||
|
||||
@property
|
||||
def elapsed_min(self) -> float | None:
|
||||
if self.ok <= 0 or self.rate <= 0:
|
||||
return None
|
||||
return self.ok / self.rate
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SolverTimeline:
|
||||
events: list[dict]
|
||||
|
||||
|
||||
def _int_field(rest: str, name: str) -> int | None:
|
||||
match = re.search(rf"\b{name}:(\d+)", rest)
|
||||
return int(match.group(1)) if match else None
|
||||
|
||||
|
||||
def _float_field(rest: str, name: str) -> float | None:
|
||||
match = re.search(rf"\b{name}:([0-9.]+)", rest)
|
||||
return float(match.group(1)) if match else None
|
||||
|
||||
|
||||
def _float_pair_field(rest: str, name: str) -> tuple[float | None, float | None]:
|
||||
match = re.search(rf"\b{name}:([0-9.]+)/([0-9.]+)", rest)
|
||||
if not match:
|
||||
return None, None
|
||||
return float(match.group(1)), float(match.group(2))
|
||||
|
||||
|
||||
def _float_triple_field(rest: str, name: str) -> tuple[float | None, float | None, float | None]:
|
||||
match = re.search(rf"\b{name}:([0-9.]+)/([0-9.]+)/([0-9.]+)", rest)
|
||||
if not match:
|
||||
return None, None, None
|
||||
return float(match.group(1)), float(match.group(2)), float(match.group(3))
|
||||
|
||||
|
||||
def _int_pair_field(rest: str, name: str) -> tuple[int | None, int | None]:
|
||||
match = re.search(rf"\b{name}:(\d+)/(\d+)", rest)
|
||||
if not match:
|
||||
return None, None
|
||||
return int(match.group(1)), int(match.group(2))
|
||||
|
||||
|
||||
def parse_monitor_lines(text_or_lines: str | Iterable[str]) -> list[MonitorRow]:
|
||||
if isinstance(text_or_lines, str):
|
||||
lines = text_or_lines.splitlines()
|
||||
else:
|
||||
lines = list(text_or_lines)
|
||||
|
||||
rows: list[MonitorRow] = []
|
||||
for line in lines:
|
||||
csp = _CSP_RE.match(line)
|
||||
if csp:
|
||||
rest = csp.group("rest")
|
||||
s_phys_wait, s_phys_hold = _float_pair_field(rest, "s_phys")
|
||||
p_phys_wait, p_phys_hold = _float_pair_field(rest, "p_phys")
|
||||
c_phys_wait, c_phys_hold = _float_pair_field(rest, "c_phys")
|
||||
p_email_create, p_page_prepare, p_send_stage = _float_triple_field(rest, "p_stage")
|
||||
c_page_acquire, c_verify, c_register = _float_triple_field(rest, "c_stage")
|
||||
c_hot_hits, c_hot_misses = _int_pair_field(rest, "c_hot")
|
||||
rows.append(
|
||||
MonitorRow(
|
||||
kind="csp",
|
||||
t=int(csp.group("t")),
|
||||
q=int(csp.group("q")),
|
||||
ok=int(csp.group("ok")),
|
||||
rate=float(csp.group("rate")),
|
||||
phys=int(csp.group("phys")),
|
||||
p_send=int(csp.group("p_send")) if csp.group("p_send") is not None else None,
|
||||
t_slot=int(csp.group("t_slot")),
|
||||
q_slot=int(csp.group("q_slot")),
|
||||
q_pend=int(csp.group("q_pend")),
|
||||
p_batch=_float_field(rest, "p_batch"),
|
||||
t_prog=_int_field(rest, "t_prog"),
|
||||
q_inflight=_int_field(rest, "q_inflight"),
|
||||
t_prod=_int_field(rest, "t_prod"),
|
||||
q_sent=_int_field(rest, "q_sent"),
|
||||
q_ret=_int_field(rest, "q_ret"),
|
||||
q_adm=_int_field(rest, "q_adm"),
|
||||
pair=_int_field(rest, "pair"),
|
||||
fail=_int_field(rest, "fail"),
|
||||
s_phys_wait=s_phys_wait,
|
||||
s_phys_hold=s_phys_hold,
|
||||
p_phys_wait=p_phys_wait,
|
||||
p_phys_hold=p_phys_hold,
|
||||
c_phys_wait=c_phys_wait,
|
||||
c_phys_hold=c_phys_hold,
|
||||
p_email_create=p_email_create,
|
||||
p_page_prepare=p_page_prepare,
|
||||
p_send_stage=p_send_stage,
|
||||
c_page_acquire=c_page_acquire,
|
||||
c_verify=c_verify,
|
||||
c_register=c_register,
|
||||
c_hot_hits=c_hot_hits,
|
||||
c_hot_misses=c_hot_misses,
|
||||
solver_goto=_float_field(rest, "solver_goto"),
|
||||
solver_inject=_float_field(rest, "solver_inject"),
|
||||
solver_initial=_float_field(rest, "solver_initial"),
|
||||
solver_click=_float_field(rest, "solver_click"),
|
||||
solver_wait=_float_field(rest, "solver_wait"),
|
||||
solver_reuse=_float_field(rest, "solver_reuse"),
|
||||
solver_visible=_float_field(rest, "solver_visible"),
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
state = _STATE_RE.match(line)
|
||||
if state:
|
||||
rows.append(
|
||||
MonitorRow(
|
||||
kind="state_machine",
|
||||
t=int(state.group("t")),
|
||||
q=int(state.group("q")),
|
||||
ok=int(state.group("ok")),
|
||||
rate=float(state.group("rate")),
|
||||
q_sent=int(state.group("q_sent")),
|
||||
q_ret=int(state.group("q_ret")),
|
||||
slots=int(state.group("slots")),
|
||||
max_slots=int(state.group("max_slots")),
|
||||
active=int(state.group("active")),
|
||||
)
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def _rate_delta(rows: list[MonitorRow], attr: str) -> float | None:
|
||||
candidates = [r for r in rows if r.elapsed_min is not None and getattr(r, attr) is not None]
|
||||
if len(candidates) < 2:
|
||||
return None
|
||||
first = candidates[0]
|
||||
last = candidates[-1]
|
||||
dt = (last.elapsed_min or 0) - (first.elapsed_min or 0)
|
||||
if dt <= 0:
|
||||
return None
|
||||
return (getattr(last, attr) - getattr(first, attr)) / dt
|
||||
|
||||
|
||||
def _leader(values: dict[str, float | None]) -> str | None:
|
||||
if any(value is None for value in values.values()):
|
||||
return None
|
||||
return max(values, key=lambda key: values[key] or 0)
|
||||
|
||||
|
||||
def _avg(values: list[float]) -> float | None:
|
||||
if not values:
|
||||
return None
|
||||
return round(sum(values) / len(values), 2)
|
||||
|
||||
|
||||
def _ratio(numerator: int, denominator: int) -> float | None:
|
||||
if denominator <= 0:
|
||||
return None
|
||||
return round(numerator / denominator, 3)
|
||||
|
||||
|
||||
def summarize_monitor_rows(rows: list[MonitorRow], recent_count: int = 6) -> dict[str, float | int | str | None]:
|
||||
if not rows:
|
||||
return {"rows": 0}
|
||||
|
||||
last = rows[-1]
|
||||
recent = rows[-recent_count:]
|
||||
summary: dict[str, float | int | str | None] = {
|
||||
"rows": len(rows),
|
||||
"kind": last.kind,
|
||||
"last_ok": last.ok,
|
||||
"last_cumulative_rate": last.rate,
|
||||
"last_q_minus_t": last.q - last.t,
|
||||
"last_phys": last.phys,
|
||||
"last_p_send_sem": last.p_send,
|
||||
"last_t_slot": last.t_slot,
|
||||
"last_q_slot": last.q_slot,
|
||||
"last_q_pend": last.q_pend,
|
||||
"last_p_batch": last.p_batch,
|
||||
"last_t_prog": last.t_prog,
|
||||
"last_q_inflight": last.q_inflight,
|
||||
"last_q_return_minus_t_prod": (
|
||||
last.q_ret - last.t_prod
|
||||
if last.q_ret is not None and last.t_prod is not None
|
||||
else None
|
||||
),
|
||||
"last_slots": last.slots,
|
||||
"last_t_prod": last.t_prod,
|
||||
"last_q_ret": last.q_ret,
|
||||
"last_q_adm": last.q_adm,
|
||||
"last_pair": last.pair,
|
||||
"last_fail": last.fail,
|
||||
"last_s_phys_wait": last.s_phys_wait,
|
||||
"last_s_phys_hold": last.s_phys_hold,
|
||||
"last_p_phys_wait": last.p_phys_wait,
|
||||
"last_p_phys_hold": last.p_phys_hold,
|
||||
"last_c_phys_wait": last.c_phys_wait,
|
||||
"last_c_phys_hold": last.c_phys_hold,
|
||||
"last_p_email_create": last.p_email_create,
|
||||
"last_p_page_prepare": last.p_page_prepare,
|
||||
"last_p_send": last.p_send_stage,
|
||||
"last_c_page_acquire": last.c_page_acquire,
|
||||
"last_c_verify": last.c_verify,
|
||||
"last_c_register": last.c_register,
|
||||
"last_c_hot_hits": last.c_hot_hits,
|
||||
"last_c_hot_misses": last.c_hot_misses,
|
||||
"last_physical_hold_leader": _leader({
|
||||
"s": last.s_phys_hold,
|
||||
"p": last.p_phys_hold,
|
||||
"c": last.c_phys_hold,
|
||||
}),
|
||||
"last_physical_wait_leader": _leader({
|
||||
"s": last.s_phys_wait,
|
||||
"p": last.p_phys_wait,
|
||||
"c": last.c_phys_wait,
|
||||
}),
|
||||
"last_solver_goto": last.solver_goto,
|
||||
"last_solver_inject": last.solver_inject,
|
||||
"last_solver_initial": last.solver_initial,
|
||||
"last_solver_click": last.solver_click,
|
||||
"last_solver_wait": last.solver_wait,
|
||||
"last_solver_reuse": last.solver_reuse,
|
||||
"last_solver_visible": last.solver_visible,
|
||||
"recent_ok_per_min": _rate_delta(recent, "ok"),
|
||||
"recent_t_prod_per_min": _rate_delta(recent, "t_prod"),
|
||||
"recent_q_ret_per_min": _rate_delta(recent, "q_ret"),
|
||||
"recent_pair_per_min": _rate_delta(recent, "pair"),
|
||||
}
|
||||
return summary
|
||||
|
||||
|
||||
def parse_solver_timelines(text_or_lines: str | Iterable[str]) -> list[SolverTimeline]:
|
||||
if isinstance(text_or_lines, str):
|
||||
lines = text_or_lines.splitlines()
|
||||
else:
|
||||
lines = list(text_or_lines)
|
||||
|
||||
timelines: list[SolverTimeline] = []
|
||||
for line in lines:
|
||||
if not line.startswith(_SOLVER_TIMELINE_PREFIX):
|
||||
continue
|
||||
payload = line[len(_SOLVER_TIMELINE_PREFIX):]
|
||||
try:
|
||||
events = json.loads(payload)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if isinstance(events, list):
|
||||
timelines.append(SolverTimeline(events=[e for e in events if isinstance(e, dict)]))
|
||||
return timelines
|
||||
|
||||
|
||||
def _last_page_trace(events: list[dict]) -> dict | None:
|
||||
for event in reversed(events):
|
||||
page_trace = event.get("page_trace")
|
||||
if isinstance(page_trace, dict):
|
||||
return page_trace
|
||||
return None
|
||||
|
||||
|
||||
def summarize_solver_timelines(timelines: list[SolverTimeline]) -> dict[str, float | int | None]:
|
||||
if not timelines:
|
||||
return {"solver_timeline_count": 0}
|
||||
|
||||
ok_count = 0
|
||||
click_calls: list[float] = []
|
||||
click_move_ms: list[float] = []
|
||||
click_down_up_ms: list[float] = []
|
||||
render_to_token_ms: list[float] = []
|
||||
token_write_to_poll_done_ms: list[float] = []
|
||||
poll_attempts: list[float] = []
|
||||
poll_read_avg_ms: list[float] = []
|
||||
click_before_count = 0
|
||||
center_iframe_hits = 0
|
||||
turnstile_iframe_seen = 0
|
||||
widget_seen = 0
|
||||
|
||||
for timeline in timelines:
|
||||
events = timeline.events
|
||||
page_trace = _last_page_trace(events)
|
||||
if page_trace:
|
||||
render_called = page_trace.get("render_called_at")
|
||||
token_written = page_trace.get("token_written_at")
|
||||
if isinstance(render_called, (int, float)) and isinstance(token_written, (int, float)):
|
||||
render_to_token_ms.append(float(token_written) - float(render_called))
|
||||
|
||||
for event in events:
|
||||
if event.get("event") == "click_before":
|
||||
click_before_count += 1
|
||||
dom = event.get("dom") if isinstance(event.get("dom"), dict) else {}
|
||||
widget = dom.get("widget") if isinstance(dom.get("widget"), dict) else {}
|
||||
center = dom.get("element_at_center") if isinstance(dom.get("element_at_center"), dict) else {}
|
||||
if widget.get("present") and widget.get("visible"):
|
||||
widget_seen += 1
|
||||
if center.get("is_iframe"):
|
||||
center_iframe_hits += 1
|
||||
if (dom.get("turnstile_iframe_count") or 0) > 0:
|
||||
turnstile_iframe_seen += 1
|
||||
|
||||
if event.get("event") == "click_after":
|
||||
call_ms = event.get("click_call_ms")
|
||||
if isinstance(call_ms, (int, float)):
|
||||
click_calls.append(float(call_ms))
|
||||
trace = event.get("click_trace") if isinstance(event.get("click_trace"), dict) else {}
|
||||
move1 = trace.get("mouse_move1_ms")
|
||||
move2 = trace.get("mouse_move2_ms")
|
||||
down = trace.get("mouse_down_ms")
|
||||
up = trace.get("mouse_up_ms")
|
||||
move_total = sum(float(v) for v in (move1, move2) if isinstance(v, (int, float)))
|
||||
down_up_total = sum(float(v) for v in (down, up) if isinstance(v, (int, float)))
|
||||
if move_total:
|
||||
click_move_ms.append(move_total)
|
||||
if down_up_total:
|
||||
click_down_up_ms.append(down_up_total)
|
||||
|
||||
if event.get("event") == "poll_done":
|
||||
if event.get("ok"):
|
||||
ok_count += 1
|
||||
attempts = event.get("poll_attempts")
|
||||
if isinstance(attempts, (int, float)):
|
||||
poll_attempts.append(float(attempts))
|
||||
read_avg = event.get("poll_read_ms_avg")
|
||||
if isinstance(read_avg, (int, float)):
|
||||
poll_read_avg_ms.append(float(read_avg))
|
||||
page_trace = event.get("page_trace") if isinstance(event.get("page_trace"), dict) else {}
|
||||
token_written = page_trace.get("token_written_at")
|
||||
event_t = event.get("t")
|
||||
created_at = page_trace.get("created_at")
|
||||
if all(isinstance(v, (int, float)) for v in (token_written, event_t, created_at)):
|
||||
token_write_to_poll_done_ms.append((float(event_t) * 1000.0) - (float(token_written) - float(created_at)))
|
||||
|
||||
return {
|
||||
"solver_timeline_count": len(timelines),
|
||||
"ok_count": ok_count,
|
||||
"avg_click_call_ms": _avg(click_calls),
|
||||
"avg_click_mouse_move_ms": _avg(click_move_ms),
|
||||
"avg_click_down_up_ms": _avg(click_down_up_ms),
|
||||
"avg_render_to_token_ms": _avg(render_to_token_ms),
|
||||
"avg_token_write_to_poll_done_ms": _avg(token_write_to_poll_done_ms),
|
||||
"avg_poll_attempts": _avg(poll_attempts),
|
||||
"avg_poll_read_ms": _avg(poll_read_avg_ms),
|
||||
"widget_seen_ratio": _ratio(widget_seen, click_before_count),
|
||||
"center_iframe_hit_ratio": _ratio(center_iframe_hits, click_before_count),
|
||||
"turnstile_iframe_seen_ratio": _ratio(turnstile_iframe_seen, click_before_count),
|
||||
}
|
||||
|
||||
|
||||
def analyze_text(text: str) -> dict[str, float | int | str | None]:
|
||||
return summarize_monitor_rows(parse_monitor_lines(text))
|
||||
Reference in New Issue
Block a user