| .. | ||
| cmd/tmctl | ||
| configs | ||
| example | ||
| internal | ||
| prompts | ||
| .env.example | ||
| go.mod | ||
| go.sum | ||
| README.md | ||
backend/ — Go-бэкенд TextMachine
Зона сессии «Бэкенд». Контракт — docs/architecture/05-decisions-log.md (D1–D17); контракты Фазы 0 — 03-implementation-notes.md; квирки провайдеров — docs/experiments/00-provider-quirks.md. .env не читать.
Карта пакетов
| Пакет | Что делает | Ключевые файлы |
|---|---|---|
cmd/tmctl |
CLI: translate --config book.yaml / report (status/redrive — в работе, D15.3) |
main.go |
internal/llm |
OpenAI-совместимый транспорт + retry/backoff, capability-слой (budget_field/temperature/reasoning per-модель), failover (написан, НЕ подключён — D4: только local-роли), нативный Anthropic = DEPRECATED-референс под будущий Gemini | httpllm.go, capability.go, failover.go |
internal/ledger |
Цены по usage (вкл. reasoning/cache-поля), PriceForResponse по фактической модели |
pricing.go |
internal/store |
SQLite (modernc, CGO-free), идемпотентные миграции v1–v5, reserve/settle+checkpoint, chunk_status, глоссарий, ruby, retrieval_state, request_log | ledger.go, migrate.go, glossary.go |
internal/config |
fail-fast загрузка models/pipeline/book; эхо-мина-гейт echoMineViolation; CheckRunnable блокирует неисполнимое (C2/fanout/judge) |
models.go, pipeline.go |
internal/pipeline |
Раннер (циклы, disposition, single-hop эскалация с ре-гейтом, resume), чанкер v4, ingest txt/epub+ruby, classify/coverage, банк памяти v2 (Aho-Corasick, спойлер-окна, disposition, post-check), рендер+инъекция | runner.go, memory.go, disposition.go, coverage.go |
internal/obs |
trace_id, структурные логи, safego |
Инварианты — ЛОМАТЬ НЕЛЬЗЯ (каждый закреплён тестами)
- Деньги: reserve → call → settle+checkpoint одной транзакцией;
committed == SUM(checkpoints)переживает kill -9 (kill9_test.go); цена — по фактически ответившей модели; потолки книга/день считают committed+reserved. - Snapshot-дисциплина: всё, что влияет на wire-байты ИЛИ на вердикты (capability, память content-hash, context-assembly, версии classify/coverage/chunker/maxtok, эскалационные оверрайды), свёрнуто в
snapshotID; правка = громкий--resnapshot. ⚠ Любой resnapshot = переоплата книги (D15) — content-addressed reuse спроектировать до онгоинга. - Эхо-мина DeepSeek: thinking у deepseek НИКОГДА не отключать —
reasoning:"off"там осознанный no-op;echoes_when_thinking_off+ рекурсивный скан extra_body валят конфиг fail-fast. У grok наоборот: off = ЯВНЫЙreasoning_effort:"none". - Порядок classify: refusal-blacklist / CJK-echo — ДО length/empty (усечённый отказ не превращается в платный ретрай); retry-flagged только {length, empty} на той же модели; loop-чек ПЕРЕД удвоением max_tokens.
- Эскалация: ровно 1 хоп на другую модель, результат РЕ-гейтится, retry-бюджет не сбрасывается, editor pinned (escalate_to только на translator — принуждается валидацией), канал adult — только permissive-провайдеры (fail-closed).
- Детерминизм: рендер — чистая функция snapshot; сортировки перед записью; никакого map-order в выводе; length-prefixed хэши. Сорс-чанкер lossless (fuzz-тесты).
- Нейтральность адаптера:
internal/llmне знает про перевод (src/target/coverage) — контент-вердикты живут в раннере (disposition-слой, post-settle).
Как гонять
go build ./... && go vet ./... && go test ./... -race # всё зелёное = норма
go run ./cmd/tmctl translate --config example/book.yaml # реальные вызовы — нужны ключи в .env
go run ./cmd/tmctl report --config example/book.yaml # $0, читает store
# live-conformance (реальные провайдеры, платно, вне CI):
set -a; . ./.env; set +a; TM_LIVE=1 go test -tags live -run TestLive -v ./internal/pipeline/
Финал каждой вехи — агентское адверсариальное селфревью (несколько дименсий → независимая верификация каждой находки → фиксы mutation-verified: реверт фикса валит именно его тест). Внешнее ревью — оркестратор.
Известный техдолг (не трогать молча — см. D-лог)
F3 at-most-once (после status/redrive); Escal.Chains — валидируемый мёртвый конфиг (раннер читает только escalate_to, полные цепочки = шаг 7); модели D3-эскалации не заведены в models.yaml (интерим-заглушки помечены); min-budget Kimi≥16k/Gemini≥8k — комментарии, не схема; фиксы границ памяти D16.