Rajan Sharma commited on
Commit
c99015b
·
verified ·
1 Parent(s): eb5677d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -49
app.py CHANGED
@@ -1,4 +1,5 @@
1
  # app.py
 
2
  from __future__ import annotations
3
  import os
4
  import traceback
@@ -8,6 +9,11 @@ from typing import List, Tuple, Dict, Any
8
  import gradio as gr
9
  import pandas as pd
10
 
 
 
 
 
 
11
  # ---- Local modules
12
  from settings import (
13
  HEALTHCARE_SETTINGS, GENERAL_CONVERSATION_PROMPT, USE_SCENARIO_ENGINE, DEBUG_PLAN,
@@ -75,11 +81,12 @@ def ping_cohere() -> str:
75
  def handle(user_msg: str, history_messages: List[Dict[str, str]], files: list) -> Tuple[List[Dict[str, str]], str]:
76
  """
77
  One entrypoint for both healthcare scenarios and general conversation.
78
- - Scenario mode: planner -> deterministic executor -> LLM narrative (Cohere) -> safety-net narrative if needed.
79
- - General mode: direct to Cohere/open fallback with a light system prompt.
 
80
  """
81
  try:
82
- # Safety
83
  safe_in, blocked_in, reason_in = safety_filter(user_msg, mode="input")
84
  if blocked_in:
85
  reply = refusal_reply(reason_in)
@@ -87,70 +94,74 @@ def handle(user_msg: str, history_messages: List[Dict[str, str]], files: list) -
87
  new_hist = _append_msg(new_hist, "assistant", reply)
88
  return new_hist, ""
89
 
90
- # Normalize files into paths (Gradio can return temp file objects or paths)
91
  file_paths: List[str] = [getattr(f, "name", None) or f for f in (files or [])]
92
 
93
- # Register CSVs for deterministic analysis
94
- registry = DataRegistry()
95
- for p in file_paths:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  try:
97
- if p:
98
- registry.add_path(p)
99
  except Exception as e:
100
- log_event("ingest_error", None, {"file": p, "err": str(e)})
101
-
102
- # Lightweight RAG: ingest any text/markdown for grounding (embeddings via Cohere)
103
- rag = RAGIndex()
104
- try:
105
- ing = extract_text_from_files(file_paths)
106
- rag.add(ing.get("chunks", []))
107
- except Exception as e:
108
- log_event("rag_ingest_error", None, {"err": str(e)})
109
-
110
- # Decide mode
111
- if is_healthcare_scenario(safe_in, bool(file_paths)) and USE_SCENARIO_ENGINE:
112
- # 1) Deterministic dataset exposure
113
  analyzer = HealthcareAnalyzer(registry)
114
- datasets = analyzer.comprehensive_analysis(safe_in) # dict[str, DataFrame]
115
  catalog = _dataset_catalog(datasets)
116
-
117
- # 2) Plan (Cohere-first; auto safety-net if LLM parse fails)
118
  plan = parse_to_plan(safe_in, catalog)
119
-
120
- # 3) Execute plan deterministically
121
  structured_md = ScenarioEngine.execute_plan(plan, datasets)
122
-
123
- # 4) Narrative (Cohere-first), grounded with RAG hits
124
  rag_hits = [txt for txt, _ in rag.retrieve(safe_in, k=6)]
125
  narrative = generate_narrative(safe_in, structured_md, rag_hits)
126
 
127
- # 5) Safety-net narrative if LLM narrative absent/failed
128
  if not narrative or "Unable to generate narrative" in narrative:
129
- # Provide generic hints only (dynamic, not hard-coded to any schema)
130
  narrative = build_narrative(
131
- scenario_text=safe_in,
132
- datasets=datasets,
133
- structured_tables=None,
134
  metric_hints=["surgery_median", "consult_median", "wait", "median", "p90", "90th"],
135
  group_hints=["facility", "specialty", "zone", "hospital", "city", "region"],
136
  min_sample=5
137
  )
138
 
139
- debug_note = ""
140
- if DEBUG_PLAN and getattr(plan, "notes", None):
141
- debug_note = f"\n\n> **Planner note:** {getattr(plan, 'notes', '')}"
142
-
143
- reply = _sanitize_text(
144
- f"{structured_md}\n\n# Narrative & Recommendations\n\n{narrative}{debug_note}"
145
- )
146
 
147
  else:
148
- # General conversation mode (Cohere-first; open-weights fallback)
149
  prompt = f"{GENERAL_CONVERSATION_PROMPT}\n\nUser: {safe_in}\nAssistant:"
150
  reply = cohere_chat(prompt) or open_fallback_chat(prompt) or "How can I help further?"
151
  reply = _sanitize_text(reply)
152
 
153
- # Append to chat history
154
  new_hist = _append_msg(history_messages, "user", user_msg)
155
  new_hist = _append_msg(new_hist, "assistant", reply)
156
  return new_hist, ""
@@ -159,7 +170,7 @@ def handle(user_msg: str, history_messages: List[Dict[str, str]], files: list) -
159
  tb = traceback.format_exc()
160
  log_event("app_error", None, {"err": str(e), "tb": tb})
161
  new_hist = _append_msg(history_messages, "user", user_msg)
162
- new_hist = _append_msg(new_hist, "assistant", f"Error: {e}\n\n{tb}")
163
  return new_hist, ""
164
 
165
 
@@ -168,15 +179,15 @@ with gr.Blocks(analytics_enabled=False) as demo:
168
  gr.Markdown("## Canadian Healthcare AI • Cohere API • Scenario-Agnostic • Deterministic Analytics")
169
 
170
  with gr.Row():
171
- # Use messages format to avoid deprecation warnings and enable role-based history
172
- chat = gr.Chatbot(type="messages", height=520)
173
  files = gr.Files(
 
174
  file_count="multiple",
175
  type="filepath",
176
  file_types=HEALTHCARE_SETTINGS["supported_file_types"]
177
  )
178
 
179
- msg = gr.Textbox(placeholder="Paste any scenario (Background / Situation / Tasks / Deliverables) or just chat.")
180
  with gr.Row():
181
  send = gr.Button("Send")
182
  clear = gr.Button("Clear")
@@ -189,14 +200,18 @@ with gr.Blocks(analytics_enabled=False) as demo:
189
 
190
  send.click(_on_send, inputs=[msg, chat, files], outputs=[chat, msg])
191
  msg.submit(_on_send, inputs=[msg, chat, files], outputs=[chat, msg])
192
- clear.click(lambda: ([], ""), outputs=[chat, msg])
193
  ping_btn.click(lambda: ping_cohere(), outputs=[ping_out])
194
 
195
  if __name__ == "__main__":
 
 
 
 
196
  log_event("startup", None, {
197
  "cohere_key_present": bool(os.getenv("COHERE_API_KEY")),
198
  "cohere_model": COHERE_MODEL_PRIMARY,
199
  "open_fallbacks": USE_OPEN_FALLBACKS,
200
  "timeout_s": COHERE_TIMEOUT_S
201
  })
202
- demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")))
 
1
  # app.py
2
+ # app.py
3
  from __future__ import annotations
4
  import os
5
  import traceback
 
9
  import gradio as gr
10
  import pandas as pd
11
 
12
+ # New additions for data analysis agent
13
+ from langchain.agents.agent_types import AgentType
14
+ from langchain_community.chat_models import ChatCohere
15
+ from langchain_experimental.agents.agent_toolkits import create_pandas_dataframe_agent
16
+
17
  # ---- Local modules
18
  from settings import (
19
  HEALTHCARE_SETTINGS, GENERAL_CONVERSATION_PROMPT, USE_SCENARIO_ENGINE, DEBUG_PLAN,
 
81
  def handle(user_msg: str, history_messages: List[Dict[str, str]], files: list) -> Tuple[List[Dict[str, str]], str]:
82
  """
83
  One entrypoint for both healthcare scenarios and general conversation.
84
+ - NEW: If files are uploaded, a data-aware agent is used to perform analysis.
85
+ - Scenario mode (no files): planner -> deterministic executor -> LLM narrative (Cohere).
86
+ - General mode: direct to Cohere with a light system prompt.
87
  """
88
  try:
89
+ # Safety filter for user input
90
  safe_in, blocked_in, reason_in = safety_filter(user_msg, mode="input")
91
  if blocked_in:
92
  reply = refusal_reply(reason_in)
 
94
  new_hist = _append_msg(new_hist, "assistant", reply)
95
  return new_hist, ""
96
 
 
97
  file_paths: List[str] = [getattr(f, "name", None) or f for f in (files or [])]
98
 
99
+ # --- NEW LOGIC: Activate data agent if files are uploaded ---
100
+ if file_paths:
101
+ try:
102
+ # For this example, we'll load the first CSV file.
103
+ # This can be extended to handle multiple DataFrames.
104
+ df = pd.read_csv(file_paths[0])
105
+
106
+ # Initialize the Cohere Chat LLM for the agent
107
+ llm = ChatCohere(model=COHERE_MODEL_PRIMARY, temperature=0)
108
+
109
+ # Create the pandas DataFrame agent, powered by Cohere
110
+ agent = create_pandas_dataframe_agent(
111
+ llm,
112
+ df,
113
+ agent_type=AgentType.OPENAI_FUNCTIONS, # Recommended for reliability
114
+ verbose=True # Set to False in production
115
+ )
116
+
117
+ # Run the agent with the user's scenario text. The agent will
118
+ # write and execute code to answer the query based on the dataframe.
119
+ reply = agent.run(safe_in)
120
+ reply = _sanitize_text(reply)
121
+
122
+ except Exception as e:
123
+ tb = traceback.format_exc()
124
+ log_event("agent_error", None, {"err": str(e), "tb": tb})
125
+ reply = f"An error occurred while analyzing the data: {e}"
126
+
127
+ # --- ORIGINAL LOGIC: Fallback for scenarios without files or general chat ---
128
+ elif is_healthcare_scenario(safe_in, bool(file_paths)) and USE_SCENARIO_ENGINE:
129
+ # This block now primarily handles scenarios where no data files are provided,
130
+ # relying on the original deterministic analysis logic.
131
+ registry = DataRegistry() # This part might be simplified if files always trigger the agent
132
+ rag = RAGIndex()
133
  try:
134
+ ing = extract_text_from_files(file_paths) # For text extraction from markdown/txt
135
+ rag.add(ing.get("chunks", []))
136
  except Exception as e:
137
+ log_event("rag_ingest_error", None, {"err": str(e)})
138
+
 
 
 
 
 
 
 
 
 
 
 
139
  analyzer = HealthcareAnalyzer(registry)
140
+ datasets = analyzer.comprehensive_analysis(safe_in)
141
  catalog = _dataset_catalog(datasets)
 
 
142
  plan = parse_to_plan(safe_in, catalog)
 
 
143
  structured_md = ScenarioEngine.execute_plan(plan, datasets)
 
 
144
  rag_hits = [txt for txt, _ in rag.retrieve(safe_in, k=6)]
145
  narrative = generate_narrative(safe_in, structured_md, rag_hits)
146
 
 
147
  if not narrative or "Unable to generate narrative" in narrative:
 
148
  narrative = build_narrative(
149
+ scenario_text=safe_in, datasets=datasets, structured_tables=None,
 
 
150
  metric_hints=["surgery_median", "consult_median", "wait", "median", "p90", "90th"],
151
  group_hints=["facility", "specialty", "zone", "hospital", "city", "region"],
152
  min_sample=5
153
  )
154
 
155
+ debug_note = f"\n\n> **Planner note:** {getattr(plan, 'notes', '')}" if DEBUG_PLAN and getattr(plan, "notes", None) else ""
156
+ reply = _sanitize_text(f"{structured_md}\n\n# Narrative & Recommendations\n\n{narrative}{debug_note}")
 
 
 
 
 
157
 
158
  else:
159
+ # General conversation mode (no files, not a structured scenario)
160
  prompt = f"{GENERAL_CONVERSATION_PROMPT}\n\nUser: {safe_in}\nAssistant:"
161
  reply = cohere_chat(prompt) or open_fallback_chat(prompt) or "How can I help further?"
162
  reply = _sanitize_text(reply)
163
 
164
+ # Append interaction to chat history
165
  new_hist = _append_msg(history_messages, "user", user_msg)
166
  new_hist = _append_msg(new_hist, "assistant", reply)
167
  return new_hist, ""
 
170
  tb = traceback.format_exc()
171
  log_event("app_error", None, {"err": str(e), "tb": tb})
172
  new_hist = _append_msg(history_messages, "user", user_msg)
173
+ new_hist = _append_msg(new_hist, "assistant", f"A critical error occurred: {e}\n\n{tb}")
174
  return new_hist, ""
175
 
176
 
 
179
  gr.Markdown("## Canadian Healthcare AI • Cohere API • Scenario-Agnostic • Deterministic Analytics")
180
 
181
  with gr.Row():
182
+ chat = gr.Chatbot(label="Chat History", type="messages", height=520)
 
183
  files = gr.Files(
184
+ label="Upload Data Files (CSV recommended)",
185
  file_count="multiple",
186
  type="filepath",
187
  file_types=HEALTHCARE_SETTINGS["supported_file_types"]
188
  )
189
 
190
+ msg = gr.Textbox(label="Prompt", placeholder="Paste any scenario (Background / Situation / Tasks / Deliverables) or just chat.")
191
  with gr.Row():
192
  send = gr.Button("Send")
193
  clear = gr.Button("Clear")
 
200
 
201
  send.click(_on_send, inputs=[msg, chat, files], outputs=[chat, msg])
202
  msg.submit(_on_send, inputs=[msg, chat, files], outputs=[chat, msg])
203
+ clear.click(lambda: ([], "", None), outputs=[chat, msg, files])
204
  ping_btn.click(lambda: ping_cohere(), outputs=[ping_out])
205
 
206
  if __name__ == "__main__":
207
+ # Ensure you have your COHERE_API_KEY set as an environment variable
208
+ if not os.getenv("COHERE_API_KEY"):
209
+ print("🔴 COHERE_API_KEY environment variable not set. Application may not function correctly.")
210
+
211
  log_event("startup", None, {
212
  "cohere_key_present": bool(os.getenv("COHERE_API_KEY")),
213
  "cohere_model": COHERE_MODEL_PRIMARY,
214
  "open_fallbacks": USE_OPEN_FALLBACKS,
215
  "timeout_s": COHERE_TIMEOUT_S
216
  })
217
+ demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")))