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 typutype == 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:
- Wymaga zalogowanego użytkownika.
- Wymaga JSON requestu.
- Pobiera
conversation_id,user_prompt,model,temperature,n. - Deleguje generowanie odpowiedzi do
backend.services.openai_service.generate_response(). openai_servicepobiera historię, konfigurację i memory context.- Prompt builder dopina
ConversationSummaryoraz najtrafniejszeMemoryItemprzed ostatnimi wiadomościami. - Po odpowiedzi OpenAI zapisuje
ConversationHistory. - Po zapisie historii wrzuca job do Redis queue
jarvis:memory:jobs. - 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:
- Pobiera JSON z formularza zgłoszenia błędu.
- Wymaga pól
title,description,severity,platform. - Leniwie tworzy klienta Azure DevOps.
- Tworzy work item typu
Issue. - Opcjonalnie wysyła powiadomienie Discord webhookiem.
- 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-webalbodiscord,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 natywnymvector,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.