Rajan Sharma commited on
Commit
86ddd31
·
verified ·
1 Parent(s): da71da2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +86 -204
app.py CHANGED
@@ -1,339 +1,221 @@
 
1
  from __future__ import annotations
2
-
3
  import os
4
- import io
5
  import traceback
6
- from contextlib import redirect_stdout
7
- from datetime import datetime
8
  from typing import List, Dict, Any
9
 
10
- import regex as re # pip install regex
11
  import gradio as gr
12
  import pandas as pd
 
13
 
14
  # --- BACKEND IMPORTS ---
15
- from langchain_cohere import ChatCohere # kept for compatibility if you use it elsewhere
16
- # If you still want PythonREPL later, you can add it back:
17
- # from langchain_community.utilities.python import PythonREPL
18
 
19
  # --- LOCAL MODULE IMPORTS ---
20
  from settings import (
21
- HEALTHCARE_SETTINGS,
22
- GENERAL_CONVERSATION_PROMPT,
23
- COHERE_MODEL_PRIMARY,
24
- COHERE_TIMEOUT_S,
25
- USE_OPEN_FALLBACKS,
26
  )
27
  from audit_log import log_event
28
  from privacy import safety_filter, refusal_reply
29
  from llm_router import cohere_chat, _co_client, cohere_embed
30
 
31
-
32
- # =========================
33
- # Utility Helpers
34
- # =========================
35
 
36
  def load_markdown_text(filepath: str) -> str:
37
- """Safely load text content from a markdown file."""
38
  try:
39
- with open(filepath, "r", encoding="utf-8") as f:
40
  return f.read()
41
  except FileNotFoundError:
42
  return f"**Error:** The document `{os.path.basename(filepath)}` was not found."
43
 
44
-
45
- def _sanitize_text(s: Any) -> str:
46
- """Remove control chars (except tabs/newlines) to keep UI stable."""
47
- if not isinstance(s, str):
48
- s = str(s)
49
- return re.sub(r"[\p{C}--[\n\t]]+", "", s)
50
-
51
-
52
- def _indent(s: str, spaces: int) -> str:
53
- pad = " " * spaces
54
- return "\n".join((pad + line) if line.strip() else line for line in s.splitlines())
55
-
56
-
57
- # =========================
58
- # AI Coding Path
59
- # =========================
60
 
61
  def _create_python_script(user_scenario: str, schema_context: str) -> str:
62
- """
63
- Ask the model to produce a single Python snippet that goes INSIDE a try: block of analyze_data(dfs).
64
- We extract the content between ```python ... ``` fences if present.
65
- """
66
  prompt_for_coder = f"""
67
- You are an expert Python data scientist. Your sole job is to write a single, complete analysis that will be inserted INSIDE:
 
68
 
69
- def analyze_data(dfs):
70
- try:
71
- # YOUR CODE HERE
72
- except Exception as e:
73
- print(f"An error occurred during analysis: {{e}}")
74
-
75
- RULES:
76
- 1) Use the provided list `dfs` of pandas DataFrames (dfs[0], dfs[1], ...).
77
- 2) Print results at each major step with print().
78
- 3) No placeholders; operate on real data in dfs.
79
- 4) The code you return must be valid Python and indentation-safe.
80
- 5) Do NOT redefine analyze_data; only provide the body INSIDE the try: block.
81
-
82
- --- DATA SCHEMA (heads) ---
83
  {schema_context}
84
  --- END SCHEMA ---
85
 
86
- --- USER SCENARIO ---
 
 
 
 
 
 
 
 
 
 
87
  {user_scenario}
88
 
89
- Return only a Python code block, fenced as ```python ... ``` containing the body that goes inside the try: block.
 
 
90
  """
91
- generated_text = cohere_chat(prompt_for_coder) or ""
92
- # Prefer fenced code
93
- fence = re.search(r"```python\s*(.*?)```", generated_text, re.DOTALL | re.IGNORECASE)
94
- if fence:
95
- body = fence.group(1).strip()
96
  else:
97
- # Fallback: take everything, best-effort trim if model didn’t fence
98
- body = generated_text.strip()
99
-
100
- # Strip any accidental wrapper definitions the model might add
101
- # e.g., remove "def analyze_data(dfs):" and a nested try:/except: if present
102
- body = re.sub(r"^def\s+analyze_data\s*\(.*?\):\s*", "", body)
103
- # We keep user's try/except if they provided, but usually we want raw steps.
104
- return body.strip() or "print('Error: No analysis steps were generated.')"
105
 
 
 
106
 
107
  def ping_cohere() -> str:
108
  """Lightweight health check against Cohere."""
109
  try:
110
  cli = _co_client()
111
- if not cli:
112
- return "Cohere client not initialized. Is COHERE_API_KEY set?"
113
  vecs = cohere_embed(["hello", "world"])
114
- if vecs:
115
- return f"Cohere OK ✅ (model={COHERE_MODEL_PRIMARY}, timeout={COHERE_TIMEOUT_S}s)"
116
- return "Cohere reachable."
117
  except Exception as e:
118
  return f"Cohere ping failed: {e}"
119
 
120
-
121
- # =========================
122
- # Core Analysis Engine
123
- # =========================
124
 
125
  def handle(user_msg: str, files: list) -> str:
126
- """
127
- Main backend engine using the 'Coder pattern':
128
- - Safety check
129
- - Load CSVs -> dfs
130
- - Build schema heads
131
- - Ask the model for analysis code (body only)
132
- - Execute analyze_data(dfs) in a safe, isolated namespace
133
- - Return captured stdout
134
- """
135
  try:
136
  safe_in, blocked_in, reason_in = safety_filter(user_msg, mode="input")
137
- if blocked_in:
138
- return refusal_reply(reason_in)
139
 
140
- # Resolve file paths (Gradio may give temp File objects or strings)
141
  file_paths: List[str] = [getattr(f, "name", None) or f for f in (files or [])]
142
 
143
  if file_paths:
144
- dataframes: List[pd.DataFrame] = []
145
- schema_parts: List[str] = []
146
  for i, p in enumerate(file_paths):
147
- if isinstance(p, str) and p.lower().endswith(".csv"):
148
  try:
149
  df = pd.read_csv(p)
150
  except UnicodeDecodeError:
151
- df = pd.read_csv(p, encoding="latin1")
152
  dataframes.append(df)
153
- head_md = df.head().to_markdown() if not df.empty else "(empty dataframe)"
154
- schema_parts.append(
155
- f"DataFrame dfs[{i}] from `{os.path.basename(p)}`:\n{head_md}\n"
156
- )
157
-
158
- if not dataframes:
159
- return "Please upload at least one CSV file."
160
 
161
  schema_context = "\n".join(schema_parts)
162
- analysis_body = _create_python_script(safe_in, schema_context)
163
-
164
- # Assemble the full script to exec
165
- script = f"""
166
- import pandas as pd
167
 
168
- def analyze_data(dfs):
169
- try:
170
- {_indent(analysis_body, 8)}
171
- except Exception as e:
172
- print(f"An error occurred during analysis: {{e}}")
173
- """
174
-
175
- # Execute in isolated namespace and capture stdout
176
- ns: Dict[str, Any] = {}
177
- ns["dfs"] = dataframes # make dfs available inside exec scope
178
-
179
- buf = io.StringIO()
180
  try:
181
- with redirect_stdout(buf):
182
- exec(script, ns, ns)
183
- # call analyze_data(dfs)
184
- ns["analyze_data"](ns["dfs"])
185
  except Exception as e:
186
- return _sanitize_text(f"An error occurred while executing the AI-generated script:\n{e}\n\nTraceback:\n{traceback.format_exc()}")
187
-
188
- output = buf.getvalue()
189
- return _sanitize_text(output or "(No output produced by the analysis.)")
190
-
191
- # No files: fall back to general conversation
192
- prompt = f"{GENERAL_CONVERSATION_PROMPT}\n\nUser: {safe_in}\nAssistant:"
193
- resp = cohere_chat(prompt) or "How can I help further?"
194
- return _sanitize_text(resp)
195
 
196
  except Exception as e:
197
  tb = traceback.format_exc()
198
  log_event("app_error", None, {"err": str(e), "tb": tb})
199
  return f"A critical error occurred: {e}"
200
 
201
-
202
- # =========================
203
- # UI (Gradio)
204
- # =========================
205
-
206
  PRIVACY_POLICY_TEXT = load_markdown_text("privacy_policy.md")
207
  TERMS_OF_SERVICE_TEXT = load_markdown_text("terms_of_service.md")
208
 
 
209
  with gr.Blocks(theme="soft", css="style.css") as demo:
210
- assessment_history = gr.State([]) # list[dict]: id, prompt, files, response
211
-
212
- # Modals
213
  with gr.Group(visible=False) as privacy_modal:
214
  with gr.Blocks():
215
  gr.Markdown(PRIVACY_POLICY_TEXT)
216
  close_privacy_btn = gr.Button("Close")
217
-
218
  with gr.Group(visible=False) as terms_modal:
219
  with gr.Blocks():
220
  gr.Markdown(TERMS_OF_SERVICE_TEXT)
221
  close_terms_btn = gr.Button("Close")
222
 
223
  gr.Markdown("# Universal AI Data Analyst")
224
-
225
  with gr.Row(variant="panel"):
226
  with gr.Column(scale=1):
227
  gr.Markdown("## New Assessment")
228
- files_input = gr.Files(
229
- label="Upload Data Files (.csv)",
230
- file_count="multiple",
231
- type="filepath",
232
- file_types=[".csv"],
233
- )
234
- prompt_input = gr.Textbox(
235
- label="Prompt",
236
- placeholder="Paste your scenario here.",
237
- lines=15,
238
- )
239
  with gr.Row():
240
  send_btn = gr.Button("▶️ Run Analysis", variant="primary", scale=2)
241
  clear_btn = gr.Button("🗑️ Clear")
242
  ping_btn = gr.Button("Ping Cohere")
243
  ping_out = gr.Markdown()
244
-
245
  with gr.Column(scale=2):
246
  with gr.Tabs():
247
  with gr.TabItem("Current Assessment", id=0):
248
- chat_history_output = gr.Chatbot(
249
- label="Analysis Output",
250
- type="messages",
251
- height=600,
252
- )
253
  with gr.TabItem("Assessment History", id=1):
254
  gr.Markdown("## Review Past Assessments")
255
- history_dropdown = gr.Dropdown(
256
- label="Select an assessment to review",
257
- choices=[],
258
- )
259
  history_display = gr.Markdown(label="Selected Assessment Details")
260
-
261
- with gr.Row():
262
- gr.Markdown("---")
263
-
264
  with gr.Row():
265
  privacy_link = gr.Button("Privacy Policy", variant="link")
266
  terms_link = gr.Button("Terms of Service", variant="link")
267
 
268
- # ---------- Callbacks ----------
269
-
270
  def run_analysis_wrapper(prompt, files, chat_history_list, history_state_list):
271
  if not prompt or not files:
272
  gr.Warning("Please provide both a prompt and at least one data file.")
273
  yield chat_history_list, history_state_list, gr.update()
274
  return
275
 
276
- chat_with_user_msg = (chat_history_list or []) + [{"role": "user", "content": prompt}]
277
- thinking_message = chat_with_user_msg + [
278
- {"role": "assistant", "content": "```\n🧠 Generating analysis script... This may take a moment.\n```"}
279
- ]
280
  yield thinking_message, history_state_list, gr.update()
281
-
282
  ai_response_text = handle(prompt, files)
283
-
284
- final_chat = chat_with_user_msg + [{"role": "assistant", "content": ai_response_text}]
285
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
286
- file_names = [os.path.basename(getattr(f, "name", f)) for f in files]
287
- new_assessment = {
288
- "id": timestamp,
289
- "prompt": prompt,
290
- "files": file_names,
291
- "response": ai_response_text,
292
- }
293
- updated_history = (history_state_list or []) + [new_assessment]
294
  history_labels = [f"{item['id']} - {item['prompt'][:40]}..." for item in updated_history]
295
  yield final_chat, updated_history, gr.update(choices=history_labels)
296
 
297
  def view_history(selection, history_state_list):
298
- if not selection or not history_state_list:
299
- return ""
300
- selected_id = selection.split(" - ")[0]
301
  selected_assessment = next((item for item in history_state_list if item["id"] == selected_id), None)
302
  if selected_assessment:
303
- file_list_md = "\n- ".join(selected_assessment["files"])
304
- return (
305
- f"### Assessment from: {selected_assessment['id']}\n"
306
- f"**Files Used:**\n- {file_list_md}\n---\n"
307
- f"**Original Prompt:**\n> {selected_assessment['prompt']}\n---\n"
308
- f"**AI Generated Response:**\n{selected_assessment['response']}"
309
- )
310
  return "Could not find the selected assessment."
311
 
312
- # Wire events
313
  send_btn.click(
314
  run_analysis_wrapper,
315
  inputs=[prompt_input, files_input, chat_history_output, assessment_history],
316
- outputs=[chat_history_output, assessment_history, history_dropdown],
317
  )
318
  history_dropdown.change(
319
  view_history,
320
  inputs=[history_dropdown, assessment_history],
321
- outputs=[history_display],
322
  )
323
- clear_btn.click(lambda: (None, None, [], []),
324
- outputs=[prompt_input, files_input, chat_history_output, assessment_history])
325
  ping_btn.click(ping_cohere, outputs=[ping_out])
326
  privacy_link.click(lambda: gr.update(visible=True), outputs=[privacy_modal])
327
  close_privacy_btn.click(lambda: gr.update(visible=False), outputs=[privacy_modal])
328
  terms_link.click(lambda: gr.update(visible=True), outputs=[terms_modal])
329
  close_terms_btn.click(lambda: gr.update(visible=False), outputs=[terms_modal])
330
 
331
-
332
- # =========================
333
- # Entrypoint
334
- # =========================
335
-
336
  if __name__ == "__main__":
337
  if not os.getenv("COHERE_API_KEY"):
338
- print("🔴 COHERE_API_KEY environment variable not set. The app may not function correctly.")
339
  demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")))
 
1
+ # app.py
2
  from __future__ import annotations
 
3
  import os
 
4
  import traceback
5
+ import regex as re2
 
6
  from typing import List, Dict, Any
7
 
 
8
  import gradio as gr
9
  import pandas as pd
10
+ from datetime import datetime
11
 
12
  # --- BACKEND IMPORTS ---
13
+ from langchain_cohere import ChatCohere
14
+ from langchain_community.utilities.python import PythonREPL # Re-introducing the standard, robust executor
 
15
 
16
  # --- LOCAL MODULE IMPORTS ---
17
  from settings import (
18
+ HEALTHCARE_SETTINGS, GENERAL_CONVERSATION_PROMPT,
19
+ COHERE_MODEL_PRIMARY, COHERE_TIMEOUT_S, USE_OPEN_FALLBACKS
 
 
 
20
  )
21
  from audit_log import log_event
22
  from privacy import safety_filter, refusal_reply
23
  from llm_router import cohere_chat, _co_client, cohere_embed
24
 
25
+ # --- UTILITY FUNCTIONS ---
 
 
 
26
 
27
  def load_markdown_text(filepath: str) -> str:
28
+ """Safely loads text content from a markdown file."""
29
  try:
30
+ with open(filepath, 'r', encoding='utf-8') as f:
31
  return f.read()
32
  except FileNotFoundError:
33
  return f"**Error:** The document `{os.path.basename(filepath)}` was not found."
34
 
35
+ def _sanitize_text(s: str) -> str:
36
+ if not isinstance(s, str): return s
37
+ return re2.sub(r'[\p{C}--[\n\t]]+', '', s)
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  def _create_python_script(user_scenario: str, schema_context: str) -> str:
40
+ """Uses an LLM to act as an "AI Coder", writing a complete Python script."""
41
+ # --- THE FINAL PROMPT FIX IS HERE ---
 
 
42
  prompt_for_coder = f"""
43
+ You are an expert Python data scientist. Your sole job is to write a single, complete, and executable Python script to answer the user's request.
44
+ You have access to a list of pandas dataframes loaded into a variable named `dfs`.
45
 
46
+ --- DATA SCHEMA ---
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  {schema_context}
48
  --- END SCHEMA ---
49
 
50
+ CRITICAL RULE: You MUST use the exact column names provided in the DATA SCHEMA. Column names are case-sensitive. Pay close attention to capitalization (e.g., 'Zone' vs 'zone'). A KeyError will cause a failure.
51
+
52
+ Based on the user's scenario below, write a single Python script that performs the entire analysis.
53
+
54
+ RULES FOR YOUR SCRIPT:
55
+ 1. **Use the DataFrames:** Your script MUST use the `dfs` list and the exact column names from the schema.
56
+ 2. **Print Your Findings:** Use the `print()` function at each step to output the results as a formatted report.
57
+ 3. **No Placeholders:** Do not use placeholder data.
58
+ 4. **Self-Contained:** The script must be entirely self-contained.
59
+
60
+ --- USER'S SCENARIO ---
61
  {user_scenario}
62
 
63
+ --- PYTHON SCRIPT ---
64
+ Now, write the complete Python script to be executed. The script should start with `import pandas as pd` and contain all the logic.
65
+ ```python
66
  """
67
+ generated_text = cohere_chat(prompt_for_coder)
68
+ match = re2.search(r"```python\n(.*?)```", generated_text, re2.DOTALL)
69
+ if match:
70
+ return match.group(1).strip()
 
71
  else:
72
+ return "print('Error: The AI failed to generate a valid Python script.')"
 
 
 
 
 
 
 
73
 
74
+ def _append_msg(history_messages: List[Dict[str, str]], role: str, content: str) -> List[Dict[str, str]]:
75
+ return (history_messages or []) + [{"role": role, "content": content}]
76
 
77
  def ping_cohere() -> str:
78
  """Lightweight health check against Cohere."""
79
  try:
80
  cli = _co_client()
81
+ if not cli: return "Cohere client not initialized."
 
82
  vecs = cohere_embed(["hello", "world"])
83
+ return f"Cohere OK ✅ (model={COHERE_MODEL_PRIMARY})" if vecs else "Cohere reachable."
 
 
84
  except Exception as e:
85
  return f"Cohere ping failed: {e}"
86
 
87
+ # --- THE CORE ANALYSIS ENGINE ---
 
 
 
88
 
89
  def handle(user_msg: str, files: list) -> str:
90
+ """This is the powerful backend engine using the "Coder" pattern."""
 
 
 
 
 
 
 
 
91
  try:
92
  safe_in, blocked_in, reason_in = safety_filter(user_msg, mode="input")
93
+ if blocked_in: return refusal_reply(reason_in)
 
94
 
 
95
  file_paths: List[str] = [getattr(f, "name", None) or f for f in (files or [])]
96
 
97
  if file_paths:
98
+ dataframes = []
99
+ schema_parts = []
100
  for i, p in enumerate(file_paths):
101
+ if p.endswith('.csv'):
102
  try:
103
  df = pd.read_csv(p)
104
  except UnicodeDecodeError:
105
+ df = pd.read_csv(p, encoding='latin1')
106
  dataframes.append(df)
107
+ schema_parts.append(f"DataFrame `dfs[{i}]` (from file `{os.path.basename(p)}`):\n{df.head().to_markdown()}\n")
108
+
109
+ if not dataframes: return "Please upload at least one CSV file."
 
 
 
 
110
 
111
  schema_context = "\n".join(schema_parts)
112
+ analysis_script = _create_python_script(safe_in, schema_context)
 
 
 
 
113
 
114
+ python_repl = PythonREPL()
115
+ local_vars = {"dfs": dataframes}
 
 
 
 
 
 
 
 
 
 
116
  try:
117
+ res = python_repl.run(command=analysis_script, locals=local_vars)
118
+ return _sanitize_text(res)
 
 
119
  except Exception as e:
120
+ return f"An error occurred executing the script: {e}\n\nGenerated Script:\n```python\n{analysis_script}\n```"
121
+ else:
122
+ prompt = f"{GENERAL_CONVERSATION_PROMPT}\n\nUser: {safe_in}\nAssistant:"
123
+ return _sanitize_text(cohere_chat(prompt) or "How can I help further?")
 
 
 
 
 
124
 
125
  except Exception as e:
126
  tb = traceback.format_exc()
127
  log_event("app_error", None, {"err": str(e), "tb": tb})
128
  return f"A critical error occurred: {e}"
129
 
130
+ # --- PRE-LOAD LEGAL DOCUMENTS ---
 
 
 
 
131
  PRIVACY_POLICY_TEXT = load_markdown_text("privacy_policy.md")
132
  TERMS_OF_SERVICE_TEXT = load_markdown_text("terms_of_service.md")
133
 
134
+ # ---------------- THE PROFESSIONAL UI WITH INTEGRATED LEGAL DOCS ----------------
135
  with gr.Blocks(theme="soft", css="style.css") as demo:
136
+ assessment_history = gr.State([])
137
+
 
138
  with gr.Group(visible=False) as privacy_modal:
139
  with gr.Blocks():
140
  gr.Markdown(PRIVACY_POLICY_TEXT)
141
  close_privacy_btn = gr.Button("Close")
142
+
143
  with gr.Group(visible=False) as terms_modal:
144
  with gr.Blocks():
145
  gr.Markdown(TERMS_OF_SERVICE_TEXT)
146
  close_terms_btn = gr.Button("Close")
147
 
148
  gr.Markdown("# Universal AI Data Analyst")
 
149
  with gr.Row(variant="panel"):
150
  with gr.Column(scale=1):
151
  gr.Markdown("## New Assessment")
152
+ files_input = gr.Files(label="Upload Data Files (.csv)", file_count="multiple", type="filepath", file_types=[".csv"])
153
+ prompt_input = gr.Textbox(label="Prompt", placeholder="Paste your scenario here.", lines=15)
 
 
 
 
 
 
 
 
 
154
  with gr.Row():
155
  send_btn = gr.Button("▶️ Run Analysis", variant="primary", scale=2)
156
  clear_btn = gr.Button("🗑️ Clear")
157
  ping_btn = gr.Button("Ping Cohere")
158
  ping_out = gr.Markdown()
 
159
  with gr.Column(scale=2):
160
  with gr.Tabs():
161
  with gr.TabItem("Current Assessment", id=0):
162
+ chat_history_output = gr.Chatbot(label="Analysis Output", type="messages", height=600)
 
 
 
 
163
  with gr.TabItem("Assessment History", id=1):
164
  gr.Markdown("## Review Past Assessments")
165
+ history_dropdown = gr.Dropdown(label="Select an assessment to review", choices=[])
 
 
 
166
  history_display = gr.Markdown(label="Selected Assessment Details")
167
+ with gr.Row(): gr.Markdown("---")
 
 
 
168
  with gr.Row():
169
  privacy_link = gr.Button("Privacy Policy", variant="link")
170
  terms_link = gr.Button("Terms of Service", variant="link")
171
 
 
 
172
  def run_analysis_wrapper(prompt, files, chat_history_list, history_state_list):
173
  if not prompt or not files:
174
  gr.Warning("Please provide both a prompt and at least one data file.")
175
  yield chat_history_list, history_state_list, gr.update()
176
  return
177
 
178
+ chat_with_user_msg = _append_msg(chat_history_list, "user", prompt)
179
+ thinking_message = _append_msg(chat_with_user_msg, "assistant", "```\n🧠 Generating analysis script... This may take a moment.\n```")
 
 
180
  yield thinking_message, history_state_list, gr.update()
181
+
182
  ai_response_text = handle(prompt, files)
183
+
184
+ final_chat = _append_msg(chat_with_user_msg, "assistant", ai_response_text)
185
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
186
+ file_names = [os.path.basename(f.name if hasattr(f, 'name') else f) for f in files]
187
+ new_assessment = {"id": timestamp, "prompt": prompt, "files": file_names, "response": ai_response_text}
188
+ updated_history = history_state_list + [new_assessment]
 
 
 
 
 
189
  history_labels = [f"{item['id']} - {item['prompt'][:40]}..." for item in updated_history]
190
  yield final_chat, updated_history, gr.update(choices=history_labels)
191
 
192
  def view_history(selection, history_state_list):
193
+ if not selection or not history_state_list: return ""
194
+ selected_id = selection.split(" - ")
 
195
  selected_assessment = next((item for item in history_state_list if item["id"] == selected_id), None)
196
  if selected_assessment:
197
+ file_list_md = "\n- ".join(selected_assessment['files'])
198
+ return f"""### Assessment from: {selected_assessment['id']}\n**Files Used:**\n- {file_list_md}\n---\n**Original Prompt:**\n> {selected_assessment['prompt']}\n---\n**AI Generated Response:**\n{selected_assessment['response']}"""
 
 
 
 
 
199
  return "Could not find the selected assessment."
200
 
 
201
  send_btn.click(
202
  run_analysis_wrapper,
203
  inputs=[prompt_input, files_input, chat_history_output, assessment_history],
204
+ outputs=[chat_history_output, assessment_history, history_dropdown]
205
  )
206
  history_dropdown.change(
207
  view_history,
208
  inputs=[history_dropdown, assessment_history],
209
+ outputs=[history_display]
210
  )
211
+ clear_btn.click(lambda: (None, None, [], []), outputs=[prompt_input, files_input, chat_history_output, assessment_history])
 
212
  ping_btn.click(ping_cohere, outputs=[ping_out])
213
  privacy_link.click(lambda: gr.update(visible=True), outputs=[privacy_modal])
214
  close_privacy_btn.click(lambda: gr.update(visible=False), outputs=[privacy_modal])
215
  terms_link.click(lambda: gr.update(visible=True), outputs=[terms_modal])
216
  close_terms_btn.click(lambda: gr.update(visible=False), outputs=[terms_modal])
217
 
 
 
 
 
 
218
  if __name__ == "__main__":
219
  if not os.getenv("COHERE_API_KEY"):
220
+ print("🔴 COHERE_API_KEY environment variable not set. Application may not function correctly.")
221
  demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")))