JARVIS Docs

Backend, Frontend i Model Danych

Application Factory

backend.create_app():

  • tworzy instancję Flask,
  • ładuje konfigurację z klasy Config,
  • inicjalizuje bazę db,
  • inicjalizuje Marshmallow ma,
  • konfiguruje logowanie i Flask-Login,
  • podłącza server-side sessions,
  • rejestruje blueprinty,
  • opcjonalnie wykonuje db.create_all(),
  • konfiguruje OpenTelemetry.

db.create_all() jest ograniczone do:

  • TESTING=True,
  • albo AUTO_CREATE_DB=true.

W produkcji preferowane powinny być migracje, nie automatyczne tworzenie tabel przy starcie aplikacji. W Docker Compose świeżą bazę inicjalizuje jednorazowy serwis db-init.

Blueprints

public.py

Obsługuje:

  • / - strona główna,
  • POST / - prosty handler interakcji typu type == 0,
  • /terms_of_service,
  • /privacy_policy,
  • /livenez - healthcheck,
  • /public/ping - publiczny ping.

docs.py

Obsługuje renderowanie dokumentacji Markdown z katalogu docs/. Endpoint akceptuje tylko znane strony dokumentacji, żeby nie pozwalać na dowolny odczyt plików.

auth.py

Obsługuje:

  • /register_page,
  • /login_page,
  • POST /register,
  • POST /login,
  • POST /logout.

Rejestracja weryfikuje payload JSON, opcjonalną reCAPTCHA, unikalność emaila i nazwy użytkownika, zgodność haseł, a potem tworzy użytkownika, domyślną konwersację i domyślną konfigurację.

users.py

Obsługuje:

  • GET /users,
  • GET /users/<user_id>,
  • PUT /users/<user_id>,
  • PUT /users/<user_id>/update_password,
  • DELETE /users/<user_id>/delete.

Aktualizacja i usuwanie użytkownika sprawdzają, czy current_user.id odpowiada user_id z URL.

chat.py

Obsługuje:

  • /chat_page,
  • POST /chat/ask,
  • PUT /chat/conversations/<conversation_id>/set_context,
  • POST /chat/conversations/add,
  • GET /chat/conversations/<conversation_id>,
  • GET /chat/conversations/all,
  • POST /update_config,
  • GET /get_config,
  • GET /get_languages,
  • GET /get_models.

Najważniejsza ścieżka POST /chat/ask:

  1. Wymaga zalogowanego użytkownika.
  2. Wymaga JSON requestu.
  3. Pobiera conversation_id, user_prompt, model, temperature, n.
  4. Deleguje generowanie odpowiedzi do backend.services.openai_service.generate_response().
  5. openai_service pobiera historię, konfigurację i memory context.
  6. Prompt builder dopina ConversationSummary oraz najtrafniejsze MemoryItem przed ostatnimi wiadomościami.
  7. Po odpowiedzi OpenAI zapisuje ConversationHistory.
  8. Po zapisie historii wrzuca job do Redis queue jarvis:memory:jobs.
  9. Zwraca wygenerowaną odpowiedź jako JSON.

Lista konwersacji jest pobierana przez ORM, bez raw SQL i twardego schema:

Conversation.query.filter_by(user_id=current_user.id)
    .order_by(Conversation.created_at.desc())
    .all()

speech.py

Obsługuje:

  • GET /speech/config,
  • POST /speech/synthesize.

GET /speech/config zwraca konfigurację głosu użytkownika, dostępne modele STT, modele TTS i głosy z katalogu backend/data/speaches-voices.json. POST /speech/synthesize wymaga włączonego Speaches dla użytkownika, waliduje model i głos, a następnie deleguje syntezę do speech_service.py.

bug_report.py

Obsługuje:

  • POST /azure/boards/add_task.

Logika:

  1. Pobiera JSON z formularza zgłoszenia błędu.
  2. Wymaga pól title, description, severity, platform.
  3. Leniwie tworzy klienta Azure DevOps.
  4. Tworzy work item typu Issue.
  5. Opcjonalnie wysyła powiadomienie Discord webhookiem.
  6. Błąd Discord notification nie powinien przewracać głównego procesu tworzenia work itemu.

auth_discord.py

Obsługuje:

  • /auth/oauth2/callback.

Callback wymienia code na access token, pobiera profil Discord usera, zapisuje dane w sesji i renderuje register.html z danymi Discord.

Znane scenariusze developerskie

Reset konta po błędzie Discord account already linked

Ten scenariusz przydaje się w środowisku developerskim, gdy użytkownik zapomniał hasła, nie ma jeszcze przepływu resetowania hasła, a rejestracja lub linkowanie Discord OAuth zwraca Discord account already linked.

Blokujący rekord może istnieć w dwóch miejscach:

  • users.discord_id - np. shadow user utworzony przez integrację Discord,
  • discord_accounts.discord_id - klasyczne powiązanie konta Discord z użytkownikiem aplikacji.

Najpierw sprawdź, co blokuje linkowanie:

docker compose exec db psql -U jarvis -d jarvis_db -v discord_id="301809643980849156" -c "SELECT id, username, email, discord_id FROM users WHERE discord_id = :'discord_id'; SELECT id, user_id, discord_id, username FROM discord_accounts WHERE discord_id = :'discord_id';"

Jeżeli zapytanie zwróci rekord w users, najbezpieczniej usuwać po konkretnym users.id. Warto użyć RETURNING, żeby od razu zobaczyć, czy końcowy DELETE faktycznie skasował użytkownika:

\set user_id '2d998e76f4f94c9689889faf7d6c0716'
\set discord_id '301809643980849156'

BEGIN;

DELETE FROM user_config
WHERE user_id = :'user_id';

DELETE FROM memory_items
WHERE user_id = :'user_id';

DELETE FROM conversation_summary
WHERE user_id = :'user_id';

DELETE FROM conversation_history
WHERE user_id = :'user_id';

DELETE FROM conversation
WHERE user_id = :'user_id';

DELETE FROM discord_accounts
WHERE user_id = :'user_id'
   OR discord_id = :'discord_id';

DELETE FROM users
WHERE id = :'user_id'
RETURNING id, username, email, discord_id;

COMMIT;

Oczekiwany wynik przy ostatnim DELETE to zwrócony usuwany rekord oraz DELETE 1. Jeżeli pojawi się błąd w środku transakcji, wykonaj ROLLBACK;, popraw przyczynę i uruchom blok ponownie.

Po wykonaniu potwierdź, że Discord ID nie występuje już w tabelach auth:

docker compose exec db psql -U jarvis -d jarvis_db -v discord_id="301809643980849156" -c "SELECT id, username, email, discord_id FROM users WHERE discord_id = :'discord_id'; SELECT id, user_id, discord_id, username FROM discord_accounts WHERE discord_id = :'discord_id';"

Oba zapytania powinny zwrócić 0 rows. Jeżeli aplikacja nadal zwraca Discord account already linked, upewnij się, że używasz tej samej bazy, zrestartuj web container i wyczyść cookies/sesję przeglądarki dla lokalnej domeny aplikacji.

Serwisy

  • openai_service.py - klient OpenAI, historia rozmowy, prompt builder i zapis odpowiedzi.
  • memory_service.py - enqueue/dequeue Redis, budowanie context memory, upsert summary i memory items.
  • memory_extractors.py - lokalny extractor przez OpenAI-compatible endpoint lub fallback heurystyczny.
  • speech_service.py - komunikacja ze Speaches, cache TTS w Redis i walidacja rozmiaru audio.
  • cache_service.py - wspólny klient Redis dla cache/session/rate-limit related flows.

Frontend

Frontend opiera się o Jinja2 templates i statyczne pliki CSS/JS.

Templates

  • home.html - strona główna.
  • chat.html - główny widok chatu i panel konfiguracji.
  • docs.html - render dokumentacji.
  • login.html - logowanie.
  • register.html - rejestracja z reCAPTCHA.
  • report_bug.html - formularz zgłaszania błędów.
  • privacy-policy.html - polityka prywatności.
  • terms-of-service.html - regulamin.

JavaScript

  • chat.js - obsługa rozmowy, wysyłanie promptów i renderowanie odpowiedzi.
  • control_panel.js - panel konfiguracji chatu i głosu.
  • speech.js - browserowy voice mode, konfiguracja Speaches i odtwarzanie TTS.
  • register.js - rejestracja i walidacja hasła.
  • login.js - logowanie.
  • report_popup.js - popup zgłaszania błędu.
  • home.js, home1.js, toggle-style.js - logika strony głównej i stylów.

CSS

  • style.css - style formularzy i części wspólnych.
  • chat.css - główny styl widoku chatu.
  • docs.css - styl dokumentacji.
  • home.css, home1.css - warianty stylów strony głównej.
  • report_popup.css - styl modalu zgłoszeń.

Model Danych

Modele znajdują się w backend/models.py.

User

Reprezentuje użytkownika aplikacji.

Pola:

  • id - string UUID hex, primary key,
  • firstname,
  • lastname,
  • username - unikalny,
  • email - unikalny,
  • discord_id - opcjonalny, unikalny,
  • password - hash hasła,
  • created_at.

Metody:

  • set_password(password, salt_length=16),
  • check_password(password),
  • json().

DiscordAccount

Reprezentuje powiązane konto Discord.

Pola:

  • id,
  • user_id,
  • discord_id,
  • username,
  • discriminator,
  • access_token.

Conversation

Reprezentuje konwersację użytkownika.

Pola:

  • id,
  • conversation_id - primary key,
  • user_id,
  • platform - web albo discord,
  • created_at.

ConversationHistory

Reprezentuje pojedynczy wpis historii rozmowy.

Pola:

  • id,
  • conversation_id,
  • prompt,
  • response,
  • user_id,
  • platform,
  • context,
  • used_tokens,
  • timestamp.

DiscordConversation

Mapuje trwały scope Discorda na konwersację webową, żeby bot mógł odtworzyć ten sam conversation_id po restarcie runtime store.

Pola:

  • id,
  • scope_key - unikalny klucz, np. guild_channel:<guild_id>:<channel_id>,
  • scope_type - np. guild_channel, dm, call, transcript,
  • guild_id,
  • channel_id,
  • discord_user_id,
  • conversation_id.

ConversationSummary

Reprezentuje rolling summary rozmowy dla użytkownika.

Pola:

  • id,
  • conversation_id,
  • user_id,
  • summary,
  • message_count,
  • updated_at.

Para conversation_id + user_id jest unikalna. Worker aktualizuje ten rekord po przetworzeniu joba pamięci.

MemoryItem

Reprezentuje trwały kandydat pamięci o użytkowniku.

Pola:

  • id,
  • user_id,
  • conversation_id - opcjonalne źródło pamięci,
  • kind - np. profile, preference, project, fact,
  • content,
  • embedding_json - przejściowy storage przed natywnym vector,
  • salience,
  • created_at,
  • updated_at.

memory_service.py deduplikuje wpisy po znormalizowanej treści, ignorując wielkość liter, nadmiarowe spacje i końcową kropkę.

UserConfig

Reprezentuje konfigurację chatu i głosu dla użytkownika.

Pola:

  • id,
  • user_id,
  • model,
  • n,
  • temperature,
  • speaches_enabled,
  • speaches_stt_model,
  • speaches_tts_model,
  • speaches_tts_voice.

Domyślna konfiguracja:

  • model = "gpt-3.5-turbo",
  • n = 1,
  • temperature = 1.0,
  • speaches_enabled = false,
  • speaches_tts_model = "speaches-ai/Kokoro-82M-v1.0-ONNX",
  • speaches_tts_voice = "af_heart".

Schematy Marshmallow

  • UserSchema,
  • ConversationHistorySchema,
  • ConversationsSchema.