From c617ab80dbd18b48a9f784b0147cc8289697bdb2 Mon Sep 17 00:00:00 2001 From: titi <74377782+qchapp@users.noreply.github.com> Date: Wed, 22 Oct 2025 23:19:38 +0200 Subject: [PATCH 1/8] New tool running software on provided image via gradio client --- CHANGELOG.md | 19 +- src/ai_agent/agent/tools/gradio_space_tool.py | 174 +++++++++++++----- 2 files changed, 142 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e19ef6..87da3ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,24 @@ All notable changes to this project will be documented in this file. +## [0.1.3] - 2025-10-22 + +### Added +- Gradio space runner tool working on the drag-and-dropped image. +- Repo info tool extracting relevant information in markdown for the agent. + +### Fixed +- Gradio UI: bound `clear.click(...)` inside the Blocks context to prevent “Cannot call click outside of a gradio.Blocks context”. +- Chatbot: migrated to `type="messages"` by adding a conversion shim (pairs <-> messages) in the handler to satisfy the new schema. +- UI polish: hide the “Demo link” textbox and “Run demo on preview” button at launch and until a tool is found; reveal them after recommendations appear and on selection, and hide again on Clear. +- Cache is now cleaned correctly. +- Fixed the preview for the png files. + ## [0.1.2] - 2025-10-07 ### Added -- Pydantic AI pipeline working with a few tools -- Better handling of the runnable example link and reranker +- Pydantic AI pipeline working with a few tools. +- Better handling of the runnable example link and reranker. ## [0.1.1] - 2025-10-02 @@ -17,4 +30,4 @@ All notable changes to this project will be documented in this file. ## [0.1.0] - 2025-09-30 ### Added -- Chat functionality \ No newline at end of file +- Chat functionality. \ No newline at end of file diff --git a/src/ai_agent/agent/tools/gradio_space_tool.py b/src/ai_agent/agent/tools/gradio_space_tool.py index 5b1825d..6534dd9 100644 --- a/src/ai_agent/agent/tools/gradio_space_tool.py +++ b/src/ai_agent/agent/tools/gradio_space_tool.py @@ -2,10 +2,14 @@ from typing import List, Optional, Dict, Any from pydantic import BaseModel -import os +import os, re, logging from .utils import get_pipeline from utils.utils import _best_runnable_link +from utils.previews import _build_preview_for_vlm from gradio_client import Client, handle_file +import tempfile +from pathlib import Path +import requests # -------- Gradio run_example tool ------------------------------------------- class RunExampleInput(BaseModel): @@ -21,39 +25,75 @@ class RunExampleOutput(BaseModel): endpoint_url: Optional[str] = None api_name: Optional[str] = None notes: Optional[str] = None + # Back-compat: 'result_image' kept as alias for preview + result_image: Optional[str] = None + result_preview: Optional[str] = None + result_origin: Optional[str] = None # original returned file (downloaded if URL) +log = logging.getLogger("agent.run_example") + +_HF_SPACE_RE = re.compile(r"^https?://huggingface\.co/spaces/([^/]+)/([^/]+)/?$") + +def _normalize_space_identifier(url_or_name: str) -> str: + """Accepts full HF Spaces URL or 'owner/space' or a direct app URL; returns a Client-acceptable src. + Prefer 'owner/space' for HF Spaces page URLs. + """ + s = (url_or_name or "").strip() + m = _HF_SPACE_RE.match(s) + if m: + owner, space = m.group(1), m.group(2) + return f"{owner}/{space}" + return s -def _choose_endpoint(endpoints: List[Dict[str, Any]], have_image: bool) -> Optional[Dict[str, Any]]: - """Pick a sensible endpoint: prefer one that accepts an image if we have one; else the first text-only.""" - def has_image(f: Dict[str, Any]) -> bool: - for i in f.get("inputs", []): - t = str(i.get("type") or i.get("component") or "").lower() - if "image" in t: - return True - return False - - if have_image: - for f in endpoints: - if has_image(f): - return f - # fallback: any endpoint - return endpoints[0] if endpoints else None - - -def _build_payload(fn: Dict[str, Any], image_path: Optional[str], extra_text: Optional[str]) -> List[Any]: - inputs = fn.get("inputs", []) - payload: List[Any] = [] - for spec in inputs: - t = str(spec.get("type") or spec.get("component") or "").lower() - # Gradio client supports passing file paths for image inputs - if "image" in t and image_path: - payload.append(handle_file(image_path) if handle_file else image_path) - elif "textbox" in t or "text" in t or "textarea" in t: - payload.append(extra_text or "") - else: - # default empty for other inputs (checkbox, number, etc.) - payload.append("") - return payload + +def _download_to_temp(url: str) -> Optional[str]: + try: + r = requests.get(url, timeout=20) + if r.status_code != 200 or not r.content: + return None + # try to preserve extension from URL + from urllib.parse import urlparse + parsed = urlparse(url) + ext = os.path.splitext(parsed.path)[1] + if not ext: + # guess based on content-type + ct = r.headers.get("content-type", "").lower() + if "tiff" in ct or "tif" in ct: + ext = ".tif" + elif "png" in ct: + ext = ".png" + elif "jpeg" in ct or "jpg" in ct: + ext = ".jpg" + else: + ext = ".bin" + fd = tempfile.NamedTemporaryFile(delete=False, prefix="demo_result_", suffix=ext) + fd.write(r.content) + fd.flush(); fd.close() + return fd.name + except Exception: + return None + + +def _materialize_result(obj: Any) -> Optional[str]: + """Try to materialize an image result to a local file and return the path. + Accepts a filepath or URL from common Gradio outputs. + """ + # Direct file path + try: + s = str(obj) + except Exception: + return None + if not s: + return None + # If it's an existing local file + p = Path(s) + if p.exists() and p.is_file(): + return str(p) + # If it's a URL, try to download + if s.lower().startswith("http://") or s.lower().startswith("https://"): + return _download_to_temp(s) + # Unknown shape + return None def tool_run_example(inp: RunExampleInput) -> RunExampleOutput: @@ -74,25 +114,63 @@ def tool_run_example(inp: RunExampleInput) -> RunExampleOutput: return RunExampleOutput(tool_name=inp.tool_name, ran=False, notes="No runnable example URL found") try: - client = Client(url) - apis = client.view_api(return_format="dict") or {} - endpoints = apis.get("endpoints") or apis.get("named_endpoints") or [] - if not isinstance(endpoints, list): - # some versions return dict of name->spec - endpoints = list(endpoints.values()) - fn = _choose_endpoint(endpoints, have_image=bool(inp.image_path)) - if not fn: - return RunExampleOutput(tool_name=inp.tool_name, ran=False, notes="No endpoints discovered", endpoint_url=url) - api_name = fn.get("api_name") or fn.get("path") or fn.get("route") - if not api_name: - # common default - api_name = "/predict" - payload = _build_payload(fn, inp.image_path, inp.extra_text) + src = _normalize_space_identifier(url) + hf_token = os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACE_TOKEN") + log.info("Gradio run_example: src=%s (from=%s), tool=%s", src, url, inp.tool_name) + client = Client(src, hf_token=hf_token) if hf_token else Client(src) + api_name = "/segment" # agreed endpoint + # For a simple segmentation endpoint that takes a single image input + if inp.image_path: + payload_file = handle_file(inp.image_path) + payload = [payload_file] + try: + log.info("Gradio run_example payload: file=%s ext=%s", inp.image_path, os.path.splitext(inp.image_path)[1].lower()) + except Exception: + pass + else: + payload = [""] try: - res = client.predict(*payload, api_name=api_name) + # Prefer keyword expected by docs ('file_obj'), then fallback to positional + res = None + try: + res = client.predict(file_obj=payload[0], api_name=api_name) + except Exception as e_kw: + log.debug("Keyword predict failed, falling back to positional: %r", e_kw) + res = client.predict(*payload, api_name=api_name) stdout = str(res) except Exception as e: return RunExampleOutput(tool_name=inp.tool_name, ran=False, notes=f"predict failed: {e}", endpoint_url=url, api_name=api_name) - return RunExampleOutput(tool_name=inp.tool_name, ran=True, stdout=str(stdout)[:6000], endpoint_url=url, api_name=str(api_name)) + + # Materialize original result file (any supported format) + origin_path = None + if isinstance(res, (list, tuple)) and res: + origin_path = _materialize_result(res[0]) or origin_path + elif isinstance(res, dict): + # common keys from outputs + for k in ("file", "filepath", "image", "output", "result", "mask"): + if k in res: + origin_path = _materialize_result(res[k]) or origin_path + if origin_path: + break + else: + origin_path = _materialize_result(res) + + preview_path = None + if origin_path: + try: + preview_path, _ = _build_preview_for_vlm([origin_path]) + except Exception as e: + log.debug("Preview build failed for %s: %r", origin_path, e) + + return RunExampleOutput( + tool_name=inp.tool_name, + ran=True, + stdout=str(stdout)[:6000], + endpoint_url=url, + api_name=str(api_name), + result_image=preview_path, + result_preview=preview_path, + result_origin=origin_path, + ) except Exception as e: return RunExampleOutput(tool_name=inp.tool_name, ran=False, notes=str(e), endpoint_url=url) \ No newline at end of file From 9f5763bccd79de9a10c749f54aad238bf339c81a Mon Sep 17 00:00:00 2001 From: titi <74377782+qchapp@users.noreply.github.com> Date: Wed, 22 Oct 2025 23:20:03 +0200 Subject: [PATCH 2/8] Cleaned ui to hide results before query --- src/ai_agent/ui/app.py | 147 +++++++++++++++++++++++++++++++++++------ 1 file changed, 125 insertions(+), 22 deletions(-) diff --git a/src/ai_agent/ui/app.py b/src/ai_agent/ui/app.py index 389f692..23a7c20 100644 --- a/src/ai_agent/ui/app.py +++ b/src/ai_agent/ui/app.py @@ -55,6 +55,7 @@ from retriever.embedders import SoftwareDoc from api.pipeline import RAGImagingPipeline from agent.agent import run_agent +from agent.tools.gradio_space_tool import tool_run_example, RunExampleInput from utils.file_validator import FileValidator from utils.tags import strip_tags, parse_exclusions, is_refine_intent, strip_refine_keywords @@ -186,6 +187,49 @@ def _choices_table_md(choices: List[dict]) -> str: rows.append(f"| {int(c.get('rank', 0))} | {name} | {acc} | {short} |") return "\n".join(rows) +# --- Chat format helpers (pairs <-> messages) -------------------------------- +def _msgs_to_pairs(msgs): + """Convert Chatbot(messages) -> legacy list[[user, assistant]].""" + if not msgs: + return [] + # If already pairs, pass through + if isinstance(msgs[0], (list, tuple)): + return msgs + pairs = [] + pending_user = None + for m in msgs: + role = m.get("role") if isinstance(m, dict) else None + content = (m.get("content", "") if isinstance(m, dict) else "") + if role == "user": + if pending_user is not None: + pairs.append([pending_user, None]) + pending_user = content + elif role == "assistant": + if pending_user is None: + pairs.append(["", content]) + else: + pairs.append([pending_user, content]) + pending_user = None + if pending_user is not None: + pairs.append([pending_user, None]) + return pairs + +def _pairs_to_msgs(pairs): + """Convert legacy list[[user, assistant]] -> Chatbot(messages).""" + if not pairs: + return [] + # If already messages, pass through + if isinstance(pairs[0], dict): + return pairs + msgs = [] + for item in pairs: + user, assistant = (item + [None, None])[:2] if isinstance(item, list) else (None, None) + if user is not None: + msgs.append({"role": "user", "content": user}) + if assistant is not None: + msgs.append({"role": "assistant", "content": assistant}) + return msgs + # --- validation --------------------------------------------------------------- def _validate_files(paths: List[str]) -> Tuple[bool, str]: if not paths: @@ -203,7 +247,7 @@ def _validate_files(paths: List[str]) -> Tuple[bool, str]: log.debug("FileValidator unavailable or raised: %r", e) return True, "" -# --- NEW: radio selection handler -------------------------------------------- +# --- Radio selection handler -------------------------------------------- def _on_pick_tool(selected_name: str, choices_map: dict): """ Update the right panel when a radio option is picked. @@ -234,7 +278,11 @@ def handle_message(message: str, if isinstance(history_rows, DataFrame): history_rows = history_rows.values.tolist() history_rows = history_rows or [] - chat_history = chat_history or [] + # Accept both old pair format and new messages format from Chatbot + if chat_history and isinstance(chat_history, list) and chat_history and isinstance(chat_history[0], dict): + chat_pairs = _msgs_to_pairs(chat_history) + else: + chat_pairs = chat_history or [] conv_history = conv_history or [] banlist = set(banlist_state or set()) base_task = (last_task_state or "").strip() @@ -246,6 +294,10 @@ def handle_message(message: str, choices_acc_hidden = gr.update(visible=False, open=False) empty_choices_map = {} + # Demo controls hidden by default + demo_link_hidden = gr.update(value="", visible=False) + run_demo_hidden = gr.update(visible=False) + # Allow control-tag-only refine messages to proceed (don't treat as empty) raw_message = (message or "") has_any_text = bool(raw_message.strip()) @@ -253,7 +305,7 @@ def handle_message(message: str, if (not has_any_text) and (not has_files): # nothing to do → DO NOT disable inputs; just return quietly - yield (chat_history, history_rows, "", "", conv_history, + yield (_pairs_to_msgs(chat_pairs), history_rows, "", demo_link_hidden, run_demo_hidden, conv_history, empty_radio, "—", gr.update(visible=False), # preview accordion hidden gr.update(value=None, visible=False), # preview image hidden @@ -308,10 +360,10 @@ def handle_message(message: str, gr.update(interactive=False), # files ) - chat_history = chat_history + [[visible_msg, None]] + chat_pairs = chat_pairs + [[visible_msg, None]] conv_history = conv_history + [f"User: {visible_msg}"] status = gr.update(value="🔄 Validating files…", visible=True) - yield (chat_history, history_rows, "", "", conv_history, + yield (_pairs_to_msgs(chat_pairs), history_rows, "", demo_link_hidden, run_demo_hidden, conv_history, empty_radio, "—", gr.update(visible=False), gr.update(value=None, visible=False), @@ -326,7 +378,7 @@ def handle_message(message: str, paths = _coerce_gradio_files_to_paths(files) ok, why_not = _validate_files(paths) if not ok: - chat_history[-1][1] = f"⚠️ File issues detected.\n\n{why_not}" + chat_pairs[-1][1] = f"⚠️ File issues detected.\n\n{why_not}" status_done = gr.update(value="⚠️ File validation failed.", visible=True) # re-enable inputs enable_inputs = ( @@ -334,7 +386,7 @@ def handle_message(message: str, gr.update(interactive=True), gr.update(interactive=True), ) - yield (chat_history, history_rows, "", "", conv_history, + yield (_pairs_to_msgs(chat_pairs), history_rows, "", "", conv_history, empty_radio, "—", gr.update(visible=False), gr.update(value=None, visible=False), @@ -349,7 +401,7 @@ def handle_message(message: str, # 2) build preview (collapsed & only visible if present) status = gr.update(value="🖼️ Building preview…", visible=True) - yield (chat_history, history_rows, "", "", conv_history, + yield (_pairs_to_msgs(chat_pairs), history_rows, "", demo_link_hidden, run_demo_hidden, conv_history, empty_radio, "—", gr.update(visible=False), gr.update(value=None, visible=False), @@ -371,7 +423,7 @@ def handle_message(message: str, preview_acc_upd = gr.update(visible=bool(preview_path)) preview_img_upd = gr.update(value=preview_path, visible=bool(preview_path)) - yield (chat_history, history_rows, "", "", conv_history, + yield (_pairs_to_msgs(chat_pairs), history_rows, "", demo_link_hidden, run_demo_hidden, conv_history, empty_radio, "—", preview_acc_upd, preview_img_upd, @@ -432,7 +484,7 @@ def _choices_render(choices): if opts: resp += "Options:\n" + "\n".join(f"• {o}" for o in opts) + "\n\n" resp += f"_{ctx}_" - chat_history[-1][1] = resp + chat_pairs[-1][1] = resp status_done = gr.update(value="ℹ️ More info needed.", visible=True) enable_inputs = ( @@ -440,7 +492,7 @@ def _choices_render(choices): gr.update(interactive=True), gr.update(interactive=True), ) - yield (chat_history, history_rows, "", "", conv_history, + yield (_pairs_to_msgs(chat_pairs), history_rows, "", demo_link_hidden, run_demo_hidden, conv_history, empty_radio, "—", preview_acc_upd, preview_img_upd, [], # excluded_names @@ -458,7 +510,7 @@ def _choices_render(choices): demo = top.get("demo_link", "") table_block = f"\n\n**Top candidates** (up to {os.getenv('NUM_CHOICES', '3')}):\n\n" + md_table - chat_history[-1][1] = ( + chat_pairs[-1][1] = ( f"I recommend **{top['name']}** ({float(top.get('accuracy',0.0)):.1f}% match)\n\n" f"_{top.get('why','')}_" + table_block ) @@ -476,12 +528,15 @@ def _choices_render(choices): choices_acc_upd = gr.update(visible=True, open=True) status_done = gr.update(value="✅ Ready.", visible=True) + # Show demo controls once a tool is found + demo_link_upd = gr.update(value=demo, visible=True) + run_demo_upd = gr.update(visible=True) enable_inputs = ( gr.update(interactive=True), gr.update(interactive=True), gr.update(interactive=True), ) - yield (chat_history, history_rows, top["name"], demo, conv_history, + yield (_pairs_to_msgs(chat_pairs), history_rows, top["name"], demo_link_upd, run_demo_upd, conv_history, radio_update, md_cards, preview_acc_upd, preview_img_upd, names, # excluded_names populated for legacy refine @@ -495,7 +550,7 @@ def _choices_render(choices): # 6) no suitable tools (terminal) reason = result.get("reason") reason_line = f"**Reason:** `{reason}`\n\n" if reason else "" - chat_history[-1][1] = ( + chat_pairs[-1][1] = ( "❌ No suitable tools found.\n\n" + reason_line + result.get("explanation", "") @@ -513,10 +568,11 @@ def _choices_render(choices): ) yield ( - chat_history, + _pairs_to_msgs(chat_pairs), history_rows, "", # chosen_tool - "", # demo_link + demo_link_hidden, # demo_link hidden + run_demo_hidden, # run_demo_btn hidden conv_history, radio_update, # choices_radio choices_md_upd, # choices_md @@ -560,9 +616,12 @@ def create_interface(): # Collapsed, hidden-by-default preview with gr.Accordion("Preview", open=False, visible=False) as preview_acc: - preview_img = gr.Image(label="", interactive=False, value=None, visible=True) + preview_img = gr.Image(label="", interactive=False, value=None, visible=True, type="filepath") + with gr.Accordion("Demo result", open=False, visible=False) as demo_result_acc: + demo_result_img = gr.Image(label="", interactive=False, value=None, visible=True) + demo_result_file = gr.File(label="Download result file", visible=False) - chatbot = gr.Chatbot(label="Conversation", type="tuples") + chatbot = gr.Chatbot(label="Conversation", type="messages") with gr.Row(): msg = gr.Textbox( @@ -579,11 +638,14 @@ def create_interface(): # RIGHT with gr.Column(scale=5): chosen_tool = gr.Markdown(label="Selected Tool") + # Hide demo controls until a tool is found demo_link = gr.Textbox( label="Demo link", show_copy_button=True, interactive=False, + visible=False, ) + run_demo_btn = gr.Button("Run demo on preview", variant="secondary", visible=False) with gr.Accordion("Top choices (details)", open=False, visible=False) as choices_acc: choices_radio = gr.Radio( @@ -612,7 +674,8 @@ def create_interface(): chatbot, # streams history_df, # table chosen_tool, - demo_link, + demo_link, + run_demo_btn, conversation_history, choices_radio, # updated via gr.update(choices=..., value=...) choices_md, # detailed markdown cards @@ -635,7 +698,7 @@ def create_interface(): inputs=[msg, chatbot, files, history_df, conversation_history, banlist_state, last_task_state, last_suggestions_state], outputs=[ - chatbot, history_df, chosen_tool, demo_link, conversation_history, + chatbot, history_df, chosen_tool, demo_link, run_demo_btn, conversation_history, choices_radio, choices_md, preview_acc, preview_img, excluded_names, @@ -646,6 +709,41 @@ def create_interface(): ], ).then(_clear_textbox, inputs=None, outputs=[msg]) + def _run_selected_demo(selected_name: str, demo_url: str, uploaded_files): + # Use the original uploaded file(s) instead of preview; prefer .tif/.tiff if present + if not selected_name: + return gr.update(visible=False), gr.update(value=None, visible=False), gr.update(value="⚠️ Select a tool first.", visible=True) + paths = _coerce_gradio_files_to_paths(uploaded_files) + if not paths: + return gr.update(visible=False), gr.update(value=None, visible=False), gr.update(value="⚠️ Please upload an image first.", visible=True) + # prefer tif/tiff if available (per remote app constraints), else first file + pick = None + for p in paths: + ext = os.path.splitext(p)[1].lower() + if ext in (".tif", ".tiff"): + pick = p + break + if not pick: + pick = paths[0] + try: + log.info("Run demo: tool=%s, path=%s, url=%s", selected_name, pick, demo_url) + out = tool_run_example(RunExampleInput(tool_name=selected_name, image_path=pick, endpoint_url=demo_url or None)) + if out.ran and (out.result_preview or out.result_image): + preview = out.result_preview or out.result_image + file_upd = gr.update(value=out.result_origin, visible=bool(out.result_origin)) + return gr.update(visible=True, open=True), gr.update(value=preview, visible=True), file_upd, gr.update(value="✅ Demo ran.", visible=True) + note = out.notes or "" + txt = f"ℹ️ Ran, no image returned. {note}" if out.ran else f"❌ Failed. {note}" + return gr.update(visible=False), gr.update(value=None, visible=False), gr.update(visible=False, value=None), gr.update(value=txt, visible=True) + except Exception as e: + return gr.update(visible=False), gr.update(value=None, visible=False), gr.update(visible=False, value=None), gr.update(value=f"❌ Error: {e}", visible=True) + + run_demo_btn.click( + _run_selected_demo, + inputs=[choices_radio, demo_link, files], + outputs=[demo_result_acc, demo_result_img, demo_result_file, status_md], + ) + choices_radio.change( _on_pick_tool, inputs=[choices_radio, last_choices_state], @@ -657,12 +755,16 @@ def create_interface(): lambda: ( [], # chatbot "", # chosen_tool - "", # demo_link + gr.update(value="", visible=False), # demo_link hidden + gr.update(visible=False), # run_demo_btn hidden [], # conversation_history gr.update(choices=[], value=None), # choices_radio reset "—", # choices_md reset gr.update(visible=False), # preview accordion hidden gr.update(value=None, visible=False),# preview img hidden + gr.update(visible=False), # demo result accordion hidden + gr.update(value=None, visible=False),# demo result img hidden + gr.update(value=None, visible=False),# demo result file hidden [], # excluded_names reset gr.update(value=None, visible=False),# status hidden gr.update(interactive=True), # msg enabled @@ -676,9 +778,10 @@ def create_interface(): ), inputs=None, outputs=[ - chatbot, chosen_tool, demo_link, conversation_history, + chatbot, chosen_tool, demo_link, run_demo_btn, conversation_history, choices_radio, choices_md, preview_acc, preview_img, + demo_result_acc, demo_result_img, demo_result_file, excluded_names, status_md, msg, submit, files, banlist_state, last_task_state, last_suggestions_state, From 0f0c61af4af4df40301238d72e479339086018bd Mon Sep 17 00:00:00 2001 From: Quentin <74377782+qchapp@users.noreply.github.com> Date: Mon, 10 Nov 2025 12:02:37 +0100 Subject: [PATCH 3/8] Removed unused variable Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/ai_agent/agent/tools/gradio_space_tool.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ai_agent/agent/tools/gradio_space_tool.py b/src/ai_agent/agent/tools/gradio_space_tool.py index 6534dd9..57a5717 100644 --- a/src/ai_agent/agent/tools/gradio_space_tool.py +++ b/src/ai_agent/agent/tools/gradio_space_tool.py @@ -131,7 +131,6 @@ def tool_run_example(inp: RunExampleInput) -> RunExampleOutput: payload = [""] try: # Prefer keyword expected by docs ('file_obj'), then fallback to positional - res = None try: res = client.predict(file_obj=payload[0], api_name=api_name) except Exception as e_kw: From 594aa7733d490467ce007534402c147028ffeeb9 Mon Sep 17 00:00:00 2001 From: Quentin <74377782+qchapp@users.noreply.github.com> Date: Mon, 10 Nov 2025 12:04:20 +0100 Subject: [PATCH 4/8] Changed indentation Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/ai_agent/ui/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ai_agent/ui/app.py b/src/ai_agent/ui/app.py index 23a7c20..cd9e650 100644 --- a/src/ai_agent/ui/app.py +++ b/src/ai_agent/ui/app.py @@ -674,8 +674,8 @@ def create_interface(): chatbot, # streams history_df, # table chosen_tool, - demo_link, - run_demo_btn, + demo_link, + run_demo_btn, conversation_history, choices_radio, # updated via gr.update(choices=..., value=...) choices_md, # detailed markdown cards From 7b72e5072cbb38f642ae68e40228e025ef5cf18b Mon Sep 17 00:00:00 2001 From: Quentin <74377782+qchapp@users.noreply.github.com> Date: Mon, 10 Nov 2025 12:04:59 +0100 Subject: [PATCH 5/8] Update src/ai_agent/ui/app.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/ai_agent/ui/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ai_agent/ui/app.py b/src/ai_agent/ui/app.py index cd9e650..0fef12b 100644 --- a/src/ai_agent/ui/app.py +++ b/src/ai_agent/ui/app.py @@ -279,7 +279,7 @@ def handle_message(message: str, history_rows = history_rows.values.tolist() history_rows = history_rows or [] # Accept both old pair format and new messages format from Chatbot - if chat_history and isinstance(chat_history, list) and chat_history and isinstance(chat_history[0], dict): + if chat_history and isinstance(chat_history, list) and isinstance(chat_history[0], dict): chat_pairs = _msgs_to_pairs(chat_history) else: chat_pairs = chat_history or [] From a51426297ec8d0f79242c392223792c45360467e Mon Sep 17 00:00:00 2001 From: titi <74377782+qchapp@users.noreply.github.com> Date: Mon, 10 Nov 2025 21:22:01 +0100 Subject: [PATCH 6/8] corrected minor issues for PR --- src/ai_agent/agent/tools/gradio_space_tool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ai_agent/agent/tools/gradio_space_tool.py b/src/ai_agent/agent/tools/gradio_space_tool.py index 57a5717..b83dcbb 100644 --- a/src/ai_agent/agent/tools/gradio_space_tool.py +++ b/src/ai_agent/agent/tools/gradio_space_tool.py @@ -101,7 +101,7 @@ def tool_run_example(inp: RunExampleInput) -> RunExampleOutput: Behavior: - Determine Space URL: prefer explicit endpoint_url, else catalog runnable link. - - Discover API endpoints via view_api and choose one matching image/no-image needs. + - Used agreed endpoint /segment for now - Build payload by mapping image path to image inputs and extra_text into text fields. """ pipe = get_pipeline() From d0d8eb44e05bed9dd92b1bdb4df3a815c1934441 Mon Sep 17 00:00:00 2001 From: titi <74377782+qchapp@users.noreply.github.com> Date: Mon, 10 Nov 2025 21:23:17 +0100 Subject: [PATCH 7/8] fixed minor issues for PR --- src/ai_agent/ui/app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ai_agent/ui/app.py b/src/ai_agent/ui/app.py index 0fef12b..a0c7e20 100644 --- a/src/ai_agent/ui/app.py +++ b/src/ai_agent/ui/app.py @@ -712,17 +712,17 @@ def create_interface(): def _run_selected_demo(selected_name: str, demo_url: str, uploaded_files): # Use the original uploaded file(s) instead of preview; prefer .tif/.tiff if present if not selected_name: - return gr.update(visible=False), gr.update(value=None, visible=False), gr.update(value="⚠️ Select a tool first.", visible=True) + return gr.update(visible=False), gr.update(visible=False, value=None), gr.update(value=None, visible=False), gr.update(value="⚠️ Select a tool first.", visible=True) paths = _coerce_gradio_files_to_paths(uploaded_files) if not paths: - return gr.update(visible=False), gr.update(value=None, visible=False), gr.update(value="⚠️ Please upload an image first.", visible=True) + return gr.update(visible=False), gr.update(visible=False, value=None), gr.update(value=None, visible=False), gr.update(value="⚠️ Please upload an image first.", visible=True) # prefer tif/tiff if available (per remote app constraints), else first file pick = None for p in paths: ext = os.path.splitext(p)[1].lower() if ext in (".tif", ".tiff"): pick = p - break + break if not pick: pick = paths[0] try: From 62a93996be8f1599aaea06a7207c32a2dd7c2d84 Mon Sep 17 00:00:00 2001 From: Quentin <74377782+qchapp@users.noreply.github.com> Date: Mon, 10 Nov 2025 22:14:46 +0100 Subject: [PATCH 8/8] Update src/ai_agent/agent/tools/gradio_space_tool.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/ai_agent/agent/tools/gradio_space_tool.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ai_agent/agent/tools/gradio_space_tool.py b/src/ai_agent/agent/tools/gradio_space_tool.py index b83dcbb..9961392 100644 --- a/src/ai_agent/agent/tools/gradio_space_tool.py +++ b/src/ai_agent/agent/tools/gradio_space_tool.py @@ -66,10 +66,10 @@ def _download_to_temp(url: str) -> Optional[str]: ext = ".jpg" else: ext = ".bin" - fd = tempfile.NamedTemporaryFile(delete=False, prefix="demo_result_", suffix=ext) - fd.write(r.content) - fd.flush(); fd.close() - return fd.name + with tempfile.NamedTemporaryFile(delete=False, prefix="demo_result_", suffix=ext) as fd: + fd.write(r.content) + fd.flush() + return fd.name except Exception: return None