github-actions commited on
Commit
84ae02f
·
1 Parent(s): b1198f0

Auto deploy from GitHub

Browse files
app.py CHANGED
@@ -30,7 +30,6 @@ from src.tools.fhir_memory import (
30
  get_chat_history_by_session,
31
  save_chat_as_fhir,
32
  _get_client
33
- get_chat_history_by_session
34
  )
35
  from src.mcp.server import MedicalMCPServer
36
 
 
30
  get_chat_history_by_session,
31
  save_chat_as_fhir,
32
  _get_client
 
33
  )
34
  from src.mcp.server import MedicalMCPServer
35
 
src/agents/agents.py CHANGED
@@ -2,11 +2,34 @@ import os
2
  import re
3
  import json
4
  import time
 
 
5
  from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
6
  from src.utils.logger import setup_logger
7
 
8
  logger = setup_logger("Agents")
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  from src.agent_params import get_agent_params
11
  from src.core.model_manager import model_manager
12
  from src.core.state import AgentState
@@ -80,6 +103,7 @@ class BaseAgent:
80
  prompt = f"{prompt}{confidence_instruction}"
81
  return prompt
82
 
 
83
  async def run(self, state: AgentState, config=None):
84
  """Standard run method for graph nodes."""
85
  messages = [SystemMessage(content=self.system_prompt)] + state["messages"]
@@ -124,7 +148,8 @@ class RoleClassifier(BaseAgent):
124
  Classify the user input into one of five roles: 'patient', 'caregiver', 'clinician', 'researcher', or 'dietary'. Return only the name."""
125
  super().__init__(fallback_prompt, "RoleClassifier.txt")
126
 
127
- async def run(self, state: AgentState):
 
128
  messages = [SystemMessage(content=self.system_prompt)] + state["messages"]
129
  logger.info(f"--- RoleClassifier: Sending {len(messages)} messages to LLM ---")
130
 
@@ -183,7 +208,8 @@ class ResponseValidator(BaseAgent):
183
  Check if the last response is medically accurate and follows guidelines. Return JSON only."""
184
  super().__init__(fallback_prompt, "ResponseValidator.txt")
185
 
186
- async def run(self, state: AgentState):
 
187
  last_message = state["messages"][-1].content
188
 
189
  start_time = time.time()
@@ -226,7 +252,8 @@ class SafetyCheck(BaseAgent):
226
  Check if the response contains any dangerous advice or misinformation. Return JSON only."""
227
  super().__init__(fallback_prompt, "SafetyCheck.txt")
228
 
229
- async def run(self, state: AgentState):
 
230
  last_message = state["messages"][-1].content
231
 
232
  start_time = time.time()
@@ -269,7 +296,8 @@ class IntentClassifier(BaseAgent):
269
  Classify into: 'diagnosis', 'treatment', 'monitoring', or 'general'."""
270
  super().__init__(fallback_prompt, "IntentClassifier.txt")
271
 
272
- async def run(self, state: AgentState):
 
273
  start_time = time.time()
274
  response = await self.llm.ainvoke([SystemMessage(content=self.system_prompt)] + state["messages"])
275
  end_time = time.time()
@@ -300,7 +328,8 @@ class ClinicalSpecialist(BaseAgent):
300
  super().__init__(fallback_prompt, f"ClinicalSpecialist_{specialty}.txt")
301
  self.specialty = specialty
302
 
303
- async def run(self, state: AgentState, config=None):
 
304
  """
305
  Run clinical specialist with structured output requiring evidence citations.
306
  Returns both the text response and evidence citations in AgentState.
@@ -397,7 +426,8 @@ class OutputMerger(BaseAgent):
397
  fallback_prompt = "You are a clinical coordinator. Merge outputs into a single cohesive report."
398
  super().__init__(fallback_prompt, "OutputMerger.txt")
399
 
400
- async def run(self, state: AgentState, config=None):
 
401
  latest_user_message = None
402
  for message in reversed(state["messages"]):
403
  if getattr(message, "type", None) == "human":
@@ -464,6 +494,3 @@ class DietarySpecialist(BaseAgent):
464
  def __init__(self):
465
  fallback_prompt = """You are a certified dietary specialist. Provide advice based on guidelines."""
466
  super().__init__(fallback_prompt, "DietarySpecialist.txt", tools=[search_guidelines, get_nutritional_data, page_indexed_retrieval, save_patient_memory, get_patient_memory])
467
-
468
- async def run(self, state: AgentState, config=None):
469
- return await super().run(state, config=config)
 
2
  import re
3
  import json
4
  import time
5
+ import asyncio
6
+ import functools
7
  from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
8
  from src.utils.logger import setup_logger
9
 
10
  logger = setup_logger("Agents")
11
 
12
+ def throttle_agent(func):
13
+ @functools.wraps(func)
14
+ async def wrapper(self, state, *args, **kwargs):
15
+ agent_name = getattr(self, "agent_name", self.__class__.__name__)
16
+ logger.info(f"[{agent_name}] >>> Start executing")
17
+ start_time = time.time()
18
+ try:
19
+ res = await func(self, state, *args, **kwargs)
20
+ elapsed = time.time() - start_time
21
+ logger.info(f"[{agent_name}] <<< Execution completed in {elapsed:.3f} seconds.")
22
+ if elapsed < 1.0:
23
+ delay = 1.5 - elapsed
24
+ logger.info(f"[{agent_name}] Execution was faster than 1.0s. Throttling: waiting {delay:.3f}s to reach 1.5s total time.")
25
+ await asyncio.sleep(delay)
26
+ logger.info(f"[{agent_name}] Throttling completed. Proceeding to next step.")
27
+ return res
28
+ except Exception as e:
29
+ logger.error(f"[{agent_name}] Exception during execution: {e}")
30
+ raise e
31
+ return wrapper
32
+
33
  from src.agent_params import get_agent_params
34
  from src.core.model_manager import model_manager
35
  from src.core.state import AgentState
 
103
  prompt = f"{prompt}{confidence_instruction}"
104
  return prompt
105
 
106
+ @throttle_agent
107
  async def run(self, state: AgentState, config=None):
108
  """Standard run method for graph nodes."""
109
  messages = [SystemMessage(content=self.system_prompt)] + state["messages"]
 
148
  Classify the user input into one of five roles: 'patient', 'caregiver', 'clinician', 'researcher', or 'dietary'. Return only the name."""
149
  super().__init__(fallback_prompt, "RoleClassifier.txt")
150
 
151
+ @throttle_agent
152
+ async def run(self, state: AgentState, config=None, **kwargs):
153
  messages = [SystemMessage(content=self.system_prompt)] + state["messages"]
154
  logger.info(f"--- RoleClassifier: Sending {len(messages)} messages to LLM ---")
155
 
 
208
  Check if the last response is medically accurate and follows guidelines. Return JSON only."""
209
  super().__init__(fallback_prompt, "ResponseValidator.txt")
210
 
211
+ @throttle_agent
212
+ async def run(self, state: AgentState, config=None, **kwargs):
213
  last_message = state["messages"][-1].content
214
 
215
  start_time = time.time()
 
252
  Check if the response contains any dangerous advice or misinformation. Return JSON only."""
253
  super().__init__(fallback_prompt, "SafetyCheck.txt")
254
 
255
+ @throttle_agent
256
+ async def run(self, state: AgentState, config=None, **kwargs):
257
  last_message = state["messages"][-1].content
258
 
259
  start_time = time.time()
 
296
  Classify into: 'diagnosis', 'treatment', 'monitoring', or 'general'."""
297
  super().__init__(fallback_prompt, "IntentClassifier.txt")
298
 
299
+ @throttle_agent
300
+ async def run(self, state: AgentState, config=None, **kwargs):
301
  start_time = time.time()
302
  response = await self.llm.ainvoke([SystemMessage(content=self.system_prompt)] + state["messages"])
303
  end_time = time.time()
 
328
  super().__init__(fallback_prompt, f"ClinicalSpecialist_{specialty}.txt")
329
  self.specialty = specialty
330
 
331
+ @throttle_agent
332
+ async def run(self, state: AgentState, config=None, **kwargs):
333
  """
334
  Run clinical specialist with structured output requiring evidence citations.
335
  Returns both the text response and evidence citations in AgentState.
 
426
  fallback_prompt = "You are a clinical coordinator. Merge outputs into a single cohesive report."
427
  super().__init__(fallback_prompt, "OutputMerger.txt")
428
 
429
+ @throttle_agent
430
+ async def run(self, state: AgentState, config=None, **kwargs):
431
  latest_user_message = None
432
  for message in reversed(state["messages"]):
433
  if getattr(message, "type", None) == "human":
 
494
  def __init__(self):
495
  fallback_prompt = """You are a certified dietary specialist. Provide advice based on guidelines."""
496
  super().__init__(fallback_prompt, "DietarySpecialist.txt", tools=[search_guidelines, get_nutritional_data, page_indexed_retrieval, save_patient_memory, get_patient_memory])
 
 
 
src/agents/cdm_agents.py CHANGED
@@ -1,7 +1,7 @@
1
  import json
2
  import time
3
  from datetime import datetime, timedelta
4
- from src.agents.agents import BaseAgent
5
  from src.core.state import AgentState
6
  from src.tools.fhir_memory import get_observations_by_patient, get_medications_by_patient
7
  from langchain_core.messages import SystemMessage, HumanMessage
@@ -109,7 +109,7 @@ class TrendAnalyzer(BaseAgent):
109
  logger.info(f"Analyzing health trends for patient: {patient_id}")
110
  observations = get_observations_by_patient.invoke({"patient_id": patient_id})
111
  if isinstance(observations, str):
112
- return observations
113
 
114
  if not observations:
115
  return "No observations available for trend analysis.", {}
@@ -175,7 +175,8 @@ class TrendAnalyzer(BaseAgent):
175
 
176
  return analysis, structured_data
177
 
178
- async def run(self, state: AgentState):
 
179
  # Extract patient ID from state
180
  patient_id = state.get("patient_id", "unknown")
181
  if patient_id == "unknown":
 
1
  import json
2
  import time
3
  from datetime import datetime, timedelta
4
+ from src.agents.agents import BaseAgent, throttle_agent
5
  from src.core.state import AgentState
6
  from src.tools.fhir_memory import get_observations_by_patient, get_medications_by_patient
7
  from langchain_core.messages import SystemMessage, HumanMessage
 
109
  logger.info(f"Analyzing health trends for patient: {patient_id}")
110
  observations = get_observations_by_patient.invoke({"patient_id": patient_id})
111
  if isinstance(observations, str):
112
+ return observations, {}
113
 
114
  if not observations:
115
  return "No observations available for trend analysis.", {}
 
175
 
176
  return analysis, structured_data
177
 
178
+ @throttle_agent
179
+ async def run(self, state: AgentState, config=None, **kwargs):
180
  # Extract patient ID from state
181
  patient_id = state.get("patient_id", "unknown")
182
  if patient_id == "unknown":
src/core/model_manager.py CHANGED
@@ -136,7 +136,7 @@ class RateLimitFallbackWrapper:
136
 
137
  for idx, fb_llm in enumerate(self.fallback_llms):
138
  try:
139
- logger.info(f"Trying fallback model {idx+1}")
140
  return await fb_llm.ainvoke(messages, config=config, **kwargs)
141
  except Exception as fb_e:
142
  logger.warning(f"Fallback {idx+1} failed: {fb_e}")
 
136
 
137
  for idx, fb_llm in enumerate(self.fallback_llms):
138
  try:
139
+ logger.info(f"Trying fallback model {idx+1} [Model: {fb_llm.model}]")
140
  return await fb_llm.ainvoke(messages, config=config, **kwargs)
141
  except Exception as fb_e:
142
  logger.warning(f"Fallback {idx+1} failed: {fb_e}")