- 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
- engine: 5-field cron parser (*/n, ranges, lists) firing at most once
per minute; device-state triggers with edge detection; manual trigger
via POST /automation/rules/:id/trigger; actions run through the AI
pipeline so rules can call any tool (IoT control, search, ...)
- reminders: due-scan scheduler delivering via WebSocket broadcast,
optional LLM-polished reminder text with plain-title fallback;
Reminder gains Done flag with ListDue/MarkDone
- tests: cron matching and validation, once-per-minute dedup, device
edge triggering, manual trigger, reminder delivery
- supervisor loop keeps the OBv11 WebSocket alive, reconnecting with
exponential backoff (1s→30s) when the connection drops; Start no
longer fails permanently when NapCat is temporarily unreachable
- Send enforces MinGap between messages (from SendOptions or the
adapter's send_interval_ms setting, now wired from the admin panel)
- parseMessage no longer panics on events missing sender/time fields
- tests: reconnect after drop, rate limit enforcement, malformed events,
send while disconnected
- orchestrator: Tracer interface with spans for intent analysis, persona,
subsession dispatch, context build, LLM streaming and each tool call;
traces finish when the stream completes — the admin Trace waterfall
finally receives data
- background: Thinker generates real LLM thoughts (post-chat + silence
triggers, recent history as context, empty/overlong outputs discarded);
thoughts broadcast to web clients as 'thinking' WS messages; pipeline
postChatHook triggers post-chat thinking after each round
- memory: ApplyDecayAll + ListUserIDs with a 6h decay/purge cron in main
(was implemented but never scheduled)
- repository: optional pgvector embedding column (migration with
CREATE EXTENSION guarded by main), SearchByVector cosine search,
vector-enabled insert path
- service: SetEmbedder — memories are embedded on Add; Search merges
vector hits with n-gram keyword matches (vector first, deduped)
- embedder was already implemented (OpenAI-compatible API + n-gram FNV
fallback) but never instantiated; main now wires it from
EMBEDDING_API_URL/EMBEDDING_MODEL config