Wording fixes from the review of the clarify + phrasing PRs.
- "На когда напомнить?" → "Когда?". After she has just been asked something,
the long form is the phrasing of a form field, not of a person.
- A reminder now wants a subject as well as a time. "напомни в 11" had a time
and nothing to say at 11, and she asked nothing at all — she now asks
"О чём напомнить?". Subject first, since a reminder with no subject is not
worth setting.
- The expiry notice is five phrasings picked at random instead of one fixed
sentence. It is the line he hears every time he walks off mid-request, so it
is the line that repeats most.
- The nudge prompt's ban on "обращения" is now "ласковые обращения". It was
meant to forbid "милый"/"дорогой", not his name — "Ками, ноутбук на трёх
процентах" is how she talks, and the eval's cringe check already only flags
pet names.
- The nudge example no longer claims she plugged the laptop in. She has no
hands and no smart plug; an example where she acts teaches the model to
invent actions Maven never took.
- replySystem: "тепло" → "спокойно и без официальных формулировок". A one-word
mood instruction a 1.7B can't act on, replaced with the behaviour meant.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
The "address him as ты" rule had only reached two of the five system
prompts. Instead of pasting it into the other three (five copies drift —
that is how this happened), there is now one block, in internal/persona,
prepended to all five: nudges, action replies, chat, note queries and
general knowledge.
The block says who he is and how to address him (a man, always "ты",
never "вы", never "он" about him; Maven stays feminine), plus the
current local date and time. It is rendered fresh each turn because the
time changes, and it is correct with an empty config — the address and
gender rules are defaults in code. Config only adds optional facts:
owner_name, city, and the existing free-text `persona` string, which is
now the static half of the block.
Russian even in front of the English prompts: the rules are Russian
grammar, so they read best stated in Russian, and there is one copy.
Vikunja #394.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
The prompts stated the feminine self-reference rule but never said whom she is
speaking to, so the model produced formal plural ("Жду вас") and talked about
him in third person ("Он не ел 11 дней"). Adds the address rule right next to
the feminine one, in the nudge prompt and the confirmation prompt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
The system prompt showed the JSON contract as {"response": "..."} and the
user prompt repeated it. A 0.8B copies whatever sits in the response slot, so
7 of 15 nudges came back as literally "...".
Changes, all prompt-side — the {"response","mood"} contract is unchanged:
- nudge system prompt is Russian, feminine self-reference, with filled-in
examples on topics that never appear as rules, so copying them is visible
- rule names get a Russian gloss and a required keyword, named last in the
prompt where a small model weights it hardest
- durations render in Russian, not English
- the no-parse fallback says something Russian instead of "water — care",
which was going straight to a Russian piper voice
- same "..." placeholder removed from replier_llm.go
Scored on internal/phraser/eval: 0/15 -> 13/15.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
The most load-bearing decision in the project was stated four incompatible
ways: the docs said Qwen3-1.7B, deploy/mavend.json said Qwen3.5-2B, the repo's
models/llm/ held an LFM2.5-1.2B gguf, and five code comments still said LFM.
Answering "which model is deployed" meant re-deriving it from scratch every
time.
Two facts the review missed, found while resolving it:
- /mnt/hdd1/llms is bind-mounted over /opt/maven/models/llm, which shadows the
repo's models/llm/. The LFM2.5 gguf sitting there was never loaded by
anything, so it was not evidence of the deployed model at all.
- That library holds Qwen3.5-0.8B, -2B and -4B, and no Qwen3-1.7B. The config
pointed at a file that does exist; the docs' Qwen3-1.7B was the stale claim,
the reverse of the assumed direction. Qwen3-1.7B is the CPT target, and that
training is still in flight (Vikunja #122), so no such gguf exists yet.
phraser.model_path moves to Qwen3.5-0.8B (Q4_K_M) — the smallest checkpoint on
disk, chosen for latency, and relevant to whether the LLM router is affordable
on this box. Docs and comments now say the same thing in one voice: 0.8B
resident now, CPT'd Qwen3-1.7B as the target, and the bind-mount shadowing
written down so the next reader does not mistake models/llm/ for ground truth.
Comments name the model, never a filename, so a swap stays a one-line config
change.
n_gpu_layers: 99 is correct and stays — compose passes /dev/dri and the render
gid for Vulkan offload to the Vega iGPU. CLAUDE.md's "CPU-only" was the stale
half of that contradiction and is corrected.
phraser.go also dropped a wrong "sub-1b, prompted not trained" size claim: the
target is trained end-to-end (RU CPT + joint persona/router SFT).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X5JApcrCRVGmqrxnhynSik
Daemon side of Decision B: parse {"response","mood"} across the 4 consumers
(replier, nudges, reminders, chat), fall back to legacy formats. Drop the
LLM router — the classifier handles routing; replier/phraser share one
llm.Client (timeout 20s->60s). llm.Client reads reasoning_content when
content is empty (thinking models).
Docs: TTS piper-student plan (OmniVoice teacher -> piper student, from
scratch, phoneme-first). CLAUDE.md training guide.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add llmphraser: LFM-based phraser implementing Phraser interface with
PhraseChat, PhraseNudge, PhraseReactive, and PhraseReminder methods.
- Add shared internal/llm/client: llama-server completion client used by
both the phraser (talking back) and router (routing), sharing one model.
- Add LLMReplier in mavend: replaces StubReplier for chat/nudge/reactive
replies, falls back to stub on model errors.
- Update Phraser interface: add PhraseChat method, update stub to match.
- Wire LLM phaser into mavend voice init, plumb LLM config from JSON.