import json, re, unicodedata
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
BASE = "Qwen/Qwen3.5-9B"
tok = AutoTokenizer.from_pretrained(BASE)
model = AutoModelForCausalLM.from_pretrained(BASE, torch_dtype="bfloat16", device_map="auto")
model = PeftModel.from_pretrained(model, "rntc/qwen3.5-9b-lymphome-ecrf-lora").eval()
SYS = ("Tu es un assistant d'extraction d'information clinique. À partir du compte-rendu, "
"extrais les valeurs des champs demandés. Réponds UNIQUEMENT en JSON. Recopie la "
"valeur EXACTEMENT depuis le texte (verbatim), n'invente rien, ne normalise pas. "
"Si un champ est absent, renvoie une liste vide [].")
def extract_section(text, section, fields, max_new_tokens=512):
champs = chr(10).join('- "%s"' % f for f in fields)
user = ("COMPTE-RENDU:\n%s\n\nCHAMPS À EXTRAIRE (section « %s ») :\n%s\n\n"
"Réponds en JSON : clés = labels ci-dessus, valeurs = listes d'extraits verbatim ([] si absent)."
% (text, section, champs))
msgs = [{"role": "system", "content": SYS}, {"role": "user", "content": user}]
prompt = tok.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True)
ids = tok(prompt, return_tensors="pt").to(model.device)
gen = model.generate(**ids, max_new_tokens=max_new_tokens, do_sample=False)
out = tok.decode(gen[0][ids.input_ids.shape[1]:], skip_special_tokens=True)
try:
return json.loads(out)
except Exception:
m = re.search(r"\{.*\}", out, re.S)
return json.loads(m.group(0)) if m else {}
text = ("Compte-rendu d'hématologie. Patiente née le 12/05/1941, sexe féminin. "
"Diagnostic le 03/2023 : lymphome B diffus à grandes cellules.")
out = extract_section(text, "demographie",
["date de naissance", "sexe", "diagnostic du lymphome"])
print(out)
def locate(text, value):
i = text.find(value)
return (i, i + len(value)) if i >= 0 else (-1, -1)
for field, vals in out.items():
for v in vals:
print(field, repr(v), locate(text, v))