diff --git a/README.md b/README.md index 9b85c23..ed0f4ea 100644 --- a/README.md +++ b/README.md @@ -215,7 +215,7 @@ curl -s -X POST http://localhost:26000/paperless/hook \ ### Paperless 배치 동기화(`/paperless/sync`) ### 문서 파이프라인(`/pipeline/ingest`) -첨부 문서(텍스트가 준비된 상태: OCR/추출 선행) → 벡터 임베딩 → 한국어 번역 → HTML 생성까지 한 번에 처리합니다. +첨부 문서(텍스트가 준비된 상태: OCR/추출 선행) → 벡터 임베딩 → 번역(옵션) → HTML 생성까지 처리합니다. ```bash curl -s -X POST http://localhost:26000/pipeline/ingest \ @@ -223,11 +223,15 @@ curl -s -X POST http://localhost:26000/pipeline/ingest \ -d '{ "doc_id": "doc-2025-08-13-001", "text": "(여기에 문서 텍스트)", - "generate_html": true + "generate_html": true, + "translate": true, + "target_language": "ko" }' ``` -응답에 `html_path`가 포함됩니다. 한국어 번역본이 `outputs/html/.html`로 생성되고, 번역문은 인덱스에 추가되어 RAG로 검색됩니다. +응답에 `html_path`가 포함됩니다. +- 번역 켜짐(`translate=true`): 번역본이 `outputs/html/.html`로 생성되고, 번역문이 인덱스에 추가됩니다. +- 번역 꺼짐(`translate=false`): 원문으로 HTML만 생성되고, 원문 텍스트가 인덱스에 추가됩니다. Paperless에서 다수 문서를 일괄 인덱싱합니다. diff --git a/server/main.py b/server/main.py index 3776fb5..817d506 100644 --- a/server/main.py +++ b/server/main.py @@ -61,6 +61,8 @@ class PipelineIngestRequest(BaseModel): doc_id: str text: str generate_html: bool = True + translate: bool = True + target_language: str = "ko" @app.get("/health") @@ -162,7 +164,14 @@ def index_reload() -> Dict[str, Any]: @app.post("/pipeline/ingest") def pipeline_ingest(req: PipelineIngestRequest, _: None = Depends(require_api_key)) -> Dict[str, Any]: - result = pipeline.process(doc_id=req.doc_id, text=req.text, index=index, generate_html=req.generate_html) + result = pipeline.process( + doc_id=req.doc_id, + text=req.text, + index=index, + generate_html=req.generate_html, + translate=req.translate, + target_language=req.target_language, + ) return {"status": "ok", "doc_id": result.doc_id, "added": result.added_chunks, "chunks": result.chunks, "html_path": result.html_path} diff --git a/server/pipeline.py b/server/pipeline.py index 71004c3..e451b86 100644 --- a/server/pipeline.py +++ b/server/pipeline.py @@ -25,16 +25,16 @@ class DocumentPipeline: self.output_dir = Path(output_dir) (self.output_dir / "html").mkdir(parents=True, exist_ok=True) - def translate_to_korean(self, parts: List[str]) -> List[str]: + def translate(self, parts: List[str], target_language: str = "ko") -> List[str]: translated: List[str] = [] sys_prompt = ( - "당신은 전문 번역가입니다. 입력 텍스트를 자연스러운 한국어로 충실히 번역하세요. " + "당신은 전문 번역가입니다. 입력 텍스트를 대상 언어로 자연스럽고 충실하게 번역하세요. " "의미를 임의로 축약하거나 추가하지 마세요. 코드/수식/표기는 가능한 유지하세요." ) for p in parts: messages = [ {"role": "system", "content": sys_prompt}, - {"role": "user", "content": f"아래 텍스트를 한국어로 번역하세요:\n\n{p}"}, + {"role": "user", "content": f"아래 텍스트를 {target_language}로 번역하세요.\n\n{p}"}, ] resp = self.ollama.chat(self.boost_model, messages, stream=False, options={"temperature": 0.2, "num_ctx": 32768}) content = resp.get("message", {}).get("content") or resp.get("response", "") @@ -58,9 +58,9 @@ h1{{font-size: 1.6rem; margin-bottom: 1rem;}} html_path.write_text(html, encoding="utf-8") return str(html_path) - def process(self, *, doc_id: str, text: str, index, generate_html: bool = True) -> PipelineResult: + def process(self, *, doc_id: str, text: str, index, generate_html: bool = True, translate: bool = True, target_language: str = "ko") -> PipelineResult: parts = chunk_text(text, max_chars=1200, overlap=200) - translated = self.translate_to_korean(parts) + translated = self.translate(parts, target_language=target_language) if translate else parts to_append: List[IndexRow] = [] for i, t in enumerate(translated): @@ -70,7 +70,8 @@ h1{{font-size: 1.6rem; margin-bottom: 1rem;}} html_path: str | None = None if generate_html: - html_path = self.build_html(doc_id, title=f"문서 {doc_id} (한국어 번역본)", ko_text="\n\n".join(translated)) + title_suffix = "번역본" if translate else "원문" + html_path = self.build_html(doc_id, title=f"문서 {doc_id} ({title_suffix})", ko_text="\n\n".join(translated)) return PipelineResult(doc_id=doc_id, html_path=html_path, added_chunks=added, chunks=len(translated))