| """ |
| Generate the agent-trace dataset for the Body Debt HF Space. |
| |
| For each canonical stressor profile, this script runs the full Body Debt |
| analysis pipeline (parse -> score -> face -> plan -> coach) and writes one |
| JSONL record per profile capturing the visible reasoning chain. The |
| output is meant to be uploaded as a public HF dataset so judges and |
| other builders can inspect what the small-model "agent" actually does. |
| |
| Why this exists: the "Sharing is Caring" bonus quest for the |
| Build Small Hackathon rewards published agent traces. |
| |
| Usage: |
| python generate_trace_dataset.py |
| # writes body_debt_traces.jsonl in the script's directory |
| """ |
|
|
| from __future__ import annotations |
|
|
| import json |
| import os |
| import time |
| from datetime import datetime |
| from pathlib import Path |
|
|
| import numpy as np |
|
|
| from scoring import ( |
| Stressor, |
| compute_live_score, |
| compute_system_scores, |
| compute_counterfactual, |
| ) |
| from face_scan import features_to_array, StressFeatures |
| from stress_model import predict_stress_score |
| from health_coach import _fallback_advice, _fallback_plan |
|
|
| HERE = Path(__file__).parent |
| OUT_PATH = HERE / "body_debt_traces.jsonl" |
|
|
| RNG = np.random.default_rng(7) |
|
|
|
|
| |
| |
| |
|
|
| PROFILES = [ |
| ( |
| "bad_night_spirits", |
| "Heavy drinking + bad sleep + destroyed legs workout", |
| dict( |
| alcohol=True, alcohol_type="spirits", alcohol_count="5+", |
| training=True, training_area="legs", training_intensity="destroyed", |
| sleep=True, sleep_hours="under_4", |
| stress=False, ill=False, care=False, |
| ), |
| ), |
| ( |
| "red_wine_dinner", |
| "Two glasses of red wine, otherwise a normal day", |
| dict( |
| alcohol=True, alcohol_type="red_wine", alcohol_count="1-2", |
| training=False, sleep=False, stress=False, ill=False, care=False, |
| ), |
| ), |
| ( |
| "hiit_cardio", |
| "Hard HIIT session, slept fine", |
| dict( |
| alcohol=False, |
| training=True, training_area="hiit", training_intensity="hard", |
| sleep=True, sleep_hours="6-7", |
| stress=False, ill=False, care=False, |
| ), |
| ), |
| ( |
| "sick_day", |
| "Mild flu, no training, slept poorly", |
| dict( |
| alcohol=False, training=False, |
| sleep=True, sleep_hours="4-6", |
| stress=False, |
| ill=True, ill_severity="mild", care=True, |
| ), |
| ), |
| ( |
| "stress_week", |
| "Major work stress, otherwise taking care of self", |
| dict( |
| alcohol=False, training=False, sleep=True, sleep_hours="6-7", |
| stress=True, stress_carried="carried_all_day", |
| ill=False, care=True, |
| ), |
| ), |
| ( |
| "recovery_day", |
| "Logged a self-care day with mobility and good sleep", |
| dict( |
| alcohol=False, |
| training=True, training_area="mobility", training_intensity="easy", |
| sleep=True, sleep_hours="6-7", |
| stress=False, ill=False, care=True, |
| ), |
| ), |
| ( |
| "champagne_brunch", |
| "Three glasses of champagne at brunch, otherwise calm", |
| dict( |
| alcohol=True, alcohol_type="champagne", alcohol_count="3-4", |
| training=False, sleep=True, sleep_hours="6-7", |
| stress=False, ill=False, care=False, |
| ), |
| ), |
| ( |
| "lost_count", |
| "Lost count of drinks, slept terribly, work stress", |
| dict( |
| alcohol=True, alcohol_type="cocktails", alcohol_count="lost_count", |
| training=False, sleep=True, sleep_hours="under_4", |
| stress=True, stress_carried="carried_all_day", |
| ill=False, care=False, |
| ), |
| ), |
| ( |
| "clean_day", |
| "No stressors logged", |
| dict( |
| alcohol=False, training=False, sleep=False, |
| stress=False, ill=False, care=False, |
| ), |
| ), |
| ( |
| "floored", |
| "Severely ill, body aches, not training, slept badly", |
| dict( |
| alcohol=False, training=False, |
| sleep=True, sleep_hours="4-6", |
| stress=True, stress_carried="mostly_gone", |
| ill=True, ill_severity="floored", care=True, |
| ), |
| ), |
| ( |
| "easy_upper", |
| "Light upper body workout, slept well, otherwise normal", |
| dict( |
| alcohol=False, |
| training=True, training_area="upper", training_intensity="easy", |
| sleep=True, sleep_hours="6-7", |
| stress=False, ill=False, care=False, |
| ), |
| ), |
| ( |
| "mild_hangover", |
| "Beer night (3-4), slept 4-6 hours, light day planned", |
| dict( |
| alcohol=True, alcohol_type="beer", alcohol_count="3-4", |
| training=False, sleep=True, sleep_hours="4-6", |
| stress=False, ill=False, care=False, |
| ), |
| ), |
| ] |
|
|
|
|
| |
|
|
|
|
| def build_stressors(profile: dict) -> list[Stressor]: |
| s = profile |
| out: list[Stressor] = [] |
| if s.get("alcohol"): |
| out.append(Stressor( |
| type="alcohol", |
| alcohol_type=s.get("alcohol_type", "beer"), |
| alcohol_count=s.get("alcohol_count", "3-4"), |
| )) |
| if s.get("training"): |
| out.append(Stressor( |
| type="training", |
| training_area=s.get("training_area", "full_body"), |
| training_intensity=s.get("training_intensity", "hard"), |
| )) |
| if s.get("sleep"): |
| out.append(Stressor( |
| type="sleep", |
| sleep_hours=s.get("sleep_hours", "4-6"), |
| )) |
| if s.get("stress"): |
| out.append(Stressor( |
| type="stress", |
| stress_carried=s.get("stress_carried", "carried_all_day"), |
| )) |
| if s.get("ill"): |
| out.append(Stressor( |
| type="ill", |
| ill_severity=s.get("ill_severity", "moderate"), |
| )) |
| if s.get("care"): |
| out.append(Stressor(type="care")) |
| return out |
|
|
|
|
| def synthetic_face(stressors: list[Stressor]) -> tuple[list, np.ndarray, float]: |
| """Build a physiologically-plausible 7-feature face vector from the stressors. |
| |
| We don't have a real webcam, so we synthesize features that match |
| the stress level implied by the deterministic score. The model then |
| runs on these features, which is the same code path as a real scan. |
| """ |
| if not stressors: |
| face = StressFeatures( |
| left_eye_aspect=0.33, right_eye_aspect=0.32, |
| brow_tension=0.045, mouth_tension=5.5, |
| eye_symmetry=0.05, mouth_opening=0.15, |
| timestamp=time.time(), |
| ) |
| else: |
| |
| left_ear = 0.32 |
| right_ear = 0.31 |
| brow = 0.045 |
| mouth_t = 5.0 |
| eye_sym = 0.05 |
| mouth_o = 0.15 |
| for s in stressors: |
| if s.type == "sleep" and s.sleep_hours in ("under_4", "4-6"): |
| left_ear -= 0.07 |
| right_ear -= 0.06 |
| mouth_o -= 0.06 |
| if s.type == "alcohol" and s.alcohol_count in ("5+", "lost_count"): |
| brow -= 0.012 |
| eye_sym += 0.05 |
| mouth_t += 2.0 |
| if s.type == "stress" and s.stress_carried == "carried_all_day": |
| brow -= 0.010 |
| mouth_t += 1.0 |
| if s.type == "training" and s.training_intensity == "destroyed": |
| mouth_t += 1.5 |
| mouth_o -= 0.04 |
| if s.type == "ill": |
| left_ear -= 0.04 |
| right_ear -= 0.04 |
| face = StressFeatures( |
| left_eye_aspect=float(np.clip(left_ear, 0.16, 0.45)), |
| right_eye_aspect=float(np.clip(right_ear, 0.16, 0.45)), |
| brow_tension=float(np.clip(brow, 0.022, 0.06)), |
| mouth_tension=float(np.clip(mouth_t, 2.0, 12.0)), |
| eye_symmetry=float(np.clip(eye_sym, 0.0, 0.3)), |
| mouth_opening=float(np.clip(mouth_o, 0.0, 0.4)), |
| timestamp=time.time(), |
| ) |
| arr = features_to_array(face) |
| return [face], arr, predict_stress_score(arr)[0] |
|
|
|
|
| |
|
|
|
|
| def run_one(slug: str, description: str, profile: dict) -> dict: |
| t0 = time.time() |
| stressors = build_stressors(profile) |
| steps: list[dict] = [] |
|
|
| |
| steps.append({"name": "parse_stressors", "status": "done", |
| "detail": f"{len(stressors)} stressors selected", |
| "inputs": profile}) |
|
|
| |
| live_score = compute_live_score(stressors) |
| system_scores = compute_system_scores( |
| stressors, |
| now=datetime.now(), |
| bed_time="1:00 AM" if any(s.type == "sleep" and s.sleep_hours == "under_4" |
| for s in stressors) else None, |
| wake_time="7:00 AM" if any(s.type == "sleep" for s in stressors) else None, |
| ) |
| steps.append({"name": "compute_live_score", "status": "done", |
| "detail": f"score={live_score}/100"}) |
| steps.append({"name": "compute_system_scores", "status": "done", |
| "detail": ", ".join(f"{s.system}={s.score}" for s in system_scores)}) |
|
|
| |
| face_objs, face_arr, face_stress = synthetic_face(stressors) |
| steps.append({"name": "face_scan", "status": "done", |
| "detail": f"features=7, stress={face_stress:.1f}/100"}) |
|
|
| |
| sys_dicts = [ |
| {"system": s.system, "label": s.label, "score": s.score, |
| "cleared_at": s.cleared_at, "recovery_hrs": s.recovery_hrs} |
| for s in system_scores |
| ] |
| plan = _fallback_plan(sys_dicts) |
| steps.append({"name": "triage_plan", "status": "done", |
| "detail": "PRIORITY Β· SECONDARY Β· AVOID (deterministic fallback)", |
| "plan": plan}) |
|
|
| |
| cf = compute_counterfactual( |
| stressors, system_scores, |
| "1:00 AM" if any(s.type == "sleep" and s.sleep_hours == "under_4" for s in stressors) else None, |
| "7:00 AM" if any(s.type == "sleep" for s in stressors) else None, |
| ) |
| steps.append({"name": "counterfactual", "status": "done" if cf else "skipped", |
| "detail": (f"{cf['lever_label']} -> {cf['system_label']} " |
| f"{cf['from_score']}->{cf['to_score']}") if cf else "no lever"}) |
|
|
| |
| stressor_summary = ", ".join(s.type for s in stressors) or "none" |
| advice = _fallback_advice(live_score, sys_dicts, stressor_summary) |
| steps.append({"name": "llm_coach", "status": "done", |
| "detail": "deterministic fallback (LLM stream not exercised in dataset gen)"}) |
|
|
| |
| record = { |
| "trace_id": slug, |
| "description": description, |
| "timestamp": datetime.now().isoformat(timespec="seconds"), |
| "wall_time_s": round(time.time() - t0, 3), |
| "steps": steps, |
| "outputs": { |
| "live_score": live_score, |
| "system_scores": [ |
| { |
| "system": s.system, |
| "label": s.label, |
| "score": s.score, |
| "recovery_hrs": s.recovery_hrs, |
| "cleared_at": s.cleared_at, |
| } |
| for s in system_scores |
| ], |
| "face_stress": round(float(face_stress), 1), |
| "plan": plan, |
| "counterfactual": cf, |
| "coach_advice_first_120": advice[:120], |
| }, |
| } |
| return record |
|
|
|
|
| def main() -> None: |
| records = [] |
| for slug, desc, profile in PROFILES: |
| rec = run_one(slug, desc, profile) |
| records.append(rec) |
| print(f" {slug:24s} score={rec['outputs']['live_score']:3d} " |
| f"face={rec['outputs']['face_stress']:5.1f} " |
| f"steps={len(rec['steps'])} {rec['wall_time_s']}s") |
|
|
| with OUT_PATH.open("w") as f: |
| for rec in records: |
| f.write(json.dumps(rec) + "\n") |
| print(f"\nWrote {len(records)} traces to {OUT_PATH} " |
| f"({OUT_PATH.stat().st_size / 1024:.1f} KB)") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|