Agentic translation engine: an LLM translation pipeline with a review/refine loop, native multi-language output, storage-agnostic structured I/O, and translation memory. Exposed via CLI, HTTP API, and MCP.
Production translation needs more than one-shot LLM calls. yaku runs an agentic loop — draft, deterministic quality gates, an independent LLM reviewer, optional back-translation — and refines until the output passes or a budget is hit. Content fragmented across separate DB fields is assembled for full context, translated together, then returned keyed by stable segment ids so the caller writes each piece back to its own field.
@yaku/core— the engine:translate(), Zod I/O schemas, the agentic refine loop, pluggable LLM providers, pluggable SQLite/Postgres translation memory, deterministic gates.@yaku/cli—yaku translate/yaku tmcommands.@yaku/api— HTTP server:POST /translate,GET /health.@yaku/mcp— MCP server exposingtranslate,tm_lookup, andtm_invalidatetools.@yaku/autotune— autonomous optimizer: tunes config knobs + prompt templates to maximize quality (LLM-as-judge on a held-out gold set) while minimizing cost, saving winners as versioned profiles.
pnpm install
pnpm build# request.json = a TranslationRequest
node packages/cli/dist/index.js translate --in request.json --out response.json --provider openai
# or pipe via stdin/stdout:
echo '{"sourceLang":"en","targetLangs":["ja","ko"],"document":{"segments":[{"id":"title","text":"Welcome"}]}}' \
| OPENAI_API_KEY=sk-... node packages/cli/dist/index.js translate --provider openaiFlags:
--source <lang>/--target <langs>(comma-separated) — override the request's source/target languages without editing the JSON.--trace <none|summary|full>— override the trace level.--tm <path>— SQLite translation-memory path (default:memory:).--profile-base <dir>— apply the active autotune profile from this directory as config defaults (request values win).
Exit codes: 0 ok, 1 partial, 2 failed. Use --provider mock to validate wiring without an API key.
Manage translation memory:
node packages/cli/dist/index.js tm export --tm yaku-tm.sqlite
node packages/cli/dist/index.js tm import --tm yaku-tm.sqlite --in entries.json
node packages/cli/dist/index.js tm invalidate --tm yaku-tm.sqlite --target ja
# wiping everything requires explicit confirmation:
node packages/cli/dist/index.js tm invalidate --tm yaku-tm.sqlite --allOPENAI_API_KEY=sk-... node packages/api/dist/index.js # listens on PORT (default 3000)
curl -s localhost:3000/translate -H 'content-type: application/json' -d @request.jsonEndpoints: POST /translate, GET /health.
OPENAI_API_KEY=sk-... node packages/mcp/dist/index.js # MCP server over stdioTools: translate, tm_lookup (exact TM lookup), tm_invalidate (an unfiltered
invalidate requires all: true).
Both API and MCP servers honor these env vars:
| Variable | Default | Meaning |
|---|---|---|
OPENAI_API_KEY |
— | OpenAI credential (required with openai provider) |
YAKU_PROVIDER |
openai |
LLM provider name |
YAKU_TM_PATH |
yaku-tm.sqlite |
SQLite translation-memory path |
YAKU_PROFILE_BASE |
unset | autotune dir whose active profile becomes defaults |
PORT (API only) |
3000 |
HTTP listen port |
@yaku/autotune runs an autonomous hill-climb that tunes engine config knobs and
prompt templates to maximize translation quality (LLM-as-judge on a held-out gold
set) while minimizing cost. Winners are saved as versioned profiles the engine
can load.
# build a held-out gold set from the activities dataset (writes autotune/gold/*.json):
node eval/build-gold.mjs --langs ja,ko --limit 5
# (gold files are TranslationRequest-shaped, no config — autotune injects that), then:
OPENAI_API_KEY=$(cat .openai-api-key) \
node packages/autotune/dist/cli.js run \
--profile activities --floor 85 --max-iter 12 --budget 5 --sample 6 \
--langs ja,ko --judge-model gpt-4o --translator-model gpt-4o-mini
node packages/autotune/dist/cli.js profiles # show the active profile
node packages/autotune/dist/cli.js show <runId> # print a run reportOther run flags: --gold <dir> (gold set directory, default autotune/gold),
--base <dir> (profiles/ledger directory, default autotune), --plateau <k>
(stop after K non-improving iterations, default 3). profiles and show also
accept --base.
Outputs: autotune/profiles/<name>-v<N>.json (winner), autotune/profiles/active.json
(pointer), autotune/ledger.jsonl (append-only audit trail), autotune/out/<runId>.md
(report). Use --dry-run to produce the profile + report without activating it.
A winner that fails to reproduce the quality floor on the full gold set is written
but never activated.
The judge model and gold set are fixed/independent of the search so the quality metric can't be gamed; during the search TM is disabled so the gold set is never memorized across candidates.
Input is a TranslationRequest: sourceLang, targetLangs[], a document with segments
(each with a stable id, text, optional metadata like group/order/maxChars/doNotTranslate/role;
role: "ui-label" marks a terse UI label so the expansion gate keeps it from ballooning into a sentence),
optional context, glossary, and config. Output is a TranslationResponse with one
LanguageResult per target language, each carrying per-segment results keyed by the same ids
(with status, sourceHash, tmMatch, confidence, warnings). Every input id appears exactly
once per language.
See docs/superpowers/specs/2026-06-26-yaku-translation-engine-design.md for the full design
and docs/superpowers/plans/2026-06-26-yaku-translation-engine.md for the implementation plan.
pnpm test # run all tests
pnpm typecheck # typecheck all packages
pnpm lint # lint