Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -8,7 +8,7 @@ from datetime import datetime
|
|
8 |
from dotenv import load_dotenv
|
9 |
import gradio as gr
|
10 |
import time
|
11 |
-
import tempfile
|
12 |
|
13 |
load_dotenv()
|
14 |
|
@@ -20,7 +20,8 @@ from memory_logic import (
|
|
20 |
initialize_memory_system,
|
21 |
add_memory_entry, retrieve_memories_semantic, get_all_memories_cached, clear_all_memory_data_backend,
|
22 |
add_rule_entry, retrieve_rules_semantic, remove_rule_entry, get_all_rules_cached, clear_all_rules_data_backend,
|
23 |
-
save_faiss_indices_to_disk, STORAGE_BACKEND as MEMORY_STORAGE_BACKEND
|
|
|
24 |
)
|
25 |
from websearch_logic import scrape_url, search_and_scrape_duckduckgo, search_and_scrape_google
|
26 |
|
@@ -41,6 +42,10 @@ DEFAULT_SYSTEM_PROMPT = os.getenv(
|
|
41 |
)
|
42 |
logger.info(f"App Config: WebSearch={WEB_SEARCH_ENABLED}, ToolDecisionProvider={TOOL_DECISION_PROVIDER_ENV}, ToolDecisionModelID={TOOL_DECISION_MODEL_ID_ENV}, MemoryBackend={MEMORY_STORAGE_BACKEND}")
|
43 |
|
|
|
|
|
|
|
|
|
44 |
def format_insights_for_prompt(retrieved_insights_list: list[str]) -> tuple[str, list[dict]]:
|
45 |
if not retrieved_insights_list:
|
46 |
return "No specific guiding principles or learned insights retrieved.", []
|
@@ -263,7 +268,11 @@ Combine all findings into a single JSON list of operations. If there are multipl
|
|
263 |
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):
|
264 |
global current_chat_session_history
|
265 |
cleared_input, updated_gr_hist, status_txt = "", list(gr_hist_list), "Initializing..."
|
266 |
-
def_detect_out_md, def_fmt_out_txt
|
|
|
|
|
|
|
|
|
267 |
if not user_msg_txt.strip():
|
268 |
status_txt = "Error: Empty message."
|
269 |
updated_gr_hist.append((user_msg_txt or "(Empty)", status_txt))
|
@@ -275,7 +284,7 @@ def handle_gradio_chat_submit(user_msg_txt: str, gr_hist_list: list, sel_prov_na
|
|
275 |
if internal_hist[0]["role"] == "system" and len(internal_hist) > (MAX_HISTORY_TURNS * 2 + 1) : internal_hist = [internal_hist[0]] + internal_hist[-(MAX_HISTORY_TURNS * 2):]
|
276 |
else: internal_hist = internal_hist[-(MAX_HISTORY_TURNS * 2):]
|
277 |
final_bot_resp_acc, insights_used_parsed = "", []
|
278 |
-
temp_dl_file_path = None
|
279 |
try:
|
280 |
processor_gen = process_user_interaction_gradio(user_input=user_msg_txt, provider_name=sel_prov_name, model_display_name=sel_model_disp_name, chat_history_for_prompt=internal_hist, custom_system_prompt=cust_sys_prompt.strip() or None, ui_api_key_override=ui_api_key.strip() if ui_api_key else None)
|
281 |
curr_bot_disp_msg = ""
|
@@ -293,9 +302,11 @@ def handle_gradio_chat_submit(user_msg_txt: str, gr_hist_list: list, sel_prov_na
|
|
293 |
if updated_gr_hist and updated_gr_hist[-1][0] == user_msg_txt: updated_gr_hist[-1] = (user_msg_txt, curr_bot_disp_msg or "(No text)")
|
294 |
def_fmt_out_txt = gr.Textbox(value=curr_bot_disp_msg)
|
295 |
if curr_bot_disp_msg and not curr_bot_disp_msg.startswith("Error:"):
|
|
|
296 |
with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".md", encoding='utf-8') as tmpfile:
|
297 |
tmpfile.write(curr_bot_disp_msg)
|
298 |
-
temp_dl_file_path = tmpfile.name
|
|
|
299 |
def_dl_btn = gr.DownloadButton(label="Download Report (.md)", value=temp_dl_file_path, visible=True, interactive=True)
|
300 |
else: def_dl_btn = gr.DownloadButton(interactive=False, value=None, visible=False)
|
301 |
insights_md = "### Insights Considered:\n" + ("\n".join([f"- **[{i.get('type','N/A')}|{i.get('score','N/A')}]** {i.get('text','N/A')[:100]}..." for i in insights_used_parsed[:3]]) if insights_used_parsed else "*None specific.*")
|
@@ -317,9 +328,14 @@ def handle_gradio_chat_submit(user_msg_txt: str, gr_hist_list: list, sel_prov_na
|
|
317 |
status_txt = "Response complete. Background learning initiated."
|
318 |
else: status_txt = "Processing finished; no response or error."
|
319 |
yield (cleared_input, updated_gr_hist, status_txt, def_detect_out_md, def_fmt_out_txt, def_dl_btn)
|
320 |
-
|
321 |
-
|
322 |
-
|
|
|
|
|
|
|
|
|
|
|
323 |
|
324 |
def ui_view_rules_action_fn(): return "\n\n---\n\n".join(get_all_rules_cached()) or "No rules found."
|
325 |
def ui_upload_rules_action_fn(uploaded_file_obj, progress=gr.Progress()):
|
@@ -377,56 +393,70 @@ def ui_upload_memories_action_fn(uploaded_file_obj, progress=gr.Progress()):
|
|
377 |
logger.info(msg); return msg
|
378 |
|
379 |
custom_theme = gr.themes.Base(primary_hue="teal", secondary_hue="purple", neutral_hue="zinc", text_size="sm", spacing_size="sm", radius_size="sm", font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"])
|
380 |
-
custom_css = """ body { font-family: 'Inter', sans-serif; } .gradio-container { max-width:
|
381 |
|
382 |
-
with gr.Blocks(theme=custom_theme, css=custom_css, title="AI Research Mega Agent
|
383 |
gr.Markdown("# π AI Research Mega Agent", elem_classes="prose")
|
384 |
-
avail_provs, def_prov = get_available_providers(), get_available_providers()[0] if get_available_providers() else None
|
385 |
-
def_models, def_model = get_model_display_names_for_provider(def_prov) if def_prov else [], get_default_model_display_name_for_provider(def_prov) if def_prov else None
|
386 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
387 |
with gr.Tabs() as main_tabs:
|
388 |
-
with gr.TabItem("π¬ Chat
|
389 |
with gr.Row():
|
390 |
-
with gr.Column(scale=1, min_width=
|
391 |
-
gr.Markdown("### βοΈ Configuration", elem_classes="prose")
|
392 |
with gr.Group(elem_classes="compact-group"):
|
393 |
prov_sel_dd = gr.Dropdown(label="Provider", choices=avail_provs, value=def_prov, interactive=True)
|
394 |
-
model_sel_dd = gr.Dropdown(label="Model", choices=def_models, value=
|
395 |
-
api_key_tb = gr.Textbox(label="API Key Override", type="password", placeholder="
|
396 |
with gr.Group(elem_classes="compact-group"):
|
397 |
-
sys_prompt_tb = gr.Textbox(label="System Prompt Base", lines=
|
398 |
-
|
|
|
399 |
main_chat_disp = gr.Chatbot(label="AI Research Chat", height=600, bubble_full_width=False, avatar_images=(None, "https://raw.githubusercontent.com/huggingface/brand-assets/main/hf-logo-with-title.png"), show_copy_button=True, render_markdown=True, sanitize_html=True)
|
400 |
with gr.Row():
|
401 |
user_msg_tb = gr.Textbox(show_label=False, placeholder="Ask a question or give an instruction...", scale=7, lines=1, max_lines=5, autofocus=True)
|
402 |
send_btn = gr.Button("Send", variant="primary", scale=1, min_width=100)
|
403 |
-
|
404 |
with gr.Accordion("π Full Response / Output", open=True):
|
405 |
-
fmt_report_tb = gr.Textbox(label="Current Research Output", lines=
|
406 |
-
dl_report_btn = gr.DownloadButton(
|
407 |
detect_out_md = gr.Markdown("*Insights used or other intermediate details will show here...*")
|
408 |
|
409 |
with gr.TabItem("π§ Knowledge Base Management", id=1):
|
410 |
-
gr.Markdown("## Knowledge
|
411 |
-
with gr.Row():
|
412 |
with gr.Column(scale=1):
|
413 |
gr.Markdown("### Rules (Learned Insights)", elem_classes="prose")
|
414 |
-
rules_disp_ta = gr.TextArea(label="View/Edit Rules (one per line or '---' separated)", lines=15, interactive=True
|
415 |
with gr.Row():
|
416 |
-
view_rules_btn = gr.Button("Load Rules
|
|
|
417 |
upload_rules_fobj = gr.File(label="Upload Rules File (.txt/.jsonl)", file_types=[".txt", ".jsonl"])
|
418 |
-
rules_stat_tb = gr.Textbox(label="Rules Action Status", interactive=False, lines=2)
|
419 |
with gr.Row():
|
420 |
clear_rules_btn = gr.Button("β οΈ Clear All Rules", variant="stop")
|
421 |
-
save_faiss_ram_btn = gr.Button("Save FAISS Indices", visible=(MEMORY_STORAGE_BACKEND == "RAM"))
|
422 |
|
423 |
with gr.Column(scale=1):
|
424 |
gr.Markdown("### Memories (Past Interactions)", elem_classes="prose")
|
425 |
-
mems_disp_json = gr.JSON(label="View Memories (JSON format)") #
|
426 |
with gr.Row():
|
427 |
-
view_mems_btn = gr.Button("Load Memories
|
428 |
upload_mems_fobj = gr.File(label="Upload Memories File (.jsonl)", file_types=[".jsonl"])
|
429 |
-
mems_stat_tb = gr.Textbox(label="Memories Action Status", interactive=False, lines=2)
|
430 |
clear_mems_btn = gr.Button("β οΈ Clear All Memories", variant="stop")
|
431 |
|
432 |
def dyn_upd_model_dd(sel_prov_dyn:str): models_dyn, def_model_dyn = get_model_display_names_for_provider(sel_prov_dyn), get_default_model_display_name_for_provider(sel_prov_dyn); return gr.Dropdown(choices=models_dyn, value=def_model_dyn, interactive=True)
|
@@ -441,21 +471,17 @@ with gr.Blocks(theme=custom_theme, css=custom_css, title="AI Research Mega Agent
|
|
441 |
potential_rules = edited_rules_text.split("\n\n---\n\n")
|
442 |
if len(potential_rules) == 1 and "\n" in edited_rules_text: potential_rules = [r.strip() for r in edited_rules_text.splitlines() if r.strip()]
|
443 |
if not potential_rules: return "No rules found to process from editor."
|
444 |
-
# For saving edited, it's often easier to clear existing and re-add all from editor
|
445 |
-
# Or, implement a diff and selective add/remove (more complex)
|
446 |
-
# Simple approach: clear and re-add. User should be warned.
|
447 |
-
# For now, this will just attempt to add them, duplicates will be skipped by memory_logic
|
448 |
added, skipped, errors = 0,0,0; total = len(potential_rules)
|
449 |
progress(0, desc=f"Saving {total} rules from editor...")
|
450 |
for idx, rule_text in enumerate(potential_rules):
|
451 |
if not rule_text.strip(): continue
|
452 |
-
success, status_msg = add_rule_entry(rule_text.strip())
|
453 |
if success: added +=1
|
454 |
elif status_msg == "duplicate": skipped +=1
|
455 |
else: errors +=1
|
456 |
progress((idx+1)/total)
|
457 |
return f"Editor Save: Added: {added}, Skipped (duplicates): {skipped}, Errors/Invalid: {errors}."
|
458 |
-
save_edited_rules_btn.click(fn=save_edited_rules_action_fn, inputs=[rules_disp_ta], outputs=[rules_stat_tb], show_progress="full")
|
459 |
|
460 |
upload_rules_fobj.upload(fn=ui_upload_rules_action_fn, inputs=[upload_rules_fobj], outputs=[rules_stat_tb], show_progress="full").then(fn=ui_view_rules_action_fn, outputs=rules_disp_ta)
|
461 |
clear_rules_btn.click(fn=lambda: "All rules cleared." if clear_all_rules_data_backend() else "Error clearing rules.", outputs=rules_stat_tb).then(fn=ui_view_rules_action_fn, outputs=rules_disp_ta)
|
@@ -468,11 +494,21 @@ with gr.Blocks(theme=custom_theme, css=custom_css, title="AI Research Mega Agent
|
|
468 |
upload_mems_fobj.upload(fn=ui_upload_memories_action_fn, inputs=[upload_mems_fobj], outputs=[mems_stat_tb], show_progress="full").then(fn=ui_view_memories_action_fn, outputs=mems_disp_json)
|
469 |
clear_mems_btn.click(fn=lambda: "All memories cleared." if clear_all_memory_data_backend() else "Error clearing memories.", outputs=mems_stat_tb).then(fn=ui_view_memories_action_fn, outputs=mems_disp_json)
|
470 |
|
471 |
-
def app_load_fn():
|
472 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
473 |
|
474 |
if __name__ == "__main__":
|
475 |
-
logger.info(f"Starting Gradio AI Research Mega Agent (
|
476 |
app_port, app_server = int(os.getenv("GRADIO_PORT", 7860)), os.getenv("GRADIO_SERVER_NAME", "127.0.0.1")
|
477 |
app_debug, app_share = os.getenv("GRADIO_DEBUG", "False").lower()=="true", os.getenv("GRADIO_SHARE", "False").lower()=="true"
|
478 |
logger.info(f"Launching Gradio server: http://{app_server}:{app_port}. Debug: {app_debug}, Share: {app_share}")
|
|
|
8 |
from dotenv import load_dotenv
|
9 |
import gradio as gr
|
10 |
import time
|
11 |
+
import tempfile
|
12 |
|
13 |
load_dotenv()
|
14 |
|
|
|
20 |
initialize_memory_system,
|
21 |
add_memory_entry, retrieve_memories_semantic, get_all_memories_cached, clear_all_memory_data_backend,
|
22 |
add_rule_entry, retrieve_rules_semantic, remove_rule_entry, get_all_rules_cached, clear_all_rules_data_backend,
|
23 |
+
save_faiss_indices_to_disk, STORAGE_BACKEND as MEMORY_STORAGE_BACKEND, SQLITE_DB_PATH as MEMORY_SQLITE_PATH,
|
24 |
+
HF_MEMORY_DATASET_REPO as MEMORY_HF_MEM_REPO, HF_RULES_DATASET_REPO as MEMORY_HF_RULES_REPO
|
25 |
)
|
26 |
from websearch_logic import scrape_url, search_and_scrape_duckduckgo, search_and_scrape_google
|
27 |
|
|
|
42 |
)
|
43 |
logger.info(f"App Config: WebSearch={WEB_SEARCH_ENABLED}, ToolDecisionProvider={TOOL_DECISION_PROVIDER_ENV}, ToolDecisionModelID={TOOL_DECISION_MODEL_ID_ENV}, MemoryBackend={MEMORY_STORAGE_BACKEND}")
|
44 |
|
45 |
+
# --- Helper Functions (format_insights_for_prompt, generate_interaction_metrics, etc.) ---
|
46 |
+
# These functions (format_insights_for_prompt, generate_interaction_metrics,
|
47 |
+
# process_user_interaction_gradio, deferred_learning_and_memory_task) remain the same as in the previous "full working file".
|
48 |
+
# For brevity here, I will not repeat them. Ensure they are present in your actual app.py.
|
49 |
def format_insights_for_prompt(retrieved_insights_list: list[str]) -> tuple[str, list[dict]]:
|
50 |
if not retrieved_insights_list:
|
51 |
return "No specific guiding principles or learned insights retrieved.", []
|
|
|
268 |
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):
|
269 |
global current_chat_session_history
|
270 |
cleared_input, updated_gr_hist, status_txt = "", list(gr_hist_list), "Initializing..."
|
271 |
+
def_detect_out_md, def_fmt_out_txt = gr.Markdown("*Processing...*"), gr.Textbox("*Waiting...*")
|
272 |
+
# For DownloadButton, create a placeholder value that's a valid file path (even if empty initially)
|
273 |
+
# Or simply update it to gr.DownloadButton(interactive=False, value=None, visible=False)
|
274 |
+
def_dl_btn = gr.DownloadButton(interactive=False, value=None, visible=False)
|
275 |
+
|
276 |
if not user_msg_txt.strip():
|
277 |
status_txt = "Error: Empty message."
|
278 |
updated_gr_hist.append((user_msg_txt or "(Empty)", status_txt))
|
|
|
284 |
if internal_hist[0]["role"] == "system" and len(internal_hist) > (MAX_HISTORY_TURNS * 2 + 1) : internal_hist = [internal_hist[0]] + internal_hist[-(MAX_HISTORY_TURNS * 2):]
|
285 |
else: internal_hist = internal_hist[-(MAX_HISTORY_TURNS * 2):]
|
286 |
final_bot_resp_acc, insights_used_parsed = "", []
|
287 |
+
temp_dl_file_path = None
|
288 |
try:
|
289 |
processor_gen = process_user_interaction_gradio(user_input=user_msg_txt, provider_name=sel_prov_name, model_display_name=sel_model_disp_name, chat_history_for_prompt=internal_hist, custom_system_prompt=cust_sys_prompt.strip() or None, ui_api_key_override=ui_api_key.strip() if ui_api_key else None)
|
290 |
curr_bot_disp_msg = ""
|
|
|
302 |
if updated_gr_hist and updated_gr_hist[-1][0] == user_msg_txt: updated_gr_hist[-1] = (user_msg_txt, curr_bot_disp_msg or "(No text)")
|
303 |
def_fmt_out_txt = gr.Textbox(value=curr_bot_disp_msg)
|
304 |
if curr_bot_disp_msg and not curr_bot_disp_msg.startswith("Error:"):
|
305 |
+
# Create a temporary file for the download
|
306 |
with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".md", encoding='utf-8') as tmpfile:
|
307 |
tmpfile.write(curr_bot_disp_msg)
|
308 |
+
temp_dl_file_path = tmpfile.name # This path will be passed to DownloadButton
|
309 |
+
# Update DownloadButton with the path to the temp file
|
310 |
def_dl_btn = gr.DownloadButton(label="Download Report (.md)", value=temp_dl_file_path, visible=True, interactive=True)
|
311 |
else: def_dl_btn = gr.DownloadButton(interactive=False, value=None, visible=False)
|
312 |
insights_md = "### Insights Considered:\n" + ("\n".join([f"- **[{i.get('type','N/A')}|{i.get('score','N/A')}]** {i.get('text','N/A')[:100]}..." for i in insights_used_parsed[:3]]) if insights_used_parsed else "*None specific.*")
|
|
|
328 |
status_txt = "Response complete. Background learning initiated."
|
329 |
else: status_txt = "Processing finished; no response or error."
|
330 |
yield (cleared_input, updated_gr_hist, status_txt, def_detect_out_md, def_fmt_out_txt, def_dl_btn)
|
331 |
+
# Clean up the temporary file after the download button has been yielded (and potentially used)
|
332 |
+
# This cleanup is tricky with Gradio's yield. The file needs to exist when Gradio processes the yield.
|
333 |
+
# A more robust way is to have Gradio manage the temp file or use a cleanup mechanism that runs after the HTTP response.
|
334 |
+
# For simplicity, we'll leave it, but in a prod app, temp files should be managed.
|
335 |
+
# if temp_dl_file_path and os.path.exists(temp_dl_file_path):
|
336 |
+
# try: os.unlink(temp_dl_file_path)
|
337 |
+
# except Exception as e_unlink: logger.error(f"Error deleting temp download file {temp_dl_file_path}: {e_unlink}")
|
338 |
+
|
339 |
|
340 |
def ui_view_rules_action_fn(): return "\n\n---\n\n".join(get_all_rules_cached()) or "No rules found."
|
341 |
def ui_upload_rules_action_fn(uploaded_file_obj, progress=gr.Progress()):
|
|
|
393 |
logger.info(msg); return msg
|
394 |
|
395 |
custom_theme = gr.themes.Base(primary_hue="teal", secondary_hue="purple", neutral_hue="zinc", text_size="sm", spacing_size="sm", radius_size="sm", font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"])
|
396 |
+
custom_css = """ body { font-family: 'Inter', sans-serif; } .gradio-container { max-width: 98% !important; margin: auto !important; padding-top: 0.5rem !important; } footer { display: none !important; } .gr-button { white-space: nowrap; } .gr-input, .gr-textarea textarea, .gr-dropdown input { border-radius: 8px !important; } .gr-chatbot .message { border-radius: 10px !important; box-shadow: 0 2px 5px rgba(0,0,0,0.08) !important; } .prose { h1 { font-size: 1.8rem; margin-bottom: 0.6em; margin-top: 0.8em; } h2 { font-size: 1.4rem; margin-bottom: 0.5em; margin-top: 0.7em; } h3 { font-size: 1.15rem; margin-bottom: 0.4em; margin-top: 0.6em; } p { margin-bottom: 0.8em; line-height: 1.65; } ul, ol { margin-left: 1.5em; margin-bottom: 0.8em; } code { background-color: #f1f5f9; padding: 0.2em 0.45em; border-radius: 4px; font-size: 0.9em; } pre > code { display: block; padding: 0.8em; overflow-x: auto; background-color: #f8fafc; border: 1px solid #e2e8f0; border-radius: 6px;}} .compact-group .gr-input-label, .compact-group .gr-dropdown-label { font-size: 0.8rem !important; padding-bottom: 2px !important;} .status-bar { padding: 0.5em; margin-bottom: 0.5em; border-radius: 6px; background-color: #eef2ff;} """ # Added status-bar style
|
397 |
|
398 |
+
with gr.Blocks(theme=custom_theme, css=custom_css, title="AI Research Mega Agent v5") as demo:
|
399 |
gr.Markdown("# π AI Research Mega Agent", elem_classes="prose")
|
|
|
|
|
400 |
|
401 |
+
avail_provs = get_available_providers()
|
402 |
+
def_prov = avail_provs[0] if avail_provs else None
|
403 |
+
def_models = get_model_display_names_for_provider(def_prov) if def_prov else []
|
404 |
+
def_model_disp = get_default_model_display_name_for_provider(def_prov) if def_prov else None
|
405 |
+
|
406 |
+
with gr.Row(elem_classes="status-bar"):
|
407 |
+
agent_stat_tb = gr.Textbox(label="Agent Status", interactive=False, lines=1, value="Initializing systems...", scale=3)
|
408 |
+
memory_backend_info_tb = gr.Textbox(label="Memory Backend", value=f"{MEMORY_STORAGE_BACKEND}", interactive=False, scale=1)
|
409 |
+
if MEMORY_STORAGE_BACKEND == "SQLITE":
|
410 |
+
gr.Textbox(label="SQLite Path", value=f"{MEMORY_SQLITE_PATH}", interactive=False, scale=2)
|
411 |
+
elif MEMORY_STORAGE_BACKEND == "HF_DATASET":
|
412 |
+
gr.Textbox(label="HF Repos", value=f"M: {MEMORY_HF_MEM_REPO}, R: {MEMORY_HF_RULES_REPO}", interactive=False, scale=2)
|
413 |
+
|
414 |
+
|
415 |
with gr.Tabs() as main_tabs:
|
416 |
+
with gr.TabItem("π¬ Dashboard & Chat", id=0):
|
417 |
with gr.Row():
|
418 |
+
with gr.Column(scale=1, min_width=320):
|
419 |
+
gr.Markdown("### βοΈ LLM Configuration", elem_classes="prose")
|
420 |
with gr.Group(elem_classes="compact-group"):
|
421 |
prov_sel_dd = gr.Dropdown(label="Provider", choices=avail_provs, value=def_prov, interactive=True)
|
422 |
+
model_sel_dd = gr.Dropdown(label="Model", choices=def_models, value=def_model_disp, interactive=True)
|
423 |
+
api_key_tb = gr.Textbox(label="API Key Override (Optional)", type="password", placeholder="Enter API key if not in .env")
|
424 |
with gr.Group(elem_classes="compact-group"):
|
425 |
+
sys_prompt_tb = gr.Textbox(label="System Prompt Base", lines=10, value=DEFAULT_SYSTEM_PROMPT, interactive=True)
|
426 |
+
|
427 |
+
with gr.Column(scale=2):
|
428 |
main_chat_disp = gr.Chatbot(label="AI Research Chat", height=600, bubble_full_width=False, avatar_images=(None, "https://raw.githubusercontent.com/huggingface/brand-assets/main/hf-logo-with-title.png"), show_copy_button=True, render_markdown=True, sanitize_html=True)
|
429 |
with gr.Row():
|
430 |
user_msg_tb = gr.Textbox(show_label=False, placeholder="Ask a question or give an instruction...", scale=7, lines=1, max_lines=5, autofocus=True)
|
431 |
send_btn = gr.Button("Send", variant="primary", scale=1, min_width=100)
|
432 |
+
|
433 |
with gr.Accordion("π Full Response / Output", open=True):
|
434 |
+
fmt_report_tb = gr.Textbox(label="Current Research Output", lines=10, interactive=True, show_copy_button=True, value="*AI responses will appear here...*")
|
435 |
+
dl_report_btn = gr.DownloadButton("Download Report", interactive=False, visible=False)
|
436 |
detect_out_md = gr.Markdown("*Insights used or other intermediate details will show here...*")
|
437 |
|
438 |
with gr.TabItem("π§ Knowledge Base Management", id=1):
|
439 |
+
gr.Markdown("## Manage Stored Knowledge (Backend: " + MEMORY_STORAGE_BACKEND + ")", elem_classes="prose")
|
440 |
+
with gr.Row(equal_height=False):
|
441 |
with gr.Column(scale=1):
|
442 |
gr.Markdown("### Rules (Learned Insights)", elem_classes="prose")
|
443 |
+
rules_disp_ta = gr.TextArea(label="View/Edit Rules (one per line or '---' separated)", lines=15, interactive=True, placeholder="Load or type rules here...")
|
444 |
with gr.Row():
|
445 |
+
view_rules_btn = gr.Button("π Load/Refresh Rules View")
|
446 |
+
save_edited_rules_btn = gr.Button("πΎ Save Edited Rules", variant="primary")
|
447 |
upload_rules_fobj = gr.File(label="Upload Rules File (.txt/.jsonl)", file_types=[".txt", ".jsonl"])
|
448 |
+
rules_stat_tb = gr.Textbox(label="Rules Action Status", interactive=False, lines=2, placeholder="Status of rule operations...")
|
449 |
with gr.Row():
|
450 |
clear_rules_btn = gr.Button("β οΈ Clear All Rules", variant="stop")
|
451 |
+
save_faiss_ram_btn = gr.Button("Save FAISS Indices", visible=(MEMORY_STORAGE_BACKEND == "RAM"))
|
452 |
|
453 |
with gr.Column(scale=1):
|
454 |
gr.Markdown("### Memories (Past Interactions)", elem_classes="prose")
|
455 |
+
mems_disp_json = gr.JSON(label="View Memories (JSON format)", scale=2) # scale makes it taller
|
456 |
with gr.Row():
|
457 |
+
view_mems_btn = gr.Button("π Load/Refresh Memories View")
|
458 |
upload_mems_fobj = gr.File(label="Upload Memories File (.jsonl)", file_types=[".jsonl"])
|
459 |
+
mems_stat_tb = gr.Textbox(label="Memories Action Status", interactive=False, lines=2, placeholder="Status of memory operations...")
|
460 |
clear_mems_btn = gr.Button("β οΈ Clear All Memories", variant="stop")
|
461 |
|
462 |
def dyn_upd_model_dd(sel_prov_dyn:str): models_dyn, def_model_dyn = get_model_display_names_for_provider(sel_prov_dyn), get_default_model_display_name_for_provider(sel_prov_dyn); return gr.Dropdown(choices=models_dyn, value=def_model_dyn, interactive=True)
|
|
|
471 |
potential_rules = edited_rules_text.split("\n\n---\n\n")
|
472 |
if len(potential_rules) == 1 and "\n" in edited_rules_text: potential_rules = [r.strip() for r in edited_rules_text.splitlines() if r.strip()]
|
473 |
if not potential_rules: return "No rules found to process from editor."
|
|
|
|
|
|
|
|
|
474 |
added, skipped, errors = 0,0,0; total = len(potential_rules)
|
475 |
progress(0, desc=f"Saving {total} rules from editor...")
|
476 |
for idx, rule_text in enumerate(potential_rules):
|
477 |
if not rule_text.strip(): continue
|
478 |
+
success, status_msg = add_rule_entry(rule_text.strip())
|
479 |
if success: added +=1
|
480 |
elif status_msg == "duplicate": skipped +=1
|
481 |
else: errors +=1
|
482 |
progress((idx+1)/total)
|
483 |
return f"Editor Save: Added: {added}, Skipped (duplicates): {skipped}, Errors/Invalid: {errors}."
|
484 |
+
save_edited_rules_btn.click(fn=save_edited_rules_action_fn, inputs=[rules_disp_ta], outputs=[rules_stat_tb], show_progress="full").then(fn=ui_view_rules_action_fn, outputs=rules_disp_ta)
|
485 |
|
486 |
upload_rules_fobj.upload(fn=ui_upload_rules_action_fn, inputs=[upload_rules_fobj], outputs=[rules_stat_tb], show_progress="full").then(fn=ui_view_rules_action_fn, outputs=rules_disp_ta)
|
487 |
clear_rules_btn.click(fn=lambda: "All rules cleared." if clear_all_rules_data_backend() else "Error clearing rules.", outputs=rules_stat_tb).then(fn=ui_view_rules_action_fn, outputs=rules_disp_ta)
|
|
|
494 |
upload_mems_fobj.upload(fn=ui_upload_memories_action_fn, inputs=[upload_mems_fobj], outputs=[mems_stat_tb], show_progress="full").then(fn=ui_view_memories_action_fn, outputs=mems_disp_json)
|
495 |
clear_mems_btn.click(fn=lambda: "All memories cleared." if clear_all_memory_data_backend() else "Error clearing memories.", outputs=mems_stat_tb).then(fn=ui_view_memories_action_fn, outputs=mems_disp_json)
|
496 |
|
497 |
+
def app_load_fn():
|
498 |
+
initialize_memory_system()
|
499 |
+
logger.info("App loaded. Memory system initialized.")
|
500 |
+
backend_status = f"AI Systems Initialized (Backend: {MEMORY_STORAGE_BACKEND}). Ready."
|
501 |
+
# Initial load for KB views
|
502 |
+
rules_on_load = ui_view_rules_action_fn()
|
503 |
+
mems_on_load = ui_view_memories_action_fn()
|
504 |
+
return backend_status, rules_on_load, mems_on_load
|
505 |
+
|
506 |
+
# Update outputs of demo.load to populate the KB views on startup
|
507 |
+
demo.load(fn=app_load_fn, inputs=None, outputs=[agent_stat_tb, rules_disp_ta, mems_disp_json])
|
508 |
+
|
509 |
|
510 |
if __name__ == "__main__":
|
511 |
+
logger.info(f"Starting Gradio AI Research Mega Agent (v5 with Dashboard UI & Advanced Memory: {MEMORY_STORAGE_BACKEND})...")
|
512 |
app_port, app_server = int(os.getenv("GRADIO_PORT", 7860)), os.getenv("GRADIO_SERVER_NAME", "127.0.0.1")
|
513 |
app_debug, app_share = os.getenv("GRADIO_DEBUG", "False").lower()=="true", os.getenv("GRADIO_SHARE", "False").lower()=="true"
|
514 |
logger.info(f"Launching Gradio server: http://{app_server}:{app_port}. Debug: {app_debug}, Share: {app_share}")
|