From 614704ec74d704ab2ef0f12d3a64fc0e113e60f0 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 24 Jun 2026 14:04:31 +0200 Subject: [PATCH] paperless_to_openwebui.py aktualisiert --- paperless_to_openwebui.py | 123 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/paperless_to_openwebui.py b/paperless_to_openwebui.py index 6576aeb..1d6af13 100644 --- a/paperless_to_openwebui.py +++ b/paperless_to_openwebui.py @@ -38,6 +38,17 @@ MAX_TEXT_LENGTH = int(os.getenv("MAX_TEXT_LENGTH", "2000000")) KNOWLEDGE_ID = None +# ============================================================ +# CONFIG – Ergänzung für OCR-Fix +# ============================================================ +LLM_BASE_URL = os.environ.get("LLM_BASE_URL", "http://ollama:11434/v1") +LLM_API_KEY = os.environ.get("LLM_API_KEY", "unused") +LLM_MODEL = os.environ.get("LLM_MODEL", "gemma3:12b") +OCR_FIX_ENABLED = os.environ.get("OCR_FIX_ENABLED", "true").lower() == "true" +SPACING_THRESHOLD = float(os.environ.get("SPACING_THRESHOLD", "0.5")) +MIN_SPACED_LINES = int(os.environ.get("MIN_SPACED_LINES", "3")) +LLM_CHUNK_SIZE = int(os.environ.get("LLM_CHUNK_SIZE", "2000")) + # ============================================================ # LOGGING # ============================================================ @@ -58,6 +69,16 @@ openwebui_headers = { "Authorization": f"Bearer {OPENWEBUI_TOKEN}", } + +from openai import OpenAI + +# ============================================================ +# LLM CLIENT +# ============================================================ +if OCR_FIX_ENABLED: + llm_client = OpenAI(base_url=LLM_BASE_URL, api_key=LLM_API_KEY) + log.info("OCR fix enabled, using model: %s", LLM_MODEL) + # ============================================================ # SPLITTER # ============================================================ @@ -544,6 +565,108 @@ def upload_to_openwebui(filename: str, content: str): except Exception: pass +# ============================================================ +# OCR FIX VIA LLM +# ============================================================ +OCR_FIX_SYSTEM_PROMPT = """Du bist ein OCR-Nachbearbeitungs-Assistent. +Deine Aufgabe ist es, fehlerhaft erkannten Text zu korrigieren. + +Regeln: +- Entferne falsche Leerzeichen in gesperrtem Text (z.B. "V e r t r a g" → "Vertrag") +- Korrigiere offensichtliche OCR-Fehler (0/O, l/1, rn/m, etc.) +- Behalte die ursprüngliche Struktur bei (Absätze, Zeilenumbrüche, Aufzählungen) +- Erfinde KEINE Inhalte hinzu +- Wenn der Text bereits korrekt ist, gib ihn unverändert zurück +- Gib NUR den korrigierten Text zurück, keine Erklärungen""" + + +def needs_ocr_fix(text: str) -> bool: + """Schneller Vorfilter: Hat der Text gesperrte Zeichen?""" + if not text or len(text) < 50: + return False + + lines = text.split('\n') + spaced_lines = 0 + + for line in lines: + stripped = line.strip() + if len(stripped) < 5: + continue + non_space = stripped.replace(' ', '') + if len(non_space) > 0: + ratio = stripped.count(' ') / len(non_space) + if ratio > SPACING_THRESHOLD: + spaced_lines += 1 + + return spaced_lines >= MIN_SPACED_LINES + + +def fix_text_with_llm(text: str) -> str: + """Schickt einen Textblock zur Korrektur ans LLM.""" + try: + response = llm_client.chat.completions.create( + model=LLM_MODEL, + messages=[ + {"role": "system", "content": OCR_FIX_SYSTEM_PROMPT}, + {"role": "user", "content": f"Korrigiere folgenden OCR-Text:\n\n{text}"} + ], + temperature=0.0, + ) + return response.choices[0].message.content + except Exception as e: + log.error("LLM OCR fix failed: %s", e) + return text # Fallback: Originaltext + + +def fix_ocr_chunked(content: str) -> str: + """Verarbeitet langen Text absatzweise durchs LLM.""" + paragraphs = content.split('\n\n') + chunks = [] + current_chunk = "" + + for para in paragraphs: + if len(current_chunk) + len(para) > LLM_CHUNK_SIZE: + if current_chunk: + chunks.append(current_chunk) + current_chunk = para + else: + current_chunk += ("\n\n" + para if current_chunk else para) + + if current_chunk: + chunks.append(current_chunk) + + fixed_chunks = [] + for i, chunk in enumerate(chunks): + log.debug(" LLM fix chunk %d/%d", i + 1, len(chunks)) + fixed_chunks.append(fix_text_with_llm(chunk)) + time.sleep(0.2) + + return "\n\n".join(fixed_chunks) + + +def update_paperless_content(doc_id: int, new_content: str) -> bool: + """Schreibt korrigierten Text zurück nach Paperless.""" + try: + r = requests.patch( + f"{PAPERLESS_URL}/api/documents/{doc_id}/", + headers={**paperless_headers, "Content-Type": "application/json"}, + json={"content": new_content}, + timeout=120, + ) + if r.ok: + log.info("Updated Paperless content for doc=%s", doc_id) + return True + else: + log.warning( + "Failed to update Paperless content doc=%s status=%s", + doc_id, r.status_code, + ) + return False + except Exception as e: + log.error("Paperless content update failed doc=%s: %s", doc_id, e) + return False + + # ============================================================ # SYNC – parallelisiert # ============================================================ -- 2.52.0