# api/learning_tracker_backend.py """ Learning Tracker AI Backend — v2.2 Generates weekly highlights and improvement suggestions for a student's learning summary. Called by the Hanbridge backend service (no sessions, no RAG). Ref: docs/AI_Interface_Design_v2.2.md §3.4 """ import json from api.config import async_client, DEFAULT_MODEL from api.quiz_backend import _strip_markdown async def generate_learning_tracker_insights( context: dict, language: str = "CN", model_name: str | None = None, ) -> tuple[dict, int]: """ Analyze a student's weekly learning summary and return AI insights. Returns: ({"weekHighlights": "...", "improvementSuggestions": "..."}, tokens_used) Raises ValueError on bad AI output. """ model = model_name or DEFAULT_MODEL lang_instruction = ( "Respond in Chinese (中文)." if language.upper() in ("CN", "ZH", "中文") else "Respond in English." ) system = ( "You are an academic advisor AI. You will receive a JSON object containing a student's " "weekly learning data: overall grade, course progress, attendance, skill mastery scores, " "learning hours, and grade trend. Analyze the data and output ONLY a valid JSON object " "with exactly two keys:\n" ' "weekHighlights": a 1-3 sentence summary of what the student did well this week,\n' ' "improvementSuggestions": a 1-3 sentence actionable recommendation for next week.\n' "Do not include any other text, markdown, or explanation outside the JSON object.\n" f"{lang_instruction}" ) user = ( "Here is the student's weekly learning summary. Analyze it and return your insights:\n\n" f"{json.dumps(context, ensure_ascii=False, default=str)}" ) resp = await async_client.chat.completions.create( model=model, messages=[{"role": "system", "content": system}, {"role": "user", "content": user}], temperature=0.4, max_tokens=800, ) content = (resp.choices[0].message.content or "").strip() tokens_used = getattr(resp.usage, "total_tokens", None) or 0 content = _strip_markdown(content) try: data = json.loads(content) except json.JSONDecodeError as exc: raise ValueError("json_parse_error") from exc if not isinstance(data, dict): raise ValueError("json_parse_error") week_highlights = (data.get("weekHighlights") or "").strip() improvement_suggestions = (data.get("improvementSuggestions") or "").strip() if not week_highlights or not improvement_suggestions: raise ValueError("missing_fields") return { "weekHighlights": week_highlights, "improvementSuggestions": improvement_suggestions, }, tokens_used