File size: 4,831 Bytes
c72a1e8 | 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 129 130 131 132 | import json
import os
from datetime import datetime
import httpx
MINIMAX_API_KEY = os.environ.get("MINIMAX_API_KEY", "")
MINIMAX_MODEL = "MiniMax-M2.5"
MINIMAX_ENDPOINT = "https://api.minimaxi.chat/v1/text/chatcompletion_v2"
SYSTEM_PROMPT = """Du bist ein ADHS-freundlicher Study Buddy. Deine Aufgabe: Zerlege Lernaufgaben in kleine, machbare 5-Minuten-Bloecke.
Regeln:
- Jeder Block hat einen eigenen Titel der sich wie ein FRISCHER ANFANG anfuehlt (z.B. "Oeffne die Seite und lies den ersten Absatz")
- NICHT: "Weiterlesen", "Aufgabe 3 von 10" — das toetet die Motivation
- Jeder Block hat einen kurzen Kontext-Satz: was genau tun, wo anfangen
- Buddy-Ton: supportiv, keine Vorwuerfe, kein Druck
- Im Panik-Modus (<48h): nur 3-5 Steps, nur das Absolut-Wichtigste, direkter Ton
Antworte NUR als JSON array (kein Markdown, kein Text drumrum):
[{"title": "...", "context": "...", "duration_minutes": 5}]"""
def _dummy_steps(title: str, panic: bool) -> list[dict]:
"""Fallback steps when no API key is configured."""
if panic:
return [
{
"title": f"Oeffne deine Unterlagen zu '{title}'",
"context": "Lies nur die Ueberschriften und fett markierten Begriffe. Nicht mehr.",
"duration_minutes": 5,
},
{
"title": "Die wichtigsten 3 Punkte notieren",
"context": "Schreib auf ein leeres Blatt: Was muss ich auf jeden Fall wissen?",
"duration_minutes": 5,
},
{
"title": "Einen Durchgang — fertig",
"context": "Lies deine 3 Punkte laut vor. Das war's. Du bist bereit.",
"duration_minutes": 5,
},
]
return [
{
"title": f"Starte mit '{title}' — nur die erste Seite",
"context": "Oeffne das Material und lies genau eine Seite. Kein Druck weiterzumachen.",
"duration_minutes": 5,
},
{
"title": "Notiere eine Frage die du hast",
"context": "Was ist noch unklar? Schreib es auf. Das hilft deinem Gehirn sich zu orientieren.",
"duration_minutes": 5,
},
{
"title": "Noch eine Seite — du bist schon drin",
"context": "Du hast den schwierigsten Teil hinter dir (das Anfangen). Lies weiter.",
"duration_minutes": 5,
},
{
"title": "Kurze Zusammenfassung in eigenen Worten",
"context": "Was hast du gerade gelesen? Ein Satz reicht. Kein perfekter Satz noetig.",
"duration_minutes": 5,
},
]
async def generate_micro_steps(
deadline_title: str,
deadline_description: str,
due_date: datetime,
hours_until_due: float,
) -> list[dict]:
panic = hours_until_due < 48
if not MINIMAX_API_KEY:
return _dummy_steps(deadline_title, panic)
if hours_until_due < 0:
mode_hint = "PANIK-MODUS: Deadline ist ueberschritten! Sofortmassnahmen. Maximal 4 Steps, nur das Absolut-Wichtigste."
elif panic:
mode_hint = f"PANIK-MODUS: Nur {int(hours_until_due)}h bis zur Deadline. Maximal 4 Steps, nur das Wichtigste."
else:
mode_hint = f"Normale Vorbereitung. Deadline in {int(hours_until_due)}h. Generiere 4-6 Steps."
user_message = (
f"Aufgabe: {deadline_title}\n"
f"Details: {deadline_description or 'keine weiteren Details'}\n"
f"Faellig: {due_date.strftime('%d.%m.%Y %H:%M')}\n"
f"{mode_hint}"
)
async with httpx.AsyncClient(timeout=60.0) as client:
response = await client.post(
MINIMAX_ENDPOINT,
headers={
"Authorization": f"Bearer {MINIMAX_API_KEY}",
"Content-Type": "application/json",
},
json={
"model": MINIMAX_MODEL,
"max_tokens": 1024,
"temperature": 0.3,
"messages": [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
},
)
response.raise_for_status()
data = response.json()
msg = data["choices"][0]["message"]
raw = (msg.get("content") or "").strip()
if not raw:
# M2.5 reasoning fallback
reasoning = (msg.get("reasoning_content") or "").strip()
if reasoning:
raw = reasoning
# Strip markdown code fences if present
if raw.startswith("```"):
raw = raw.split("```")[1]
if raw.startswith("json"):
raw = raw[4:]
raw = raw.strip()
steps = json.loads(raw)
# Ensure required fields
for step in steps:
step.setdefault("duration_minutes", 5)
return steps
|