Spaces:
Paused
Paused
File size: 4,993 Bytes
29fd274 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | from collections import defaultdict
from pathlib import Path
from fastapi import APIRouter, HTTPException, Query
from app.config import get_settings
from app.models import CatchUpRequest, ChatRequest, MatchState, ReplayAdvanceRequest
from app.services.cricket import ReplayData, state_at, timeline_at
from app.services.gemini import GeminiService
from app.services.match_data import MatchRepository
from app.services.rag import CricketRetriever
from app.services.workflow import answer
from app.services.intent import IntentClassifier
from app.models import StoryItem
from app.services.stories import build_match_stories
settings = get_settings()
repository = MatchRepository(settings.resolved_data_dir)
gemini = GeminiService(settings)
retriever = CricketRetriever(settings)
classifier = IntentClassifier(settings)
router = APIRouter(prefix="/api")
def get_data(match_id: str) -> ReplayData:
try:
return repository.get(match_id)
except KeyError as exc:
raise HTTPException(status_code=404, detail=f"Match {match_id} was not found") from exc
@router.get("/health")
def health() -> dict[str, str | None]:
return {
"status": "ok",
"mode": "historical_replay",
"gemini": gemini.status,
"gemini_model": gemini.model,
"gemini_error": gemini.error_kind,
"gemini_error_summary": gemini.error_summary,
"qdrant": retriever.status,
"intent_classifier": classifier.source,
}
@router.get("/matches")
def matches():
return repository.all()
@router.get("/matches/{match_id}")
def match(match_id: str):
try:
return repository.metadata(match_id)
except KeyError as exc:
raise HTTPException(status_code=404, detail="Match was not found") from exc
@router.get("/matches/{match_id}/state")
def state(match_id: str, ball_index: int = Query(ge=0)) -> MatchState:
return state_at(get_data(match_id), ball_index)
@router.get("/matches/{match_id}/timeline")
def timeline(match_id: str, ball_index: int = Query(ge=0)):
return timeline_at(get_data(match_id), ball_index)
@router.post("/matches/{match_id}/replay/advance")
def advance(match_id: str, request: ReplayAdvanceRequest):
data = get_data(match_id)
return {"match_id": match_id, "ball_index": max(0, min(len(data.deliveries), request.from_index + request.steps)), "total_deliveries": len(data.deliveries)}
@router.post("/matches/{match_id}/catch-up")
def catch_up(match_id: str, request: CatchUpRequest):
data = get_data(match_id)
end = max(0, min(request.ball_index, len(data.deliveries)))
start = max(0, end - request.overs * 6)
events = data.deliveries[start:end]
runs = sum(event.total_runs for event in events)
wickets = sum(1 for event in events if event.wicket)
legal = sum(event.is_legal for event in events)
boundaries = sum(event.runs_off_bat in (4, 6) for event in events)
dots = sum(event.runs_off_bat == 0 and event.extras == 0 for event in events)
from app.models import CatchUpStatistic, CatchUpResponse, KeyMoment
moments = [KeyMoment(title="Wicket", detail=event.commentary, over_label=event.over_label, tone="wicket") for event in events if event.wicket]
moments += [KeyMoment(title="Boundary", detail=event.commentary, over_label=event.over_label, tone="boundary") for event in events if event.runs_off_bat in (4, 6)]
return CatchUpResponse(
match_id=match_id, from_over=events[0].over_label if events else "0.1",
to_over=events[-1].over_label if events else "0.1",
headline=f"{runs} runs from the last {request.overs} overs",
summary=f"Historical replay interval produced {runs} runs and {wickets} wickets.",
key_moments=moments[:6], important_events=events[-6:],
statistics=[
CatchUpStatistic(label="Runs", value=str(runs)),
CatchUpStatistic(label="Wickets", value=str(wickets)),
CatchUpStatistic(label="Boundaries", value=str(boundaries)),
CatchUpStatistic(label="Dot balls", value=str(dots)),
CatchUpStatistic(label="Scoring rate", value=f"{runs * 6 / legal:.2f}" if legal else "0.00"),
],
key_players=[], evidence=[], source="backend", last_processed_ball_index=end,
)
@router.post("/matches/{match_id}/ask")
@router.post("/chat")
def ask(request: ChatRequest):
data = get_data(request.match_id)
return answer(data, request.question, request.ball_index, gemini, retriever, classifier)
@router.get("/matches/{match_id}/insights")
def insights(match_id: str, ball_index: int = Query(ge=0)):
current = state_at(get_data(match_id), ball_index)
return [f"Scoring at {current.current_run_rate:.2f} runs per over", f"{current.remaining_runs} runs remain in this historical chase"]
@router.get(
"/matches/{match_id}/stories",
response_model=list[StoryItem],
)
def stories(match_id: str) -> list[StoryItem]:
data = get_data(match_id)
return build_match_stories(data)
|