96 lines
3.8 KiB
Python
96 lines
3.8 KiB
Python
#!/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}") |