- services/rag: knowledge_docs store — vector search via the shared
embedder (HNSW index, graceful ILIKE fallback), list/delete scoped to
the user, search intentionally global for the single-user deployment
- gateway: GET/POST /knowledge/docs, DELETE /knowledge/docs/:id,
POST /knowledge/search
- plugins/knowledge: knowledge_search and knowledge_ingest tools so the
persona can save and recall information mid-conversation
- main: migrations, store wiring, plugin registration
- llm: Adapter.Vision sends multimodal chat requests (text + image_url,
data URLs supported) and logs the call as purpose 'vision'
- gateway /images/analyze now accepts image_url or image_base64 and
returns real analysis instead of a placeholder message
- main wires the default LLM adapter as the vision analyzer
- httptest coverage for the vision request/response shape
- assistant messages get a 🔊 button that fetches TTS audio and plays
it, toggling to stop
- ChatInput gains a mic button: MediaRecorder capture, base64 upload to
/voice/stt, recognized text fills the input
- api client: voice.tts (blob) and voice.stt helpers
- new emotion package: rule-based sentiment (positive/negative lexicon)
drives mood transitions (开心/平静/难过) with 30min inactivity decay;
affection 0-100 grows with positive interaction, with 初识→熟悉→信赖→
羁绊 tiers
- pipeline records each user message and injects a mood line into the
system prompt so the persona's tone follows her mood
- mood changes broadcast over WS; frontend shows a mood badge with
affection tooltip; also handles the 'reminder' message type from the
reminder scheduler
- tests: sentiment scoring, mood shift, affection bounds, prompt line,
onChange callback
- SearchModal queries GET /messages/search and jumps to the matching
session; opened from the sidebar button or Ctrl+K
- session items in the sidebar gain a hover export button that
downloads the markdown export via an authenticated fetch + blob
(plain links cannot send the JWT)
- web-search: real SearXNG /search?format=json query, top-5 formatted
results (was a 'not configured' stub)
- web-fetch: fetch page, strip scripts/styles/tags, truncate to 4000
runes for the LLM context
- http: generic request tool (GET/POST/PUT/DELETE/PATCH) with scheme
and method validation, status + truncated body
- file: sandboxed read/write/list inside the data directory with path
escape rejection; split into file_read/file_write/file_list
- all tool schemas converted to proper JSON Schema; httptest/tempdir
tests for each plugin