Μετάβαση στο περιεχόμενο

Αρχιτεκτονική

Το Adaptive Learner είναι μια 4-επίπεδη εφαρμογή βασισμένη σε plugins.

┌─────────────────────────────────────────────────────────────┐
│ Frontend           React 19 + TypeScript 6 + Vite 8 +       │
│                    Vitest 4 + Dexie 4 (IndexedDB) + TipTap  │
└─────────────────────────────────────────────────────────────┘
                            ↑↓ /api/*
┌─────────────────────────────────────────────────────────────┐
│ Backend            FastAPI ^0.136 + SQLAlchemy ^2.0 +       │
│                    Pydantic v2 + Alembic + Fernet           │
└─────────────────────────────────────────────────────────────┘
                            ↑↓ hookspecs
┌─────────────────────────────────────────────────────────────┐
│ PluginForge        ^0.10.0 (εξωτερικό PyPI· ταυτοποίηση    │
│                    μέσω target_application)                 │
└─────────────────────────────────────────────────────────────┘
                            ↑↓ entry_points
┌─────────────────────────────────────────────────────────────┐
│ Plugins            10 πακέτα στο plugins/                   │
│                    (ai-{anthropic,openai,gemini}, assessment,│
│                    session, tracking, tools, gamification,  │
│                    anki, notebooklm)                        │
└─────────────────────────────────────────────────────────────┘

Νέα χαρακτηριστικά ανήκουν ΠΑΝΤΑ σε plugin, εκτός αν αφορούν τον πυρήνα (users / projects / settings / curriculum / topics / lessons / backup / sync / system / import).

Δυαδική αποθήκευση (v0.7.0)

Το frontend έχει ένα μοναδικό σημείο επιλογής του αποθηκευτικού υποστρώματος: getStorage(): IStorageService. Δύο υλοποιήσεις ικανοποιούν το ίδιο συμβόλαιο:

  • apiStorage (προεπιλογή): λεπτό περιτύλιγμα γύρω από το api/client.ts που επικοινωνεί με το FastAPI backend.
  • dexieStorage (local-first): πλήρης στοίβα IndexedDB που αντικατοπτρίζει τα 25 μοντέλα SQLAlchemy. Οι κλήσεις ΤΝ εκτελούνται απευθείας από τον browser μέσω storage/ai-providers.ts.

Το IStorageService εκθέτει 22 namespaces (users, projects, settings, assessment, session με streaming, tracking, tools, curricula, topics, lessons, plugins, system, backup, export, subjects, tags, projectTaxonomy, imports, gamification, anki, pronunciation, notebooklm). Και οι δύο υλοποιήσεις καλύπτουν κάθε μέθοδο.

Το factory διαβάζει localStorage["adaptive-learner.storage_mode"] και μετά VITE_STORAGE_MODE (ορίζεται στο GH Pages build) και αφήνει ως προεπιλογή το api. Η εναλλαγή λειτουργιών δεν γίνεται εν ζωή: η σελίδα Ρυθμίσεων αποθηκεύει την επιλογή και εμφανίζει ειδοποίηση για επανεκκίνηση.

Τριεπίπεδα μυστικά (v1.20.0 / Phase 34)

env vars            > secrets.yaml         > Fernet DB column
ADAPTIVE_LEARNER_*    ~/.config/...yaml      api_key_<provider>

Κάθε κλήση ΤΝ διατρέχει την αλυσίδα μέσω services/settings.resolve_api_key:

  1. Μεταβλητή περιβάλλοντος ADAPTIVE_LEARNER_<PROVIDER>_API_KEY.
  2. ai.<provider>.api_key στο ~/.config/adaptive_learner/secrets.yaml.
  3. Fernet-αποκρυπτογραφημένη στήλη DB.
  4. None — η κλήση ΤΝ εμφανίζει σφάλμα στο UI.

Η απόδοση πηγής αποθηκεύεται στο UserSettingsOut.key_source_* (enum: env / secrets_yaml / settings / none). Το UI Ρυθμίσεων απενεργοποιεί Αποθήκευση / Αφαίρεση όταν η πηγή είναι env ή secrets_yaml.

Η ίδια αλυσίδα εφαρμόζεται και στις παρακάμψεις default_model ανά πάροχο· το secrets.yaml υπερτερεί της παράκαμψης UI σύμφωνα με τον σχεδιασμό Phase 34 (η διαμόρφωση αρχείου κερδίζει έναντι UI για τους προχωρημένους χρήστες).

Δομή plugin

plugins/adaptive-learner-plugin-<name>/
  adaptive_learner_<name>/
    plugin.py     # <Name>Plugin(BasePlugin), υλοποιήσεις hooks
    routes.py     # FastAPI router (αναθέτει σε service functions)
    <module>.py   # επιχειρηματική λογική
  tests/
    test_*.py     # pytest tests
  pyproject.toml  # entry point: [project.entry-points."adaptive_learner.plugins"]
  • Η κλάση plugin κληρονομεί από BasePlugin (pluginforge).
  • Η επιχειρηματική λογική βρίσκεται στα δικά της modules, ΟΧΙ στο routes.py.
  • Το routes.py περιέχει μόνο FastAPI endpoints που αναθέτουν.
  • Οι hook specs βρίσκονται στο backend/app/hookspecs.py.
  • Εξαρτήσεις plugin ως class attribute: depends_on = ["session"].
  • Όλα τα plugins είναι δωρεάν (MIT). Η υποδομή αδειοδότησης υπάρχει αλλά είναι αδρανής (LICENSING_ENABLED = False).

Hooks (8 specs στο backend/app/hookspecs.py)

Hook Πότε First-result;
get_assessment_questions(lang) Φόρτωση σελίδας Αξιολόγησης ναι
calculate_profile(answers) Υποβολή Αξιολόγησης ναι
create_session_prompt(...) Κάθε γύρος συνομιλίας ναι
ai_complete(messages, model, api_key, max_tokens) Τυπική κλήση ΤΝ ναι (δρομολόγηση παρόχου κατά πρόθεμα μοντέλου)
ai_complete_async(...) Παράλληλη αξιολόγηση ορίου κύκλου (v1.5.0) ναι
ai_complete_stream(...) Streaming απάντηση συνεδρίας (v1.6.0) ναι
recommend_method_switch(...) Ταμπλό + Συνεδρία ναι
on_session_complete(session, rating) Τέλος συνεδρίας broadcast
get_progress_summary(project_id) Widgets Ταμπλό broadcast
get_tool_recommendations(profile, lang) Εργαλεία Ταμπλό broadcast

Ροή δεδομένων

UI (React) → IStorageService
            → (API mode) FastAPI router → service → SQLAlchemy → SQLite
            → (Dexie mode) Dexie table → IndexedDB
            AI orchestrator → resolve_api_key (env > yaml > DB)
                            → pluginforge → provider plugin's ai_complete*
                            → Anthropic / OpenAI / Gemini SDK

Μονοκατευθυντική. Χωρίς άμεση πρόσβαση DB από routers (οι services κατέχουν τη δουλειά με SQLAlchemy). Χωρίς frontend κώδικα στο backend.

Διαχείριση σφαλμάτων

Frontend       ApiError (status + detail) → toast για τον χρήστη
API client     HTTP error → μετατροπή σε ApiError
Router         Λεπτό, δεν πιάνει τίποτα. Παγκόσμιος handler αντιστοιχίζει.
Service        Εκπέμπει υποκλάσεις AdaptiveLearnerError
Plugin         Εκπέμπει PluginError(plugin_name, message)
External       ExternalServiceError(service, message) για provider SDKs

Οι services ΠΟΤΕ δεν εκπέμπουν HTTPException· τα routers δεν πιάνουν ΤΙΠΟΤΑ. Ο παγκόσμιος exception handler στο main.py αντιστοιχίζει domain errors σε HTTP status codes. Δες .claude/rules/code-hygiene.md για το πλήρες πρότυπο.

Επιμονή

  • Backend: SQLAlchemy + SQLite. Μεταναστεύσεις Alembic στο backend/migrations/versions/.
  • Επιφάνεια συγχρονισμού: 28 πίνακες (baseline v1.19.0). Γραμμές ιστορικού μόνο-προσάρτησης (sessions, messages, ratings, progress commits, step evaluations, method switches, imported conversations, imported messages, anki cards, study questions) συν μεταβλητές γραμμές settings + curriculum.
  • Μορφή αντιγράφου: JSON· τα API keys εξαιρούνται κατά την εξαγωγή· η επαναφορά είναι συγχώνευση.
  • Απομόνωση τεστ: τα directories παραγωγικών δεδομένων φέρουν ένα marker .adaptive-learner-production· αν ένα τεστ το δει ποτέ, η εκτέλεση τερματίζει με pytest.exit(returncode=2).

Θεματοποίηση

5 θέματα (Classic, Cool Modern, Nord, Notebook, Studio) × light/dark = 10 παραλλαγές. CSS variables παντού· χωρίς Tailwind. Custom properties στο frontend/src/styles/global.css. Τα νέα UI στοιχεία ΠΡΕΠΕΙ να χρησιμοποιούν το σύνολο μεταβλητών.

Κινητά / PWA

Το @media (max-width: 768px) είναι η κανονική διακοπή για κινητά (drawer hamburger, touch targets 44×44, stacked layouts). Το @media (max-width: 360px) είναι το δίχτυ ασφαλείας για πολύ στενές οθόνες. Οι στυλ desktop ≥769px παραμένουν αμετάβλητοι.

Service worker (Workbox μέσω vite-plugin-pwa): NetworkFirst σε GET /api/ με timeout 4s, LRU 24h, όριο 60 εγγραφών. Το μεταβλητό /api/ είναι NetworkOnly.