#!/usr/bin/env python3 """ debug_one_pdf.py - Verarbeitet EIN PDF und zeigt die ROHE Modell-Antwort, egal ob sie als JSON parst oder nicht. Zum Debuggen einzelner Problemfaelle. Aufruf: python3 debug_one_pdf.py \ --api http://localhost:8000/v1 --model "gemma-4-12b" \ --pdf ~/data/S/"Scott, Dexter Notes 031226.pdf" """ import argparse, io, base64, json, re from openai import OpenAI from pypdf import PdfReader from pdf2image import convert_from_path # --- dieselbe Konfiguration wie im Hauptscript --- JSON_SCHEMA = { "type": "object", "properties": { "is_buyer_sheet": {"type": "boolean"}, "name_company": {"type": ["string", "null"]}, "prospective_buyer": {"type": ["string", "null"]}, "company": {"type": ["string", "null"]}, "phone": {"type": ["string", "null"]}, "cell": {"type": ["string", "null"]}, "email": {"type": ["string", "null"]}, "address": {"type": ["string", "null"]}, "state": {"type": ["string", "null"]}, "how_did_you_hear": {"type": ["string", "null"]}, "interested_in_updates": {"type": ["boolean", "null"]}, "types_of_business_raw": {"type": ["string", "null"]}, "types_of_business": {"type": "array", "items": {"type": "string"}}, "background_experience": {"type": ["string", "null"]}, "date_of_introduction": {"type": ["string", "null"]}, }, "required": ["is_buyer_sheet", "name_company", "prospective_buyer", "company", "phone", "cell", "email", "address", "state", "how_did_you_hear", "interested_in_updates", "types_of_business_raw", "types_of_business", "background_experience", "date_of_introduction"], } SYSTEM_PROMPT = """Du bist ein praezises Datenextraktions-System fuer Formulare der Firma "BizMatch Business Brokerage". Extrahiere die verlangten Felder als JSON. Fehlende Felder -> null. ERFINDE NICHTS.""" ap = argparse.ArgumentParser() ap.add_argument("--api", default="http://localhost:8000/v1") ap.add_argument("--model", default="gemma-4-12b") ap.add_argument("--pdf", required=True) ap.add_argument("--max-tokens", type=int, default=512) args = ap.parse_args() client = OpenAI(base_url=args.api, api_key="x", timeout=120, max_retries=0) n_pages = len(PdfReader(args.pdf).pages) print(f"PDF: {args.pdf}") print(f"Seiten: {n_pages}") # Vision-Pfad (2 Seiten, wie im Hauptscript) imgs = convert_from_path(args.pdf, first_page=1, last_page=2, dpi=150) payload = [{"type":"text","text":"Extrahiere die Felder aus diesem Buyer Information Sheet:"}] for img in imgs: w,h = img.size scale = min(1.0, 1600/max(w,h)) if scale < 1.0: img = img.resize((int(w*scale), int(h*scale))) buf = io.BytesIO(); img.convert("RGB").save(buf, format="JPEG", quality=80) b64 = base64.b64encode(buf.getvalue()).decode() payload.append({"type":"image_url","image_url":{"url":f"data:image/jpeg;base64,{b64}"}}) print(f"\nSende {len(imgs)} Bild(er) an das Modell, max_tokens={args.max_tokens}...\n") resp = client.chat.completions.create( model=args.model, messages=[{"role":"system","content":SYSTEM_PROMPT}, {"role":"user","content":payload}], temperature=0.0, max_tokens=args.max_tokens, response_format={"type":"json_schema","json_schema":{"name":"buyer","schema":JSON_SCHEMA}}, extra_body={"chat_template_kwargs":{"enable_thinking":False}}, ) choice = resp.choices[0] raw = choice.message.content print("=== finish_reason ===") print(choice.finish_reason) print(f"\n=== ROHE ANTWORT ({len(raw or '')} Zeichen) ===") print(repr(raw)) print("\n=== ANTWORT LESBAR ===") print(raw) print("\n=== PARSE-VERSUCH ===") try: parsed = json.loads(raw) print("OK, parst sauber:") print(json.dumps(parsed, indent=2, ensure_ascii=False)) except Exception as e: print(f"FEHLER: {e}")