Add semantic note search (nomic-embed-text) and per-conversation note cache
- New NoteEmbedding model + migration 0014 stores float embeddings (JSONB) - services/embeddings.py: get_embedding, upsert_note_embedding, semantic_search_notes (cosine similarity), backfill_note_embeddings - build_context() now tries semantic search first, falls back to keyword search; accepts cached_note_ids to reuse last-turn notes and stabilise the system prompt prefix for Ollama's KV cache - generation_buffer.py: per-conversation note ID cache (get/set/clear) - generation_task.py: passes cached IDs into build_context, updates cache after each turn, and invalidates it after create_note/update_note/create_task - app.py: pulls nomic-embed-text at startup and launches a background backfill to embed all existing notes (30 s delay so Ollama has time to load the model) - routes/notes.py + services/tools.py: fire-and-forget embedding update on every note create or update via the API or LLM tool calls Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -84,6 +84,7 @@ def create_app() -> Quart:
|
||||
async def startup():
|
||||
import asyncio
|
||||
|
||||
from fabledassistant.services.embeddings import backfill_note_embeddings
|
||||
from fabledassistant.services.generation_buffer import start_cleanup_loop
|
||||
from fabledassistant.services.llm import ensure_model
|
||||
from fabledassistant.services.logging import start_log_retention_loop
|
||||
@@ -108,9 +109,22 @@ def create_app() -> Quart:
|
||||
models_to_pull = {Config.OLLAMA_MODEL}
|
||||
if Config.OLLAMA_INTENT_MODEL and Config.OLLAMA_INTENT_MODEL != Config.OLLAMA_MODEL:
|
||||
models_to_pull.add(Config.OLLAMA_INTENT_MODEL)
|
||||
# Also pull the embedding model (nomic-embed-text by default).
|
||||
models_to_pull.add(Config.EMBEDDING_MODEL)
|
||||
for _model in models_to_pull:
|
||||
asyncio.create_task(_pull_model(_model))
|
||||
|
||||
# After models are pulled, backfill embeddings for existing notes.
|
||||
# Runs in the background so it never blocks the server from accepting requests.
|
||||
async def _delayed_backfill() -> None:
|
||||
await asyncio.sleep(30) # Give Ollama time to load the embedding model
|
||||
try:
|
||||
await backfill_note_embeddings()
|
||||
except Exception:
|
||||
logger.warning("Embedding backfill failed", exc_info=True)
|
||||
|
||||
asyncio.create_task(_delayed_backfill())
|
||||
|
||||
@app.route("/")
|
||||
async def serve_index():
|
||||
resp = await make_response(
|
||||
|
||||
Reference in New Issue
Block a user