Spaces:
Sleeping
Sleeping
| # 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 | |