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