Jacobina / api /analysis.py
marinarosa's picture
initial commit
be82719
Raw
History Blame Contribute Delete
2.71 kB
"""Log analysis: visualize previously exported generation logs (schema v1+v2).
The frontend reads the uploaded file client-side and POSTs the parsed JSON.
"""
from __future__ import annotations
import numpy as np
from api.serialize import error_payload, fig_json
from miru_tracer.core.logging_config import get_logger
from miru_tracer.core.schema import parse_log
from miru_tracer.visualization.plots import plot_probability_visualizations
logger = get_logger(__name__)
def analyze_log(data: dict, heatmap_ranks: int, prob_mode: str) -> dict:
if not isinstance(data, dict):
return error_payload("No log data provided")
def truncate(text, limit=200):
return text[:limit] + "..." if len(text) > limit else text
try:
log = parse_log(data)
metadata = {
"schema_version": log.schema_version,
"mode": log.mode,
"prompt": truncate(log.prompt),
"generated_text": truncate(log.generated_text),
"timestamp": log.timestamp,
"num_steps": log.num_steps,
"sampling_params": log.sampling_params,
}
if not log.history:
return {
"ok": True,
"metadata": metadata,
"stats": "No history data found in log",
"fig_heatmap": None,
"fig_confidence": None,
}
probs = [step.probability for step in log.history]
stats_text = (
f"Mean: {np.mean(probs):.4f}\n"
f"Std Dev: {np.std(probs):.4f}\n"
f"Min: {np.min(probs):.4f}\n"
f"Max: {np.max(probs):.4f}\n"
f"Median: {np.median(probs):.4f}\n"
f"Total steps: {len(log.history)}\n"
)
# Cap heatmap ranks at what was actually logged
ranks = min(
int(heatmap_ranks) if heatmap_ranks else 10,
len(log.history[0].top_k_tokens),
)
figures = plot_probability_visualizations(
log.history,
top_k=ranks,
probability_mode=prob_mode,
temperature=log.temperature,
)
return {
"ok": True,
"metadata": metadata,
"stats": stats_text,
"fig_heatmap": fig_json(figures[0]) if figures else None,
"fig_confidence": fig_json(figures[1]) if len(figures) > 1 else None,
}
except ValueError as e:
# parse_log rejects files that aren't miru-tracer/Jacobina logs
return error_payload(str(e))
except Exception as e:
logger.error(f"Log analysis error: {e}", exc_info=True)
return error_payload(f"Error analyzing log:\n\n{e}", trace=True)