File size: 6,965 Bytes
c7a6fe6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import os
import json
from openai import OpenAI
import tqdm
import re
from FH_es import fernandez_huerta
# Initialize client (ensure you have OPENAI_API_KEY in env vars)
client = OpenAI(api_key=json.load(open('/home/mshahidul/api.json', 'r'))['openai_api_key'])

PROMPTS_ES = {
    "B1": """Eres un asistente que reescribe resúmenes de casos clínicos para niñas y niños de primaria (aprox. 6–11 años).
Escribe SIEMPRE en español claro.

Objetivo de legibilidad (aprox. Fernández–Huerta): 70–100.
Restricciones de forma (cumple todas):
- Longitud total: 45–90 palabras.
- Oraciones: 4–6 oraciones.
- Promedio de palabras por oración: 8–12.
- Palabras: prefiere palabras cortas (1–2 sílabas). Evita tecnicismos. Si un término médico es inevitable, explícalo con 3–8 palabras sencillas.
- Conectores simples: “y”, “pero”, “porque”. Evita oraciones subordinadas largas.
- No inventes información. Sé fiel al artículo y al resumen experto.
- Prohibido: viñetas, listas, emojis, abreviaturas técnicas, explicaciones de pronunciación, títulos/cabeceras.

Tono y contenido:
- Amable, tranquilizador, sin alarmar.
- Destaca 1–3 ideas principales. Explica hallazgos normales con calma; anormalidades con lenguaje sencillo y breve.
Responde solo con el resumen (sin prefacios, sin notas).""",

    "B2": """Eres un asistente que reescribe resúmenes de casos clínicos para estudiantes de secundaria (aprox. 11–17 años).
Escribe SIEMPRE en español claro.

Objetivo de legibilidad (aprox. Fernández–Huerta): 55–65.
Restricciones de forma (cumple todas):
- Longitud total: 90–140 palabras.
- Oraciones: 5–8 oraciones.
- Promedio de palabras por oración: 12–18.
- Palabras: evita jerga innecesaria. Puedes usar términos médicos comunes con una breve explicación (3–10 palabras) la primera vez.
- Conectores permitidos: “porque”, “aunque”, “sin embargo”, “por eso”. Oraciones compuestas moderadas.
- No inventes información. Sé fiel al artículo y al resumen experto.
- Prohibido: viñetas, listas, emojis, explicaciones de pronunciación, títulos/cabeceras.

Tono y contenido:
- Claro y empático.
- Distingue hallazgos normales y anormales, e incluye posibles pasos siguientes cuando sea útil.
Responde solo con el resumen (sin prefacios, sin notas).""",

    "B3": """Eres un asistente que reescribe resúmenes de casos clínicos para lectores con nivel universitario (17+), sin especialización médica.
Escribe SIEMPRE en español claro.

Objetivo de legibilidad (aprox. Fernández–Huerta): 40–55.
Restricciones de forma (cumple todas):
- Longitud total: 140–220 palabras.
- Oraciones: 6–10 oraciones.
- Promedio de palabras por oración: 18–25.
- Palabras: se permiten términos técnicos de uso común; define brevemente solo los poco conocidos. Se aceptan oraciones subordinadas si mantienen claridad.
- Conectores: “sin embargo”, “por lo tanto”, “además”, “no obstante”, “en consecuencia”.
- No inventes información. Sé fiel al artículo y al resumen experto.
- Prohibido: viñetas, listas, emojis, explicaciones de pronunciación, títulos/cabeceras.

Tono y contenido:
- Preciso y empático.
- Estructura más detallada: contexto breve, hallazgos clave, implicaciones y posibles próximos pasos.
Responde solo con el resumen (sin prefacios, sin notas)."""
}


FH_TARGETS = {
    "B1": (70, 100),
    "B2": (55, 65),
    "B3": (40, 55),
}

def count_syllables(word):
    # Simple Spanish syllable counter
    word = word.lower()
    word = re.sub(r'[^a-záéíóúüñ]', '', word)
    return len(re.findall(r'[aeiouáéíóúü]+', word))



def generate_synthetic_summary(article, gold_summary, band, lang='es'):
    prompt_user = f"""Artículo:
{article}

Resumen experto:
{gold_summary}

Tarea:
Genera un resumen en la banda {band} indicada por el sistema. Responde solo con el resumen."""
    response = client.chat.completions.create(
        model="gpt-4.1-mini",  # <-- Check this model name!
        messages=[
            {"role": "system", "content": PROMPTS_ES[band]},
            {"role": "user", "content": prompt_user}
        ],
        temperature=0.4,
    )
    return response.choices[0].message.content.strip()

def revise_to_band(text, band):
    adjustments = {
        "B1": "Acorta oraciones a 8–12 palabras, usa palabras más comunes y evita tecnicismos.",
        "B2": "Ajusta oraciones a 12–18 palabras y limita tecnicismos con breve explicación.",
        "B3": "Usa 18–25 palabras por oración, permite frases subordinadas y vocabulario más técnico.",
    }
    msg = f"""Reescribe el texto para que cumpla la banda {band}:
- {adjustments[band]}
- Mantén fidelidad al contenido.
Devuelve solo el texto revisado, sin comentarios."""
    r = client.chat.completions.create(
        model="gpt-4.1-mini",
        messages=[
            {"role": "system", "content": PROMPTS_ES[band]},
            {"role": "user", "content": text},
            {"role": "user", "content": msg}
        ],
        temperature=0.3,
    )
    return r.choices[0].message.content.strip()

def build_synthetic_dataset(input_path, output_path, max_samples=None):
    """Generate synthetic dataset from a JSON file with {fulltext, summary}"""
    results = []
    seen_articles = set()
    if os.path.exists(output_path):
        with open(output_path, 'r') as f:
            results = json.load(f)
            seen_articles = set(r['article'] for r in results)
    with open(input_path, "r") as f:
        data = json.load(f)
        for item in tqdm.tqdm(data):
            if max_samples and len(results) >= max_samples:
                break
            article, gold = item["fulltext"], item["summary"]
            if article in seen_articles:
                continue
            temp = {}
            for band in ["B1", "B2", "B3"]:
                synthetic = generate_synthetic_summary(article, gold, band)
                fh = fernandez_huerta(synthetic)
                lo, hi = FH_TARGETS[band]
                if fh is None or not (lo <= fh <= hi):
                    synthetic = revise_to_band(synthetic, band)
                temp[band] = synthetic
            results.append({
                "article": article,
                "gold_summary": gold,
                "synthetic_summary": temp
            })
            seen_articles.add(article)
            if len(results) % 5 == 0:
                print(f"Processed {len(results)} samples, saving progress...")
                with open(output_path, "w") as f:
                    json.dump(results, f, ensure_ascii=False, indent=4)
    with open(output_path, "w") as f:
        json.dump(results, f, ensure_ascii=False, indent=4)

# Example usage:
lang = "es"
path = f"/home/mshahidul/readctrl/data/testing_data_gs/multiclinsum_gs_train_{lang}.json"
build_synthetic_dataset(path, f"/home/mshahidul/readctrl/generating_data/{lang}_synthetic.json", max_samples=100)