@llm-translate/cli 1.0.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +51 -0
- package/.env.example +33 -0
- package/.github/workflows/docs-pages.yml +57 -0
- package/.github/workflows/release.yml +49 -0
- package/.translaterc.json +44 -0
- package/CLAUDE.md +243 -0
- package/Dockerfile +55 -0
- package/README.md +371 -0
- package/RFC.md +1595 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +4494 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +1152 -0
- package/dist/index.js +3841 -0
- package/dist/index.js.map +1 -0
- package/docker-compose.yml +56 -0
- package/docs/.vitepress/config.ts +161 -0
- package/docs/api/agent.md +262 -0
- package/docs/api/engine.md +274 -0
- package/docs/api/index.md +171 -0
- package/docs/api/providers.md +304 -0
- package/docs/changelog.md +64 -0
- package/docs/cli/dir.md +243 -0
- package/docs/cli/file.md +213 -0
- package/docs/cli/glossary.md +273 -0
- package/docs/cli/index.md +129 -0
- package/docs/cli/init.md +158 -0
- package/docs/cli/serve.md +211 -0
- package/docs/glossary.json +235 -0
- package/docs/guide/chunking.md +272 -0
- package/docs/guide/configuration.md +139 -0
- package/docs/guide/cost-optimization.md +237 -0
- package/docs/guide/docker.md +371 -0
- package/docs/guide/getting-started.md +150 -0
- package/docs/guide/glossary.md +241 -0
- package/docs/guide/index.md +86 -0
- package/docs/guide/ollama.md +515 -0
- package/docs/guide/prompt-caching.md +221 -0
- package/docs/guide/providers.md +232 -0
- package/docs/guide/quality-control.md +206 -0
- package/docs/guide/vitepress-integration.md +265 -0
- package/docs/index.md +63 -0
- package/docs/ja/api/agent.md +262 -0
- package/docs/ja/api/engine.md +274 -0
- package/docs/ja/api/index.md +171 -0
- package/docs/ja/api/providers.md +304 -0
- package/docs/ja/changelog.md +64 -0
- package/docs/ja/cli/dir.md +243 -0
- package/docs/ja/cli/file.md +213 -0
- package/docs/ja/cli/glossary.md +273 -0
- package/docs/ja/cli/index.md +111 -0
- package/docs/ja/cli/init.md +158 -0
- package/docs/ja/guide/chunking.md +271 -0
- package/docs/ja/guide/configuration.md +139 -0
- package/docs/ja/guide/cost-optimization.md +30 -0
- package/docs/ja/guide/getting-started.md +150 -0
- package/docs/ja/guide/glossary.md +214 -0
- package/docs/ja/guide/index.md +32 -0
- package/docs/ja/guide/ollama.md +410 -0
- package/docs/ja/guide/prompt-caching.md +221 -0
- package/docs/ja/guide/providers.md +232 -0
- package/docs/ja/guide/quality-control.md +137 -0
- package/docs/ja/guide/vitepress-integration.md +265 -0
- package/docs/ja/index.md +58 -0
- package/docs/ko/api/agent.md +262 -0
- package/docs/ko/api/engine.md +274 -0
- package/docs/ko/api/index.md +171 -0
- package/docs/ko/api/providers.md +304 -0
- package/docs/ko/changelog.md +64 -0
- package/docs/ko/cli/dir.md +243 -0
- package/docs/ko/cli/file.md +213 -0
- package/docs/ko/cli/glossary.md +273 -0
- package/docs/ko/cli/index.md +111 -0
- package/docs/ko/cli/init.md +158 -0
- package/docs/ko/guide/chunking.md +271 -0
- package/docs/ko/guide/configuration.md +139 -0
- package/docs/ko/guide/cost-optimization.md +30 -0
- package/docs/ko/guide/getting-started.md +150 -0
- package/docs/ko/guide/glossary.md +214 -0
- package/docs/ko/guide/index.md +32 -0
- package/docs/ko/guide/ollama.md +410 -0
- package/docs/ko/guide/prompt-caching.md +221 -0
- package/docs/ko/guide/providers.md +232 -0
- package/docs/ko/guide/quality-control.md +137 -0
- package/docs/ko/guide/vitepress-integration.md +265 -0
- package/docs/ko/index.md +58 -0
- package/docs/zh/api/agent.md +262 -0
- package/docs/zh/api/engine.md +274 -0
- package/docs/zh/api/index.md +171 -0
- package/docs/zh/api/providers.md +304 -0
- package/docs/zh/changelog.md +64 -0
- package/docs/zh/cli/dir.md +243 -0
- package/docs/zh/cli/file.md +213 -0
- package/docs/zh/cli/glossary.md +273 -0
- package/docs/zh/cli/index.md +111 -0
- package/docs/zh/cli/init.md +158 -0
- package/docs/zh/guide/chunking.md +271 -0
- package/docs/zh/guide/configuration.md +139 -0
- package/docs/zh/guide/cost-optimization.md +30 -0
- package/docs/zh/guide/getting-started.md +150 -0
- package/docs/zh/guide/glossary.md +214 -0
- package/docs/zh/guide/index.md +32 -0
- package/docs/zh/guide/ollama.md +410 -0
- package/docs/zh/guide/prompt-caching.md +221 -0
- package/docs/zh/guide/providers.md +232 -0
- package/docs/zh/guide/quality-control.md +137 -0
- package/docs/zh/guide/vitepress-integration.md +265 -0
- package/docs/zh/index.md +58 -0
- package/package.json +91 -0
- package/release.config.mjs +15 -0
- package/schemas/glossary.schema.json +110 -0
- package/src/cli/commands/dir.ts +469 -0
- package/src/cli/commands/file.ts +291 -0
- package/src/cli/commands/glossary.ts +221 -0
- package/src/cli/commands/init.ts +68 -0
- package/src/cli/commands/serve.ts +60 -0
- package/src/cli/index.ts +64 -0
- package/src/cli/options.ts +59 -0
- package/src/core/agent.ts +1119 -0
- package/src/core/chunker.ts +391 -0
- package/src/core/engine.ts +634 -0
- package/src/errors.ts +188 -0
- package/src/index.ts +147 -0
- package/src/integrations/vitepress.ts +549 -0
- package/src/parsers/markdown.ts +383 -0
- package/src/providers/claude.ts +259 -0
- package/src/providers/interface.ts +109 -0
- package/src/providers/ollama.ts +379 -0
- package/src/providers/openai.ts +308 -0
- package/src/providers/registry.ts +153 -0
- package/src/server/index.ts +152 -0
- package/src/server/middleware/auth.ts +93 -0
- package/src/server/middleware/logger.ts +90 -0
- package/src/server/routes/health.ts +84 -0
- package/src/server/routes/translate.ts +210 -0
- package/src/server/types.ts +138 -0
- package/src/services/cache.ts +899 -0
- package/src/services/config.ts +217 -0
- package/src/services/glossary.ts +247 -0
- package/src/types/analysis.ts +164 -0
- package/src/types/index.ts +265 -0
- package/src/types/modes.ts +121 -0
- package/src/types/mqm.ts +157 -0
- package/src/utils/logger.ts +141 -0
- package/src/utils/tokens.ts +116 -0
- package/tests/fixtures/glossaries/ml-glossary.json +53 -0
- package/tests/fixtures/input/lynq-installation.ko.md +350 -0
- package/tests/fixtures/input/lynq-installation.md +350 -0
- package/tests/fixtures/input/simple.ko.md +27 -0
- package/tests/fixtures/input/simple.md +27 -0
- package/tests/unit/chunker.test.ts +229 -0
- package/tests/unit/glossary.test.ts +146 -0
- package/tests/unit/markdown.test.ts +205 -0
- package/tests/unit/tokens.test.ts +81 -0
- package/tsconfig.json +28 -0
- package/tsup.config.ts +34 -0
- package/vitest.config.ts +16 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
version: '3.8'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
# ==============================================================================
|
|
5
|
+
# llm-translate API Server
|
|
6
|
+
# ==============================================================================
|
|
7
|
+
llm-translate:
|
|
8
|
+
build: .
|
|
9
|
+
container_name: llm-translate-api
|
|
10
|
+
restart: unless-stopped
|
|
11
|
+
ports:
|
|
12
|
+
- "${TRANSLATE_PORT:-3000}:3000"
|
|
13
|
+
environment:
|
|
14
|
+
- NODE_ENV=production
|
|
15
|
+
- TRANSLATE_API_KEY=${TRANSLATE_API_KEY}
|
|
16
|
+
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
|
17
|
+
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
|
18
|
+
- OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://ollama:11434}
|
|
19
|
+
healthcheck:
|
|
20
|
+
test: ["CMD", "node", "-e", "fetch('http://localhost:3000/health/live').then(r => r.ok ? process.exit(0) : process.exit(1))"]
|
|
21
|
+
interval: 30s
|
|
22
|
+
timeout: 5s
|
|
23
|
+
retries: 3
|
|
24
|
+
start_period: 10s
|
|
25
|
+
logging:
|
|
26
|
+
driver: json-file
|
|
27
|
+
options:
|
|
28
|
+
max-size: "10m"
|
|
29
|
+
max-file: "3"
|
|
30
|
+
deploy:
|
|
31
|
+
resources:
|
|
32
|
+
limits:
|
|
33
|
+
memory: 512M
|
|
34
|
+
reservations:
|
|
35
|
+
memory: 256M
|
|
36
|
+
|
|
37
|
+
# ==============================================================================
|
|
38
|
+
# Optional: Local Ollama service for self-hosted LLM
|
|
39
|
+
# Enable with: docker compose --profile with-ollama up
|
|
40
|
+
# ==============================================================================
|
|
41
|
+
ollama:
|
|
42
|
+
image: ollama/ollama:latest
|
|
43
|
+
container_name: ollama
|
|
44
|
+
profiles:
|
|
45
|
+
- with-ollama
|
|
46
|
+
ports:
|
|
47
|
+
- "11434:11434"
|
|
48
|
+
volumes:
|
|
49
|
+
- ollama-models:/root/.ollama
|
|
50
|
+
deploy:
|
|
51
|
+
resources:
|
|
52
|
+
limits:
|
|
53
|
+
memory: 4G
|
|
54
|
+
|
|
55
|
+
volumes:
|
|
56
|
+
ollama-models:
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { defineConfig } from 'vitepress';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { generateLocaleConfig } from '../../dist/index.js';
|
|
5
|
+
|
|
6
|
+
// Get docs directory path relative to this config file
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const docsDir = resolve(__dirname, '..');
|
|
9
|
+
|
|
10
|
+
// Generate locale configurations automatically
|
|
11
|
+
const locales = generateLocaleConfig(docsDir, {
|
|
12
|
+
defaultLocale: 'en',
|
|
13
|
+
locales: ['ko', 'ja', 'zh'],
|
|
14
|
+
labels: {
|
|
15
|
+
en: 'English',
|
|
16
|
+
ko: '한국어',
|
|
17
|
+
ja: '日本語',
|
|
18
|
+
zh: '中文',
|
|
19
|
+
},
|
|
20
|
+
descriptions: {
|
|
21
|
+
en: 'LLM-powered document translation CLI with glossary enforcement',
|
|
22
|
+
ko: 'LLM 기반 문서 번역 CLI - 용어집 적용 및 품질 관리',
|
|
23
|
+
ja: 'LLM駆動のドキュメント翻訳CLI - 用語集の適用と品質管理',
|
|
24
|
+
zh: 'LLM驱动的文档翻译CLI - 术语表应用和质量管理',
|
|
25
|
+
},
|
|
26
|
+
sidebarDirs: ['guide', 'cli', 'api'],
|
|
27
|
+
useTitleFromHeading: true,
|
|
28
|
+
translations: {
|
|
29
|
+
ko: {
|
|
30
|
+
editLinkText: 'GitHub에서 이 페이지 편집하기',
|
|
31
|
+
docFooter: { prev: '이전 페이지', next: '다음 페이지' },
|
|
32
|
+
outline: { label: '목차' },
|
|
33
|
+
lastUpdated: { text: '최종 업데이트' },
|
|
34
|
+
returnToTopLabel: '맨 위로',
|
|
35
|
+
sidebarMenuLabel: '메뉴',
|
|
36
|
+
darkModeSwitchLabel: '다크 모드',
|
|
37
|
+
},
|
|
38
|
+
ja: {
|
|
39
|
+
editLinkText: 'GitHubでこのページを編集する',
|
|
40
|
+
docFooter: { prev: '前のページ', next: '次のページ' },
|
|
41
|
+
outline: { label: '目次' },
|
|
42
|
+
lastUpdated: { text: '最終更新' },
|
|
43
|
+
returnToTopLabel: 'トップへ戻る',
|
|
44
|
+
sidebarMenuLabel: 'メニュー',
|
|
45
|
+
darkModeSwitchLabel: 'ダークモード',
|
|
46
|
+
},
|
|
47
|
+
zh: {
|
|
48
|
+
editLinkText: '在 GitHub 上编辑此页',
|
|
49
|
+
docFooter: { prev: '上一页', next: '下一页' },
|
|
50
|
+
outline: { label: '目录' },
|
|
51
|
+
lastUpdated: { text: '最后更新' },
|
|
52
|
+
returnToTopLabel: '返回顶部',
|
|
53
|
+
sidebarMenuLabel: '菜单',
|
|
54
|
+
darkModeSwitchLabel: '深色模式',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Shared configuration
|
|
60
|
+
const shared = defineConfig({
|
|
61
|
+
title: 'llm-translate',
|
|
62
|
+
base: '/llm-translate/',
|
|
63
|
+
head: [['link', { rel: 'icon', href: '/favicon.ico' }]],
|
|
64
|
+
|
|
65
|
+
themeConfig: {
|
|
66
|
+
logo: '/logo.svg',
|
|
67
|
+
socialLinks: [
|
|
68
|
+
{ icon: 'github', link: 'https://github.com/selenehyun/llm-translate' },
|
|
69
|
+
],
|
|
70
|
+
search: {
|
|
71
|
+
provider: 'local',
|
|
72
|
+
options: {
|
|
73
|
+
locales: {
|
|
74
|
+
ko: {
|
|
75
|
+
translations: {
|
|
76
|
+
button: {
|
|
77
|
+
buttonText: '검색',
|
|
78
|
+
buttonAriaLabel: '검색',
|
|
79
|
+
},
|
|
80
|
+
modal: {
|
|
81
|
+
displayDetails: '상세 목록 표시',
|
|
82
|
+
resetButtonTitle: '검색 지우기',
|
|
83
|
+
backButtonTitle: '검색 닫기',
|
|
84
|
+
noResultsText: '결과를 찾을 수 없습니다',
|
|
85
|
+
footer: {
|
|
86
|
+
selectText: '선택',
|
|
87
|
+
selectKeyAriaLabel: '선택하기',
|
|
88
|
+
navigateText: '탐색',
|
|
89
|
+
navigateUpKeyAriaLabel: '위로',
|
|
90
|
+
navigateDownKeyAriaLabel: '아래로',
|
|
91
|
+
closeText: '닫기',
|
|
92
|
+
closeKeyAriaLabel: 'esc',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
ja: {
|
|
98
|
+
translations: {
|
|
99
|
+
button: {
|
|
100
|
+
buttonText: '検索',
|
|
101
|
+
buttonAriaLabel: '検索',
|
|
102
|
+
},
|
|
103
|
+
modal: {
|
|
104
|
+
displayDetails: '詳細リストを表示',
|
|
105
|
+
resetButtonTitle: '検索をクリア',
|
|
106
|
+
backButtonTitle: '検索を閉じる',
|
|
107
|
+
noResultsText: '結果が見つかりませんでした',
|
|
108
|
+
footer: {
|
|
109
|
+
selectText: '選択',
|
|
110
|
+
selectKeyAriaLabel: '選択する',
|
|
111
|
+
navigateText: '移動',
|
|
112
|
+
navigateUpKeyAriaLabel: '上へ',
|
|
113
|
+
navigateDownKeyAriaLabel: '下へ',
|
|
114
|
+
closeText: '閉じる',
|
|
115
|
+
closeKeyAriaLabel: 'esc',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
zh: {
|
|
121
|
+
translations: {
|
|
122
|
+
button: {
|
|
123
|
+
buttonText: '搜索',
|
|
124
|
+
buttonAriaLabel: '搜索',
|
|
125
|
+
},
|
|
126
|
+
modal: {
|
|
127
|
+
displayDetails: '显示详细列表',
|
|
128
|
+
resetButtonTitle: '清除搜索',
|
|
129
|
+
backButtonTitle: '关闭搜索',
|
|
130
|
+
noResultsText: '未找到结果',
|
|
131
|
+
footer: {
|
|
132
|
+
selectText: '选择',
|
|
133
|
+
selectKeyAriaLabel: '选择',
|
|
134
|
+
navigateText: '导航',
|
|
135
|
+
navigateUpKeyAriaLabel: '向上',
|
|
136
|
+
navigateDownKeyAriaLabel: '向下',
|
|
137
|
+
closeText: '关闭',
|
|
138
|
+
closeKeyAriaLabel: 'esc',
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
editLink: {
|
|
147
|
+
pattern: 'https://github.com/selenehyun/llm-translate/edit/main/docs/:path',
|
|
148
|
+
text: 'Edit this page on GitHub',
|
|
149
|
+
},
|
|
150
|
+
footer: {
|
|
151
|
+
message: 'Released under the MIT License.',
|
|
152
|
+
copyright: 'Copyright © 2025-present',
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Main config with locales
|
|
158
|
+
export default defineConfig({
|
|
159
|
+
...shared,
|
|
160
|
+
locales,
|
|
161
|
+
});
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# TranslationAgent
|
|
2
|
+
|
|
3
|
+
::: info Translations
|
|
4
|
+
All non-English documentation is automatically translated using Claude Sonnet 4.
|
|
5
|
+
:::
|
|
6
|
+
|
|
7
|
+
Low-level translation agent implementing the Self-Refine algorithm.
|
|
8
|
+
|
|
9
|
+
## Constructor
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { TranslationAgent } from '@llm-translate/cli';
|
|
13
|
+
|
|
14
|
+
const agent = new TranslationAgent(options: TranslationAgentOptions);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Options
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
interface TranslationAgentOptions {
|
|
21
|
+
provider: LLMProvider;
|
|
22
|
+
qualityThreshold?: number; // Default: 85
|
|
23
|
+
maxIterations?: number; // Default: 4
|
|
24
|
+
verbose?: boolean; // Default: false
|
|
25
|
+
strictQuality?: boolean; // Default: false
|
|
26
|
+
enableCaching?: boolean; // Default: true for Claude
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Methods
|
|
31
|
+
|
|
32
|
+
### translate
|
|
33
|
+
|
|
34
|
+
Translate content using Self-Refine loop.
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
const result = await agent.translate(request: TranslationRequest);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### Request
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
interface TranslationRequest {
|
|
44
|
+
content: string; // Source text to translate
|
|
45
|
+
sourceLang: string; // Source language code
|
|
46
|
+
targetLang: string; // Target language code
|
|
47
|
+
glossary?: ResolvedGlossary; // Resolved glossary for target language
|
|
48
|
+
context?: {
|
|
49
|
+
documentPurpose?: string;
|
|
50
|
+
previousChunks?: string[];
|
|
51
|
+
documentSummary?: string;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### Result
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
interface TranslationResult {
|
|
60
|
+
content: string; // Translated text
|
|
61
|
+
metadata: {
|
|
62
|
+
qualityScore: number;
|
|
63
|
+
qualityThreshold: number;
|
|
64
|
+
thresholdMet: boolean;
|
|
65
|
+
iterations: number;
|
|
66
|
+
tokensUsed: {
|
|
67
|
+
input: number;
|
|
68
|
+
output: number;
|
|
69
|
+
cacheRead?: number;
|
|
70
|
+
cacheWrite?: number;
|
|
71
|
+
};
|
|
72
|
+
duration: number;
|
|
73
|
+
provider: string;
|
|
74
|
+
model: string;
|
|
75
|
+
};
|
|
76
|
+
glossaryCompliance?: {
|
|
77
|
+
applied: string[]; // Terms successfully applied
|
|
78
|
+
missed: string[]; // Terms not found in translation
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Self-Refine Algorithm
|
|
84
|
+
|
|
85
|
+
The agent implements this iterative refinement process:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// Pseudocode
|
|
89
|
+
async translate(request) {
|
|
90
|
+
// 1. Initial translation with glossary
|
|
91
|
+
let translation = await generateInitialTranslation(request);
|
|
92
|
+
let iterations = 1;
|
|
93
|
+
|
|
94
|
+
while (iterations < maxIterations) {
|
|
95
|
+
// 2. Evaluate quality
|
|
96
|
+
const evaluation = await evaluateQuality(translation);
|
|
97
|
+
|
|
98
|
+
if (evaluation.score >= qualityThreshold) {
|
|
99
|
+
break; // Quality met
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 3. Generate improvement suggestions
|
|
103
|
+
const suggestions = await generateReflection(translation);
|
|
104
|
+
|
|
105
|
+
// 4. Apply improvements
|
|
106
|
+
translation = await improveTranslation(translation, suggestions);
|
|
107
|
+
iterations++;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return { content: translation, metadata: { ... } };
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Example Usage
|
|
115
|
+
|
|
116
|
+
### Basic Translation
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { TranslationAgent, createClaudeProvider } from '@llm-translate/cli';
|
|
120
|
+
|
|
121
|
+
const provider = createClaudeProvider();
|
|
122
|
+
const agent = new TranslationAgent({
|
|
123
|
+
provider,
|
|
124
|
+
qualityThreshold: 85,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const result = await agent.translate({
|
|
128
|
+
content: 'Hello, world!',
|
|
129
|
+
sourceLang: 'en',
|
|
130
|
+
targetLang: 'ko',
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
console.log(result.content); // 안녕하세요, 세계!
|
|
134
|
+
console.log(result.metadata.qualityScore); // 92
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### With Glossary
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { loadGlossary, resolveGlossary } from '@llm-translate/cli';
|
|
141
|
+
|
|
142
|
+
const glossary = await loadGlossary('./glossary.json');
|
|
143
|
+
const resolved = resolveGlossary(glossary, 'ko');
|
|
144
|
+
|
|
145
|
+
const result = await agent.translate({
|
|
146
|
+
content: 'The component renders a prop.',
|
|
147
|
+
sourceLang: 'en',
|
|
148
|
+
targetLang: 'ko',
|
|
149
|
+
glossary: resolved,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
console.log(result.glossaryCompliance);
|
|
153
|
+
// { applied: ['component', 'prop'], missed: [] }
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### With Context
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const result = await agent.translate({
|
|
160
|
+
content: 'Click the button to continue.',
|
|
161
|
+
sourceLang: 'en',
|
|
162
|
+
targetLang: 'ko',
|
|
163
|
+
context: {
|
|
164
|
+
documentPurpose: 'User interface instructions',
|
|
165
|
+
previousChunks: [
|
|
166
|
+
'이전 단계에서 설정을 완료했습니다.', // Previous translation
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Strict Quality Mode
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { TranslationError, ErrorCode } from '@llm-translate/cli';
|
|
176
|
+
|
|
177
|
+
const agent = new TranslationAgent({
|
|
178
|
+
provider,
|
|
179
|
+
qualityThreshold: 95,
|
|
180
|
+
strictQuality: true, // Throw if threshold not met
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
const result = await agent.translate(request);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (error instanceof TranslationError &&
|
|
187
|
+
error.code === ErrorCode.QUALITY_THRESHOLD_NOT_MET) {
|
|
188
|
+
console.log(`Quality: ${error.details.score}/${error.details.threshold}`);
|
|
189
|
+
console.log(`Issues: ${error.details.issues.join(', ')}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Quality Evaluation
|
|
195
|
+
|
|
196
|
+
The agent evaluates translations on four criteria:
|
|
197
|
+
|
|
198
|
+
| Criterion | Weight | Description |
|
|
199
|
+
|-----------|--------|-------------|
|
|
200
|
+
| Semantic Accuracy | 40% | Meaning preservation |
|
|
201
|
+
| Fluency | 25% | Natural language flow |
|
|
202
|
+
| Glossary Compliance | 20% | Term consistency |
|
|
203
|
+
| Format Preservation | 15% | Structure maintenance |
|
|
204
|
+
|
|
205
|
+
### Evaluation Result
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
interface QualityEvaluation {
|
|
209
|
+
score: number; // 0-100
|
|
210
|
+
breakdown: {
|
|
211
|
+
accuracy: number; // 0-40
|
|
212
|
+
fluency: number; // 0-25
|
|
213
|
+
glossary: number; // 0-20
|
|
214
|
+
format: number; // 0-15
|
|
215
|
+
};
|
|
216
|
+
issues: string[]; // Specific problems identified
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Prompt Caching
|
|
221
|
+
|
|
222
|
+
When `enableCaching` is true (default for Claude), the agent structures prompts for caching:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
┌─────────────────────────────────┐
|
|
226
|
+
│ System Instructions (CACHED) │ ← Reused across chunks
|
|
227
|
+
├─────────────────────────────────┤
|
|
228
|
+
│ Glossary (CACHED) │ ← Reused across chunks
|
|
229
|
+
├─────────────────────────────────┤
|
|
230
|
+
│ Source Text (NOT cached) │ ← Changes per chunk
|
|
231
|
+
└─────────────────────────────────┘
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
This can reduce costs by 40-50% for multi-chunk documents.
|
|
235
|
+
|
|
236
|
+
## Advanced: Custom Evaluation
|
|
237
|
+
|
|
238
|
+
Override the default evaluation logic:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
class CustomAgent extends TranslationAgent {
|
|
242
|
+
protected async evaluateQuality(
|
|
243
|
+
source: string,
|
|
244
|
+
translation: string,
|
|
245
|
+
sourceLang: string,
|
|
246
|
+
targetLang: string
|
|
247
|
+
): Promise<QualityEvaluation> {
|
|
248
|
+
// Custom evaluation logic
|
|
249
|
+
const baseEval = await super.evaluateQuality(
|
|
250
|
+
source, translation, sourceLang, targetLang
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
// Add custom checks
|
|
254
|
+
if (translation.length < source.length * 0.5) {
|
|
255
|
+
baseEval.score -= 10;
|
|
256
|
+
baseEval.issues.push('Translation suspiciously short');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return baseEval;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|