AI-first клиент redose: чат с Евой + живой HUD марафона (поверхность ядра)
  • Python 93%
  • Nix 7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Aleksandr 58e4a729dd 🧰 Лобби-фильтр, /clear, /model, слэш-меню, примеры без конкретики
Прошедшие записи расписания больше не показываются (дата без года:
кандидат дальше трёх дней в будущем — прошлогодний). /clear заводит новый
ephemeral-чат, не трогая ленту, — положение дел уезжает Еве заново первым
туром. /model: список моделей ядра, выбор на сессию (пер-туровый model),
сброс через «/model -»; дефолт — [kernel] model / HM-опция model как
surface_model. Команды — через меню с автокомплитом (↑/↓, tab, enter), как
в eva-tui. В примерах <вещество> вместо конкретики; README — гайд без Nix.

Co-Authored-By: Eva
2026-08-09 14:11:46 +03:00
eva_redose 🧰 Лобби-фильтр, /clear, /model, слэш-меню, примеры без конкретики 2026-08-09 14:11:46 +03:00
tests 🧰 Лобби-фильтр, /clear, /model, слэш-меню, примеры без конкретики 2026-08-09 14:11:46 +03:00
.gitignore 🎀 Рождение eva-redose: чат с Евой поверх журнала redose 2026-08-09 13:18:23 +03:00
flake.lock 🎀 Рождение eva-redose: чат с Евой поверх журнала redose 2026-08-09 13:18:23 +03:00
flake.nix 🧰 Лобби-фильтр, /clear, /model, слэш-меню, примеры без конкретики 2026-08-09 14:11:46 +03:00
pyproject.toml 🎀 Рождение eva-redose: чат с Евой поверх журнала redose 2026-08-09 13:18:23 +03:00
README.md 🧰 Лобби-фильтр, /clear, /model, слэш-меню, примеры без конкретики 2026-08-09 14:11:46 +03:00

eva-redose

AI-first клиент redose: чат с Евой вместо форм и экранов. Журнал ведётся разговором — «принял <вещество> 60 мг интраназально», «накрывает на 7», «начался спад» — Ева записывает через журнальные тулы и предостерегает, когда толерантность ещё высока или запас уходит в минус.

Интерфейс

Одна колонка, как в eva-tui --code: лента + строка ввода. Пока марафона нет — лобби: приглашение начать и что по расписанию (давно прошедшие записи не показываются); «давай начнём марафон: <вещество>, 1000 мг в запасе» или «давай начнём запланированный марафон» — Ева стартует (спонтанный или сегодняшний по плану), и экран сам переключается на марафонский HUD, разговор остаётся в ленте.

С активным марафоном сверху — живой HUD, обновляется каждую секунду:

Марафон #3 · спонтанный · идёт 5ч 12м
запас: Мефедрон 1840/2000 мг
💊 Мефедрон 60 мг · Интраназально · 14:32 (32м назад) · редоз: через 28м
фаза: плато ▓▓▓▓▓░░░░░░░ 0:42:10 · след: спад через 1ч 05м

Журнальные события — свои и чужие (соседний redose TUI, MCP-сервер) — падают в ленту dim-строками: база одна, WAL, изменения ловятся по data_version.

Команды — по / с автокомплитом (↑/↓ выбор, tab дополнить, enter выполнить): /new — новый чат с чистым экраном, /clearсброс контекста Евы (положение дел текущего марафона уедет ей заново первым же сообщением), /model — список моделей ядра, /model <имя> — модель на сессию, /model -сброс к дефолту, /status /log /help /quit. esc прерывает ответ, сообщения во время тура встают в очередь.

Как это устроено

Поверхность ядра Евы: surface: "redose", чаты ephemeral — ядро жнёт их по TTL простоя, привязок на диске нет. Каждый тур несёт context (стабильная английская рамка, кэшируется провайдером) и <state of affairs> — живое положение дел в теле сообщения, только когда изменилось (рецепт mtl).

Журнал живёт за одним из двух бэкендов:

  • remote ([redose] mcp = "https://…/redose") — канонический журнал у ядра. Пишет только Ева, своими mcp_redose_*-руками; клиент поллит get_status / get_log через тот же redose-mcp (streamable HTTP, X-Redose-Token) и рисует HUD с лентой. Свои тулы не объявляются — у журнала одно перо.
  • local (без mcp) — база redose на этой же машине. Тур объявляет extra_tools (journal_trip, journal_note, journal_phase, journal_start — план по номеру или имени, journal_end, journal_status, journal_log, journal_substance, journal_scheduled, journal_plans), исполняет их сама поверхность через redose.actions; HUD секундный, прямо из базы.

Запуск

Nix (флейк тянет redose сам):

nix run git+https://git.desu.church/eva/redose.git
nix develop            # dev shell: python + redose + textual + httpx + pytest
nix build              # ./result/bin/eva-redose

Или по-старинке:

pip install -e ../redose -e .
eva-redose
eva-redose --url http://host:8090 --token-file ~/.eva-token --db /tmp/test.db

Конфиг: ~/.config/eva-redose/config.toml

[kernel]
url = "https://eva.desu.church/"
token_file = "/run/secrets/eva-token"
# model = "moonshotai/kimi-k3"   # дефолтная модель поверхности; /model сильнее

[redose]                     # секция есть → remote-режим
mcp = "https://eva.desu.church/redose"
mcp_token_file = "/run/secrets/redose-token"
# или local-режим: без mcp, опционально db = "/path/to/redose.db"

Env: EVA_REDOSE_URL, EVA_REDOSE_TOKEN(_FILE), EVA_REDOSE_MCP_URL, EVA_REDOSE_MCP_TOKEN(_FILE); CLI --url --token-file --mcp --mcp-token-file --db сильнее всего.

Home-manager модуль (homeManagerModules.default):

programs.eva-redose = {
  enable = true;
  kernel = "https://eva.desu.church/";
  tokenFile = "/run/secrets/eva-token";          # строки-пути, не в стор
  redoseMcp = "https://eva.desu.church/redose";
  redoseMcpTokenFile = "/run/secrets/redose-token";
  # model = "moonshotai/kimi-k3";
  # либо local-режим: db = "/var/lib/redose/redose/redose.db";
};

Сетап без Nix

Нужен Python ≥ 3.11 и два пакета (redose — библиотекой, с PyPI он не ставится):

python -m venv ~/.local/share/eva-redose-venv
~/.local/share/eva-redose-venv/bin/pip install \
  git+https://git.desu.church/nero/redose.git \
  git+https://git.desu.church/eva/redose.git
ln -s ~/.local/share/eva-redose-venv/bin/eva-redose ~/.local/bin/

Дальше — конфиг ~/.config/eva-redose/config.toml как выше. Для remote-режима положи токены в файлы (например, ~/.config/eva-redose/eva-token и redose-token, chmod 600) и укажи их в token_file / mcp_token_file.

Что должно быть на стороне сервера: ядро Евы за обратным прокси с X-Eva-Token, и — для remote-режима — redose-mcp, опубликованный тем же прокси с X-Redose-Token (см. redose-mcp --transport http). Если ядра под рукой нет, а redose стоит локально, — просто не указывай [redose] mcp: клиент будет вести локальную базу сам (local-режим), ядру хватит url.

Тесты

pytest -q              # в dev shell; в сборке пакета гоняются сами