Onboarding's 姿 step offers dead persona bundles instead of Live2D models #52

Closed
opened 2026-07-30 15:09:38 +00:00 by aiko · 2 comments
Owner

Onboarding's 姿 step offers a choice of character configs, which are persona bundles the go-between no longer honours, rather than the Live2D models the step claims to be about. Found by walking onboarding on the desktop app against a live Hermes.

What it does today

src/renderer/src/hooks/receiver/use-onboarding.ts:157-159 builds the list from the go-between's config files:

const models = useMemo(
  () => configFiles.map((config) => ({ name: config.name, uid: config.filename })),
  [configFiles],
);

Those are characters/*.yaml. On this machine the entire set is upstream Open-LLM-VTuber demo material — en_nuke_debate.yaml, en_unhelpful_ai.yaml, zh_米粒.yaml, zh_翻译腔.yaml. None of them is Denpa's character.

Each one carries a character_config block holding persona_prompt and live2d_model_name.

Why it is wrong twice

The persona half is dead. persona_prompt is not referenced anywhere in src/open_llm_vtuber/#21 stripped the go-between to TTS and Live2D, and the brain is Hermes. Whatever the operator picks here, her personality comes from Hermes' own configuration and is untouched.

The half that still works is a footgun. live2d_model_name is live (conf.yaml:16 currently mao_pro). Selecting one of the four demo characters switches the active config, so it changes her appearance to that demo's model. A step whose stated purpose is 姿 — appearance — achieves it only by dragging a dead persona along, and only towards characters that are not hers.

The copy already describes the intended thing correctly: MODEL_BODY is 「中継局が持っている姿」, "the appearances the relay station has". The data source does not match the words.

What is needed

  • 姿 lists Live2D modelsmodel_dict.json is the register, and conf.yaml:16 names the active one — not character config files.
  • Selecting one changes her appearance and nothing else. No persona, no LLM config, no voice, comes along with it.
  • Decide what happens to characters/*.yaml. If the only live field is live2d_model_name and the personas are dead, the upstream demo characters are dead weight in the repo and reading them as a menu is what created this. Deleting them is the obvious move; if any is kept, say why.
  • Onboarding must not present a choice whose main effect is invisible and whose visible effect is wrong.
  • #21 — stripped the go-between to TTS and Live2D, which is what made persona_prompt dead
  • #29 — the 調整 settings sheet, where a model picker may belong permanently
  • denpa/docs/adr/0011-devices-pair-to-the-go-between.md — the wider move of authority off the go-between
Onboarding's 姿 step offers a choice of **character configs**, which are persona bundles the go-between no longer honours, rather than the Live2D models the step claims to be about. Found by walking onboarding on the desktop app against a live Hermes. ## What it does today `src/renderer/src/hooks/receiver/use-onboarding.ts:157-159` builds the list from the go-between's config files: ```ts const models = useMemo( () => configFiles.map((config) => ({ name: config.name, uid: config.filename })), [configFiles], ); ``` Those are `characters/*.yaml`. On this machine the entire set is upstream Open-LLM-VTuber demo material — `en_nuke_debate.yaml`, `en_unhelpful_ai.yaml`, `zh_米粒.yaml`, `zh_翻译腔.yaml`. **None of them is Denpa's character.** Each one carries a `character_config` block holding `persona_prompt` **and** `live2d_model_name`. ## Why it is wrong twice **The persona half is dead.** `persona_prompt` is not referenced anywhere in `src/open_llm_vtuber/` — `#21` stripped the go-between to TTS and Live2D, and the brain is Hermes. Whatever the operator picks here, her personality comes from Hermes' own configuration and is untouched. **The half that still works is a footgun.** `live2d_model_name` *is* live (`conf.yaml:16` currently `mao_pro`). Selecting one of the four demo characters switches the active config, so it changes her appearance to that demo's model. A step whose stated purpose is 姿 — appearance — achieves it only by dragging a dead persona along, and only towards characters that are not hers. The copy already describes the intended thing correctly: `MODEL_BODY` is 「中継局が持っている姿」, *"the appearances the relay station has"*. The data source does not match the words. ## What is needed - 姿 lists **Live2D models** — `model_dict.json` is the register, and `conf.yaml:16` names the active one — not character config files. - Selecting one changes her appearance and nothing else. No persona, no LLM config, no voice, comes along with it. - Decide what happens to `characters/*.yaml`. If the only live field is `live2d_model_name` and the personas are dead, the upstream demo characters are dead weight in the repo and reading them as a menu is what created this. Deleting them is the obvious move; if any is kept, say why. - Onboarding must not present a choice whose main effect is invisible and whose visible effect is wrong. ## Related - `#21` — stripped the go-between to TTS and Live2D, which is what made `persona_prompt` dead - `#29` — the 調整 settings sheet, where a model picker may belong permanently - `denpa/docs/adr/0011-devices-pair-to-the-go-between.md` — the wider move of authority off the go-between
Author
Owner

In v1. Operator's call, 2026-07-30: the dead personas get cleaned up as part of v1, not deferred.

That makes the scope concrete, and it is larger than fixing the picker's data source:

  1. 姿 lists Live2D models, from model_dict.json, with conf.yaml:16's live2d_model_name as the active one. Selecting one changes her appearance and nothing else.
  2. The dead persona bundles go. characters/en_nuke_debate.yaml, en_unhelpful_ai.yaml, zh_米粒.yaml, zh_翻译腔.yaml are upstream Open-LLM-VTuber demo material, none of them hers, and their persona_prompt is referenced nowhere in src/open_llm_vtuber/ since #21 stripped the go-between to TTS and Live2D. Deleting them is the point of this issue, not a side effect: while they exist, something will read them as a menu again.
  3. Whatever still needs character_configlive2d_model_name is the only live field — gets it from somewhere that is not a persona bundle.

The hazard to avoid while doing it: switch-config currently swaps the whole config, which is why picking a "model" today can drag a demo character's appearance along. If the model picker keeps using switch-config, this issue is not fixed, only relabelled.

**In v1.** Operator's call, 2026-07-30: the dead personas get cleaned up as part of v1, not deferred. That makes the scope concrete, and it is larger than fixing the picker's data source: 1. **姿 lists Live2D models**, from `model_dict.json`, with `conf.yaml:16`'s `live2d_model_name` as the active one. Selecting one changes her appearance and nothing else. 2. **The dead persona bundles go.** `characters/en_nuke_debate.yaml`, `en_unhelpful_ai.yaml`, `zh_米粒.yaml`, `zh_翻译腔.yaml` are upstream Open-LLM-VTuber demo material, none of them hers, and their `persona_prompt` is referenced nowhere in `src/open_llm_vtuber/` since `#21` stripped the go-between to TTS and Live2D. Deleting them is the point of this issue, not a side effect: while they exist, something will read them as a menu again. 3. **Whatever still needs `character_config`** — `live2d_model_name` is the only live field — gets it from somewhere that is not a persona bundle. The hazard to avoid while doing it: `switch-config` currently swaps the *whole* config, which is why picking a "model" today can drag a demo character's appearance along. If the model picker keeps using `switch-config`, this issue is not fixed, only relabelled.
Author
Owner

Done, across both repos.

Go-between (aiko/ollvt-hermes-bridge, merge d2f9f87, commit 168484b; doc follow-up cb6b7d0)

  • characters/en_nuke_debate.yaml, en_unhelpful_ai.yaml, zh_米粒.yaml, zh_翻译腔.yaml and the directory's upstream README are deleted. GET /contents/characters on the remote is now a 404. A test asserts the glob stays empty, so nothing can quietly reintroduce a menu.
  • fetch-live2d-models{"type": "live2d-models", "models": [{name, description}]}, read from model_dict.json (live2d_model.py:available_models).
  • switch-live2d-model{"model": name} calls ServiceContext.init_live2d and replies set-model-and-conf. conf_name and conf_uid come back untouched — that is the whole reason it is not switch-config. A name the register does not hold is refused rather than swallowed by init_live2d's except, which would have left her in the old body while the viewer was told otherwise.
  • The frame deliberately does not carry an active field: set-model-and-conf already names the register entry in force, and a second answer to the same question is one that can disagree.

Client (aiko/denpa, merge 1b4614b, commit aae2559)

  • websocket-service.tsx sends fetch-live2d-models on connect where it sent fetch-configs.
  • New hooks/utils/use-switch-model.ts sends switch-live2d-model. It is not useSwitchCharacter, and settings-wired.test.ts now asserts that in both directions — the trap you named is pinned by a test, not by intent.
  • 姿 (use-onboarding.ts) lists useLive2DConfig().models and marks modelInfo?.name. configFiles, confName and getFilenameByName are gone from the hook.
  • 調整's 相手 tab moved with it (use-receiver-chrome.ts:227-229); it drew the same list through the same swap, and leaving it would have left a picker of persona bundles that no longer exist.
  • The 姿 copy needed no change — MODEL_BODY, MODEL_EMPTY and SKIP_COST.model already said appearance.

Gates. Go-between 227 passed. Client 50 files / 770 tests passed (from 48/742), cargo test 18 passed, npx tsc --noEmit exits 0 with no output.


What I did not do, deliberately, and what should probably become issues:

  1. switch-config is now dead wire and I left it standing. With characters/ gone, fetch-configs returns only conf.yaml, so the tray's character submenu (src-tauri/src/tray.rs:178) and the legacy sidebar picker (components/sidebar/setting/general.tsx) degrade to a single entry that reloads the base config. Deleting the path end to end reaches tray.rs, its Rust tests, tauri-tray.ts, character-config-context.tsx and use-switch-character.tsx — which would lower the Rust and vitest counts. That is a deletion issue of its own, not a rider on this one.
  2. conf.yaml:7 still says config_alts_dir: 'characters', pointing at a directory that no longer exists. Harmless (os.walk yields nothing) but untrue; the field is Field(...)-required on SystemConfig, so removing it is a schema change plus both config templates.
  3. model_dict.json registers exactly one model (mao_pro), though live2d-models/shizuku/ is on disk — upstream trimmed the register in 5b889ec. So 姿 currently offers one appearance. That is honest and matches 「中継局が持っている姿」, but it is not yet a choice. Registering more bodies, or moving the picker permanently into #29's 調整, is a separate call I did not make for you.
Done, across both repos. **Go-between** (`aiko/ollvt-hermes-bridge`, merge `d2f9f87`, commit `168484b`; doc follow-up `cb6b7d0`) - `characters/en_nuke_debate.yaml`, `en_unhelpful_ai.yaml`, `zh_米粒.yaml`, `zh_翻译腔.yaml` and the directory's upstream README are **deleted**. `GET /contents/characters` on the remote is now a 404. A test asserts the glob stays empty, so nothing can quietly reintroduce a menu. - `fetch-live2d-models` → `{"type": "live2d-models", "models": [{name, description}]}`, read from `model_dict.json` (`live2d_model.py:available_models`). - `switch-live2d-model` → `{"model": name}` calls `ServiceContext.init_live2d` and replies `set-model-and-conf`. `conf_name` and `conf_uid` come back untouched — that is the whole reason it is not `switch-config`. A name the register does not hold is refused rather than swallowed by `init_live2d`'s `except`, which would have left her in the old body while the viewer was told otherwise. - The frame deliberately does **not** carry an `active` field: `set-model-and-conf` already names the register entry in force, and a second answer to the same question is one that can disagree. **Client** (`aiko/denpa`, merge `1b4614b`, commit `aae2559`) - `websocket-service.tsx` sends `fetch-live2d-models` on connect where it sent `fetch-configs`. - New `hooks/utils/use-switch-model.ts` sends `switch-live2d-model`. It is **not** `useSwitchCharacter`, and `settings-wired.test.ts` now asserts that in both directions — the trap you named is pinned by a test, not by intent. - 姿 (`use-onboarding.ts`) lists `useLive2DConfig().models` and marks `modelInfo?.name`. `configFiles`, `confName` and `getFilenameByName` are gone from the hook. - 調整's 相手 tab moved with it (`use-receiver-chrome.ts:227-229`); it drew the same list through the same swap, and leaving it would have left a picker of persona bundles that no longer exist. - The 姿 copy needed no change — `MODEL_BODY`, `MODEL_EMPTY` and `SKIP_COST.model` already said appearance. **Gates.** Go-between `227 passed`. Client `50 files / 770 tests passed` (from 48/742), `cargo test` `18 passed`, `npx tsc --noEmit` exits 0 with no output. --- **What I did not do**, deliberately, and what should probably become issues: 1. **`switch-config` is now dead wire and I left it standing.** With `characters/` gone, `fetch-configs` returns only `conf.yaml`, so the tray's character submenu (`src-tauri/src/tray.rs:178`) and the legacy sidebar picker (`components/sidebar/setting/general.tsx`) degrade to a single entry that reloads the base config. Deleting the path end to end reaches `tray.rs`, its Rust tests, `tauri-tray.ts`, `character-config-context.tsx` and `use-switch-character.tsx` — which would *lower* the Rust and vitest counts. That is a deletion issue of its own, not a rider on this one. 2. **`conf.yaml:7` still says `config_alts_dir: 'characters'`**, pointing at a directory that no longer exists. Harmless (`os.walk` yields nothing) but untrue; the field is `Field(...)`-required on `SystemConfig`, so removing it is a schema change plus both config templates. 3. **`model_dict.json` registers exactly one model** (`mao_pro`), though `live2d-models/shizuku/` is on disk — upstream trimmed the register in `5b889ec`. So 姿 currently offers one appearance. That is honest and matches 「中継局が持っている姿」, but it is not yet a choice. Registering more bodies, or moving the picker permanently into #29's 調整, is a separate call I did not make for you.
aiko closed this issue 2026-07-30 19:19:39 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiko/denpa#52
No description provided.