Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -44,8 +44,10 @@ logger.info(f"App Config: WebSearch={WEB_SEARCH_ENABLED}, ToolDecisionProvider={
|
|
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
|
48 |
-
#
|
|
|
|
|
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.", []
|
@@ -190,17 +192,14 @@ def deferred_learning_and_memory_task(user_input: str, bot_response: str, provid
|
|
190 |
summary = f"User:\"{user_input}\"\nAI:\"{bot_response}\"\nMetrics(takeaway):{metrics.get('takeaway','N/A')},Success:{metrics.get('response_success_score','N/A')}"
|
191 |
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."
|
192 |
insight_sys_prompt = """You are an expert AI knowledge base curator. Your primary function is to meticulously analyze an interaction and update the AI's guiding principles (insights/rules) to improve its future performance and self-understanding.
|
193 |
-
|
194 |
**CRITICAL OUTPUT REQUIREMENT: You MUST output a single, valid JSON list of operation objects.**
|
195 |
This list can and SHOULD contain MULTIPLE distinct operations if various learnings occurred.
|
196 |
If no operations are warranted, output an empty JSON list: `[]`.
|
197 |
ABSOLUTELY NO other text, explanations, or markdown should precede or follow this JSON list.
|
198 |
-
|
199 |
Each operation object in the JSON list must have these keys and string values:
|
200 |
1. `"action"`: A string, either `"add"` (for entirely new rules) or `"update"` (to replace an existing rule with a better one).
|
201 |
2. `"insight"`: A string, the full, refined insight text including its `[TYPE|SCORE]` prefix (e.g., `"[CORE_RULE|1.0] My name is Lumina, an AI assistant."`).
|
202 |
3. `"old_insight_to_replace"`: (ONLY for `"update"` action) A string, the *exact, full text* of an existing insight that the new `"insight"` should replace. If action is `"add"`, this key should be omitted or its value should be `null` or an empty string.
|
203 |
-
|
204 |
**CRITICAL JSON STRING FORMATTING RULES (for values of "insight" and "old_insight_to_replace"):**
|
205 |
- All string values MUST be enclosed in double quotes (`"`).
|
206 |
- Any literal double quote (`"`) character *within* the string content MUST be escaped as `\\"`.
|
@@ -208,7 +207,6 @@ Each operation object in the JSON list must have these keys and string values:
|
|
208 |
- Any newline characters *within* the string content MUST be escaped as `\\n`. Avoid literal newlines in JSON string values; use `\\n` instead.
|
209 |
*Example of correctly escaped insight string in JSON:*
|
210 |
`"insight": "[RESPONSE_PRINCIPLE|0.8] User prefers concise answers, stating: \\"Just the facts!\\". Avoid verbose explanations unless asked.\\nFollow up with a question if appropriate."`
|
211 |
-
|
212 |
**Your Reflection Process (Consider each step and generate operations accordingly):**
|
213 |
**STEP 1: Core Identity & Purpose Review (Result: Primarily 'update' operations)**
|
214 |
- Examine all `CORE_RULE`s related to my identity (name, fundamental purpose, core unchanging capabilities, origin) from the "Potentially Relevant Existing Rules".
|
@@ -224,12 +222,10 @@ Each operation object in the JSON list must have these keys and string values:
|
|
224 |
- Did I learn to modify or improve an existing behavior, response style, or operational guideline (that is NOT part of core identity)?
|
225 |
- For example, if an existing `RESPONSE_PRINCIPLE` was "Be formal," and the interaction showed the user prefers informality, update that principle.
|
226 |
- Propose 'update' operations for the relevant `RESPONSE_PRINCIPLE` or `BEHAVIORAL_ADJUSTMENT`. Only update if the change is significant.
|
227 |
-
|
228 |
**General Guidelines for Insight Content and Actions:**
|
229 |
- Ensure the "insight" field (for both add/update) always contains the properly formatted insight string: `[TYPE|SCORE] Text`. `TYPE` can be `CORE_RULE`, `RESPONSE_PRINCIPLE`, `BEHAVIORAL_ADJUSTMENT`. Scores should reflect confidence/importance (0.0-1.0).
|
230 |
- Be precise with "old_insight_to_replace" β it must *exactly* match an existing rule string from the "Potentially Relevant Existing Rules" context.
|
231 |
- Aim for a comprehensive set of operations that reflects ALL key learnings from the interaction.
|
232 |
-
|
233 |
**Example of a comprehensive JSON output with MULTIPLE operations (This is how your output should look):**
|
234 |
[
|
235 |
{"action": "update", "old_insight_to_replace": "[CORE_RULE|1.0] My designated name is 'LearnerAI'.", "insight": "[CORE_RULE|1.0] I am Lumina, an AI assistant designed to chat, provide information, and remember context like the secret word 'rocksyrup'."},
|
@@ -261,7 +257,7 @@ Combine all findings into a single JSON list of operations. If there are multipl
|
|
261 |
json_match_ops = re.search(r"```json\s*(\[.*?\])\s*```", raw_ops_json, re.DOTALL|re.I) or re.search(r"(\[.*?\])", raw_ops_json, re.DOTALL)
|
262 |
if json_match_ops:
|
263 |
try: ops = json.loads(json_match_ops.group(1))
|
264 |
-
except Exception as e: logger.error(f"DEFERRED [{task_id}]: JSON ops parse error: {e}. Raw: {json_match_ops.group(1)[:500]}")
|
265 |
if isinstance(ops, list) and ops:
|
266 |
logger.info(f"DEFERRED [{task_id}]: LLM provided {len(ops)} insight ops.")
|
267 |
for op in ops:
|
@@ -399,238 +395,118 @@ def ui_upload_memories_action_fn(uploaded_file_obj, progress=gr.Progress()):
|
|
399 |
logger.info(msg); return msg
|
400 |
|
401 |
# --- UI Definition ---
|
402 |
-
custom_theme = gr.themes.
|
403 |
primary_hue="teal",
|
404 |
secondary_hue="purple",
|
405 |
neutral_hue="zinc",
|
406 |
-
text_size="
|
407 |
-
spacing_size="md",
|
408 |
radius_size="sm",
|
409 |
-
font=[gr.themes.GoogleFont("Inter"), "System UI", "sans-serif"]
|
410 |
)
|
411 |
|
|
|
412 |
custom_css = """
|
413 |
-
body, html {
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
}
|
421 |
-
.
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
}
|
431 |
-
.gr-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
}
|
438 |
-
.
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
border: 1px solid rgba(189, 195, 199, 0.1) !important;
|
443 |
-
}
|
444 |
-
.gr-textbox, .gr-dropdown, .gr-button, .gr-code, .gr-chat-message, .gr-json, .gr-file input[type="file"], .gr-file .OgniCn {
|
445 |
-
border: 1px solid rgba(189, 195, 199, 0.3) !important;
|
446 |
-
background-color: rgba(52, 73, 94, 0.9) !important;
|
447 |
-
color: #ecf0f1 !important;
|
448 |
-
border-radius: 6px !important;
|
449 |
-
}
|
450 |
-
.gr-file {
|
451 |
-
background-color: transparent !important;
|
452 |
-
border: none !important;
|
453 |
-
padding: 0 !important;
|
454 |
-
}
|
455 |
-
.gr-file > .label-text {
|
456 |
-
color: #bdc3c7 !important;
|
457 |
-
}
|
458 |
-
.gr-textarea textarea, .gr-textbox input {
|
459 |
-
color: #ecf0f1 !important;
|
460 |
-
}
|
461 |
-
.gr-button.gr-button-primary {
|
462 |
-
background-color: #1abc9c !important;
|
463 |
-
color: white !important;
|
464 |
-
border-color: #16a085 !important;
|
465 |
-
}
|
466 |
-
.gr-button.gr-button-secondary {
|
467 |
-
background-color: #9b59b6 !important;
|
468 |
-
color: white !important;
|
469 |
-
border-color: #8e44ad !important;
|
470 |
-
}
|
471 |
-
.gr-button.gr-button-stop {
|
472 |
-
background-color: #e74c3c !important;
|
473 |
-
color: white !important;
|
474 |
-
border-color: #c0392b !important;
|
475 |
-
}
|
476 |
-
.gr-markdown {
|
477 |
-
padding: 0px;
|
478 |
-
}
|
479 |
-
.gr-markdown h1, .gr-markdown h2, .gr-markdown h3 {
|
480 |
-
color: #1abc9c !important;
|
481 |
-
border-bottom: 1px solid rgba(189, 195, 199, 0.2) !important;
|
482 |
-
padding-bottom: 0.3em;
|
483 |
-
margin-top: 0.8em;
|
484 |
-
margin-bottom: 0.6em;
|
485 |
-
}
|
486 |
-
.gr-markdown p, .gr-markdown li {
|
487 |
-
color: #ecf0f1 !important;
|
488 |
-
line-height: 1.6;
|
489 |
-
}
|
490 |
-
.gr-markdown pre code {
|
491 |
-
background-color: rgba(30, 40, 50, 0.95) !important;
|
492 |
-
border: 1px solid rgba(189, 195, 199, 0.3) !important;
|
493 |
-
color: #ecf0f1;
|
494 |
-
border-radius: 4px;
|
495 |
-
padding: 0.8em;
|
496 |
-
}
|
497 |
-
.gr-chatbot {
|
498 |
-
background-color: rgba(44, 62, 80, 0.7) !important;
|
499 |
-
border: 1px solid rgba(189, 195, 199, 0.2) !important;
|
500 |
-
}
|
501 |
-
.gr-chatbot .message {
|
502 |
-
background-color: rgba(52, 73, 94, 0.9) !important;
|
503 |
-
color: #ecf0f1 !important;
|
504 |
-
border: 1px solid rgba(189, 195, 199, 0.2) !important;
|
505 |
-
box-shadow: 0 1px 2px rgba(0,0,0,0.1) !important;
|
506 |
-
}
|
507 |
-
.gr-chatbot .message.user {
|
508 |
-
background-color: rgba(46, 204, 113, 0.8) !important;
|
509 |
-
color: #2c3e50 !important;
|
510 |
-
}
|
511 |
-
.gr-input-label > .label-text, .gr-dropdown-label > .label-text, .gr-checkbox-label > .label-text {
|
512 |
-
color: #bdc3c7 !important;
|
513 |
-
font-size: 0.9rem !important;
|
514 |
-
margin-bottom: 0.2rem !important;
|
515 |
-
}
|
516 |
-
.gr-info {
|
517 |
-
color: #bdc3c7 !important;
|
518 |
-
font-size: 0.85rem !important;
|
519 |
-
}
|
520 |
-
.status-bar {
|
521 |
-
background-color: rgba(44, 62, 80, 0.8) !important;
|
522 |
-
padding: 0.5rem 1rem !important;
|
523 |
-
border-radius: 6px;
|
524 |
-
margin: 1rem 0 !important;
|
525 |
-
}
|
526 |
-
.tabnav button {
|
527 |
-
background-color: rgba(52, 73, 94, 0.8) !important;
|
528 |
-
color: #ecf0f1 !important;
|
529 |
-
border-bottom: 2px solid transparent !important;
|
530 |
-
border-top-left-radius: 6px !important;
|
531 |
-
border-top-right-radius: 6px !important;
|
532 |
-
margin-right: 2px !important;
|
533 |
-
padding: 0.5em 1em !important;
|
534 |
-
}
|
535 |
-
.tabnav button.selected {
|
536 |
-
background-color: rgba(44, 62, 80, 0.95) !important;
|
537 |
-
color: #1abc9c !important;
|
538 |
-
border-bottom: 2px solid #1abc9c !important;
|
539 |
-
}
|
540 |
-
.main-title {
|
541 |
-
text-align: center;
|
542 |
-
margin: 1.5rem 0 0.5rem 0 !important;
|
543 |
-
}
|
544 |
-
.main-subtitle {
|
545 |
-
text-align: center;
|
546 |
-
color: #bdc3c7;
|
547 |
-
font-size: 0.95rem;
|
548 |
-
margin: 0 0 1.5rem 0 !important;
|
549 |
-
}
|
550 |
-
@media (max-width: 768px) {
|
551 |
-
.gradio-container {
|
552 |
-
padding: 1.5em 0.5em !important;
|
553 |
-
}
|
554 |
-
.main-title {
|
555 |
-
font-size: 1.5rem !important;
|
556 |
-
margin-top: 1rem !important;
|
557 |
-
}
|
558 |
-
.main-subtitle {
|
559 |
-
font-size: 0.85rem !important;
|
560 |
-
}
|
561 |
-
.status-bar {
|
562 |
-
margin: 0.5rem 0 !important;
|
563 |
-
}
|
564 |
-
}
|
565 |
"""
|
566 |
|
567 |
-
with gr.Blocks(theme=custom_theme, css=custom_css, title="AI Research Agent") as demo:
|
568 |
-
gr.HTML("<style>" + custom_css + "</style>")
|
569 |
|
570 |
-
gr.
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
with gr.
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
|
|
585 |
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
|
|
|
|
|
|
597 |
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
mems_stat_tb = gr.Textbox(label="Memories Operation Status", interactive=False, lines=2, placeholder="Status...")
|
633 |
-
clear_mems_btn = gr.Button("β οΈ Clear All Memories", variant="stop")
|
634 |
|
635 |
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)
|
636 |
prov_sel_dd.change(fn=dyn_upd_model_dd, inputs=prov_sel_dd, outputs=model_sel_dd)
|
@@ -659,9 +535,9 @@ with gr.Blocks(theme=custom_theme, css=custom_css, title="AI Research Agent") as
|
|
659 |
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)
|
660 |
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)
|
661 |
|
662 |
-
if MEMORY_STORAGE_BACKEND == "RAM" and '
|
663 |
-
def
|
664 |
-
|
665 |
|
666 |
view_mems_btn.click(fn=ui_view_memories_action_fn, outputs=mems_disp_json)
|
667 |
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)
|
@@ -678,7 +554,7 @@ with gr.Blocks(theme=custom_theme, css=custom_css, title="AI Research Agent") as
|
|
678 |
demo.load(fn=app_load_fn, inputs=None, outputs=[agent_stat_tb, rules_disp_ta, mems_disp_json])
|
679 |
|
680 |
if __name__ == "__main__":
|
681 |
-
logger.info(f"Starting Gradio AI Research Mega Agent (v5.
|
682 |
app_port, app_server = int(os.getenv("GRADIO_PORT", 7860)), os.getenv("GRADIO_SERVER_NAME", "127.0.0.1")
|
683 |
app_debug, app_share = os.getenv("GRADIO_DEBUG", "False").lower()=="true", os.getenv("GRADIO_SHARE", "False").lower()=="true"
|
684 |
logger.info(f"Launching Gradio server: http://{app_server}:{app_port}. Debug: {app_debug}, Share: {app_share}")
|
|
|
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, handle_gradio_chat_submit,
|
48 |
+
# ui_view_rules_action_fn, ui_upload_rules_action_fn, ui_view_memories_action_fn, ui_upload_memories_action_fn)
|
49 |
+
# remain the same as in the previous "full working file".
|
50 |
+
# For brevity here, I will not repeat them fully. Ensure they are present in your actual app.py.
|
51 |
def format_insights_for_prompt(retrieved_insights_list: list[str]) -> tuple[str, list[dict]]:
|
52 |
if not retrieved_insights_list:
|
53 |
return "No specific guiding principles or learned insights retrieved.", []
|
|
|
192 |
summary = f"User:\"{user_input}\"\nAI:\"{bot_response}\"\nMetrics(takeaway):{metrics.get('takeaway','N/A')},Success:{metrics.get('response_success_score','N/A')}"
|
193 |
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."
|
194 |
insight_sys_prompt = """You are an expert AI knowledge base curator. Your primary function is to meticulously analyze an interaction and update the AI's guiding principles (insights/rules) to improve its future performance and self-understanding.
|
|
|
195 |
**CRITICAL OUTPUT REQUIREMENT: You MUST output a single, valid JSON list of operation objects.**
|
196 |
This list can and SHOULD contain MULTIPLE distinct operations if various learnings occurred.
|
197 |
If no operations are warranted, output an empty JSON list: `[]`.
|
198 |
ABSOLUTELY NO other text, explanations, or markdown should precede or follow this JSON list.
|
|
|
199 |
Each operation object in the JSON list must have these keys and string values:
|
200 |
1. `"action"`: A string, either `"add"` (for entirely new rules) or `"update"` (to replace an existing rule with a better one).
|
201 |
2. `"insight"`: A string, the full, refined insight text including its `[TYPE|SCORE]` prefix (e.g., `"[CORE_RULE|1.0] My name is Lumina, an AI assistant."`).
|
202 |
3. `"old_insight_to_replace"`: (ONLY for `"update"` action) A string, the *exact, full text* of an existing insight that the new `"insight"` should replace. If action is `"add"`, this key should be omitted or its value should be `null` or an empty string.
|
|
|
203 |
**CRITICAL JSON STRING FORMATTING RULES (for values of "insight" and "old_insight_to_replace"):**
|
204 |
- All string values MUST be enclosed in double quotes (`"`).
|
205 |
- Any literal double quote (`"`) character *within* the string content MUST be escaped as `\\"`.
|
|
|
207 |
- Any newline characters *within* the string content MUST be escaped as `\\n`. Avoid literal newlines in JSON string values; use `\\n` instead.
|
208 |
*Example of correctly escaped insight string in JSON:*
|
209 |
`"insight": "[RESPONSE_PRINCIPLE|0.8] User prefers concise answers, stating: \\"Just the facts!\\". Avoid verbose explanations unless asked.\\nFollow up with a question if appropriate."`
|
|
|
210 |
**Your Reflection Process (Consider each step and generate operations accordingly):**
|
211 |
**STEP 1: Core Identity & Purpose Review (Result: Primarily 'update' operations)**
|
212 |
- Examine all `CORE_RULE`s related to my identity (name, fundamental purpose, core unchanging capabilities, origin) from the "Potentially Relevant Existing Rules".
|
|
|
222 |
- Did I learn to modify or improve an existing behavior, response style, or operational guideline (that is NOT part of core identity)?
|
223 |
- For example, if an existing `RESPONSE_PRINCIPLE` was "Be formal," and the interaction showed the user prefers informality, update that principle.
|
224 |
- Propose 'update' operations for the relevant `RESPONSE_PRINCIPLE` or `BEHAVIORAL_ADJUSTMENT`. Only update if the change is significant.
|
|
|
225 |
**General Guidelines for Insight Content and Actions:**
|
226 |
- Ensure the "insight" field (for both add/update) always contains the properly formatted insight string: `[TYPE|SCORE] Text`. `TYPE` can be `CORE_RULE`, `RESPONSE_PRINCIPLE`, `BEHAVIORAL_ADJUSTMENT`. Scores should reflect confidence/importance (0.0-1.0).
|
227 |
- Be precise with "old_insight_to_replace" β it must *exactly* match an existing rule string from the "Potentially Relevant Existing Rules" context.
|
228 |
- Aim for a comprehensive set of operations that reflects ALL key learnings from the interaction.
|
|
|
229 |
**Example of a comprehensive JSON output with MULTIPLE operations (This is how your output should look):**
|
230 |
[
|
231 |
{"action": "update", "old_insight_to_replace": "[CORE_RULE|1.0] My designated name is 'LearnerAI'.", "insight": "[CORE_RULE|1.0] I am Lumina, an AI assistant designed to chat, provide information, and remember context like the secret word 'rocksyrup'."},
|
|
|
257 |
json_match_ops = re.search(r"```json\s*(\[.*?\])\s*```", raw_ops_json, re.DOTALL|re.I) or re.search(r"(\[.*?\])", raw_ops_json, re.DOTALL)
|
258 |
if json_match_ops:
|
259 |
try: ops = json.loads(json_match_ops.group(1))
|
260 |
+
except Exception as e: logger.error(f"DEFERRED [{task_id}]: JSON ops parse error: {e}. Raw: {json_match_ops.group(1)[:500]}")
|
261 |
if isinstance(ops, list) and ops:
|
262 |
logger.info(f"DEFERRED [{task_id}]: LLM provided {len(ops)} insight ops.")
|
263 |
for op in ops:
|
|
|
395 |
logger.info(msg); return msg
|
396 |
|
397 |
# --- UI Definition ---
|
398 |
+
custom_theme = gr.themes.Base(
|
399 |
primary_hue="teal",
|
400 |
secondary_hue="purple",
|
401 |
neutral_hue="zinc",
|
402 |
+
text_size="sm",
|
403 |
+
spacing_size="md",
|
404 |
radius_size="sm",
|
405 |
+
font=[gr.themes.GoogleFont("Inter"), "System UI", "sans-serif"]
|
406 |
)
|
407 |
|
408 |
+
# Adjusted CSS for better layout control and scrolling
|
409 |
custom_css = """
|
410 |
+
body, html { margin: 0; padding: 0; height: 100%; overflow-x: hidden; /* Prevent horizontal scroll on body */ }
|
411 |
+
body { background: linear-gradient(to bottom right, #2c3e50, #34495e); color: #ecf0f1; font-family: 'Inter', 'System UI', sans-serif;}
|
412 |
+
.gradio-container { background: transparent !important; max-width: 100% !important; padding: 0 !important; /* Remove container padding */ box-sizing: border-box; min-height: 100vh; display: flex; flex-direction: column; overflow: hidden !important; /* Gradio container should not scroll itself */ }
|
413 |
+
.main-app-content-wrapper { padding: 1rem; overflow-y: auto; flex-grow: 1; /* This makes the content area scrollable */ }
|
414 |
+
.gr-block.gr-group, .gr-tabs, .gr-accordion { background-color: rgba(44, 62, 80, 0.85) !important; border: 1px solid rgba(189, 195, 199, 0.2) !important; border-radius: 8px !important; box-shadow: 0 3px 5px rgba(0,0,0,0.15); margin-bottom: 1em; padding: 1em; }
|
415 |
+
.gr-tabitem { background-color: rgba(52, 73, 94, 0.8) !important; border-radius: 6px !important; padding: 1em !important; border: 1px solid rgba(189, 195, 199, 0.1) !important;}
|
416 |
+
.gr-textbox, .gr-dropdown, .gr-button, .gr-code, .gr-chat-message, .gr-json, .gr-file input[type="file"], .gr-file button { border: 1px solid rgba(189, 195, 199, 0.3) !important; background-color: rgba(52, 73, 94, 0.9) !important; color: #ecf0f1 !important; border-radius: 6px !important;}
|
417 |
+
.gr-file { background-color: transparent !important; border: none !important; padding:0 !important;}
|
418 |
+
.gr-file > .label-text { color: #bdc3c7 !important;}
|
419 |
+
.gr-textarea textarea, .gr-textbox input { color: #ecf0f1 !important; background-color: transparent !important; }
|
420 |
+
.gr-button.gr-button-primary { background-color: #1abc9c !important; color: white !important; border-color: #16a085 !important; }
|
421 |
+
.gr-button.gr-button-secondary { background-color: #9b59b6 !important; color: white !important; border-color: #8e44ad !important; }
|
422 |
+
.gr-button.gr-button-stop { background-color: #e74c3c !important; color: white !important; border-color: #c0392b !important; }
|
423 |
+
.gr-markdown { padding: 5px; }
|
424 |
+
.gr-markdown h1, .gr-markdown h2, .gr-markdown h3 { color: #1abc9c !important; border-bottom: 1px solid rgba(189, 195, 199, 0.2) !important; padding-bottom: 0.3em; margin-top:0.5em; margin-bottom: 0.5em; }
|
425 |
+
.gr-markdown p, .gr-markdown li { color: #ecf0f1 !important; line-height:1.6;}
|
426 |
+
.gr-markdown pre code { background-color: rgba(30, 40, 50, 0.95) !important; border: 1px solid rgba(189, 195, 199, 0.3) !important; color: #ecf0f1; border-radius: 4px; padding: 0.8em; }
|
427 |
+
.gr-chatbot { background-color: rgba(44, 62, 80, 0.7) !important; border: 1px solid rgba(189, 195, 199, 0.2) !important; }
|
428 |
+
.gr-chatbot .message { background-color: rgba(52, 73, 94, 0.9) !important; color: #ecf0f1 !important; border: 1px solid rgba(189, 195, 199, 0.2) !important; box-shadow: 0 1px 2px rgba(0,0,0,0.1) !important; }
|
429 |
+
.gr-chatbot .message.user { background-color: rgba(46, 204, 113, 0.8) !important; color: #2c3e50 !important; }
|
430 |
+
.gr-input-label > .label-text, .gr-dropdown-label > .label-text, .gr-checkbox-label > .label-text { color: #bdc3c7 !important; font-size: 0.9rem !important; margin-bottom: 0.2rem !important; }
|
431 |
+
.gr-info { color: #bdc3c7 !important; font-size: 0.85rem !important; }
|
432 |
+
.status-bar { background-color: rgba(44, 62, 80, 0.8) !important; padding: 0.75rem 1rem !important; border-radius: 8px; margin-bottom: 1rem; }
|
433 |
+
.tabnav button { background-color: rgba(52, 73, 94, 0.8) !important; color: #ecf0f1 !important; border-bottom: 3px solid transparent !important; border-top-left-radius: 6px !important; border-top-right-radius: 6px !important; margin-right: 3px !important; padding: 0.6em 1.2em !important; font-weight: 500;}
|
434 |
+
.tabnav button.selected { background-color: rgba(44, 62, 80, 0.95) !important; color: #1abc9c !important; border-bottom: 3px solid #1abc9c !important;}
|
435 |
+
.main-title { text-align: center; margin: 0 0 0.25rem 0 !important; padding-top: 0.5rem !important; color: #ecf0f1 !important; font-size: 2rem !important;} /* Added padding-top */
|
436 |
+
.main-subtitle { text-align: center; color: #bdc3c7; font-size: 1rem; margin: 0 0 1rem 0 !important; }
|
437 |
+
.sidebar-column { max-height: calc(100vh - 10rem); overflow-y: auto; /* Make sidebar scrollable if content exceeds height */ }
|
438 |
+
.main-content-column { max-height: calc(100vh - 10rem); overflow-y: auto; /* Make main content scrollable */ }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
439 |
"""
|
440 |
|
441 |
+
with gr.Blocks(theme=custom_theme, css=custom_css, title="AI Research Agent v5.6") as demo:
|
442 |
+
gr.HTML("<style>" + custom_css + "</style>")
|
443 |
|
444 |
+
with gr.Column(elem_classes="main-app-content-wrapper"): # This outer column will handle overall page scroll
|
445 |
+
gr.Markdown("# π€ AI Research Agent", elem_classes="main-title")
|
446 |
+
gr.Markdown("Configure AI, chat for research, and manage its knowledge base.", elem_classes="main-subtitle")
|
447 |
+
|
448 |
+
avail_provs = get_available_providers()
|
449 |
+
def_prov = avail_provs[0] if avail_provs else None
|
450 |
+
def_models = get_model_display_names_for_provider(def_prov) if def_prov else []
|
451 |
+
def_model_disp = get_default_model_display_name_for_provider(def_prov) if def_prov else None
|
452 |
+
|
453 |
+
with gr.Row(elem_classes="status-bar"):
|
454 |
+
with gr.Column(scale=3): agent_stat_tb = gr.Textbox(label="Agent Status", interactive=False, lines=1, value="Initializing systems...")
|
455 |
+
with gr.Column(scale=1): memory_backend_info_tb = gr.Textbox(label="Memory Backend", value=f"{MEMORY_STORAGE_BACKEND}", interactive=False)
|
456 |
+
if MEMORY_STORAGE_BACKEND == "SQLITE":
|
457 |
+
with gr.Column(scale=2): gr.Textbox(label="SQLite Path", value=f"{MEMORY_SQLITE_PATH}", interactive=False)
|
458 |
+
elif MEMORY_STORAGE_BACKEND == "HF_DATASET":
|
459 |
+
with gr.Column(scale=2): gr.Textbox(label="HF Repos", value=f"M: {MEMORY_HF_MEM_REPO}, R: {MEMORY_HF_RULES_REPO}", interactive=False)
|
460 |
|
461 |
+
with gr.Row(equal_height=False):
|
462 |
+
with gr.Column(scale=1, min_width=360, elem_classes="sidebar-column"):
|
463 |
+
gr.Markdown("## βοΈ Configuration")
|
464 |
+
with gr.Group():
|
465 |
+
gr.Markdown("### AI Model Settings")
|
466 |
+
api_key_tb = gr.Textbox(label="AI Provider API Key (Optional Override)", type="password", placeholder="Paste key or use .env")
|
467 |
+
prov_sel_dd = gr.Dropdown(label="AI Provider", choices=avail_provs, value=def_prov, interactive=True)
|
468 |
+
model_sel_dd = gr.Dropdown(label="AI Model", choices=def_models, value=def_model_disp, interactive=True)
|
469 |
+
with gr.Group():
|
470 |
+
gr.Markdown("### System Prompt")
|
471 |
+
sys_prompt_tb = gr.Textbox(label="System Prompt Base", lines=12, value=DEFAULT_SYSTEM_PROMPT, interactive=True)
|
472 |
+
gr.Markdown(f"**Memory Backend:** {MEMORY_STORAGE_BACKEND}")
|
473 |
+
if MEMORY_STORAGE_BACKEND == "RAM":
|
474 |
+
save_faiss_sidebar_btn = gr.Button("Save FAISS Indices", variant="secondary")
|
475 |
|
476 |
+
with gr.Column(scale=2, elem_classes="main-content-column"):
|
477 |
+
with gr.Tabs():
|
478 |
+
with gr.TabItem("π¬ AI Chat & Research Output"):
|
479 |
+
gr.Markdown("## π¬ AI Chat Interface")
|
480 |
+
main_chat_disp = gr.Chatbot(label="AI Research Chat", height=500, 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)
|
481 |
+
with gr.Row():
|
482 |
+
user_msg_tb = gr.Textbox(show_label=False, placeholder="Ask your research question or give an instruction...", scale=7, lines=1, max_lines=5, autofocus=True)
|
483 |
+
send_btn = gr.Button("Send", variant="primary", scale=1, min_width=100)
|
484 |
+
with gr.Accordion("π Full Response / Output Details", open=True):
|
485 |
+
fmt_report_tb = gr.Textbox(label="Full AI Response", lines=10, interactive=True, show_copy_button=True, value="*AI responses will appear here...*")
|
486 |
+
dl_report_btn = gr.DownloadButton("Download Report", interactive=False, visible=False)
|
487 |
+
detect_out_md = gr.Markdown("*Insights used or other intermediate details will show here...*")
|
488 |
|
489 |
+
with gr.TabItem("π§ Knowledge Base Management"):
|
490 |
+
gr.Markdown("## Manage Stored Knowledge")
|
491 |
+
with gr.Row(equal_height=False):
|
492 |
+
with gr.Column(scale=1):
|
493 |
+
with gr.Group():
|
494 |
+
gr.Markdown("### π Rules (Learned Insights)")
|
495 |
+
rules_disp_ta = gr.TextArea(label="View/Edit Rules", lines=15, interactive=True, placeholder="Load or type rules here (one per line or '---' separated for multiple).")
|
496 |
+
with gr.Row():
|
497 |
+
view_rules_btn = gr.Button("π Load/Refresh Rules"); save_edited_rules_btn = gr.Button("πΎ Save Edited Rules", variant="primary")
|
498 |
+
upload_rules_fobj = gr.File(label="Upload Rules File (.txt/.jsonl)", file_types=[".txt", ".jsonl"])
|
499 |
+
rules_stat_tb = gr.Textbox(label="Rules Operation Status", interactive=False, lines=2, placeholder="Status...")
|
500 |
+
with gr.Row(): clear_rules_btn = gr.Button("β οΈ Clear All Rules", variant="stop")
|
501 |
+
|
502 |
+
with gr.Column(scale=1): # This was nested incorrectly before
|
503 |
+
with gr.Group():
|
504 |
+
gr.Markdown("### π Memories (Past Interactions)")
|
505 |
+
mems_disp_json = gr.JSON(label="View Memories (JSON format)")
|
506 |
+
with gr.Row(): view_mems_btn = gr.Button("π Load/Refresh Memories")
|
507 |
+
upload_mems_fobj = gr.File(label="Upload Memories File (.jsonl)", file_types=[".jsonl"])
|
508 |
+
mems_stat_tb = gr.Textbox(label="Memories Operation Status", interactive=False, lines=2, placeholder="Status...")
|
509 |
+
clear_mems_btn = gr.Button("β οΈ Clear All Memories", variant="stop")
|
|
|
|
|
510 |
|
511 |
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)
|
512 |
prov_sel_dd.change(fn=dyn_upd_model_dd, inputs=prov_sel_dd, outputs=model_sel_dd)
|
|
|
535 |
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)
|
536 |
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)
|
537 |
|
538 |
+
if MEMORY_STORAGE_BACKEND == "RAM" and 'save_faiss_sidebar_btn' in locals() and save_faiss_sidebar_btn is not None: # Check button in sidebar
|
539 |
+
def save_faiss_action_with_feedback_sidebar_fn(): save_faiss_indices_to_disk(); gr.Info("Attempted to save FAISS indices to disk (Sidebar).")
|
540 |
+
save_faiss_sidebar_btn.click(fn=save_faiss_action_with_feedback_sidebar_fn, inputs=None, outputs=None)
|
541 |
|
542 |
view_mems_btn.click(fn=ui_view_memories_action_fn, outputs=mems_disp_json)
|
543 |
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)
|
|
|
554 |
demo.load(fn=app_load_fn, inputs=None, outputs=[agent_stat_tb, rules_disp_ta, mems_disp_json])
|
555 |
|
556 |
if __name__ == "__main__":
|
557 |
+
logger.info(f"Starting Gradio AI Research Mega Agent (v5.6 - UI Layout Fix, Memory: {MEMORY_STORAGE_BACKEND})...")
|
558 |
app_port, app_server = int(os.getenv("GRADIO_PORT", 7860)), os.getenv("GRADIO_SERVER_NAME", "127.0.0.1")
|
559 |
app_debug, app_share = os.getenv("GRADIO_DEBUG", "False").lower()=="true", os.getenv("GRADIO_SHARE", "False").lower()=="true"
|
560 |
logger.info(f"Launching Gradio server: http://{app_server}:{app_port}. Debug: {app_debug}, Share: {app_share}")
|