Blame view

api/translator_app.py 18.1 KB
0fd2f875   tangwang   translate
1
  """Translator service HTTP app."""
768ad710   tangwang   MySQL到ES字段映射说明-业务...
2
  
768ad710   tangwang   MySQL到ES字段映射说明-业务...
3
  import argparse
1e370dc9   tangwang   1. 翻译性能优化
4
  import asyncio
0fd2f875   tangwang   translate
5
  import logging
cd4ce66d   tangwang   trans logs
6
7
8
  import os
  import pathlib
  import time
14e67b71   tangwang   分句后的 batching 现在是...
9
  import uuid
0fd2f875   tangwang   translate
10
  from contextlib import asynccontextmanager
1e370dc9   tangwang   1. 翻译性能优化
11
  from functools import lru_cache, partial
cd4ce66d   tangwang   trans logs
12
  from logging.handlers import TimedRotatingFileHandler
0fd2f875   tangwang   translate
13
14
  from typing import List, Optional, Union
  
1e370dc9   tangwang   1. 翻译性能优化
15
  import anyio
768ad710   tangwang   MySQL到ES字段映射说明-业务...
16
  import uvicorn
14e67b71   tangwang   分句后的 batching 现在是...
17
  from fastapi import FastAPI, HTTPException, Request
768ad710   tangwang   MySQL到ES字段映射说明-业务...
18
  from fastapi.middleware.cors import CORSMiddleware
0fd2f875   tangwang   translate
19
20
  from fastapi.responses import JSONResponse
  from pydantic import BaseModel, ConfigDict, Field
768ad710   tangwang   MySQL到ES字段映射说明-业务...
21
  
d4cadc13   tangwang   翻译重构
22
  from config.services_config import get_translation_config
14e67b71   tangwang   分句后的 batching 现在是...
23
24
25
26
27
  from translation.logging_utils import (
      TranslationRequestFilter,
      bind_translation_request_id,
      reset_translation_request_id,
  )
5e4dc8e4   tangwang   翻译架构按“一个翻译服务 +
28
  from translation.service import TranslationService
0fd2f875   tangwang   translate
29
30
31
32
33
  from translation.settings import (
      get_enabled_translation_models,
      normalize_translation_model,
      normalize_translation_scene,
  )
768ad710   tangwang   MySQL到ES字段映射说明-业务...
34
  
cd4ce66d   tangwang   trans logs
35
36
37
38
39
40
41
42
43
  
  def configure_translator_logging() -> None:
      log_dir = pathlib.Path("logs")
      verbose_dir = log_dir / "verbose"
      log_dir.mkdir(exist_ok=True)
      verbose_dir.mkdir(parents=True, exist_ok=True)
  
      log_level = os.getenv("LOG_LEVEL", "INFO").upper()
      numeric_level = getattr(logging, log_level, logging.INFO)
14e67b71   tangwang   分句后的 batching 现在是...
44
45
      formatter = logging.Formatter("%(asctime)s | reqid:%(reqid)s | %(name)s | %(levelname)s | %(message)s")
      request_filter = TranslationRequestFilter()
cd4ce66d   tangwang   trans logs
46
47
48
49
50
51
52
53
  
      root_logger = logging.getLogger()
      root_logger.setLevel(numeric_level)
      root_logger.handlers.clear()
  
      console_handler = logging.StreamHandler()
      console_handler.setLevel(numeric_level)
      console_handler.setFormatter(formatter)
14e67b71   tangwang   分句后的 batching 现在是...
54
      console_handler.addFilter(request_filter)
cd4ce66d   tangwang   trans logs
55
56
57
58
59
60
61
62
63
64
65
      root_logger.addHandler(console_handler)
  
      file_handler = TimedRotatingFileHandler(
          filename=log_dir / "translator_api.log",
          when="midnight",
          interval=1,
          backupCount=30,
          encoding="utf-8",
      )
      file_handler.setLevel(numeric_level)
      file_handler.setFormatter(formatter)
14e67b71   tangwang   分句后的 batching 现在是...
66
      file_handler.addFilter(request_filter)
cd4ce66d   tangwang   trans logs
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
      root_logger.addHandler(file_handler)
  
      verbose_logger = logging.getLogger("translator.verbose")
      verbose_logger.setLevel(numeric_level)
      verbose_logger.handlers.clear()
      verbose_logger.propagate = False
  
      verbose_handler = TimedRotatingFileHandler(
          filename=verbose_dir / "translator_verbose.log",
          when="midnight",
          interval=1,
          backupCount=30,
          encoding="utf-8",
      )
      verbose_handler.setLevel(numeric_level)
      verbose_handler.setFormatter(formatter)
14e67b71   tangwang   分句后的 batching 现在是...
83
      verbose_handler.addFilter(request_filter)
cd4ce66d   tangwang   trans logs
84
85
86
87
      verbose_logger.addHandler(verbose_handler)
  
  
  configure_translator_logging()
768ad710   tangwang   MySQL到ES字段映射说明-业务...
88
  logger = logging.getLogger(__name__)
cd4ce66d   tangwang   trans logs
89
  verbose_logger = logging.getLogger("translator.verbose")
768ad710   tangwang   MySQL到ES字段映射说明-业务...
90
  
768ad710   tangwang   MySQL到ES字段映射说明-业务...
91
  
0fd2f875   tangwang   translate
92
  @lru_cache(maxsize=1)
5e4dc8e4   tangwang   翻译架构按“一个翻译服务 +
93
  def get_translation_service() -> TranslationService:
0fd2f875   tangwang   translate
94
      return TranslationService(get_translation_config())
768ad710   tangwang   MySQL到ES字段映射说明-业务...
95
96
  
  
1e370dc9   tangwang   1. 翻译性能优化
97
98
99
100
101
102
103
104
105
106
107
  _TRANSLATION_MAX_INFLIGHT = int(os.getenv("TRANSLATION_MAX_INFLIGHT", "4"))
  _translation_inflight_semaphore = asyncio.Semaphore(max(1, _TRANSLATION_MAX_INFLIGHT))
  
  
  async def _run_translation_blocking(fn, /, *args, **kwargs):
      """Run blocking translation work in a thread with bounded concurrency."""
      bound = partial(fn, *args, **kwargs)
      async with _translation_inflight_semaphore:
          return await anyio.to_thread.run_sync(bound)
  
  
768ad710   tangwang   MySQL到ES字段映射说明-业务...
108
109
110
  # Request/Response models
  class TranslationRequest(BaseModel):
      """Translation request model."""
768ad710   tangwang   MySQL到ES字段映射说明-业务...
111
  
0fd2f875   tangwang   translate
112
113
      model_config = ConfigDict(
          json_schema_extra={
768ad710   tangwang   MySQL到ES字段映射说明-业务...
114
115
116
              "example": {
                  "text": "商品名称",
                  "target_lang": "en",
3cd09b3b   tangwang   翻译接口改为调用qwen-mt-f...
117
                  "source_lang": "zh",
d4cadc13   tangwang   翻译重构
118
                  "model": "llm",
0fd2f875   tangwang   translate
119
                  "scene": "sku_name",
768ad710   tangwang   MySQL到ES字段映射说明-业务...
120
121
              }
          }
0fd2f875   tangwang   translate
122
123
124
125
126
127
128
      )
  
      text: Union[str, List[str]] = Field(..., description="Text to translate (string or list of strings)")
      target_lang: str = Field(..., description="Target language code (zh, en, ru, etc.)")
      source_lang: Optional[str] = Field(None, description="Source language code (optional, auto-detect if not provided)")
      model: Optional[str] = Field(None, description="Enabled translation capability name")
      scene: Optional[str] = Field(None, description="Translation scene, paired with model routing")
768ad710   tangwang   MySQL到ES字段映射说明-业务...
129
130
131
132
  
  
  class TranslationResponse(BaseModel):
      """Translation response model."""
6f7840cf   tangwang   refactor: rename ...
133
      text: Union[str, List[str]] = Field(..., description="Original text (string or list)")
768ad710   tangwang   MySQL到ES字段映射说明-业务...
134
135
      target_lang: str = Field(..., description="Target language code")
      source_lang: Optional[str] = Field(None, description="Source language code (detected or provided)")
6f7840cf   tangwang   refactor: rename ...
136
137
138
139
      translated_text: Union[str, List[Optional[str]]] = Field(
          ...,
          description="Translated text (string or list; list elements may be null on failure)",
      )
768ad710   tangwang   MySQL到ES字段映射说明-业务...
140
      status: str = Field(..., description="Translation status")
3cd09b3b   tangwang   翻译接口改为调用qwen-mt-f...
141
      model: str = Field(..., description="Translation model used")
5e4dc8e4   tangwang   翻译架构按“一个翻译服务 +
142
      scene: str = Field(..., description="Translation scene used")
768ad710   tangwang   MySQL到ES字段映射说明-业务...
143
144
  
  
0fd2f875   tangwang   translate
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
  def _normalize_scene(service: TranslationService, scene: Optional[str]) -> str:
      return normalize_translation_scene(service.config, scene)
  
  
  def _normalize_model(service: TranslationService, model: Optional[str]) -> str:
      return normalize_translation_model(service.config, model or service.config["default_model"])
  
  
  def _ensure_valid_text(text: Union[str, List[str]]) -> None:
      if isinstance(text, list):
          if not text:
              raise HTTPException(status_code=400, detail="Text list cannot be empty")
          return
      if not text or not text.strip():
          raise HTTPException(status_code=400, detail="Text cannot be empty")
  
  
  def _normalize_batch_result(
      original: List[str],
      translated: Union[str, List[Optional[str]], None],
  ) -> List[Optional[str]]:
      if translated is None:
          return [None for _ in original]
      if not isinstance(translated, list):
          raise HTTPException(status_code=500, detail="Batch translation provider returned non-list result")
      return [translated[idx] if idx < len(translated) else None for idx, _ in enumerate(original)]
  
  
cd4ce66d   tangwang   trans logs
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
  def _text_preview(text: Optional[str], limit: int = 20) -> str:
      normalized = str(text or "").replace("\n", "\\n")
      return normalized[:limit]
  
  
  def _request_metrics(text: Union[str, List[str]]) -> dict:
      if isinstance(text, list):
          lengths = [len(str(item or "")) for item in text]
          return {
              "request_count": len(text),
              "lengths": lengths,
              "first_preview": _text_preview(text[0] if text else ""),
          }
      return {
          "request_count": 1,
          "lengths": [len(str(text or ""))],
          "first_preview": _text_preview(str(text or "")),
      }
  
  
  def _result_preview(translated: Union[str, List[Optional[str]], None]) -> str:
      if isinstance(translated, list):
          if not translated:
              return ""
          first = translated[0]
          return _text_preview("" if first is None else str(first))
      if translated is None:
          return ""
      return _text_preview(str(translated))
  
  
14e67b71   tangwang   分句后的 batching 现在是...
204
205
206
207
208
209
210
  def _resolve_request_id(http_request: Request) -> str:
      header_value = http_request.headers.get("X-Request-ID")
      if header_value and header_value.strip():
          return header_value.strip()[:32]
      return str(uuid.uuid4())[:8]
  
  
0fd2f875   tangwang   translate
211
212
213
214
215
216
217
218
219
220
  def _translate_batch(
      service: TranslationService,
      raw_text: List[str],
      *,
      target_lang: str,
      source_lang: Optional[str],
      model: str,
      scene: str,
  ) -> List[Optional[str]]:
      backend = service.get_backend(model)
cd4ce66d   tangwang   trans logs
221
      logger.info(
14e67b71   tangwang   分句后的 batching 现在是...
222
223
          "Translation batch dispatch | execution=%s count=%s lengths=%s first_preview=%s",
          "backend-batch" if getattr(backend, "supports_batch", False) else "per-item",
cd4ce66d   tangwang   trans logs
224
225
226
          len(raw_text),
          [len(str(item or "")) for item in raw_text],
          _text_preview(raw_text[0] if raw_text else ""),
cd4ce66d   tangwang   trans logs
227
      )
0fd2f875   tangwang   translate
228
229
230
231
232
233
234
235
236
      if getattr(backend, "supports_batch", False):
          try:
              translated = service.translate(
                  text=raw_text,
                  target_lang=target_lang,
                  source_lang=source_lang,
                  model=model,
                  scene=scene,
              )
cd4ce66d   tangwang   trans logs
237
238
239
240
241
242
243
              verbose_logger.info(
                  "Translation batch result | model=%s scene=%s count=%s first_result=%s",
                  model,
                  scene,
                  len(raw_text),
                  _result_preview(translated),
              )
0fd2f875   tangwang   translate
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
              return _normalize_batch_result(raw_text, translated)
          except ValueError:
              raise
          except Exception as exc:
              logger.error("Batch translation failed: %s", exc, exc_info=True)
  
      results: List[Optional[str]] = []
      for item in raw_text:
          if item is None or not str(item).strip():
              results.append(item)  # type: ignore[arg-type]
              continue
          try:
              out = service.translate(
                  text=str(item),
                  target_lang=target_lang,
                  source_lang=source_lang,
                  model=model,
                  scene=scene,
              )
          except ValueError:
              raise
          except Exception as exc:
cd4ce66d   tangwang   trans logs
266
267
268
269
270
271
272
273
274
275
276
              logger.warning(
                  "Per-item translation failed | model=%s scene=%s target_lang=%s source_lang=%s item_len=%s item_preview=%s error=%s",
                  model,
                  scene,
                  target_lang,
                  source_lang or "auto",
                  len(str(item or "")),
                  _text_preview(str(item or "")),
                  exc,
                  exc_info=True,
              )
0fd2f875   tangwang   translate
277
278
279
280
281
282
283
              out = None
          results.append(out)
      return results
  
  
  @asynccontextmanager
  async def lifespan(_: FastAPI):
cd4ce66d   tangwang   trans logs
284
      """Initialize all enabled translation backends on process startup."""
0fd2f875   tangwang   translate
285
286
      logger.info("Starting Translation Service API")
      service = get_translation_service()
f07947a5   tangwang   Improve portabili...
287
288
      failed_models = list(getattr(service, "failed_models", []))
      backend_errors = dict(getattr(service, "backend_errors", {}))
0fd2f875   tangwang   translate
289
      logger.info(
f07947a5   tangwang   Improve portabili...
290
          "Translation service ready | default_model=%s default_scene=%s available_models=%s loaded_models=%s failed_models=%s",
0fd2f875   tangwang   translate
291
          service.config["default_model"],
cd4ce66d   tangwang   trans logs
292
          service.config["default_scene"],
0fd2f875   tangwang   translate
293
294
          service.available_models,
          service.loaded_models,
f07947a5   tangwang   Improve portabili...
295
          failed_models,
0fd2f875   tangwang   translate
296
297
      )
      logger.info(
f07947a5   tangwang   Improve portabili...
298
          "Translation backends initialized on startup | loaded=%s failed=%s",
cd4ce66d   tangwang   trans logs
299
          service.loaded_models,
f07947a5   tangwang   Improve portabili...
300
          backend_errors,
cd4ce66d   tangwang   trans logs
301
302
303
304
305
306
      )
      verbose_logger.info(
          "Translation startup detail | capabilities=%s cache_ttl_seconds=%s cache_sliding_expiration=%s",
          service.available_models,
          service.config["cache"]["ttl_seconds"],
          service.config["cache"]["sliding_expiration"],
0fd2f875   tangwang   translate
307
308
309
310
      )
      yield
  
  
768ad710   tangwang   MySQL到ES字段映射说明-业务...
311
312
313
  # Create FastAPI app
  app = FastAPI(
      title="Translation Service API",
0fd2f875   tangwang   translate
314
      description="Translation service with pluggable capabilities and scene routing",
768ad710   tangwang   MySQL到ES字段映射说明-业务...
315
316
      version="1.0.0",
      docs_url="/docs",
0fd2f875   tangwang   translate
317
318
      redoc_url="/redoc",
      lifespan=lifespan,
768ad710   tangwang   MySQL到ES字段映射说明-业务...
319
320
321
322
323
324
325
326
327
328
329
330
  )
  
  # Add CORS middleware
  app.add_middleware(
      CORSMiddleware,
      allow_origins=["*"],
      allow_credentials=True,
      allow_methods=["*"],
      allow_headers=["*"],
  )
  
  
768ad710   tangwang   MySQL到ES字段映射说明-业务...
331
332
333
334
  @app.get("/health")
  async def health_check():
      """Health check endpoint."""
      try:
5e4dc8e4   tangwang   翻译架构按“一个翻译服务 +
335
          service = get_translation_service()
f07947a5   tangwang   Improve portabili...
336
337
          failed_models = list(getattr(service, "failed_models", []))
          backend_errors = dict(getattr(service, "backend_errors", {}))
cd4ce66d   tangwang   trans logs
338
          logger.info(
f07947a5   tangwang   Improve portabili...
339
              "Health check | default_model=%s default_scene=%s loaded_models=%s failed_models=%s",
cd4ce66d   tangwang   trans logs
340
341
342
              service.config["default_model"],
              service.config["default_scene"],
              service.loaded_models,
f07947a5   tangwang   Improve portabili...
343
              failed_models,
cd4ce66d   tangwang   trans logs
344
          )
768ad710   tangwang   MySQL到ES字段映射说明-业务...
345
346
347
          return {
              "status": "healthy",
              "service": "translation",
1e370dc9   tangwang   1. 翻译性能优化
348
              "max_inflight": _TRANSLATION_MAX_INFLIGHT,
0fd2f875   tangwang   translate
349
350
              "default_model": service.config["default_model"],
              "default_scene": service.config["default_scene"],
5e4dc8e4   tangwang   翻译架构按“一个翻译服务 +
351
              "available_models": service.available_models,
0fd2f875   tangwang   translate
352
353
              "enabled_capabilities": get_enabled_translation_models(service.config),
              "loaded_models": service.loaded_models,
f07947a5   tangwang   Improve portabili...
354
355
              "failed_models": failed_models,
              "backend_errors": backend_errors,
768ad710   tangwang   MySQL到ES字段映射说明-业务...
356
357
358
359
360
361
362
363
364
365
366
367
368
          }
      except Exception as e:
          logger.error(f"Health check failed: {e}")
          return JSONResponse(
              status_code=503,
              content={
                  "status": "unhealthy",
                  "error": str(e)
              }
          )
  
  
  @app.post("/translate", response_model=TranslationResponse)
14e67b71   tangwang   分句后的 batching 现在是...
369
  async def translate(request: TranslationRequest, http_request: Request):
0fd2f875   tangwang   translate
370
371
      _ensure_valid_text(request.text)
  
768ad710   tangwang   MySQL到ES字段映射说明-业务...
372
      if not request.target_lang:
0fd2f875   tangwang   translate
373
374
          raise HTTPException(status_code=400, detail="target_lang is required")
  
14e67b71   tangwang   分句后的 batching 现在是...
375
      _, request_token = bind_translation_request_id(_resolve_request_id(http_request))
cd4ce66d   tangwang   trans logs
376
      request_started = time.perf_counter()
768ad710   tangwang   MySQL到ES字段映射说明-业务...
377
      try:
5e4dc8e4   tangwang   翻译架构按“一个翻译服务 +
378
          service = get_translation_service()
0fd2f875   tangwang   translate
379
380
          scene = _normalize_scene(service, request.scene)
          model = _normalize_model(service, request.model)
5e4dc8e4   tangwang   翻译架构按“一个翻译服务 +
381
          translator = service.get_backend(model)
6f7840cf   tangwang   refactor: rename ...
382
          raw_text = request.text
cd4ce66d   tangwang   trans logs
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
          metrics = _request_metrics(raw_text)
          logger.info(
              "Translation request | model=%s scene=%s target_lang=%s source_lang=%s count=%s lengths=%s first_preview=%s backend=%s",
              model,
              scene,
              request.target_lang,
              request.source_lang or "auto",
              metrics["request_count"],
              metrics["lengths"],
              metrics["first_preview"],
              getattr(translator, "model", model),
          )
          verbose_logger.info(
              "Translation request detail | model=%s scene=%s target_lang=%s source_lang=%s payload=%s",
              model,
              scene,
              request.target_lang,
              request.source_lang or "auto",
              raw_text,
          )
6f7840cf   tangwang   refactor: rename ...
403
  
6f7840cf   tangwang   refactor: rename ...
404
          if isinstance(raw_text, list):
1e370dc9   tangwang   1. 翻译性能优化
405
406
              results = await _run_translation_blocking(
                  _translate_batch,
0fd2f875   tangwang   translate
407
408
409
410
411
412
413
                  service,
                  raw_text,
                  target_lang=request.target_lang,
                  source_lang=request.source_lang,
                  model=model,
                  scene=scene,
              )
cd4ce66d   tangwang   trans logs
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
              latency_ms = (time.perf_counter() - request_started) * 1000
              logger.info(
                  "Translation response | model=%s scene=%s count=%s first_result=%s latency_ms=%.2f",
                  model,
                  scene,
                  len(raw_text),
                  _result_preview(results),
                  latency_ms,
              )
              verbose_logger.info(
                  "Translation response detail | model=%s scene=%s translated=%s latency_ms=%.2f",
                  model,
                  scene,
                  results,
                  latency_ms,
              )
6f7840cf   tangwang   refactor: rename ...
430
431
432
433
434
435
436
              return TranslationResponse(
                  text=raw_text,
                  target_lang=request.target_lang,
                  source_lang=request.source_lang,
                  translated_text=results,
                  status="success",
                  model=str(getattr(translator, "model", model)),
5e4dc8e4   tangwang   翻译架构按“一个翻译服务 +
437
                  scene=scene,
6f7840cf   tangwang   refactor: rename ...
438
439
              )
  
1e370dc9   tangwang   1. 翻译性能优化
440
441
          translated_text = await _run_translation_blocking(
              service.translate,
6f7840cf   tangwang   refactor: rename ...
442
              text=raw_text,
768ad710   tangwang   MySQL到ES字段映射说明-业务...
443
444
              target_lang=request.target_lang,
              source_lang=request.source_lang,
5e4dc8e4   tangwang   翻译架构按“一个翻译服务 +
445
446
              model=model,
              scene=scene,
768ad710   tangwang   MySQL到ES字段映射说明-业务...
447
          )
6f7840cf   tangwang   refactor: rename ...
448
  
768ad710   tangwang   MySQL到ES字段映射说明-业务...
449
          if translated_text is None:
0fd2f875   tangwang   translate
450
              raise HTTPException(status_code=500, detail="Translation failed")
6f7840cf   tangwang   refactor: rename ...
451
  
cd4ce66d   tangwang   trans logs
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
          latency_ms = (time.perf_counter() - request_started) * 1000
          logger.info(
              "Translation response | model=%s scene=%s count=1 first_result=%s latency_ms=%.2f",
              model,
              scene,
              _result_preview(translated_text),
              latency_ms,
          )
          verbose_logger.info(
              "Translation response detail | model=%s scene=%s translated=%s latency_ms=%.2f",
              model,
              scene,
              translated_text,
              latency_ms,
          )
  
768ad710   tangwang   MySQL到ES字段映射说明-业务...
468
          return TranslationResponse(
6f7840cf   tangwang   refactor: rename ...
469
              text=raw_text,
768ad710   tangwang   MySQL到ES字段映射说明-业务...
470
471
472
              target_lang=request.target_lang,
              source_lang=request.source_lang,
              translated_text=translated_text,
3cd09b3b   tangwang   翻译接口改为调用qwen-mt-f...
473
              status="success",
5e4dc8e4   tangwang   翻译架构按“一个翻译服务 +
474
475
              model=str(getattr(translator, "model", model)),
              scene=scene,
768ad710   tangwang   MySQL到ES字段映射说明-业务...
476
477
          )
      
cd4ce66d   tangwang   trans logs
478
479
480
481
482
483
484
485
      except HTTPException as exc:
          latency_ms = (time.perf_counter() - request_started) * 1000
          logger.warning(
              "Translation request failed | status_code=%s detail=%s latency_ms=%.2f",
              exc.status_code,
              exc.detail,
              latency_ms,
          )
768ad710   tangwang   MySQL到ES字段映射说明-业务...
486
          raise
0fd2f875   tangwang   translate
487
      except ValueError as e:
cd4ce66d   tangwang   trans logs
488
          latency_ms = (time.perf_counter() - request_started) * 1000
14e67b71   tangwang   分句后的 batching 现在是...
489
          logger.warning("Translation validation error | error=%s latency_ms=%.2f", e, latency_ms)
0fd2f875   tangwang   translate
490
          raise HTTPException(status_code=400, detail=str(e)) from e
f07947a5   tangwang   Improve portabili...
491
492
493
494
      except RuntimeError as e:
          latency_ms = (time.perf_counter() - request_started) * 1000
          logger.warning("Translation backend unavailable | error=%s latency_ms=%.2f", e, latency_ms)
          raise HTTPException(status_code=503, detail=str(e)) from e
768ad710   tangwang   MySQL到ES字段映射说明-业务...
495
      except Exception as e:
cd4ce66d   tangwang   trans logs
496
497
          latency_ms = (time.perf_counter() - request_started) * 1000
          logger.error("Translation error | error=%s latency_ms=%.2f", e, latency_ms, exc_info=True)
0fd2f875   tangwang   translate
498
          raise HTTPException(status_code=500, detail=f"Translation error: {str(e)}")
14e67b71   tangwang   分句后的 batching 现在是...
499
500
      finally:
          reset_translation_request_id(request_token)
768ad710   tangwang   MySQL到ES字段映射说明-业务...
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
  
  
  @app.get("/")
  async def root():
      """Root endpoint with API information."""
      return {
          "service": "Translation Service API",
          "version": "1.0.0",
          "status": "running",
          "endpoints": {
              "translate": "POST /translate",
              "health": "GET /health",
              "docs": "GET /docs"
          }
      }
  
  
  if __name__ == "__main__":
      parser = argparse.ArgumentParser(description='Start translation API service')
      parser.add_argument('--host', default='0.0.0.0', help='Host to bind to')
      parser.add_argument('--port', type=int, default=6006, help='Port to bind to')
      parser.add_argument('--reload', action='store_true', help='Enable auto-reload')
      args = parser.parse_args()
  
      # Run server
      uvicorn.run(
          "api.translator_app:app",
          host=args.host,
          port=args.port,
          reload=args.reload
      )