Spaces:
Sleeping
Sleeping
| """Structured coaching payload for the web UI.""" | |
| from __future__ import annotations | |
| from typing import Any | |
| from chess_tutor.cohort.opening_families import bucket_display_name | |
| from chess_tutor.playstyle_coaching.opening_climbers import position_heading | |
| from chess_tutor.inference.output import format_elo_outlook_lines | |
| from chess_tutor.inference.pipeline import InferenceResult | |
| from chess_tutor.web.embedding import build_tsne_web_plots | |
| from chess_tutor.web.narrative_sections import build_web_narrative_sections | |
| from chess_tutor.web.puzzle_series import build_puzzle_series | |
| from chess_tutor.web.sections import ( | |
| DEFAULT_WEB_UI_LAYOUT, | |
| WebSectionId, | |
| WebSectionSpec, | |
| ensure_complete_layout, | |
| section_enabled, | |
| ui_layout_config, | |
| ) | |
| from chess_tutor.web.style_elo_plot import build_style_scale | |
| from chess_tutor.web.style_traits import build_style_traits | |
| from chess_tutor.web.version import version_info | |
| def _opening_cards(result: InferenceResult) -> list[dict[str, Any]]: | |
| plan = result.actionable_plan | |
| if plan is None or not plan.opening_insights: | |
| return [] | |
| cards: list[dict[str, Any]] = [] | |
| for rank, ins in enumerate(plan.opening_insights, start=1): | |
| title = position_heading( | |
| rank, | |
| position_label=ins.position_label, | |
| bucket=ins.bucket, | |
| ) | |
| cards.append( | |
| { | |
| "rank": rank, | |
| "bucket": title, | |
| "context": bucket_display_name(ins.bucket) if ins.bucket else None, | |
| "position_label": ins.position_label, | |
| "you_play": ins.user_move_san, | |
| "you_play_moves": ins.user_move_uci, | |
| "you_play_share": ins.user_move_at_position_share, | |
| "sample_share": ins.user_current_share, | |
| "bucket_n_games": ins.position_visit_count or ins.bucket_n_games, | |
| "recommended": ins.recommended_move_san, | |
| "recommended_moves": ins.recommended_move_uci, | |
| "recommended_label": ins.recommended_opening or ins.family_label, | |
| "climber_switch_rate": ins.climber_switch_rate, | |
| } | |
| ) | |
| return cards | |
| def build_web_payload( | |
| result: InferenceResult, | |
| *, | |
| corpus_id: str = "standard_600", | |
| layout: tuple[WebSectionSpec, ...] | None = None, | |
| ) -> dict[str, Any]: | |
| """JSON-serializable report for the local web UI.""" | |
| resolved_layout = ensure_complete_layout( | |
| layout if layout is not None else DEFAULT_WEB_UI_LAYOUT | |
| ) | |
| ctx = result.coaching_report.context | |
| outlook_lines = format_elo_outlook_lines(result, ctx) | |
| narrative = result.coaching_narrative.strip() | |
| meta = ( | |
| dict(result.actionable_plan.metadata) | |
| if result.actionable_plan is not None | |
| else {} | |
| ) | |
| payload: dict[str, Any] = { | |
| "ui_layout": ui_layout_config(sections=resolved_layout), | |
| "header": { | |
| "username": result.username, | |
| "site": result.site, | |
| "player_elo": result.player_elo, | |
| "native_player_elo": result.native_player_elo, | |
| "elo_bracket": ctx.elo_bracket, | |
| "n_games": result.n_games_downloaded, | |
| "policy": result.coaching_policy, | |
| }, | |
| "rating_outlook": outlook_lines, | |
| "narrative_markdown": narrative, | |
| "lichess_study_url": meta.get("lichess_study_url"), | |
| "lichess_study_error": meta.get("lichess_study_error"), | |
| "version": version_info(), | |
| } | |
| if section_enabled(WebSectionId.NARRATIVE, layout=resolved_layout): | |
| payload["narrative_sections"] = build_web_narrative_sections(narrative) | |
| else: | |
| payload["narrative_sections"] = [] | |
| if section_enabled(WebSectionId.OPENINGS, layout=resolved_layout): | |
| payload["opening_cards"] = _opening_cards(result) | |
| else: | |
| payload["opening_cards"] = [] | |
| if section_enabled(WebSectionId.PUZZLES, layout=resolved_layout): | |
| payload["puzzle_series"] = build_puzzle_series(result.actionable_plan) | |
| else: | |
| payload["puzzle_series"] = [] | |
| if section_enabled(WebSectionId.TSNE_PLOT, layout=resolved_layout) or section_enabled( | |
| WebSectionId.ELO_TSNE_PLOT, layout=resolved_layout | |
| ): | |
| embedding_plots = build_tsne_web_plots(result, corpus_id=corpus_id) | |
| payload["tsne_plot"] = ( | |
| embedding_plots["tsne_plot"] | |
| if section_enabled(WebSectionId.TSNE_PLOT, layout=resolved_layout) | |
| else None | |
| ) | |
| payload["elo_tsne_plot"] = ( | |
| embedding_plots["elo_tsne_plot"] | |
| if section_enabled(WebSectionId.ELO_TSNE_PLOT, layout=resolved_layout) | |
| else None | |
| ) | |
| else: | |
| payload["tsne_plot"] = None | |
| payload["elo_tsne_plot"] = None | |
| if section_enabled(WebSectionId.STYLE_SCALE, layout=resolved_layout): | |
| payload["style_scale"] = build_style_scale(result, corpus_id=corpus_id) | |
| else: | |
| payload["style_scale"] = None | |
| if section_enabled(WebSectionId.STYLE_TRAITS, layout=resolved_layout): | |
| payload["style_traits"] = build_style_traits(result) | |
| else: | |
| payload["style_traits"] = [] | |
| return payload | |