Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -169,7 +169,7 @@ def process_user_interaction_gradio(user_input: str, provider_name: str, model_d
|
|
169 |
logger.debug(f"PUI_GRADIO [{request_id}]: Final LLM User Prompt Start: {final_user_prompt_content_str[:200]}...")
|
170 |
streamed_response, time_before_llm = "", time.time()
|
171 |
try:
|
172 |
-
for chunk in call_model_stream(provider=provider_name, model_display_name=model_display_name, messages=final_llm_messages, api_key_override=ui_api_key_override, temperature=0.6, max_tokens=2500):
|
173 |
if isinstance(chunk, str) and chunk.startswith("Error:"): streamed_response += f"\n{chunk}\n"; yield "response_chunk", f"\n{chunk}\n"; break
|
174 |
streamed_response += chunk; yield "response_chunk", chunk
|
175 |
except Exception as e: streamed_response += f"\n\n(Error: {str(e)[:150]})"; yield "response_chunk", f"\n\n(Error: {str(e)[:150]})"
|
@@ -184,7 +184,8 @@ def deferred_learning_and_memory_task(user_input: str, bot_response: str, provid
|
|
184 |
try:
|
185 |
metrics = generate_interaction_metrics(user_input, bot_response, provider, model_disp_name, api_key_override)
|
186 |
logger.info(f"DEFERRED [{task_id}]: Metrics: {metrics}")
|
187 |
-
add_memory_entry(user_input, metrics, bot_response)
|
|
|
188 |
summary = f"User:\"{user_input}\"\nAI:\"{bot_response}\"\nMetrics(takeaway):{metrics.get('takeaway','N/A')},Success:{metrics.get('response_success_score','N/A')}"
|
189 |
existing_rules_ctx = "\n".join([f"- \"{r}\"" for r in retrieve_rules_semantic(f"{summary}\n{user_input}", k=10)]) or "No existing rules context."
|
190 |
|
@@ -240,12 +241,10 @@ Combine all findings into a single, valid XML structure as specified in the syst
|
|
240 |
if i_d_n: insight_prov, insight_model_disp = i_p, i_d_n
|
241 |
logger.info(f"DEFERRED [{task_id}]: Generating insights with {insight_prov}/{insight_model_disp} (expecting XML)")
|
242 |
|
243 |
-
|
244 |
-
raw_ops_xml_full = "".join(list(call_model_stream(provider=insight_prov, model_display_name=insight_model_disp, messages=insight_msgs, api_key_override=api_key_override, temperature=0.0, max_tokens=3500))).strip() # Increased max_tokens
|
245 |
|
246 |
ops_data_list, processed_count = [], 0
|
247 |
|
248 |
-
# Try to find XML block, possibly within markdown
|
249 |
xml_match = re.search(r"```xml\s*(<operations_list>.*</operations_list>)\s*```", raw_ops_xml_full, re.DOTALL | re.IGNORECASE) or \
|
250 |
re.search(r"(<operations_list>.*</operations_list>)", raw_ops_xml_full, re.DOTALL | re.IGNORECASE)
|
251 |
|
@@ -267,7 +266,7 @@ Combine all findings into a single, valid XML structure as specified in the syst
|
|
267 |
ops_data_list.append({
|
268 |
"action": action,
|
269 |
"insight": insight_text,
|
270 |
-
"old_insight_to_replace": old_insight_text
|
271 |
})
|
272 |
else:
|
273 |
logger.warning(f"DEFERRED [{task_id}]: Skipped XML operation due to missing action or insight text. Action: {action}, Insight: {insight_text}")
|
@@ -287,25 +286,24 @@ Combine all findings into a single, valid XML structure as specified in the syst
|
|
287 |
insight_text = op_data["insight"]
|
288 |
old_insight = op_data["old_insight_to_replace"]
|
289 |
|
290 |
-
# Validation of insight_text format
|
291 |
if not re.match(r"\[(CORE_RULE|RESPONSE_PRINCIPLE|BEHAVIORAL_ADJUSTMENT|GENERAL_LEARNING)\|([\d\.]+?)\]", insight_text, re.I|re.DOTALL):
|
292 |
logger.warning(f"DEFERRED [{task_id}]: Op {op_idx}: Skipped op due to invalid insight_text format from XML: '{insight_text[:100]}...'")
|
293 |
continue
|
294 |
|
295 |
if action == "add":
|
296 |
-
success, status_msg = add_rule_entry(insight_text)
|
297 |
if success: processed_count +=1
|
298 |
else: logger.warning(f"DEFERRED [{task_id}]: Op {op_idx} (add from XML): Failed to add rule '{insight_text[:50]}...'. Status: {status_msg}")
|
299 |
elif action == "update":
|
300 |
if old_insight:
|
301 |
if old_insight != insight_text:
|
302 |
-
remove_success = remove_rule_entry(old_insight)
|
303 |
if not remove_success:
|
304 |
logger.warning(f"DEFERRED [{task_id}]: Op {op_idx} (update from XML): Failed to remove old rule '{old_insight[:50]}...' before adding new.")
|
305 |
else:
|
306 |
logger.info(f"DEFERRED [{task_id}]: Op {op_idx} (update from XML): Old insight is identical to new insight. Skipping removal.")
|
307 |
|
308 |
-
success, status_msg = add_rule_entry(insight_text)
|
309 |
if success: processed_count +=1
|
310 |
else: logger.warning(f"DEFERRED [{task_id}]: Op {op_idx} (update from XML): Failed to add/update rule '{insight_text[:50]}...'. Status: {status_msg}")
|
311 |
else:
|
@@ -318,15 +316,10 @@ Combine all findings into a single, valid XML structure as specified in the syst
|
|
318 |
except Exception as e: logger.error(f"DEFERRED [{task_id}]: CRITICAL ERROR in deferred task: {e}", exc_info=True)
|
319 |
logger.info(f"DEFERRED [{task_id}]: END. Total: {time.time() - start_time:.2f}s")
|
320 |
|
321 |
-
# --- handle_gradio_chat_submit, UI functions, Gradio Layout ... ---
|
322 |
-
# (The rest of the file: handle_gradio_chat_submit, all ui_... functions, and the
|
323 |
-
# `with gr.Blocks(...) as demo:` section, and `if __name__ == "__main__":`
|
324 |
-
# remain THE SAME as the previous fully correct version (v6.1) you provided.
|
325 |
-
# No changes are needed in those sections for this XML switch.)
|
326 |
-
|
327 |
def handle_gradio_chat_submit(user_msg_txt: str, gr_hist_list: list, sel_prov_name: str, sel_model_disp_name: str, ui_api_key: str|None, cust_sys_prompt: str):
|
328 |
global current_chat_session_history
|
329 |
cleared_input, updated_gr_hist, status_txt = "", list(gr_hist_list), "Initializing..."
|
|
|
330 |
def_detect_out_md = gr.Markdown(visible=False)
|
331 |
def_fmt_out_txt = gr.Textbox(value="*Waiting...*", interactive=True)
|
332 |
def_dl_btn = gr.DownloadButton(interactive=False, value=None, visible=False)
|
@@ -402,7 +395,9 @@ def handle_gradio_chat_submit(user_msg_txt: str, gr_hist_list: list, sel_prov_na
|
|
402 |
if len(current_chat_session_history) > hist_len_check:
|
403 |
current_chat_session_history = ([current_chat_session_history[0]] if current_chat_session_history[0]["role"] == "system" else []) + current_chat_session_history[-(MAX_HISTORY_TURNS * 2):]
|
404 |
|
405 |
-
|
|
|
|
|
406 |
status_txt = "Response complete. Background learning initiated."
|
407 |
else:
|
408 |
status_txt = "Processing finished; no valid response or error occurred."
|
@@ -413,8 +408,19 @@ def handle_gradio_chat_submit(user_msg_txt: str, gr_hist_list: list, sel_prov_na
|
|
413 |
def_fmt_out_txt = gr.Textbox(value=final_bot_resp_acc, interactive=True)
|
414 |
def_dl_btn = gr.DownloadButton(interactive=False, value=None, visible=False)
|
415 |
|
|
|
416 |
yield (cleared_input, updated_gr_hist, status_txt, def_detect_out_md, def_fmt_out_txt, def_dl_btn)
|
417 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
418 |
if temp_dl_file_path and os.path.exists(temp_dl_file_path):
|
419 |
try: os.unlink(temp_dl_file_path)
|
420 |
except Exception as e_unlink: logger.error(f"Error deleting temp download file {temp_dl_file_path}: {e_unlink}")
|
@@ -456,7 +462,6 @@ def ui_upload_rules_action_fn(uploaded_file_obj, progress=gr.Progress()):
|
|
456 |
line = line.strip()
|
457 |
if line:
|
458 |
try:
|
459 |
-
# Expect each line to be a JSON string containing the rule text itself
|
460 |
rule_text_in_json_string = json.loads(line)
|
461 |
if isinstance(rule_text_in_json_string, str):
|
462 |
potential_rules.append(rule_text_in_json_string)
|
@@ -713,15 +718,28 @@ with gr.Blocks(
|
|
713 |
chat_ins = [user_msg_tb, main_chat_disp, prov_sel_dd, model_sel_dd, api_key_tb, sys_prompt_tb]
|
714 |
chat_outs = [user_msg_tb, main_chat_disp, agent_stat_tb, detect_out_md, fmt_report_tb, dl_report_btn]
|
715 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
716 |
chat_event_args = {"fn": handle_gradio_chat_submit, "inputs": chat_ins, "outputs": chat_outs}
|
717 |
|
|
|
718 |
send_btn_click_event = send_btn.click(**chat_event_args)
|
719 |
user_msg_submit_event = user_msg_tb.submit(**chat_event_args)
|
720 |
|
|
|
|
|
|
|
721 |
for event in [send_btn_click_event, user_msg_submit_event]:
|
722 |
event.then(fn=ui_refresh_rules_display_fn, inputs=None, outputs=rules_disp_ta, show_progress=False)
|
723 |
event.then(fn=ui_refresh_memories_display_fn, inputs=None, outputs=mems_disp_json, show_progress=False)
|
724 |
|
|
|
725 |
# Rules Management events
|
726 |
dl_rules_btn.click(fn=ui_download_rules_action_fn, inputs=None, outputs=dl_rules_btn)
|
727 |
|
@@ -770,7 +788,7 @@ with gr.Blocks(
|
|
770 |
clear_rules_btn.click(
|
771 |
fn=lambda: ("All rules cleared." if clear_all_rules_data_backend() else "Error clearing rules."),
|
772 |
outputs=rules_stat_tb,
|
773 |
-
show_progress=False
|
774 |
).then(fn=ui_refresh_rules_display_fn, outputs=rules_disp_ta, show_progress=False)
|
775 |
|
776 |
# Memories Management events
|
@@ -786,7 +804,7 @@ with gr.Blocks(
|
|
786 |
clear_mems_btn.click(
|
787 |
fn=lambda: ("All memories cleared." if clear_all_memory_data_backend() else "Error clearing memories."),
|
788 |
outputs=mems_stat_tb,
|
789 |
-
show_progress=False
|
790 |
).then(fn=ui_refresh_memories_display_fn, outputs=mems_disp_json, show_progress=False)
|
791 |
|
792 |
if MEMORY_STORAGE_BACKEND == "RAM" and 'save_faiss_sidebar_btn' in locals():
|
@@ -822,7 +840,7 @@ with gr.Blocks(
|
|
822 |
|
823 |
|
824 |
if __name__ == "__main__":
|
825 |
-
logger.info(f"Starting Gradio AI Research Mega Agent (v6.
|
826 |
app_port = int(os.getenv("GRADIO_PORT", 7860))
|
827 |
app_server = os.getenv("GRADIO_SERVER_NAME", "127.0.0.1")
|
828 |
app_debug = os.getenv("GRADIO_DEBUG", "False").lower() == "true"
|
|
|
169 |
logger.debug(f"PUI_GRADIO [{request_id}]: Final LLM User Prompt Start: {final_user_prompt_content_str[:200]}...")
|
170 |
streamed_response, time_before_llm = "", time.time()
|
171 |
try:
|
172 |
+
for chunk in call_model_stream(provider=provider_name, model_display_name=model_display_name, messages=final_llm_messages, api_key_override=ui_api_key_override, temperature=0.6, max_tokens=2500):
|
173 |
if isinstance(chunk, str) and chunk.startswith("Error:"): streamed_response += f"\n{chunk}\n"; yield "response_chunk", f"\n{chunk}\n"; break
|
174 |
streamed_response += chunk; yield "response_chunk", chunk
|
175 |
except Exception as e: streamed_response += f"\n\n(Error: {str(e)[:150]})"; yield "response_chunk", f"\n\n(Error: {str(e)[:150]})"
|
|
|
184 |
try:
|
185 |
metrics = generate_interaction_metrics(user_input, bot_response, provider, model_disp_name, api_key_override)
|
186 |
logger.info(f"DEFERRED [{task_id}]: Metrics: {metrics}")
|
187 |
+
add_memory_entry(user_input, metrics, bot_response) # This updates in-memory lists quickly
|
188 |
+
|
189 |
summary = f"User:\"{user_input}\"\nAI:\"{bot_response}\"\nMetrics(takeaway):{metrics.get('takeaway','N/A')},Success:{metrics.get('response_success_score','N/A')}"
|
190 |
existing_rules_ctx = "\n".join([f"- \"{r}\"" for r in retrieve_rules_semantic(f"{summary}\n{user_input}", k=10)]) or "No existing rules context."
|
191 |
|
|
|
241 |
if i_d_n: insight_prov, insight_model_disp = i_p, i_d_n
|
242 |
logger.info(f"DEFERRED [{task_id}]: Generating insights with {insight_prov}/{insight_model_disp} (expecting XML)")
|
243 |
|
244 |
+
raw_ops_xml_full = "".join(list(call_model_stream(provider=insight_prov, model_display_name=insight_model_disp, messages=insight_msgs, api_key_override=api_key_override, temperature=0.0, max_tokens=3500))).strip()
|
|
|
245 |
|
246 |
ops_data_list, processed_count = [], 0
|
247 |
|
|
|
248 |
xml_match = re.search(r"```xml\s*(<operations_list>.*</operations_list>)\s*```", raw_ops_xml_full, re.DOTALL | re.IGNORECASE) or \
|
249 |
re.search(r"(<operations_list>.*</operations_list>)", raw_ops_xml_full, re.DOTALL | re.IGNORECASE)
|
250 |
|
|
|
266 |
ops_data_list.append({
|
267 |
"action": action,
|
268 |
"insight": insight_text,
|
269 |
+
"old_insight_to_replace": old_insight_text
|
270 |
})
|
271 |
else:
|
272 |
logger.warning(f"DEFERRED [{task_id}]: Skipped XML operation due to missing action or insight text. Action: {action}, Insight: {insight_text}")
|
|
|
286 |
insight_text = op_data["insight"]
|
287 |
old_insight = op_data["old_insight_to_replace"]
|
288 |
|
|
|
289 |
if not re.match(r"\[(CORE_RULE|RESPONSE_PRINCIPLE|BEHAVIORAL_ADJUSTMENT|GENERAL_LEARNING)\|([\d\.]+?)\]", insight_text, re.I|re.DOTALL):
|
290 |
logger.warning(f"DEFERRED [{task_id}]: Op {op_idx}: Skipped op due to invalid insight_text format from XML: '{insight_text[:100]}...'")
|
291 |
continue
|
292 |
|
293 |
if action == "add":
|
294 |
+
success, status_msg = add_rule_entry(insight_text) # This updates in-memory lists quickly
|
295 |
if success: processed_count +=1
|
296 |
else: logger.warning(f"DEFERRED [{task_id}]: Op {op_idx} (add from XML): Failed to add rule '{insight_text[:50]}...'. Status: {status_msg}")
|
297 |
elif action == "update":
|
298 |
if old_insight:
|
299 |
if old_insight != insight_text:
|
300 |
+
remove_success = remove_rule_entry(old_insight) # This updates in-memory lists quickly
|
301 |
if not remove_success:
|
302 |
logger.warning(f"DEFERRED [{task_id}]: Op {op_idx} (update from XML): Failed to remove old rule '{old_insight[:50]}...' before adding new.")
|
303 |
else:
|
304 |
logger.info(f"DEFERRED [{task_id}]: Op {op_idx} (update from XML): Old insight is identical to new insight. Skipping removal.")
|
305 |
|
306 |
+
success, status_msg = add_rule_entry(insight_text) # This updates in-memory lists quickly
|
307 |
if success: processed_count +=1
|
308 |
else: logger.warning(f"DEFERRED [{task_id}]: Op {op_idx} (update from XML): Failed to add/update rule '{insight_text[:50]}...'. Status: {status_msg}")
|
309 |
else:
|
|
|
316 |
except Exception as e: logger.error(f"DEFERRED [{task_id}]: CRITICAL ERROR in deferred task: {e}", exc_info=True)
|
317 |
logger.info(f"DEFERRED [{task_id}]: END. Total: {time.time() - start_time:.2f}s")
|
318 |
|
|
|
|
|
|
|
|
|
|
|
|
|
319 |
def handle_gradio_chat_submit(user_msg_txt: str, gr_hist_list: list, sel_prov_name: str, sel_model_disp_name: str, ui_api_key: str|None, cust_sys_prompt: str):
|
320 |
global current_chat_session_history
|
321 |
cleared_input, updated_gr_hist, status_txt = "", list(gr_hist_list), "Initializing..."
|
322 |
+
# Ensure these are initialized with their correct Gradio update types
|
323 |
def_detect_out_md = gr.Markdown(visible=False)
|
324 |
def_fmt_out_txt = gr.Textbox(value="*Waiting...*", interactive=True)
|
325 |
def_dl_btn = gr.DownloadButton(interactive=False, value=None, visible=False)
|
|
|
395 |
if len(current_chat_session_history) > hist_len_check:
|
396 |
current_chat_session_history = ([current_chat_session_history[0]] if current_chat_session_history[0]["role"] == "system" else []) + current_chat_session_history[-(MAX_HISTORY_TURNS * 2):]
|
397 |
|
398 |
+
# Start the deferred task
|
399 |
+
deferred_task_thread = threading.Thread(target=deferred_learning_and_memory_task, args=(user_msg_txt, final_bot_resp_acc, sel_prov_name, sel_model_disp_name, insights_used_parsed, ui_api_key.strip() if ui_api_key else None), daemon=True)
|
400 |
+
deferred_task_thread.start()
|
401 |
status_txt = "Response complete. Background learning initiated."
|
402 |
else:
|
403 |
status_txt = "Processing finished; no valid response or error occurred."
|
|
|
408 |
def_fmt_out_txt = gr.Textbox(value=final_bot_resp_acc, interactive=True)
|
409 |
def_dl_btn = gr.DownloadButton(interactive=False, value=None, visible=False)
|
410 |
|
411 |
+
# Final yield for the main chat processing
|
412 |
yield (cleared_input, updated_gr_hist, status_txt, def_detect_out_md, def_fmt_out_txt, def_dl_btn)
|
413 |
|
414 |
+
# After the main response is fully yielded, and the deferred task thread is started,
|
415 |
+
# we can add a small delay here before the .then() clauses trigger the UI refresh.
|
416 |
+
# This gives the deferred task's initial, fast in-memory updates (like add_memory_entry)
|
417 |
+
# a better chance to complete before the UI tries to read that data.
|
418 |
+
# The LLM-based rule generation within the deferred task will still take longer.
|
419 |
+
if 'deferred_task_thread' in locals() and deferred_task_thread.is_alive():
|
420 |
+
# Wait a very short period for synchronous parts of deferred task
|
421 |
+
# This is a heuristic. The value might need tuning.
|
422 |
+
time.sleep(0.2) # e.g., 200 milliseconds
|
423 |
+
|
424 |
if temp_dl_file_path and os.path.exists(temp_dl_file_path):
|
425 |
try: os.unlink(temp_dl_file_path)
|
426 |
except Exception as e_unlink: logger.error(f"Error deleting temp download file {temp_dl_file_path}: {e_unlink}")
|
|
|
462 |
line = line.strip()
|
463 |
if line:
|
464 |
try:
|
|
|
465 |
rule_text_in_json_string = json.loads(line)
|
466 |
if isinstance(rule_text_in_json_string, str):
|
467 |
potential_rules.append(rule_text_in_json_string)
|
|
|
718 |
chat_ins = [user_msg_tb, main_chat_disp, prov_sel_dd, model_sel_dd, api_key_tb, sys_prompt_tb]
|
719 |
chat_outs = [user_msg_tb, main_chat_disp, agent_stat_tb, detect_out_md, fmt_report_tb, dl_report_btn]
|
720 |
|
721 |
+
# Define a dummy function to introduce a delay if needed for UI refresh.
|
722 |
+
# For now, we will try without an explicit Gradio-level delay here,
|
723 |
+
# relying on the small time.sleep in handle_gradio_chat_submit's final part.
|
724 |
+
# def delayed_refresh_trigger():
|
725 |
+
# time.sleep(0.2) # Small delay
|
726 |
+
# return True # Or any value, just to trigger the next .then()
|
727 |
+
|
728 |
+
# chat_event_args is a dictionary containing common arguments for click/submit
|
729 |
chat_event_args = {"fn": handle_gradio_chat_submit, "inputs": chat_ins, "outputs": chat_outs}
|
730 |
|
731 |
+
# Setup events for chat submission
|
732 |
send_btn_click_event = send_btn.click(**chat_event_args)
|
733 |
user_msg_submit_event = user_msg_tb.submit(**chat_event_args)
|
734 |
|
735 |
+
# Chain UI refreshes to occur after the chat processing logic (including the small sleep)
|
736 |
+
# The `handle_gradio_chat_submit` now includes a small sleep before its final yield
|
737 |
+
# if a deferred task was started. This should be sufficient for the fast in-memory updates.
|
738 |
for event in [send_btn_click_event, user_msg_submit_event]:
|
739 |
event.then(fn=ui_refresh_rules_display_fn, inputs=None, outputs=rules_disp_ta, show_progress=False)
|
740 |
event.then(fn=ui_refresh_memories_display_fn, inputs=None, outputs=mems_disp_json, show_progress=False)
|
741 |
|
742 |
+
|
743 |
# Rules Management events
|
744 |
dl_rules_btn.click(fn=ui_download_rules_action_fn, inputs=None, outputs=dl_rules_btn)
|
745 |
|
|
|
788 |
clear_rules_btn.click(
|
789 |
fn=lambda: ("All rules cleared." if clear_all_rules_data_backend() else "Error clearing rules."),
|
790 |
outputs=rules_stat_tb,
|
791 |
+
show_progress=False # It's a quick operation
|
792 |
).then(fn=ui_refresh_rules_display_fn, outputs=rules_disp_ta, show_progress=False)
|
793 |
|
794 |
# Memories Management events
|
|
|
804 |
clear_mems_btn.click(
|
805 |
fn=lambda: ("All memories cleared." if clear_all_memory_data_backend() else "Error clearing memories."),
|
806 |
outputs=mems_stat_tb,
|
807 |
+
show_progress=False # Quick op
|
808 |
).then(fn=ui_refresh_memories_display_fn, outputs=mems_disp_json, show_progress=False)
|
809 |
|
810 |
if MEMORY_STORAGE_BACKEND == "RAM" and 'save_faiss_sidebar_btn' in locals():
|
|
|
840 |
|
841 |
|
842 |
if __name__ == "__main__":
|
843 |
+
logger.info(f"Starting Gradio AI Research Mega Agent (v6.3 - Delayed UI Refresh Attempt, Memory: {MEMORY_STORAGE_BACKEND})...")
|
844 |
app_port = int(os.getenv("GRADIO_PORT", 7860))
|
845 |
app_server = os.getenv("GRADIO_SERVER_NAME", "127.0.0.1")
|
846 |
app_debug = os.getenv("GRADIO_DEBUG", "False").lower() == "true"
|