@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.
Files changed (157) hide show
  1. package/.dockerignore +51 -0
  2. package/.env.example +33 -0
  3. package/.github/workflows/docs-pages.yml +57 -0
  4. package/.github/workflows/release.yml +49 -0
  5. package/.translaterc.json +44 -0
  6. package/CLAUDE.md +243 -0
  7. package/Dockerfile +55 -0
  8. package/README.md +371 -0
  9. package/RFC.md +1595 -0
  10. package/dist/cli/index.d.ts +2 -0
  11. package/dist/cli/index.js +4494 -0
  12. package/dist/cli/index.js.map +1 -0
  13. package/dist/index.d.ts +1152 -0
  14. package/dist/index.js +3841 -0
  15. package/dist/index.js.map +1 -0
  16. package/docker-compose.yml +56 -0
  17. package/docs/.vitepress/config.ts +161 -0
  18. package/docs/api/agent.md +262 -0
  19. package/docs/api/engine.md +274 -0
  20. package/docs/api/index.md +171 -0
  21. package/docs/api/providers.md +304 -0
  22. package/docs/changelog.md +64 -0
  23. package/docs/cli/dir.md +243 -0
  24. package/docs/cli/file.md +213 -0
  25. package/docs/cli/glossary.md +273 -0
  26. package/docs/cli/index.md +129 -0
  27. package/docs/cli/init.md +158 -0
  28. package/docs/cli/serve.md +211 -0
  29. package/docs/glossary.json +235 -0
  30. package/docs/guide/chunking.md +272 -0
  31. package/docs/guide/configuration.md +139 -0
  32. package/docs/guide/cost-optimization.md +237 -0
  33. package/docs/guide/docker.md +371 -0
  34. package/docs/guide/getting-started.md +150 -0
  35. package/docs/guide/glossary.md +241 -0
  36. package/docs/guide/index.md +86 -0
  37. package/docs/guide/ollama.md +515 -0
  38. package/docs/guide/prompt-caching.md +221 -0
  39. package/docs/guide/providers.md +232 -0
  40. package/docs/guide/quality-control.md +206 -0
  41. package/docs/guide/vitepress-integration.md +265 -0
  42. package/docs/index.md +63 -0
  43. package/docs/ja/api/agent.md +262 -0
  44. package/docs/ja/api/engine.md +274 -0
  45. package/docs/ja/api/index.md +171 -0
  46. package/docs/ja/api/providers.md +304 -0
  47. package/docs/ja/changelog.md +64 -0
  48. package/docs/ja/cli/dir.md +243 -0
  49. package/docs/ja/cli/file.md +213 -0
  50. package/docs/ja/cli/glossary.md +273 -0
  51. package/docs/ja/cli/index.md +111 -0
  52. package/docs/ja/cli/init.md +158 -0
  53. package/docs/ja/guide/chunking.md +271 -0
  54. package/docs/ja/guide/configuration.md +139 -0
  55. package/docs/ja/guide/cost-optimization.md +30 -0
  56. package/docs/ja/guide/getting-started.md +150 -0
  57. package/docs/ja/guide/glossary.md +214 -0
  58. package/docs/ja/guide/index.md +32 -0
  59. package/docs/ja/guide/ollama.md +410 -0
  60. package/docs/ja/guide/prompt-caching.md +221 -0
  61. package/docs/ja/guide/providers.md +232 -0
  62. package/docs/ja/guide/quality-control.md +137 -0
  63. package/docs/ja/guide/vitepress-integration.md +265 -0
  64. package/docs/ja/index.md +58 -0
  65. package/docs/ko/api/agent.md +262 -0
  66. package/docs/ko/api/engine.md +274 -0
  67. package/docs/ko/api/index.md +171 -0
  68. package/docs/ko/api/providers.md +304 -0
  69. package/docs/ko/changelog.md +64 -0
  70. package/docs/ko/cli/dir.md +243 -0
  71. package/docs/ko/cli/file.md +213 -0
  72. package/docs/ko/cli/glossary.md +273 -0
  73. package/docs/ko/cli/index.md +111 -0
  74. package/docs/ko/cli/init.md +158 -0
  75. package/docs/ko/guide/chunking.md +271 -0
  76. package/docs/ko/guide/configuration.md +139 -0
  77. package/docs/ko/guide/cost-optimization.md +30 -0
  78. package/docs/ko/guide/getting-started.md +150 -0
  79. package/docs/ko/guide/glossary.md +214 -0
  80. package/docs/ko/guide/index.md +32 -0
  81. package/docs/ko/guide/ollama.md +410 -0
  82. package/docs/ko/guide/prompt-caching.md +221 -0
  83. package/docs/ko/guide/providers.md +232 -0
  84. package/docs/ko/guide/quality-control.md +137 -0
  85. package/docs/ko/guide/vitepress-integration.md +265 -0
  86. package/docs/ko/index.md +58 -0
  87. package/docs/zh/api/agent.md +262 -0
  88. package/docs/zh/api/engine.md +274 -0
  89. package/docs/zh/api/index.md +171 -0
  90. package/docs/zh/api/providers.md +304 -0
  91. package/docs/zh/changelog.md +64 -0
  92. package/docs/zh/cli/dir.md +243 -0
  93. package/docs/zh/cli/file.md +213 -0
  94. package/docs/zh/cli/glossary.md +273 -0
  95. package/docs/zh/cli/index.md +111 -0
  96. package/docs/zh/cli/init.md +158 -0
  97. package/docs/zh/guide/chunking.md +271 -0
  98. package/docs/zh/guide/configuration.md +139 -0
  99. package/docs/zh/guide/cost-optimization.md +30 -0
  100. package/docs/zh/guide/getting-started.md +150 -0
  101. package/docs/zh/guide/glossary.md +214 -0
  102. package/docs/zh/guide/index.md +32 -0
  103. package/docs/zh/guide/ollama.md +410 -0
  104. package/docs/zh/guide/prompt-caching.md +221 -0
  105. package/docs/zh/guide/providers.md +232 -0
  106. package/docs/zh/guide/quality-control.md +137 -0
  107. package/docs/zh/guide/vitepress-integration.md +265 -0
  108. package/docs/zh/index.md +58 -0
  109. package/package.json +91 -0
  110. package/release.config.mjs +15 -0
  111. package/schemas/glossary.schema.json +110 -0
  112. package/src/cli/commands/dir.ts +469 -0
  113. package/src/cli/commands/file.ts +291 -0
  114. package/src/cli/commands/glossary.ts +221 -0
  115. package/src/cli/commands/init.ts +68 -0
  116. package/src/cli/commands/serve.ts +60 -0
  117. package/src/cli/index.ts +64 -0
  118. package/src/cli/options.ts +59 -0
  119. package/src/core/agent.ts +1119 -0
  120. package/src/core/chunker.ts +391 -0
  121. package/src/core/engine.ts +634 -0
  122. package/src/errors.ts +188 -0
  123. package/src/index.ts +147 -0
  124. package/src/integrations/vitepress.ts +549 -0
  125. package/src/parsers/markdown.ts +383 -0
  126. package/src/providers/claude.ts +259 -0
  127. package/src/providers/interface.ts +109 -0
  128. package/src/providers/ollama.ts +379 -0
  129. package/src/providers/openai.ts +308 -0
  130. package/src/providers/registry.ts +153 -0
  131. package/src/server/index.ts +152 -0
  132. package/src/server/middleware/auth.ts +93 -0
  133. package/src/server/middleware/logger.ts +90 -0
  134. package/src/server/routes/health.ts +84 -0
  135. package/src/server/routes/translate.ts +210 -0
  136. package/src/server/types.ts +138 -0
  137. package/src/services/cache.ts +899 -0
  138. package/src/services/config.ts +217 -0
  139. package/src/services/glossary.ts +247 -0
  140. package/src/types/analysis.ts +164 -0
  141. package/src/types/index.ts +265 -0
  142. package/src/types/modes.ts +121 -0
  143. package/src/types/mqm.ts +157 -0
  144. package/src/utils/logger.ts +141 -0
  145. package/src/utils/tokens.ts +116 -0
  146. package/tests/fixtures/glossaries/ml-glossary.json +53 -0
  147. package/tests/fixtures/input/lynq-installation.ko.md +350 -0
  148. package/tests/fixtures/input/lynq-installation.md +350 -0
  149. package/tests/fixtures/input/simple.ko.md +27 -0
  150. package/tests/fixtures/input/simple.md +27 -0
  151. package/tests/unit/chunker.test.ts +229 -0
  152. package/tests/unit/glossary.test.ts +146 -0
  153. package/tests/unit/markdown.test.ts +205 -0
  154. package/tests/unit/tokens.test.ts +81 -0
  155. package/tsconfig.json +28 -0
  156. package/tsup.config.ts +34 -0
  157. package/vitest.config.ts +16 -0
@@ -0,0 +1,90 @@
1
+ import { createMiddleware } from 'hono/factory';
2
+ import type { Context, Next } from 'hono';
3
+
4
+ // ============================================================================
5
+ // Types
6
+ // ============================================================================
7
+
8
+ export interface LoggerConfig {
9
+ json: boolean;
10
+ }
11
+
12
+ interface LogEntry {
13
+ timestamp: string;
14
+ requestId: string;
15
+ method: string;
16
+ path: string;
17
+ status: number;
18
+ duration: number;
19
+ userAgent?: string;
20
+ }
21
+
22
+ // ============================================================================
23
+ // Logger Middleware
24
+ // ============================================================================
25
+
26
+ /**
27
+ * Request logging middleware with structured JSON output for containers
28
+ */
29
+ export function createLoggerMiddleware(config: LoggerConfig) {
30
+ return createMiddleware(async (c: Context, next: Next) => {
31
+ const start = Date.now();
32
+ const requestId = generateRequestId();
33
+
34
+ // Store request ID for correlation in other middleware/handlers
35
+ c.set('requestId', requestId);
36
+
37
+ const method = c.req.method;
38
+ const path = c.req.path;
39
+
40
+ await next();
41
+
42
+ const duration = Date.now() - start;
43
+ const status = c.res.status;
44
+
45
+ if (config.json) {
46
+ // Structured JSON logging for container environments
47
+ const entry: LogEntry = {
48
+ timestamp: new Date().toISOString(),
49
+ requestId,
50
+ method,
51
+ path,
52
+ status,
53
+ duration,
54
+ userAgent: c.req.header('User-Agent'),
55
+ };
56
+ console.log(JSON.stringify(entry));
57
+ } else {
58
+ // Human-readable logging for development
59
+ const statusColor = getStatusColor(status);
60
+ console.log(`${statusColor}${status}\x1b[0m ${method} ${path} - ${duration}ms`);
61
+ }
62
+ });
63
+ }
64
+
65
+ // ============================================================================
66
+ // Helper Functions
67
+ // ============================================================================
68
+
69
+ /**
70
+ * Generate a short unique request ID
71
+ */
72
+ function generateRequestId(): string {
73
+ return Math.random().toString(36).substring(2, 10);
74
+ }
75
+
76
+ /**
77
+ * Get ANSI color code based on HTTP status
78
+ */
79
+ function getStatusColor(status: number): string {
80
+ if (status >= 500) {
81
+ return '\x1b[31m'; // Red for server errors
82
+ }
83
+ if (status >= 400) {
84
+ return '\x1b[33m'; // Yellow for client errors
85
+ }
86
+ if (status >= 300) {
87
+ return '\x1b[36m'; // Cyan for redirects
88
+ }
89
+ return '\x1b[32m'; // Green for success
90
+ }
@@ -0,0 +1,84 @@
1
+ import { Hono } from 'hono';
2
+ import type { HealthResponse } from '../types.js';
3
+ import {
4
+ getAvailableProviders,
5
+ getProviderConfigFromEnv,
6
+ } from '../../providers/registry.js';
7
+ import type { ProviderName } from '../../types/index.js';
8
+
9
+ // ============================================================================
10
+ // Health Router
11
+ // ============================================================================
12
+
13
+ const healthRouter = new Hono();
14
+
15
+ // Track server start time for uptime calculation
16
+ const startTime = Date.now();
17
+
18
+ /**
19
+ * GET /health - Comprehensive health check endpoint
20
+ * Suitable for k8s liveness/readiness probes
21
+ */
22
+ healthRouter.get('/', async (c) => {
23
+ const providers = getAvailableProviders();
24
+
25
+ const providerStatus = providers.map((name: ProviderName) => {
26
+ const config = getProviderConfigFromEnv(name);
27
+
28
+ // Check if provider has required configuration
29
+ let available = false;
30
+ if (name === 'ollama') {
31
+ // Ollama doesn't require API key, just assumes server is running
32
+ available = true;
33
+ } else {
34
+ available = !!config.apiKey;
35
+ }
36
+
37
+ return { name, available };
38
+ });
39
+
40
+ const anyProviderAvailable = providerStatus.some((p) => p.available);
41
+
42
+ const response: HealthResponse = {
43
+ status: anyProviderAvailable ? 'healthy' : 'degraded',
44
+ version: process.env['npm_package_version'] ?? '0.1.0',
45
+ uptime: Math.floor((Date.now() - startTime) / 1000),
46
+ providers: providerStatus,
47
+ };
48
+
49
+ // Return 503 if no providers available (for k8s readiness probe)
50
+ const status = anyProviderAvailable ? 200 : 503;
51
+
52
+ return c.json(response, status);
53
+ });
54
+
55
+ /**
56
+ * GET /health/live - Simple liveness probe
57
+ * Returns 200 as long as the server is running
58
+ */
59
+ healthRouter.get('/live', (c) => {
60
+ return c.json({ status: 'ok' });
61
+ });
62
+
63
+ /**
64
+ * GET /health/ready - Readiness probe
65
+ * Returns 200 if the server is ready to accept requests
66
+ */
67
+ healthRouter.get('/ready', async (c) => {
68
+ const providers = getAvailableProviders();
69
+
70
+ // Check if at least one provider is configured
71
+ const hasConfiguredProvider = providers.some((name: ProviderName) => {
72
+ if (name === 'ollama') return true;
73
+ const config = getProviderConfigFromEnv(name);
74
+ return !!config.apiKey;
75
+ });
76
+
77
+ if (hasConfiguredProvider) {
78
+ return c.json({ status: 'ready' });
79
+ }
80
+
81
+ return c.json({ status: 'not_ready', reason: 'No providers configured' }, 503);
82
+ });
83
+
84
+ export { healthRouter };
@@ -0,0 +1,210 @@
1
+ import { Hono, type Context } from 'hono';
2
+ import { zValidator } from '@hono/zod-validator';
3
+ import {
4
+ TranslateRequestSchema,
5
+ MODE_PRESETS,
6
+ type TranslateResponse,
7
+ type ErrorResponse,
8
+ type InlineGlossaryTerm,
9
+ type HonoVariables,
10
+ } from '../types.js';
11
+ import { createTranslationEngine } from '../../core/engine.js';
12
+ import { loadConfig } from '../../services/config.js';
13
+ import { TranslationError, ErrorCode } from '../../errors.js';
14
+ import type { ResolvedGlossary, ResolvedGlossaryTerm } from '../../types/index.js';
15
+
16
+ // ============================================================================
17
+ // Translate Router
18
+ // ============================================================================
19
+
20
+ const translateRouter = new Hono<{ Variables: HonoVariables }>();
21
+
22
+ /**
23
+ * POST /translate - Main translation endpoint
24
+ */
25
+ translateRouter.post(
26
+ '/',
27
+ zValidator('json', TranslateRequestSchema, (result, c) => {
28
+ if (!result.success) {
29
+ const errors = result.error.errors.map((e) => ({
30
+ field: e.path.join('.'),
31
+ message: e.message,
32
+ }));
33
+
34
+ return c.json<ErrorResponse>(
35
+ {
36
+ error: 'Validation failed',
37
+ code: 'VALIDATION_ERROR',
38
+ details: { errors },
39
+ },
40
+ 400
41
+ );
42
+ }
43
+ // Validation successful - continue to handler
44
+ return undefined;
45
+ }),
46
+ async (c) => {
47
+ const body = c.req.valid('json');
48
+ const requestId = c.get('requestId') ?? 'unknown';
49
+ const startTime = Date.now();
50
+
51
+ try {
52
+ // Load base config
53
+ const baseConfig = await loadConfig();
54
+
55
+ // Get mode presets
56
+ const modeConfig = MODE_PRESETS[body.mode ?? 'balanced'];
57
+
58
+ // Build config with overrides
59
+ const config = {
60
+ ...baseConfig,
61
+ languages: {
62
+ ...baseConfig.languages,
63
+ source: body.sourceLang,
64
+ targets: [body.targetLang],
65
+ },
66
+ provider: {
67
+ ...baseConfig.provider,
68
+ default: body.provider ?? baseConfig.provider.default,
69
+ model: body.model ?? baseConfig.provider.model,
70
+ },
71
+ quality: {
72
+ ...baseConfig.quality,
73
+ threshold: body.qualityThreshold ?? modeConfig.qualityThreshold,
74
+ maxIterations: body.maxIterations ?? modeConfig.maxIterations,
75
+ },
76
+ };
77
+
78
+ // Create engine (API mode doesn't use file cache)
79
+ const engine = createTranslationEngine({
80
+ config,
81
+ verbose: false,
82
+ noCache: true,
83
+ });
84
+
85
+ // Convert inline glossary to resolved format if provided
86
+ // Note: glossary support via inline terms will be implemented
87
+ // when TranslationEngine supports passing glossary directly
88
+ if (body.glossary && body.glossary.length > 0) {
89
+ // TODO: Pass glossary to engine when supported
90
+ convertInlineGlossary(body.glossary, body.sourceLang, body.targetLang);
91
+ }
92
+
93
+ // Translate content
94
+ const result = await engine.translateContent({
95
+ content: body.content,
96
+ sourceLang: body.sourceLang,
97
+ targetLang: body.targetLang,
98
+ format: body.format,
99
+ qualityThreshold: config.quality.threshold,
100
+ maxIterations: config.quality.maxIterations,
101
+ context: body.context,
102
+ });
103
+
104
+ const duration = Date.now() - startTime;
105
+
106
+ const response: TranslateResponse = {
107
+ translated: result.content,
108
+ quality: result.metadata.averageQuality,
109
+ iterations: result.metadata.totalIterations,
110
+ tokensUsed: {
111
+ input: result.metadata.tokensUsed.input,
112
+ output: result.metadata.tokensUsed.output,
113
+ },
114
+ glossaryCompliance: result.glossaryCompliance
115
+ ? {
116
+ applied: result.glossaryCompliance.applied,
117
+ missed: result.glossaryCompliance.missed,
118
+ }
119
+ : undefined,
120
+ duration,
121
+ provider: result.metadata.provider,
122
+ model: result.metadata.model,
123
+ };
124
+
125
+ return c.json(response, 200);
126
+ } catch (error) {
127
+ return handleTranslationError(c, error, requestId);
128
+ }
129
+ }
130
+ );
131
+
132
+ // ============================================================================
133
+ // Helper Functions
134
+ // ============================================================================
135
+
136
+ /**
137
+ * Convert inline glossary terms to resolved glossary format
138
+ */
139
+ function convertInlineGlossary(
140
+ terms: InlineGlossaryTerm[],
141
+ sourceLang: string,
142
+ targetLang: string
143
+ ): ResolvedGlossary {
144
+ return {
145
+ metadata: {
146
+ name: 'inline',
147
+ sourceLang,
148
+ targetLang,
149
+ version: '1.0',
150
+ },
151
+ terms: terms.map(
152
+ (term): ResolvedGlossaryTerm => ({
153
+ source: term.source,
154
+ target: term.doNotTranslate ? term.source : term.target,
155
+ context: term.context,
156
+ caseSensitive: term.caseSensitive ?? false,
157
+ doNotTranslate: term.doNotTranslate ?? false,
158
+ })
159
+ ),
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Handle translation errors and return appropriate HTTP response
165
+ */
166
+ type StatusCode = 200 | 400 | 401 | 422 | 429 | 500 | 502;
167
+
168
+ function handleTranslationError(
169
+ c: Context<{ Variables: HonoVariables }>,
170
+ error: unknown,
171
+ requestId: string
172
+ ): Response {
173
+ if (error instanceof TranslationError) {
174
+ const statusMap: Record<string, StatusCode> = {
175
+ [ErrorCode.PROVIDER_AUTH_FAILED]: 401,
176
+ [ErrorCode.PROVIDER_RATE_LIMITED]: 429,
177
+ [ErrorCode.PROVIDER_ERROR]: 502,
178
+ [ErrorCode.PROVIDER_NOT_FOUND]: 400,
179
+ [ErrorCode.QUALITY_THRESHOLD_NOT_MET]: 422,
180
+ [ErrorCode.GLOSSARY_INVALID]: 400,
181
+ [ErrorCode.GLOSSARY_NOT_FOUND]: 400,
182
+ [ErrorCode.CONFIG_INVALID]: 400,
183
+ [ErrorCode.UNSUPPORTED_FORMAT]: 400,
184
+ };
185
+
186
+ const status: StatusCode = statusMap[error.code] ?? 500;
187
+
188
+ return c.json<ErrorResponse>(
189
+ {
190
+ error: error.message,
191
+ code: error.code,
192
+ details: error.details,
193
+ },
194
+ status
195
+ );
196
+ }
197
+
198
+ // Unknown error
199
+ console.error(`[${requestId}] Translation error:`, error);
200
+
201
+ return c.json<ErrorResponse>(
202
+ {
203
+ error: 'Internal server error',
204
+ code: 'INTERNAL_ERROR',
205
+ },
206
+ 500
207
+ );
208
+ }
209
+
210
+ export { translateRouter };
@@ -0,0 +1,138 @@
1
+ import { z } from 'zod';
2
+
3
+ // ============================================================================
4
+ // Request Validation Schemas
5
+ // ============================================================================
6
+
7
+ /**
8
+ * Translation mode enum
9
+ */
10
+ export const TranslationModeSchema = z.enum(['fast', 'balanced', 'quality']);
11
+ export type TranslationMode = z.infer<typeof TranslationModeSchema>;
12
+
13
+ /**
14
+ * Inline glossary term schema (simplified for API)
15
+ */
16
+ export const InlineGlossaryTermSchema = z.object({
17
+ source: z.string().min(1, 'Source term is required'),
18
+ target: z.string().min(1, 'Target term is required'),
19
+ context: z.string().optional(),
20
+ caseSensitive: z.boolean().optional(),
21
+ doNotTranslate: z.boolean().optional(),
22
+ });
23
+
24
+ export type InlineGlossaryTerm = z.infer<typeof InlineGlossaryTermSchema>;
25
+
26
+ /**
27
+ * POST /translate request body schema
28
+ */
29
+ export const TranslateRequestSchema = z.object({
30
+ content: z.string().min(1, 'Content is required'),
31
+ sourceLang: z
32
+ .string()
33
+ .min(2, 'Source language code must be at least 2 characters')
34
+ .max(10, 'Source language code must be at most 10 characters'),
35
+ targetLang: z
36
+ .string()
37
+ .min(2, 'Target language code must be at least 2 characters')
38
+ .max(10, 'Target language code must be at most 10 characters'),
39
+ format: z.enum(['markdown', 'html', 'text']).optional().default('text'),
40
+ glossary: z.array(InlineGlossaryTermSchema).optional(),
41
+ provider: z.enum(['claude', 'openai', 'ollama']).optional(),
42
+ model: z.string().optional(),
43
+ mode: TranslationModeSchema.optional().default('balanced'),
44
+ qualityThreshold: z.number().min(0).max(100).optional(),
45
+ maxIterations: z.number().min(1).max(10).optional(),
46
+ context: z.string().optional(),
47
+ });
48
+
49
+ export type TranslateRequest = z.infer<typeof TranslateRequestSchema>;
50
+
51
+ // ============================================================================
52
+ // Response Types
53
+ // ============================================================================
54
+
55
+ /**
56
+ * POST /translate response
57
+ */
58
+ export interface TranslateResponse {
59
+ translated: string;
60
+ quality: number;
61
+ iterations: number;
62
+ tokensUsed: {
63
+ input: number;
64
+ output: number;
65
+ };
66
+ glossaryCompliance?: {
67
+ applied: string[];
68
+ missed: string[];
69
+ };
70
+ duration: number;
71
+ provider: string;
72
+ model: string;
73
+ }
74
+
75
+ /**
76
+ * GET /health response
77
+ */
78
+ export interface HealthResponse {
79
+ status: 'healthy' | 'degraded';
80
+ version: string;
81
+ uptime: number;
82
+ providers: {
83
+ name: string;
84
+ available: boolean;
85
+ }[];
86
+ }
87
+
88
+ /**
89
+ * Error response format
90
+ */
91
+ export interface ErrorResponse {
92
+ error: string;
93
+ code: string;
94
+ details?: Record<string, unknown>;
95
+ }
96
+
97
+ // ============================================================================
98
+ // Server Configuration
99
+ // ============================================================================
100
+
101
+ /**
102
+ * Server configuration options
103
+ */
104
+ export interface ServerConfig {
105
+ port: number;
106
+ host: string;
107
+ enableAuth: boolean;
108
+ enableCors: boolean;
109
+ apiKey?: string;
110
+ jsonLogging?: boolean;
111
+ }
112
+
113
+ // ============================================================================
114
+ // Hono Context Variables
115
+ // ============================================================================
116
+
117
+ /**
118
+ * Custom variables stored in Hono context
119
+ */
120
+ export interface HonoVariables {
121
+ requestId: string;
122
+ }
123
+
124
+ // ============================================================================
125
+ // Mode Presets
126
+ // ============================================================================
127
+
128
+ /**
129
+ * Mode configuration presets
130
+ */
131
+ export const MODE_PRESETS: Record<
132
+ TranslationMode,
133
+ { qualityThreshold: number; maxIterations: number }
134
+ > = {
135
+ fast: { qualityThreshold: 0, maxIterations: 1 },
136
+ balanced: { qualityThreshold: 75, maxIterations: 2 },
137
+ quality: { qualityThreshold: 85, maxIterations: 4 },
138
+ };