File size: 7,697 Bytes
6754826
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import asyncio
import json
from datetime import datetime

import blood_rag
import reports
from rag import search_supplements as _search_supplements

# ── Notepad state ──────────────────────────────────────────────────────────────
_notes: list    = []   # [{"id": int, "text": str, "time": str}]
_listeners: list = []  # one asyncio.Queue per connected SSE client
_counter: int   = 0


def _broadcast(payload: dict):
    data = json.dumps(payload)
    for q in list(_listeners):
        try:
            q.put_nowait(data)
        except asyncio.QueueFull:
            pass


def add_listener(q: asyncio.Queue):
    _listeners.append(q)


def remove_listener(q: asyncio.Queue):
    if q in _listeners:
        _listeners.remove(q)


def get_notes_snapshot() -> dict:
    return {"notes": list(_notes)}


# ── Tool implementations ───────────────────────────────────────────────────────
def write_to_notepad(text: str) -> str:
    global _counter
    _counter += 1
    entry = {
        "id":   _counter,
        "text": text.strip(),
        "time": datetime.now().strftime("%I:%M:%S %p"),
    }
    _notes.append(entry)
    _broadcast(get_notes_snapshot())
    return f'Written to notepad: "{text.strip()}"'


def read_notepad() -> str:
    if not _notes:
        return "The notepad is empty."
    lines = [f"{n['time']} β€” {n['text']}" for n in _notes]
    return "Current notes:\n" + "\n".join(lines)


def clear_notepad() -> str:
    _notes.clear()
    _broadcast(get_notes_snapshot())
    return "Notepad cleared."


def get_datetime() -> str:
    return datetime.now().strftime("%A, %B %d %Y β€” %I:%M %p")


# ── Clinical tools ────────────────────────────────────────────────────────────
async def get_blood_report_summary() -> str:
    report = reports.get_current_report()
    if not report.get("has_report"):
        return (
            "No blood test report has been uploaded yet. Ask the user to upload their "
            "blood test report (PDF) using the Blood Test Report uploader."
        )
    return report["summary"]


async def search_supplements(query: str) -> str:
    return await _search_supplements(query)


async def search_blood_knowledge(query: str) -> str:
    """Search the locally indexed blood test documents."""
    return await blood_rag.search(query)


async def run_tool(name: str, args: dict) -> str:
    if name == "write_to_notepad":
        return write_to_notepad(args.get("text", ""))
    if name == "read_notepad":
        return read_notepad()
    if name == "clear_notepad":
        return clear_notepad()
    if name == "get_datetime":
        return get_datetime()
    if name == "get_blood_report_summary":
        return await get_blood_report_summary()
    if name == "search_supplements":
        return await search_supplements(args.get("query", ""))
    if name == "search_blood_knowledge":
        return await search_blood_knowledge(args.get("query", ""))
    return f"Unknown tool: {name}"


# ── OpenAI tool schemas ────────────────────────────────────────────────────────
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "write_to_notepad",
            "description": (
                "Write a note to the live notepad. Use this whenever the user says "
                "'note that', 'write down', 'remember', 'save this', 'keep track of', "
                "or asks you to record anything."
            ),
            "parameters": {
                "type": "object",
                "properties": {
                    "text": {
                        "type": "string",
                        "description": (
                            "The note text. Be concise β€” capture the key information "
                            "in plain language, no bullet points or markdown."
                        ),
                    }
                },
                "required": ["text"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "read_notepad",
            "description": (
                "Read all notes currently on the notepad. Use when the user asks "
                "'what did I note', 'read my notes', or 'what have you written down'."
            ),
            "parameters": {"type": "object", "properties": {}, "required": []},
        },
    },
    {
        "type": "function",
        "function": {
            "name": "clear_notepad",
            "description": "Erase all notes from the notepad. Use only when the user explicitly asks to clear or delete all notes.",
            "parameters": {"type": "object", "properties": {}, "required": []},
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_datetime",
            "description": "Get the current date and time.",
            "parameters": {"type": "object", "properties": {}, "required": []},
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_blood_report_summary",
            "description": (
                "Get a summary of the user's most recently uploaded blood test report, "
                "including which markers are low, high, or normal. Call this first "
                "whenever the user asks about their blood test, lab results, deficiencies, "
                "or what supplements they should take."
            ),
            "parameters": {"type": "object", "properties": {}, "required": []},
        },
    },
    {
        "type": "function",
        "function": {
            "name": "search_blood_knowledge",
            "description": (
                "Search the locally indexed blood test RAG for relevant passages about "
                "a patient's blood test results, markers, deficiencies, or lab values. "
                "Use this when the user asks about their blood work, specific markers, "
                "or what their results mean."
            ),
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "The question or marker to search for, e.g. 'iron deficiency anemia' or 'vitamin D levels'.",
                    }
                },
                "required": ["query"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "search_supplements",
            "description": (
                "Search the supplement knowledge base for recommendations related to a "
                "specific marker or deficiency (e.g. 'low vitamin D', 'low iron / "
                "ferritin'). Call this once per abnormal marker found in the user's blood "
                "report before recommending supplements."
            ),
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "The marker or deficiency to look up, e.g. 'low vitamin D' or 'high triglycerides'.",
                    }
                },
                "required": ["query"],
            },
        },
    },
]