Spaces:
Running
Running
| from __future__ import annotations | |
| from typing import Any | |
| def _listify(value: Any) -> list[str]: | |
| if value is None: | |
| return [] | |
| if isinstance(value, list): | |
| return [str(v).strip() for v in value if str(v).strip()] | |
| if isinstance(value, str): | |
| return [part.strip() for part in value.split("|") if part.strip()] | |
| return [str(value).strip()] if str(value).strip() else [] | |
| def _dedupe(items: list[str]) -> list[str]: | |
| seen: set[str] = set() | |
| ordered: list[str] = [] | |
| for item in items: | |
| norm = item.casefold() | |
| if not item or norm in seen: | |
| continue | |
| seen.add(norm) | |
| ordered.append(item) | |
| return ordered | |
| def _template_text(template_key: str, template_inputs: dict[str, Any] | None = None) -> str: | |
| inputs = dict(template_inputs or {}) | |
| lineup_slot = inputs.get("lineup_slot_used") | |
| lineup_slot_txt = f"No. {int(lineup_slot)} slot" if lineup_slot not in {None, "", "nan", "None"} else "projected slot" | |
| venue = str(inputs.get("venue") or "").strip() | |
| templates = { | |
| "pitcher_attackable": "The opposing pitcher profile is giving up HR-friendly contact", | |
| "pitcher_suppresses_hr": "The pitcher profile suppresses home-run damage", | |
| "trend_up": "Recent batted-ball form is trending up", | |
| "trend_down": "Recent batted-ball form has cooled", | |
| "zone_favorable": "The zone matchup lines up with his damage areas", | |
| "zone_tough": "This zone profile limits his best contact lanes", | |
| "family_zone_favorable": "The family-zone matchup boosts his contact shape", | |
| "family_zone_tough": "The family-zone matchup works against his usual damage path", | |
| "arsenal_favorable": "The arsenal mix fits his power profile", | |
| "arsenal_tough": "This pitch mix is a tougher fit for his power profile", | |
| "platoon_advantage": "The handedness split helps the matchup", | |
| "platoon_disadvantage": "Same-handed conditions trim the HR ceiling", | |
| "pulled_contact_strength": "His pulled-air damage keeps the HR ceiling live", | |
| "pulled_contact_light": "The pulled-air profile is lighter than ideal for this HR spot", | |
| "weather_supportive": "Weather conditions add a little extra carry", | |
| "weather_suppressive": "Weather conditions are holding down carry", | |
| "park_supportive": f"{venue} plays friendlier for carry" if venue else "The park adds a small carry boost", | |
| "park_suppressive": f"{venue} suppresses HR carry" if venue else "The park trims carry", | |
| "trajectory_helpful": "Pitch shape is more hittable than usual here", | |
| "trajectory_tough": "Pitch shape and tunneling make clean lift harder here", | |
| "rolling_up": "Recent form is moving in the right direction", | |
| "rolling_down": "Recent form has cooled", | |
| "opportunity_strong": f"The {lineup_slot_txt} adds plate-appearance upside", | |
| "opportunity_light": "The opportunity projection is lighter than usual", | |
| "pitcher_unresolved": "The opposing pitcher is still unresolved", | |
| "lineup_unknown": "The lineup slot is still unknown", | |
| "lineup_projected": "The lineup slot is projected rather than confirmed", | |
| "strikeout_whiff_profile": "The whiff profile supports the strikeout look", | |
| "strikeout_price_close": "The price is keeping the strikeout edge tight", | |
| } | |
| return templates.get(template_key, template_key.replace("_", " ").capitalize()) | |
| def build_hr_model_voice(row: dict[str, Any]) -> dict[str, Any]: | |
| candidates = row.get("model_voice_reason_candidates") or [] | |
| supportive = [c for c in candidates if str(c.get("direction") or "").strip().lower() == "supportive"] | |
| cautions = [c for c in candidates if str(c.get("direction") or "").strip().lower() == "caution"] | |
| primary = supportive[0] if supportive else candidates[0] if candidates else None | |
| caveat = None | |
| if primary and str(primary.get("direction") or "").strip().lower() == "caution": | |
| caveat = cautions[1] if len(cautions) > 1 else None | |
| else: | |
| caveat = cautions[0] if cautions else None | |
| primary_reason = ( | |
| _template_text(str(primary.get("template_key") or ""), primary.get("template_inputs")) | |
| if primary | |
| else "His current power baseline is keeping the matchup in range" | |
| ) | |
| caveat_reason = ( | |
| _template_text(str(caveat.get("template_key") or ""), caveat.get("template_inputs")) | |
| if caveat | |
| else "" | |
| ) | |
| voice = primary_reason | |
| if caveat_reason: | |
| voice = f"{primary_reason}, but {caveat_reason[:1].lower()}{caveat_reason[1:] if len(caveat_reason) > 1 else ''}" | |
| tags = _dedupe( | |
| [ | |
| str(candidate.get("template_key") or "").strip() | |
| for candidate in candidates | |
| if str(candidate.get("template_key") or "").strip() | |
| ] | |
| ) | |
| return { | |
| "model_voice": voice.rstrip(".") + ".", | |
| "model_voice_primary_reason": primary_reason, | |
| "model_voice_caveat": caveat_reason or None, | |
| "model_voice_tags": tags, | |
| "model_voice_for": primary_reason, | |
| "model_voice_against": caveat_reason or None, | |
| } | |
| def build_strikeout_model_voice(result: dict[str, Any]) -> dict[str, Any]: | |
| selection_side = str(result.get("selection_side") or "").strip().lower() | |
| line = result.get("line") | |
| expected_ks = result.get("expected_strikeouts") | |
| projected_bf = result.get("projected_batters_faced") | |
| leash_risk = result.get("leash_risk_subscore") | |
| positives = _dedupe(_listify(result.get("reason_tags_for"))) | |
| negatives = _dedupe(_listify(result.get("reason_tags_against")) + _listify(result.get("confidence_reasons"))) | |
| if selection_side == "under": | |
| try: | |
| line_txt = f"{float(line):.1f}" if line is not None else "the number" | |
| except Exception: | |
| line_txt = "the number" | |
| primary_reason = f"The line is a little high relative to the projected strikeout total ({line_txt} Ks)" | |
| if expected_ks is not None: | |
| try: | |
| primary_reason = f"Projected strikeouts land below the line ({float(expected_ks):.1f} vs {line_txt} Ks)" | |
| except Exception: | |
| pass | |
| if projected_bf is not None and float(projected_bf) <= 21.5: | |
| primary_reason = "Projected batters faced are lighter than ideal for the strikeout line" | |
| if leash_risk is not None and float(leash_risk) >= 0.48: | |
| primary_reason = "Pitch-count and leash risk keep the under live even with swing-and-miss stuff" | |
| caveat = positives[0] if positives else (negatives[0] if negatives else "") | |
| else: | |
| primary_reason = positives[0] if positives else "The whiff profile supports the strikeout look" | |
| caveat = negatives[0] if negatives else "" | |
| voice = primary_reason | |
| if caveat: | |
| voice = f"{primary_reason}, but {caveat[:1].lower()}{caveat[1:] if len(caveat) > 1 else ''}" | |
| tags = [] | |
| if selection_side == "under": | |
| tags.extend(["strikeout_line_high", "strikeout_opportunity_cap"]) | |
| elif positives: | |
| tags.append("strikeout_whiff_profile") | |
| if negatives: | |
| tags.append("strikeout_price_close") | |
| tags = _dedupe(tags) | |
| return { | |
| "model_voice": voice.rstrip(".") + ".", | |
| "model_voice_primary_reason": primary_reason, | |
| "model_voice_caveat": caveat or None, | |
| "model_voice_tags": [tag for tag in tags if tag], | |
| "model_voice_for": primary_reason, | |
| "model_voice_against": caveat or None, | |
| } | |