diff --git a/ai-hub/app/core/grpc/core/mirror.py b/ai-hub/app/core/grpc/core/mirror.py index 2ffe413..b9811c6 100644 --- a/ai-hub/app/core/grpc/core/mirror.py +++ b/ai-hub/app/core/grpc/core/mirror.py @@ -506,8 +506,15 @@ for entry in os.scandir(self.storage_root): if entry.is_dir() and entry.name not in active_set: try: - shutil.rmtree(entry.path) - print(f" [📁🗑️] Purged orphaned ghost mirror: {entry.name}") + # Robust recursive delete with small retry for race conditions + for i in range(3): + try: + shutil.rmtree(entry.path) + print(f" [📁🗑️] Purged orphaned ghost mirror: {entry.name}") + break + except OSError as se: + if i == 2: raise se + time.sleep(0.5) except Exception as e: print(f" [📁⚠️] Failed to purge orphaned mirror {entry.name}: {e}") diff --git a/ai-hub/app/core/orchestration/agent_loop.py b/ai-hub/app/core/orchestration/agent_loop.py index 5d60387..cf31f4d 100644 --- a/ai-hub/app/core/orchestration/agent_loop.py +++ b/ai-hub/app/core/orchestration/agent_loop.py @@ -56,7 +56,7 @@ try: inner_db = SessionLocal() inner_instance = inner_db.query(AgentInstance).filter(AgentInstance.id == agent_id).first() - if not inner_instance or inner_instance.status != "active": + if not inner_instance or inner_instance.status not in ["active", "starting"]: inner_db.close() break inner_instance.last_heartbeat = datetime.utcnow() @@ -362,9 +362,19 @@ registry.emit(instance.mesh_node_id, "status_update", {"evaluation_status": instance.evaluation_status}) # transparency context for Auditor + available_tools = [] + if services and hasattr(services, "tool_service"): + try: + # We pass features="chat" or relevant filter? + # For the auditor, we want to know what the agent *could* have called. + tools = services.tool_service.get_available_tools(db, instance.user_id, session_id=session_id) + available_tools = [t["function"]["name"] for t in tools] + except Exception as te: + logger.warning(f"Auditor failed to fetch tool list: {te}") + partner_ctx = { "system_prompt": template.system_prompt_content, - "skills": [s.name for s in (agent_session.skills if agent_session and agent_session.skills else [])] + "skills": available_tools } blind_eval = await evaluator.evaluate_blind(prompt, rubric_content, final_answer, partner_context=partner_ctx) diff --git a/ai-hub/app/core/orchestration/scheduler.py b/ai-hub/app/core/orchestration/scheduler.py index 2bf6bee..50f2831 100644 --- a/ai-hub/app/core/orchestration/scheduler.py +++ b/ai-hub/app/core/orchestration/scheduler.py @@ -35,8 +35,7 @@ try: from app.db.session import get_db_session with get_db_session() as db: - # Find active agents that haven't heartbeat in 3+ minutes - timeout = datetime.utcnow() - timedelta(minutes=3) + timeout = datetime.utcnow() - timedelta(minutes=10) zombies = db.query(AgentInstance).filter( AgentInstance.status == 'active', AgentInstance.last_heartbeat < timeout diff --git a/ai-hub/app/core/providers/llm/general.py b/ai-hub/app/core/providers/llm/general.py index 9424eb3..f93def1 100644 --- a/ai-hub/app/core/providers/llm/general.py +++ b/ai-hub/app/core/providers/llm/general.py @@ -1,4 +1,6 @@ import litellm +import asyncio +from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type class GeneralProvider: def __init__(self, model_name: str, api_key: str, system_prompt: str = None, **kwargs): @@ -23,9 +25,28 @@ return messages + @retry( + stop=stop_after_attempt(5), + wait=wait_exponential(multiplier=1, min=2, max=10), + retry=retry_if_exception_type((litellm.ServiceUnavailableError, litellm.InternalServerError)), + reraise=True + ) + async def _acompletion_with_retry(self, request): + """Internal helper for filtered retries.""" + try: + return await litellm.acompletion(**request) + except Exception as e: + # Handle specific LiteLLM errors that tenacity doesn't catch natively via type + err_msg = str(e).lower() + if "503" in err_msg or "unavailable" in err_msg or "high demand" in err_msg: + # Force retry if it looks like a temporary provider spike + provider_hint = self.model_name.split("/")[0] if "/" in self.model_name else "litellm" + raise litellm.ServiceUnavailableError(f"Temporary Provider Spike detected: {e}", model=self.model_name, llm_provider=provider_hint) + raise + async def acompletion(self, prompt=None, messages=None, **kwargs): """ - Asynchronous completion pass using LiteLLM. + Asynchronous completion pass using LiteLLM with intelligent retries. """ prepared_messages = self._prepare_messages(prompt=prompt, messages=messages) @@ -37,9 +58,11 @@ **kwargs, } try: - return await litellm.acompletion(**request) + return await self._acompletion_with_retry(request) except Exception as e: err_msg = str(e) if "authentication" in err_msg.lower() or "401" in err_msg: raise RuntimeError(f"Authentication failed for {self.model_name}. Check your API key.") - raise RuntimeError(f"LiteLLM Error ({self.model_name}): {err_msg}") \ No newline at end of file + + # If we still fail after retries, wrap it in a cleaner runtime error + raise RuntimeError(f"Core Orchestrator Fault: {err_msg}") \ No newline at end of file diff --git a/ai-hub/integration_tests/conftest.py b/ai-hub/integration_tests/conftest.py index cac24c4..911ca33 100644 --- a/ai-hub/integration_tests/conftest.py +++ b/ai-hub/integration_tests/conftest.py @@ -52,7 +52,7 @@ "providers": { "gemini": { "api_key": os.getenv("GEMINI_API_KEY", ""), - "model": "gemini/gemini-3-flash-preview" + "model": "gemini/gemini-1.5-flash" } } }, diff --git a/ai-hub/integration_tests/test_coworker_flow.py b/ai-hub/integration_tests/test_coworker_flow.py index 327951e..46bc9d7 100644 --- a/ai-hub/integration_tests/test_coworker_flow.py +++ b/ai-hub/integration_tests/test_coworker_flow.py @@ -44,7 +44,7 @@ print(f"\n[test] Waiting for agent {instance_id} to reach evaluation status...") found_evaluating = False sync_workspace_id = r_deploy.json().get("sync_workspace_id") - for _ in range(450): # 900s timeout (increased for multi-stage LLM chains) + for _ in range(750): # 1500s timeout r_agent = client.get(f"{BASE_URL}/agents/{instance_id}", headers=_headers()) if r_agent.status_code == 200: agent = r_agent.json() @@ -119,7 +119,7 @@ print(f"\n[test] Waiting for agent {instance_id} to reach 'failed_limit' status...") failed_limit = False latest_score = None - for _ in range(450): # 900s timeout + for _ in range(750): # 1500s timeout r_agents = client.get(f"{BASE_URL}/agents", headers=_headers()) if r_agents.status_code == 200: agents = r_agents.json() @@ -176,7 +176,7 @@ client.post(f"{BASE_URL}/agents/{instance_id}/webhook", params={"token": secret}, json={"prompt": "Go!"}) found_reworking = False - for _ in range(450): # 900s timeout + for _ in range(750): # 1500s timeout r_agents = client.get(f"{BASE_URL}/agents", headers=_headers()) if r_agents.status_code == 200: agent = next((a for a in r_agents.json() if a["id"] == instance_id), None) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cc54a13..fa32c2c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -24,6 +24,7 @@ "react-scripts": "5.0.1", "reactflow": "^11.11.4", "recharts": "^3.8.0", + "remark-gfm": "^4.0.1", "web-vitals": "^2.1.4" }, "devDependencies": { @@ -13099,6 +13100,16 @@ "tmpl": "1.0.5" } }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -13108,6 +13119,34 @@ "node": ">= 0.4" } }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mdast-util-from-markdown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", @@ -13131,6 +13170,107 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-mdx-expression": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", @@ -13380,6 +13520,127 @@ "micromark-util-types": "^2.0.0" } }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/micromark-factory-destination": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", @@ -16822,6 +17083,24 @@ "node": ">= 0.10" } }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-parse": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", @@ -16853,6 +17132,21 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index fdc4a42..a99d6df 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,6 +19,7 @@ "react-scripts": "5.0.1", "reactflow": "^11.11.4", "recharts": "^3.8.0", + "remark-gfm": "^4.0.1", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/frontend/src/features/agents/components/AgentDrillDown.js b/frontend/src/features/agents/components/AgentDrillDown.js index bf2d48d..79dd2f4 100644 --- a/frontend/src/features/agents/components/AgentDrillDown.js +++ b/frontend/src/features/agents/components/AgentDrillDown.js @@ -140,6 +140,7 @@ availableModels={availableModels} fetchingModels={fetchingModels} allProviders={allProviders} + userConfig={userConfig} /> )} diff --git a/frontend/src/features/agents/components/drilldown/ChatTracker.js b/frontend/src/features/agents/components/drilldown/ChatTracker.js index ead8c49..cf0eb73 100644 --- a/frontend/src/features/agents/components/drilldown/ChatTracker.js +++ b/frontend/src/features/agents/components/drilldown/ChatTracker.js @@ -79,9 +79,16 @@
70 ? 'border-amber-500/30 border-t-amber-500' : 'border-indigo-500/30 border-t-indigo-500'} animate-spin rounded-full shrink-0`}>
)}
- - {currentAction.display} - +
+ + {currentAction.display} + + {currentAction.isDone && agent?.latest_quality_score !== undefined && ( + + QUALITY: {agent.latest_quality_score}% + + )} +
{ return (
@@ -94,36 +95,42 @@ />
-
-
- - -
-
- - -
+
+ + +

+ Models are pre-configured by administrators via group permissions. +

@@ -148,6 +155,33 @@
+ +
+
+
+ Session Lockdown + Prevents accidental history cleanup while active. +
+ +
+
+
+ Auto-Clear Context + Clear message history after every successful mission. +
+ +
+
diff --git a/frontend/src/features/chat/components/ChatWindow.css b/frontend/src/features/chat/components/ChatWindow.css index fe7f801..8811162 100644 --- a/frontend/src/features/chat/components/ChatWindow.css +++ b/frontend/src/features/chat/components/ChatWindow.css @@ -246,4 +246,39 @@ .chat-history-container { background-color: var(--chat-bg) !important; -} \ No newline at end of file +} +/* Table Styles - Added for Professional Technical Reports */ +.markdown-preview table { + width: 100% !important; + border-collapse: collapse !important; + margin: 1.25rem 0 !important; + font-size: 0.8125rem !important; + border: 1px solid var(--border-subtle) !important; + border-radius: 0.75rem !important; + overflow: hidden !important; + box-shadow: 0 1px 3px rgba(0,0,0,0.05) !important; +} + +.markdown-preview th { + background: rgba(99, 102, 241, 0.05) !important; + font-weight: 800 !important; + text-transform: uppercase !important; + letter-spacing: 0.05em !important; + font-size: 0.7rem !important; + padding: 0.875rem 1rem !important; + text-align: left !important; + border-bottom: 2px solid var(--border-subtle) !important; + color: #4f46e5 !important; +} + +.markdown-preview td { + padding: 0.875rem 1rem !important; + border-bottom: 1px solid var(--border-subtle) !important; + line-height: 1.5 !important; + vertical-align: top !important; +} + +.dark .markdown-preview th { + background: rgba(255, 255, 255, 0.03) !important; + color: #818cf8 !important; +} diff --git a/frontend/src/features/chat/components/ChatWindow.js b/frontend/src/features/chat/components/ChatWindow.js index a20fe20..75bc20a 100644 --- a/frontend/src/features/chat/components/ChatWindow.js +++ b/frontend/src/features/chat/components/ChatWindow.js @@ -1,5 +1,6 @@ import React, { useEffect, useRef, useState } from "react"; import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; import './ChatWindow.css'; import { FaRegCopy, FaCopy, FaVolumeUp, FaPlay, FaPause, FaDownload, FaSyncAlt } from 'react-icons/fa'; // Import the icons @@ -137,14 +138,14 @@ className={`mt-2 text-xs transition-all duration-500 ease-in-out overflow-y-auto thought-panel pl-4 pr-2 py-1 custom-scrollbar ${isReasoningExpanded ? "max-h-[400px] opacity-100 mb-2" : "max-h-0 opacity-0" } text-gray-600 dark:text-gray-400 font-light leading-relaxed markdown-preview max-w-none`} > - {message.reasoning} + {message.reasoning}
)}
- {message.text} + {message.text}
{/* Per-Answer Evaluation Metadata (Area 4: Quality Rubric / Feedback / History) */} @@ -166,7 +167,7 @@
📝 Quality Rubric
- {effectiveEval.rubric} + {effectiveEval.rubric}
)} @@ -178,7 +179,7 @@
🤖 Co-Worker Feedback
- {effectiveEval.feedback} + {effectiveEval.feedback}
)} @@ -369,7 +370,7 @@
{(() => { const lastAssistantIndex = history.slice().reverse().findIndex(m => m.sender === 'assistant' || m.sender === 'assistant'); // Logic for assistant diff --git a/scripts/fix_table_prompt.py b/scripts/fix_table_prompt.py new file mode 100644 index 0000000..ab0f20d --- /dev/null +++ b/scripts/fix_table_prompt.py @@ -0,0 +1,45 @@ +import sqlite3 +import os + +db_path = "/Users/axieyangb/Project/CortexAI/ai-hub/data/ai_hub.db" + +if not os.path.exists(db_path): + print(f"Error: DB not found at {db_path}") + exit(1) + +conn = sqlite3.connect(db_path) +cursor = conn.cursor() + +# Get the monitor template +cursor.execute("SELECT id, system_prompt_content FROM agent_templates WHERE name LIKE '%monitor%' OR name LIKE '%Technical%'") +templates = cursor.fetchall() + +table_instruction = """ +IMPORTANT FORMATTING RULES: +When providing system status or technical reports, ALWAYS use standard GitHub Flavored Markdown (GFM) tables. +- Each row MUST be on a new line. +- You MUST include the separator row (e.g., |---|---|---|) immediately after the header. +- Do NOT skip newlines between table rows. +- Ensure proper alignment and vertical padding. + +Example: +| Component | Status | Notes | +| :--- | :--- | :--- | +| Connectivity | ACTIVE | Responsive via gRPC | +| CPU | 15% | Nominal | +""" + +for tid, content in templates: + if content: + if "IMPORTANT FORMATTING RULES" not in content: + new_content = content + "\n\n" + table_instruction + cursor.execute("UPDATE agent_templates SET system_prompt_content = ? WHERE id = ?", (new_content, tid)) + print(f"Updated template {tid}") + else: + # Fallback if content is null + cursor.execute("UPDATE agent_templates SET system_prompt_content = ? WHERE id = ?", (table_instruction, tid)) + print(f"Initialized template {tid} with instruction") + +conn.commit() +conn.close() +print("Done.")