Add LLM chat integration with streaming responses via Ollama

Phase 4: Full chat system with SSE streaming, note-aware context, and
conversation persistence.

Backend:
- Migration 0005: conversations + messages tables with FKs and indexes
- Conversation/Message SQLAlchemy models with relationships
- LLM service: ensure_model (auto-pull on startup), stream_chat (NDJSON),
  generate_completion, fetch_url_content (HTML stripping), build_context
  (keyword extraction, related note search, URL content injection)
- Chat service: conversation CRUD, save_response_as_note,
  summarize_conversation_as_note
- Chat routes blueprint: 9 endpoints including SSE streaming for messages,
  save/summarize as note, Ollama model listing
- Auto-pull llama3.1 model on app startup (non-blocking)

Frontend:
- apiStreamPost: SSE client using fetch + ReadableStream
- Chat Pinia store with streaming state management
- ChatView: dedicated /chat page with conversation sidebar + message thread
- ChatPanel: slide-out panel with contextNoteId from current route
- ChatMessage: markdown-rendered message bubble with "Save as Note" action
- Updated AppHeader with Chat nav link + panel toggle button
- Updated App.vue to mount ChatPanel with route-derived context
- Added /chat and /chat/:id routes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-10 18:45:22 -05:00
parent 807cde30be
commit d2b8ab8fe8
19 changed files with 1906 additions and 35 deletions
+15
View File
@@ -5,6 +5,7 @@ from quart import Quart, jsonify, make_response, request, send_from_directory
from fabledassistant.config import Config
from fabledassistant.routes.api import api
from fabledassistant.routes.chat import chat_bp
from fabledassistant.routes.notes import notes_bp
from fabledassistant.routes.tasks import tasks_bp
@@ -17,9 +18,23 @@ def create_app() -> Quart:
app.secret_key = Config.SECRET_KEY
app.register_blueprint(api)
app.register_blueprint(chat_bp)
app.register_blueprint(notes_bp)
app.register_blueprint(tasks_bp)
@app.before_serving
async def startup():
from fabledassistant.services.llm import ensure_model
try:
await ensure_model(Config.OLLAMA_MODEL)
except Exception:
logger.warning(
"Failed to ensure model '%s' on startup — chat may not work until model is available",
Config.OLLAMA_MODEL,
exc_info=True,
)
@app.route("/")
async def serve_index():
resp = await make_response(