Skip to content

exilis/yaku

Repository files navigation

yaku

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.

Why

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.

Packages

  • @yaku/core — the engine: translate(), Zod I/O schemas, the agentic refine loop, pluggable LLM providers, pluggable SQLite/Postgres translation memory, deterministic gates.
  • @yaku/cliyaku translate / yaku tm commands.
  • @yaku/api — HTTP server: POST /translate, GET /health.
  • @yaku/mcp — MCP server exposing translate, tm_lookup, and tm_invalidate tools.
  • @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.

Install & build

pnpm install
pnpm build

CLI

# 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 openai

Flags:

  • --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 --all

API

OPENAI_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.json

Endpoints: POST /translate, GET /health.

MCP

OPENAI_API_KEY=sk-... node packages/mcp/dist/index.js   # MCP server over stdio

Tools: 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

Autotune (self-improving optimizer)

@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 report

Other 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.

Request / Response shape

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.

Development

pnpm test        # run all tests
pnpm typecheck   # typecheck all packages
pnpm lint        # lint

About

Agentic translation engine: review/refine loop, native multi-language, structured I/O, translation memory. CLI + API + MCP.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors