File size: 9,072 Bytes
59b983d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
"""Small, curated diabetes education knowledge base.

The application uses deterministic keyword retrieval so the language model receives
short summaries from reputable public-health sources. This is intentionally simple
and transparent for a course project; it is not a clinical retrieval system.
"""

from __future__ import annotations

from dataclasses import dataclass
import re
from typing import Iterable


@dataclass(frozen=True)
class Source:
    source_id: str
    title: str
    organization: str
    url: str
    summary: str
    keywords: tuple[str, ...]


SOURCES: tuple[Source, ...] = (
    Source(
        source_id="S1",
        title="What Is Diabetes?",
        organization="National Institute of Diabetes and Digestive and Kidney Diseases (NIDDK)",
        url="https://www.niddk.nih.gov/health-information/diabetes/overview/what-is-diabetes",
        summary=(
            "Diabetes occurs when blood glucose is too high because the body does not make "
            "enough insulin, makes no insulin, or does not use insulin properly. The main types "
            "include type 1, type 2, and gestational diabetes."
        ),
        keywords=(
            "diabetes", "type 1", "type one", "type 2", "type two", "gestational",
            "insulin", "blood sugar", "blood glucose", "glucose", "prediabetes",
            "diabetes tipo", "azúcar en la sangre", "glucosa",
        ),
    ),
    Source(
        source_id="S2",
        title="Diabetes Tests & Diagnosis",
        organization="NIDDK",
        url="https://www.niddk.nih.gov/health-information/diabetes/overview/tests-diagnosis",
        summary=(
            "Health professionals diagnose diabetes and prediabetes with blood tests. Home blood "
            "glucose meters cannot diagnose diabetes, and abnormal results may need confirmation."
        ),
        keywords=(
            "a1c", "hba1c", "test", "diagnosis", "diagnose", "screen", "fasting",
            "oral glucose", "lab", "laboratory", "prediabetes range", "prueba", "diagnóstico",
        ),
    ),
    Source(
        source_id="S3",
        title="About Insulin Resistance and Type 2 Diabetes",
        organization="Centers for Disease Control and Prevention (CDC)",
        url="https://www.cdc.gov/diabetes/about/insulin-resistance-type-2-diabetes.html",
        summary=(
            "Insulin resistance means cells do not respond well to insulin. The pancreas may make "
            "more insulin at first, but blood glucose can rise over time and lead to prediabetes "
            "or type 2 diabetes."
        ),
        keywords=(
            "insulin resistance", "resistant", "prediabetes", "type 2", "metabolic",
            "resistencia a la insulina", "prediabetes",
        ),
    ),
    Source(
        source_id="S4",
        title="Diabetes Meal Planning",
        organization="CDC",
        url="https://www.cdc.gov/diabetes/healthy-eating/diabetes-meal-planning.html",
        summary=(
            "Meal planning can support nutrition and blood glucose management. Common educational "
            "tools include carbohydrate awareness and the plate method. Individual plans should be "
            "created with a qualified clinician or diabetes educator."
        ),
        keywords=(
            "food", "meal", "diet", "carb", "carbohydrate", "plate method", "portion",
            "breakfast", "lunch", "dinner", "snack", "nutrition", "eat", "eating",
            "comida", "alimentación", "carbohidrato", "plato", "nutrición",
        ),
    ),
    Source(
        source_id="S5",
        title="Healthy Living with Diabetes",
        organization="NIDDK",
        url="https://www.niddk.nih.gov/health-information/diabetes/overview/healthy-living-with-diabetes",
        summary=(
            "Healthy living with diabetes may include balanced eating, physical activity, adequate "
            "sleep, stress management, and collaboration with a health care team. Plans should be "
            "adapted to the person's health status and treatment."
        ),
        keywords=(
            "exercise", "activity", "walking", "workout", "sleep", "stress", "lifestyle",
            "weight", "healthy living", "physical activity", "ejercicio", "actividad", "sueño",
        ),
    ),
    Source(
        source_id="S6",
        title="Low Blood Sugar (Hypoglycemia)",
        organization="CDC",
        url="https://www.cdc.gov/diabetes/about/low-blood-sugar-hypoglycemia.html",
        summary=(
            "Low blood glucose can be dangerous and requires prompt attention. People at risk should "
            "know their care plan, recognize warning signs, and discuss prevention and treatment with "
            "their health care team."
        ),
        keywords=(
            "low blood sugar", "low glucose", "hypoglycemia", "hypoglycaemia", "shaky",
            "sweating", "confused", "glucagon", "azúcar baja", "hipoglucemia", "temblor",
        ),
    ),
    Source(
        source_id="S7",
        title="Manage Blood Sugar",
        organization="CDC",
        url="https://www.cdc.gov/diabetes/treatment/index.html",
        summary=(
            "Blood glucose can become too high or too low for many reasons. Monitoring frequency and "
            "target ranges should be determined with a health professional, especially for people "
            "using insulin or medicines that can cause hypoglycemia."
        ),
        keywords=(
            "high blood sugar", "high glucose", "hyperglycemia", "ketone", "monitor", "meter",
            "cgm", "continuous glucose", "target range", "azúcar alta", "hiperglucemia", "cetona",
        ),
    ),
    Source(
        source_id="S8",
        title="Insulin, Medicines, & Other Diabetes Treatments",
        organization="NIDDK",
        url="https://www.niddk.nih.gov/health-information/diabetes/overview/insulin-medicines-treatments",
        summary=(
            "Diabetes treatment may involve lifestyle measures, oral medicines, injectable medicines, "
            "or insulin. The appropriate treatment depends on diabetes type, other health conditions, "
            "side effects, cost, access, and individual circumstances. Medication changes require a clinician."
        ),
        keywords=(
            "medicine", "medication", "drug", "insulin", "metformin", "glp-1", "glp1",
            "side effect", "dose", "dosage", "injection", "medicina", "medicamento", "dosis",
        ),
    ),
    Source(
        source_id="S9",
        title="Preventing Diabetes Problems",
        organization="NIDDK",
        url="https://www.niddk.nih.gov/health-information/diabetes/overview/preventing-problems",
        summary=(
            "Diabetes can affect the heart, blood vessels, kidneys, eyes, nerves, and feet. Routine "
            "care and management of blood glucose, blood pressure, cholesterol, and smoking status "
            "can help reduce complication risks."
        ),
        keywords=(
            "complication", "heart", "kidney", "eye", "vision", "nerve", "neuropathy", "foot",
            "feet", "stroke", "cholesterol", "blood pressure", "complicación", "riñón", "pie",
        ),
    ),
)


def _tokenize(text: str) -> set[str]:
    return set(re.findall(r"[a-záéíóúñ0-9-]+", text.lower()))


def retrieve_sources(query: str, limit: int = 3) -> list[Source]:
    """Return the most relevant curated sources for a user query.

    Scores use exact phrase matches plus token overlap. A general diabetes source
    is always available as a fallback.
    """
    normalized = query.lower().strip()
    query_tokens = _tokenize(normalized)
    ranked: list[tuple[float, Source]] = []

    for source in SOURCES:
        score = 0.0
        for keyword in source.keywords:
            key = keyword.lower()
            if key in normalized:
                score += 4.0 if " " in key else 2.0
            key_tokens = _tokenize(key)
            score += 0.35 * len(query_tokens.intersection(key_tokens))
        ranked.append((score, source))

    ranked.sort(key=lambda item: item[0], reverse=True)
    selected = [source for score, source in ranked if score > 0][:limit]

    if not selected:
        selected = [SOURCES[0], SOURCES[4]][:limit]
    elif SOURCES[0] not in selected and len(selected) < limit:
        selected.append(SOURCES[0])

    return selected[:limit]


def format_context(sources: Iterable[Source]) -> str:
    """Format source summaries for inclusion in the model prompt."""
    blocks = []
    for source in sources:
        blocks.append(
            f"[{source.source_id}] {source.title}{source.organization}\n"
            f"Summary: {source.summary}\n"
            f"URL: {source.url}"
        )
    return "\n\n".join(blocks)


def format_reference_list(sources: Iterable[Source]) -> str:
    """Format deterministic Markdown references appended to each answer."""
    items = [
        f"- [{source.source_id}] [{source.title}]({source.url}) — {source.organization}"
        for source in sources
    ]
    return "\n".join(items)