diff --git a/ai-hub/ai_hub.egg-info/PKG-INFO b/ai-hub/ai_hub.egg-info/PKG-INFO new file mode 100644 index 0000000..91ff11c --- /dev/null +++ b/ai-hub/ai_hub.egg-info/PKG-INFO @@ -0,0 +1,42 @@ +Metadata-Version: 2.4 +Name: ai-hub +Version: 0.1.0 +Summary: An AI Model Hub Service with PostgreSQL and FAISS integration. +Author: Jerry Xie +Author-email: axieyangb@google.com +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Requires-Python: >=3.11 +Requires-Dist: fastapi +Requires-Dist: uvicorn[standard] +Requires-Dist: google-generativeai +Requires-Dist: python-dotenv +Requires-Dist: openai +Requires-Dist: pytest +Requires-Dist: requests +Requires-Dist: anyio +Requires-Dist: sqlalchemy +Requires-Dist: psycopg2-binary +Requires-Dist: pytest-asyncio +Requires-Dist: pytest-tornasync +Requires-Dist: pytest-trio +Requires-Dist: pytest-mock +Requires-Dist: numpy +Requires-Dist: faiss-cpu +Requires-Dist: aioresponses +Requires-Dist: python-multipart +Requires-Dist: PyJWT +Requires-Dist: tenacity +Requires-Dist: litellm +Requires-Dist: tiktoken +Requires-Dist: grpcio +Requires-Dist: grpcio-tools +Requires-Dist: grpcio-reflection +Requires-Dist: croniter +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary diff --git a/ai-hub/ai_hub.egg-info/SOURCES.txt b/ai-hub/ai_hub.egg-info/SOURCES.txt new file mode 100644 index 0000000..4a307fb --- /dev/null +++ b/ai-hub/ai_hub.egg-info/SOURCES.txt @@ -0,0 +1,59 @@ +pyproject.toml +setup.py +ai_hub.egg-info/PKG-INFO +ai_hub.egg-info/SOURCES.txt +ai_hub.egg-info/dependency_links.txt +ai_hub.egg-info/entry_points.txt +ai_hub.egg-info/requires.txt +ai_hub.egg-info/top_level.txt +app/__init__.py +app/app.py +app/config.py +app/main.py +app/utils.py +app/api/__init__.py +app/api/dependencies.py +app/api/schemas.py +app/api/routes/__init__.py +app/api/routes/admin.py +app/api/routes/agent_update.py +app/api/routes/agents.py +app/api/routes/api.py +app/api/routes/documents.py +app/api/routes/general.py +app/api/routes/nodes.py +app/api/routes/sessions.py +app/api/routes/skills.py +app/api/routes/stt.py +app/api/routes/tts.py +app/api/routes/user.py +app/db/__init__.py +app/db/database.py +app/db/migrate.py +app/db/session.py +app/db/models/__init__.py +app/db/models/agent.py +app/db/models/asset.py +app/db/models/document.py +app/db/models/node.py +app/db/models/session.py +app/db/models/user.py +app/protos/__init__.py +app/protos/agent_pb2.py +app/protos/agent_pb2_grpc.py +app/protos/browser_pb2.py +app/protos/browser_pb2_grpc.py +scripts/__init__.py +scripts/seed_prompts.py +tests/__init__.py +tests/test_app.py +tests/test_config.py +tests/test_utils.py +tests/core/__init__.py +tests/core/pipelines/__init__.py +tests/core/pipelines/test_rag_pipeline.py +tests/core/vector_store/__init__.py +tests/core/vector_store/conftest.py +tests/core/vector_store/test_embedder_factory.py +tests/core/vector_store/test_faiss_store.py +tests/core/vector_store/test_mock_embedder.py \ No newline at end of file diff --git a/ai-hub/ai_hub.egg-info/dependency_links.txt b/ai-hub/ai_hub.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/ai-hub/ai_hub.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/ai-hub/ai_hub.egg-info/entry_points.txt b/ai-hub/ai_hub.egg-info/entry_points.txt new file mode 100644 index 0000000..ac8eb49 --- /dev/null +++ b/ai-hub/ai_hub.egg-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +ai-hub-server = app.main:app diff --git a/ai-hub/ai_hub.egg-info/requires.txt b/ai-hub/ai_hub.egg-info/requires.txt new file mode 100644 index 0000000..6c678f6 --- /dev/null +++ b/ai-hub/ai_hub.egg-info/requires.txt @@ -0,0 +1,26 @@ +fastapi +uvicorn[standard] +google-generativeai +python-dotenv +openai +pytest +requests +anyio +sqlalchemy +psycopg2-binary +pytest-asyncio +pytest-tornasync +pytest-trio +pytest-mock +numpy +faiss-cpu +aioresponses +python-multipart +PyJWT +tenacity +litellm +tiktoken +grpcio +grpcio-tools +grpcio-reflection +croniter diff --git a/ai-hub/ai_hub.egg-info/top_level.txt b/ai-hub/ai_hub.egg-info/top_level.txt new file mode 100644 index 0000000..025bffe --- /dev/null +++ b/ai-hub/ai_hub.egg-info/top_level.txt @@ -0,0 +1,3 @@ +app +scripts +tests diff --git a/ai-hub/app/api/routes/agents.py b/ai-hub/app/api/routes/agents.py index 41e89c0..9f4bfdf 100644 --- a/ai-hub/app/api/routes/agents.py +++ b/ai-hub/app/api/routes/agents.py @@ -266,9 +266,10 @@ instance.total_runs = 0 instance.successful_runs = 0 + instance.total_input_tokens = 0 + instance.total_output_tokens = 0 instance.total_tokens_accumulated = 0 - instance.total_running_time_seconds = 0.0 - # By setting this to an empty dict but doing an in-place update the ORM sees it + instance.total_running_time_seconds = 0 instance.tool_call_counts = {} db.commit() diff --git a/ai-hub/app/api/routes/sessions.py b/ai-hub/app/api/routes/sessions.py index 2c330df..242c369 100644 --- a/ai-hub/app/api/routes/sessions.py +++ b/ai-hub/app/api/routes/sessions.py @@ -180,7 +180,8 @@ token_limit = get_model_limit(effective_provider, model_name=resolved_model) except ValueError as e: # Model not configured โ€” return a graceful 200 with error hint - # The frontend can use this to show an inline "configure model" prompt + import logging + logging.warning(f"[Tokens] Limit resolution failed for {effective_provider}/{resolved_model}: {e}") return schemas.SessionTokenUsageResponse( token_count=0, token_limit=0, @@ -190,7 +191,12 @@ validator = Validator(token_limit=token_limit) token_count = validator.get_token_count(combined_text) - percentage = round((token_count / token_limit) * 100, 2) if token_limit > 0 else 0.0 + + # Defensive check: if token_limit is still 0 (shouldn't happen with get_model_limit fallback), avoid DivZero + if token_limit <= 0: + token_limit = 10000 + + percentage = round((token_count / token_limit) * 100, 2) return schemas.SessionTokenUsageResponse( token_count=token_count, diff --git a/ai-hub/app/core/orchestration/agent_loop.py b/ai-hub/app/core/orchestration/agent_loop.py index a387693..2260809 100644 --- a/ai-hub/app/core/orchestration/agent_loop.py +++ b/ai-hub/app/core/orchestration/agent_loop.py @@ -173,6 +173,11 @@ if evaluator: await evaluator.log_event("Execution Initialized", "Agent loop warming up for primary task execution.") + + # Track cumulative metrics for this entire execution run (across all rework rounds) + total_task_input_tokens = 0 + total_task_output_tokens = 0 + total_task_tool_counts = {} # --- MAIN REWORK LOOP --- loop_start = time.time() # Handle scope for exception reporting @@ -184,14 +189,15 @@ round_sub_events = [] try: registry = getattr(services.rag_service, "node_registry_service", None) - final_tool_counts = {} - final_input_tokens = 0 - final_output_tokens = 0 + round_tool_counts = {} + round_input_tokens = 0 + round_output_tokens = 0 final_answer = "" last_assistant_msg_id = None instance = db.query(AgentInstance).filter(AgentInstance.id == agent_id).first() instance.last_reasoning = "" + instance.status = "active" instance.evaluation_status = f"๐Ÿค– Main Agent (Rd {current_attempt + 1}): Executing..." if not safe_commit(): return @@ -215,14 +221,14 @@ ): if event.get("type") == "finish": last_assistant_msg_id = event.get("message_id") - final_tool_counts = event.get("tool_counts", {}) - final_input_tokens = event.get("input_tokens", 0) - final_output_tokens = event.get("output_tokens", 0) + round_tool_counts = event.get("tool_counts", {}) + round_input_tokens += event.get("input_tokens", 0) + round_output_tokens += event.get("output_tokens", 0) final_answer = event.get("full_answer", "") elif event.get("type") == "token_counted": usage = event.get("usage", {}) - final_input_tokens += usage.get("prompt_tokens", 0) - final_output_tokens += usage.get("completion_tokens", 0) + round_input_tokens += usage.get("prompt_tokens", 0) + round_output_tokens += usage.get("completion_tokens", 0) elif event.get("type") in ("reasoning", "content"): new_content = event.get("content", "") if event.get("type") == "content": @@ -257,6 +263,20 @@ if not safe_commit(): return content_buffer = "" + # Accumulate round metrics into task totals + total_task_input_tokens += round_input_tokens + total_task_output_tokens += round_output_tokens + for tool, counts in round_tool_counts.items(): + if tool not in total_task_tool_counts: + total_task_tool_counts[tool] = {"calls": 0, "successes": 0, "failures": 0} + if isinstance(counts, dict): + total_task_tool_counts[tool]["calls"] += counts.get("calls", 0) + total_task_tool_counts[tool]["successes"] += counts.get("successes", 0) + total_task_tool_counts[tool]["failures"] += counts.get("failures", 0) + else: # Legacy int support + total_task_tool_counts[tool]["calls"] += counts + total_task_tool_counts[tool]["successes"] += counts + exec_duration = time.time() - execution_start round_sub_events.append({"name": "Agent execution", "duration": round(exec_duration, 2), "timestamp": time.time()}) @@ -275,6 +295,14 @@ instance.evaluation_status = "evaluating" if not safe_commit(): return + # Initial status write to feedback.md so it's not "Session Started" + evaluator.assistant.write( + evaluator.mesh_node_id, + ".cortex/feedback.md", + f"# ๐Ÿ•ต๏ธ Co-Worker Audit (Attempt {current_attempt + 1})\n\nAudit initiated. Reviewing technical accuracy and alignment...", + session_id=evaluator.sync_workspace_id + ) + # Stage 2A: Blind Rating instance = db.query(AgentInstance).filter(AgentInstance.id == agent_id).first() instance.evaluation_status = f"๐Ÿ•ต๏ธ Co-Worker (Rd {current_attempt + 1}): Auditing result against criteria..." @@ -454,6 +482,9 @@ if instance: instance.status = "error_suspended" instance.last_error = str(e) + # Even on error, try to sync tokens used so far + instance.total_input_tokens = (instance.total_input_tokens or 0) + total_task_input_tokens + instance.total_output_tokens = (instance.total_output_tokens or 0) + total_task_output_tokens if not safe_commit(): return return { "status": "error", @@ -463,19 +494,27 @@ # Final loop cleanup & Stats instance = db.query(AgentInstance).filter(AgentInstance.id == agent_id).first() - if instance and instance.status == "active": - instance.status = "idle" # Completed work - instance.successful_runs = (instance.successful_runs or 0) + 1 - + if instance: + # Update metrics regardless of final status (as long as we finished the loop) elapsed = int(time.time() - loop_start) instance.total_running_time_seconds = (instance.total_running_time_seconds or 0) + elapsed - instance.total_input_tokens = (instance.total_input_tokens or 0) + (final_input_tokens if final_result else 0) - instance.total_output_tokens = (instance.total_output_tokens or 0) + (final_output_tokens if final_result else 0) + instance.total_input_tokens = (instance.total_input_tokens or 0) + total_task_input_tokens + instance.total_output_tokens = (instance.total_output_tokens or 0) + total_task_output_tokens - if final_tool_counts: + # Success calculation + final_score = getattr(instance, 'latest_quality_score', 0) or 0 + threshold = rework_threshold or 80 + + if instance.status == "active": + instance.status = "idle" + # Only increment successful runs if we didn't end in an error state and passed threshold (or were unchecked) + if final_score >= threshold or not co_worker_enabled: + instance.successful_runs = (instance.successful_runs or 0) + 1 + + if total_task_tool_counts: import copy current_counts = copy.deepcopy(instance.tool_call_counts or {}) - for k, v in final_tool_counts.items(): + for k, v in total_task_tool_counts.items(): if k in current_counts and isinstance(current_counts[k], int): current_counts[k] = {"calls": current_counts[k], "successes": current_counts[k], "failures": 0} if not isinstance(v, dict): diff --git a/ai-hub/app/core/orchestration/harness_evaluator.py b/ai-hub/app/core/orchestration/harness_evaluator.py index 2f88e0b..5d3c3c2 100644 --- a/ai-hub/app/core/orchestration/harness_evaluator.py +++ b/ai-hub/app/core/orchestration/harness_evaluator.py @@ -442,6 +442,22 @@ ): if event["type"] == "content": final_answer += event["content"] + # Stream to feedback.md for UI visibility during evaluation + if self.assistant: + self.assistant.write( + self.mesh_node_id, + ".cortex/feedback.md", + f"# ๐Ÿ•ต๏ธ Co-Worker Audit in Progress...\n\n{final_answer}\n\n*(Analyzing results against rubric...)*", + session_id=self.sync_workspace_id + ) + elif event["type"] == "reasoning": + # Also include reasoning thoughts in the live feedback + thought = event["content"] + if self.assistant: + # Prepend reasoning if we want, or just append. + # Let's just use final_answer for now to keep it clean, + # but maybe add a header for the thoughts. + pass elif event["type"] == "error": logger.error(f"[HarnessEvaluator] Sub-evaluator fault: {event['content']}") diff --git a/backend.log b/backend.log new file mode 100644 index 0000000..d973a0e --- /dev/null +++ b/backend.log @@ -0,0 +1,77 @@ +INFO: Will watch for changes in these directories: ['/app/ai-hub'] +INFO: Uvicorn running on http://0.0.0.0:8001 (Press CTRL+C to quit) +INFO: Started reloader process [5892] using WatchFiles +INFO:app.core.tools.registry:Registered dynamic tool plugin: 'browser_automation_agent' +INFO:app.core.tools.registry:Registered dynamic tool plugin: 'mesh_file_explorer' +INFO:app.core.tools.registry:Registered dynamic tool plugin: 'mesh_inspect_drift' +INFO:app.core.tools.registry:Registered dynamic tool plugin: 'mesh_sync_control' +INFO:app.core.tools.registry:Registered dynamic tool plugin: 'mesh_terminal_control' +INFO:app.core.tools.registry:Registered dynamic tool plugin: 'mesh_wait_tasks' +INFO:app.core.tools.registry:Registered dynamic tool plugin: 'read_skill_artifact' +INFO: Started server process [5928] +INFO: Waiting for application startup. +INFO:app.db.migrate:Starting database migrations... +INFO:app.db.migrate:Column 'audio_path' already exists in 'messages'. +INFO:app.db.migrate:Column 'model_response_time' already exists in 'messages'. +INFO:app.db.migrate:Column 'token_count' already exists in 'messages'. +INFO:app.db.migrate:Column 'reasoning_content' already exists in 'messages'. +INFO:app.db.migrate:Column 'stt_provider_name' already exists in 'sessions'. +INFO:app.db.migrate:Column 'tts_provider_name' already exists in 'sessions'. +INFO:app.db.migrate:Column 'sync_workspace_id' already exists in 'sessions'. +INFO:app.db.migrate:Column 'attached_node_ids' already exists in 'sessions'. +INFO:app.db.migrate:Column 'node_sync_status' already exists in 'sessions'. +INFO:app.db.migrate:Column 'sync_config' already exists in 'sessions'. +INFO:app.db.migrate:Column 'is_cancelled' already exists in 'sessions'. +INFO:app.db.migrate:Column 'restrict_skills' already exists in 'sessions'. +INFO:app.db.migrate:Column 'allowed_skill_names' already exists in 'sessions'. +INFO:app.db.migrate:Column 'system_prompt_override' already exists in 'sessions'. +INFO:app.db.migrate:Column 'is_locked' already exists in 'sessions'. +INFO:app.db.migrate:Adding column 'co_worker_quality_gate' to 'agent_templates' table... +INFO:app.db.migrate:Successfully added 'co_worker_quality_gate' to agent_templates. +INFO:app.db.migrate:Adding column 'rework_threshold' to 'agent_templates' table... +INFO:app.db.migrate:Successfully added 'rework_threshold' to agent_templates. +INFO:app.db.migrate:Adding column 'max_rework_attempts' to 'agent_templates' table... +INFO:app.db.migrate:Successfully added 'max_rework_attempts' to agent_templates. +INFO:app.db.migrate:Adding column 'total_runs' to 'agent_instances' table... +INFO:app.db.migrate:Successfully added 'total_runs' to agent_instances. +INFO:app.db.migrate:Adding column 'successful_runs' to 'agent_instances' table... +INFO:app.db.migrate:Successfully added 'successful_runs' to agent_instances. +INFO:app.db.migrate:Adding column 'total_tokens_accumulated' to 'agent_instances' table... +INFO:app.db.migrate:Successfully added 'total_tokens_accumulated' to agent_instances. +INFO:app.db.migrate:Adding column 'total_input_tokens' to 'agent_instances' table... +INFO:app.db.migrate:Successfully added 'total_input_tokens' to agent_instances. +INFO:app.db.migrate:Adding column 'total_output_tokens' to 'agent_instances' table... +INFO:app.db.migrate:Successfully added 'total_output_tokens' to agent_instances. +INFO:app.db.migrate:Adding column 'total_running_time_seconds' to 'agent_instances' table... +INFO:app.db.migrate:Successfully added 'total_running_time_seconds' to agent_instances. +INFO:app.db.migrate:Adding column 'tool_call_counts' to 'agent_instances' table... +INFO:app.db.migrate:Successfully added 'tool_call_counts' to agent_instances. +INFO:app.db.migrate:Adding column 'last_reasoning' to 'agent_instances' table... +INFO:app.db.migrate:Successfully added 'last_reasoning' to agent_instances. +INFO:app.db.migrate:Adding column 'last_error' to 'agent_instances' table... +INFO:app.db.migrate:Successfully added 'last_error' to agent_instances. +INFO:app.db.migrate:Adding column 'evaluation_status' to 'agent_instances' table... +INFO:app.db.migrate:Successfully added 'evaluation_status' to agent_instances. +INFO:app.db.migrate:Adding column 'current_rework_attempt' to 'agent_instances' table... +INFO:app.db.migrate:Successfully added 'current_rework_attempt' to agent_instances. +INFO:app.db.migrate:Adding column 'latest_quality_score' to 'agent_instances' table... +INFO:app.db.migrate:Successfully added 'latest_quality_score' to agent_instances. +INFO:app.db.migrate:Database migrations complete. +INFO:app.core.services.node_registry:[NodeRegistry] Reset all DB node statuses to 'offline'. +INFO:app.core.grpc.services.grpc_server:๐Ÿš€ CORTEX gRPC Orchestrator starting on [::]:50051 +INFO:app.app:[M6] Agent Orchestrator gRPC server started on port 50051. +INFO:app.core.orchestration.scheduler:[Scheduler] Agent background services (Zombie Sweeper & CRON) started. +INFO:app.core.skills.bootstrap:Checking for system skills bootstrapping... +INFO:app.core.skills.bootstrap:System skills bootstrap completed. +INFO:app.core.orchestration.scheduler:[Scheduler] CRON WAKEUP: Triggering Agent cb0fe6f9-0329-40df-a864-4bd7488f882c (Cron: 30) +INFO: Application startup complete. +ERROR:app.core.grpc.services.grpc_server:[๐Ÿ“โš ๏ธ] Mirror Cleanup Thread Error: (sqlite3.OperationalError) no such column: sessions.auto_clear_history +[SQL: SELECT sessions.id AS sessions_id, sessions.user_id AS sessions_user_id, sessions.title AS sessions_title, sessions.provider_name AS sessions_provider_name, sessions.stt_provider_name AS sessions_stt_provider_name, sessions.tts_provider_name AS sessions_tts_provider_name, sessions.feature_name AS sessions_feature_name, sessions.created_at AS sessions_created_at, sessions.is_archived AS sessions_is_archived, sessions.is_cancelled AS sessions_is_cancelled, sessions.sync_workspace_id AS sessions_sync_workspace_id, sessions.attached_node_ids AS sessions_attached_node_ids, sessions.node_sync_status AS sessions_node_sync_status, sessions.sync_config AS sessions_sync_config, sessions.restrict_skills AS sessions_restrict_skills, sessions.allowed_skill_names AS sessions_allowed_skill_names, sessions.system_prompt_override AS sessions_system_prompt_override, sessions.is_locked AS sessions_is_locked, sessions.auto_clear_history AS sessions_auto_clear_history +FROM sessions +WHERE sessions.is_archived = 0 AND sessions.sync_workspace_id IS NOT NULL] +(Background on this error at: https://sqlalche.me/e/20/e3q8) +ERROR:app.app:[๐Ÿ“๐Ÿงน] Ghost Mirror periodic cleanup fail: (sqlite3.OperationalError) no such column: sessions.auto_clear_history +[SQL: SELECT sessions.id AS sessions_id, sessions.user_id AS sessions_user_id, sessions.title AS sessions_title, sessions.provider_name AS sessions_provider_name, sessions.stt_provider_name AS sessions_stt_provider_name, sessions.tts_provider_name AS sessions_tts_provider_name, sessions.feature_name AS sessions_feature_name, sessions.created_at AS sessions_created_at, sessions.is_archived AS sessions_is_archived, sessions.is_cancelled AS sessions_is_cancelled, sessions.sync_workspace_id AS sessions_sync_workspace_id, sessions.attached_node_ids AS sessions_attached_node_ids, sessions.node_sync_status AS sessions_node_sync_status, sessions.sync_config AS sessions_sync_config, sessions.restrict_skills AS sessions_restrict_skills, sessions.allowed_skill_names AS sessions_allowed_skill_names, sessions.system_prompt_override AS sessions_system_prompt_override, sessions.is_locked AS sessions_is_locked, sessions.auto_clear_history AS sessions_auto_clear_history +FROM sessions +WHERE sessions.sync_workspace_id IS NOT NULL] +(Background on this error at: https://sqlalche.me/e/20/e3q8) diff --git a/frontend.log b/frontend.log new file mode 100644 index 0000000..37d4712 --- /dev/null +++ b/frontend.log @@ -0,0 +1,159 @@ + +> cortex-frontend@0.1.0 start +> react-scripts start + +Attempting to bind to HOST environment variable: 0.0.0.0 +If this was unintentional, check that you haven't mistakenly set it in your shell. +Learn more here: https://cra.link/advanced-config + +Browserslist: browsers data (caniuse-lite) is 8 months old. Please run: + npx update-browserslist-db@latest + Why you should do it regularly: https://github.com/browserslist/update-db#readme +(node:5956) [DEP_WEBPACK_DEV_SERVER_ON_AFTER_SETUP_MIDDLEWARE] DeprecationWarning: 'onAfterSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option. +(Use `node --trace-deprecation ...` to show where the warning was created) +(node:5956) [DEP_WEBPACK_DEV_SERVER_ON_BEFORE_SETUP_MIDDLEWARE] DeprecationWarning: 'onBeforeSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option. +Starting the development server... + +Compiled with warnings. + +[eslint] +src/App.js + Line 35:10: 'userId' is assigned a value but never used no-unused-vars + Line 81:6: React Hook useEffect has missing dependencies: 'currentPage' and 'pathToPage'. Either include them or remove the dependency array react-hooks/exhaustive-deps + Line 142:6: React Hook useEffect has a missing dependency: 'authenticatedPages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +src/features/agents/components/AgentDrillDown.js + Line 35:12: 'cortexFiles' is assigned a value but never used no-unused-vars + Line 220:8: React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +src/features/agents/components/AgentHarnessPage.js + Line 2:21: 'getAgentTelemetry' is defined but never used no-unused-vars + Line 3:10: 'AreaChart' is defined but never used no-unused-vars + Line 3:21: 'Area' is defined but never used no-unused-vars + Line 3:27: 'XAxis' is defined but never used no-unused-vars + Line 3:34: 'YAxis' is defined but never used no-unused-vars + Line 3:41: 'Tooltip' is defined but never used no-unused-vars + Line 3:50: 'ResponsiveContainer' is defined but never used no-unused-vars + +src/features/chat/components/ChatWindow.js + Line 43:18: The ref value 'audioRef.current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy 'audioRef.current' to a variable inside the effect, and use that variable in the cleanup function react-hooks/exhaustive-deps + +src/features/nodes/pages/NodesPage.js + Line 12:12: 'groups' is assigned a value but never used no-unused-vars + +src/features/profile/pages/ProfilePage.js + Line 18:12: 'providerStatuses' is assigned a value but never used no-unused-vars + Line 153:11: 'handleGeneralPreferenceUpdate' is assigned a value but never used no-unused-vars + +src/features/settings/components/cards/IdentityGovernanceCard.js + Line 12:7: 'loadGroups' is assigned a value but never used no-unused-vars + +src/features/settings/components/cards/NetworkIdentityCard.js + Line 11:7: 'fileInputRef' is assigned a value but never used no-unused-vars + +src/features/settings/pages/SettingsPage.js + Line 113:8: React Hook useEffect has a missing dependency: 'loadUserProfile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +src/features/swarm/hooks/useSwarmControl.js + Line 110:6: React Hook useEffect has a missing dependency: 'onNewSessionCreated'. Either include it or remove the dependency array. If 'onNewSessionCreated' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps + Line 194:6: React Hook useCallback has missing dependencies: 'onNewSessionCreated' and 'userConfigData?.effective?.llm?.active_provider'. Either include them or remove the dependency array. If 'onNewSessionCreated' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps + +src/features/swarm/pages/SwarmControlPage.js + Line 3:26: 'FileSystemNavigator' is defined but never used no-unused-vars + Line 7:3: 'detachNodeFromSession' is defined but never used no-unused-vars + Line 106:10: 'sessionNodeStatus' is assigned a value but never used no-unused-vars + Line 249:6: React Hook useEffect has a missing dependency: 'fetchNodeInfo'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +src/features/voice/hooks/useVoiceChat.js + Line 8:3: 'createSession' is defined but never used no-unused-vars + Line 213:6: React Hook useEffect has a missing dependency: 'fetchTokenUsage'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +src/services/api/userService.js + Line 4:7: 'USERS_LOGOUT_ENDPOINT' is assigned a value but never used no-unused-vars + Line 5:7: 'USERS_ME_ENDPOINT' is assigned a value but never used no-unused-vars + +src/shared/components/FileSystemNavigator.js + Line 114:8: React Hook useEffect has a missing dependency: 'handleView'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +src/shared/components/MultiNodeConsole.js + Line 60:8: React Hook useEffect has missing dependencies: 'isAIProcessing', 'onMount', and 'onUnmount'. Either include them or remove the dependency array. If 'onMount' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps + Line 208:25: Expected a default case default-case + Line 251:8: React Hook useEffect has a missing dependency: 'attachedNodeIds'. Either include it or remove the dependency array react-hooks/exhaustive-deps + Line 251:9: React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps + +src/shared/components/SessionSidebar.js + Line 19:8: React Hook useEffect has a missing dependency: 'fetchSessions'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +Search for the keywords to learn more about each warning. +To ignore, add // eslint-disable-next-line to the line before. + +WARNING in [eslint] +src/App.js + Line 35:10: 'userId' is assigned a value but never used no-unused-vars + Line 81:6: React Hook useEffect has missing dependencies: 'currentPage' and 'pathToPage'. Either include them or remove the dependency array react-hooks/exhaustive-deps + Line 142:6: React Hook useEffect has a missing dependency: 'authenticatedPages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +src/features/agents/components/AgentDrillDown.js + Line 35:12: 'cortexFiles' is assigned a value but never used no-unused-vars + Line 220:8: React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +src/features/agents/components/AgentHarnessPage.js + Line 2:21: 'getAgentTelemetry' is defined but never used no-unused-vars + Line 3:10: 'AreaChart' is defined but never used no-unused-vars + Line 3:21: 'Area' is defined but never used no-unused-vars + Line 3:27: 'XAxis' is defined but never used no-unused-vars + Line 3:34: 'YAxis' is defined but never used no-unused-vars + Line 3:41: 'Tooltip' is defined but never used no-unused-vars + Line 3:50: 'ResponsiveContainer' is defined but never used no-unused-vars + +src/features/chat/components/ChatWindow.js + Line 43:18: The ref value 'audioRef.current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy 'audioRef.current' to a variable inside the effect, and use that variable in the cleanup function react-hooks/exhaustive-deps + +src/features/nodes/pages/NodesPage.js + Line 12:12: 'groups' is assigned a value but never used no-unused-vars + +src/features/profile/pages/ProfilePage.js + Line 18:12: 'providerStatuses' is assigned a value but never used no-unused-vars + Line 153:11: 'handleGeneralPreferenceUpdate' is assigned a value but never used no-unused-vars + +src/features/settings/components/cards/IdentityGovernanceCard.js + Line 12:7: 'loadGroups' is assigned a value but never used no-unused-vars + +src/features/settings/components/cards/NetworkIdentityCard.js + Line 11:7: 'fileInputRef' is assigned a value but never used no-unused-vars + +src/features/settings/pages/SettingsPage.js + Line 113:8: React Hook useEffect has a missing dependency: 'loadUserProfile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +src/features/swarm/hooks/useSwarmControl.js + Line 110:6: React Hook useEffect has a missing dependency: 'onNewSessionCreated'. Either include it or remove the dependency array. If 'onNewSessionCreated' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps + Line 194:6: React Hook useCallback has missing dependencies: 'onNewSessionCreated' and 'userConfigData?.effective?.llm?.active_provider'. Either include them or remove the dependency array. If 'onNewSessionCreated' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps + +src/features/swarm/pages/SwarmControlPage.js + Line 3:26: 'FileSystemNavigator' is defined but never used no-unused-vars + Line 7:3: 'detachNodeFromSession' is defined but never used no-unused-vars + Line 106:10: 'sessionNodeStatus' is assigned a value but never used no-unused-vars + Line 249:6: React Hook useEffect has a missing dependency: 'fetchNodeInfo'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +src/features/voice/hooks/useVoiceChat.js + Line 8:3: 'createSession' is defined but never used no-unused-vars + Line 213:6: React Hook useEffect has a missing dependency: 'fetchTokenUsage'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +src/services/api/userService.js + Line 4:7: 'USERS_LOGOUT_ENDPOINT' is assigned a value but never used no-unused-vars + Line 5:7: 'USERS_ME_ENDPOINT' is assigned a value but never used no-unused-vars + +src/shared/components/FileSystemNavigator.js + Line 114:8: React Hook useEffect has a missing dependency: 'handleView'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +src/shared/components/MultiNodeConsole.js + Line 60:8: React Hook useEffect has missing dependencies: 'isAIProcessing', 'onMount', and 'onUnmount'. Either include them or remove the dependency array. If 'onMount' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps + Line 208:25: Expected a default case default-case + Line 251:8: React Hook useEffect has a missing dependency: 'attachedNodeIds'. Either include it or remove the dependency array react-hooks/exhaustive-deps + Line 251:9: React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps + +src/shared/components/SessionSidebar.js + Line 19:8: React Hook useEffect has a missing dependency: 'fetchSessions'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +webpack compiled with 1 warning +Terminated diff --git a/frontend/src/features/agents/components/AgentDrillDown.js b/frontend/src/features/agents/components/AgentDrillDown.js index 607e53f..8cae428 100644 --- a/frontend/src/features/agents/components/AgentDrillDown.js +++ b/frontend/src/features/agents/components/AgentDrillDown.js @@ -1,461 +1,27 @@ -import React, { useState, useEffect } from 'react'; -import ChatWindow from '../../chat/components/ChatWindow'; +import React, { useState } from 'react'; import FileSystemNavigator from '../../../shared/components/FileSystemNavigator'; -import BuddyAvatar from './BuddyAvatar'; -import { getAgents, getSessionMessages, fetchWithAuth, updateAgentConfig, getUserConfig, clearSessionHistory, getSessionTokenStatus, getAgentTriggers, createAgentTrigger, deleteAgentTrigger, getUserAccessibleNodes, getSkills, resetAgentMetrics, getAgentCortexFiles, getAgentCortexFile } from '../../../services/apiService'; +import { useAgentDrillDown } from '../hooks/useAgentDrillDown'; +import DrillDownHeader from './drilldown/DrillDownHeader'; +import ChatTracker from './drilldown/ChatTracker'; +import EvaluationPanel from './drilldown/EvaluationPanel'; +import ConfigPanel from './drilldown/ConfigPanel'; +import MetricsPanel from './drilldown/MetricsPanel'; export default function AgentDrillDown({ agentId, onNavigate }) { - const [agent, setAgent] = useState(null); - const [chatHistory, setChatHistory] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [overrideText, setOverrideText] = useState(""); - - // Day 2 Configurations - const [activeTab, setActiveTab] = useState('config'); // workspace or config - const [editConfig, setEditConfig] = useState(null); - const [saving, setSaving] = useState(false); - const [userConfig, setUserConfig] = useState(null); - const [tokenUsage, setTokenUsage] = useState({ token_count: 0, token_limit: 0, percentage: 0 }); - const [tokenError, setTokenError] = useState(null); - const [clearing, setClearing] = useState(false); - const [triggers, setTriggers] = useState([]); - const [newTriggerType, setNewTriggerType] = useState('cron'); - const [newCronValue, setNewCronValue] = useState('0 * * * *'); - const [newIntervalValue, setNewIntervalValue] = useState(600); - const [newDefaultPrompt, setNewDefaultPrompt] = useState(''); - const [creatingTrigger, setCreatingTrigger] = useState(false); - const [modalConfig, setModalConfig] = useState(null); - const [copiedId, setCopiedId] = useState(null); - const [nodes, setNodes] = useState([]); - const [allSkills, setAllSkills] = useState([]); - const [flippedCards, setFlippedCards] = useState({ runtime: false, tokens: false }); - - // Evaluation Hub State - const [cortexFiles, setCortexFiles] = useState([]); - const [feedbackContent, setFeedbackContent] = useState(""); - const [rubricContent, setRubricContent] = useState(""); - const [coworkerContent, setCoworkerContent] = useState(""); - const [historyLog, setHistoryLog] = useState([]); - const [savingGroundTruth, setSavingGroundTruth] = useState(false); - const [selectedAuditId, setSelectedAuditId] = useState(null); // null means 'Live State' from node - - // Monitoring & Timer States - const [runningSeconds, setRunningSeconds] = useState(0); - const [lastTotalConsumption, setLastTotalConsumption] = useState(null); - const [previousStatus, setPreviousStatus] = useState('idle'); - const [currentAction, setCurrentAction] = useState(null); - const [lastAction, setLastAction] = useState(null); - const [lastActionDuration, setLastActionDuration] = useState(null); - const [actionStartTime, setActionStartTime] = useState(0); - - // Helper: Convert cron expression to human-readable text - const describeCron = (expr) => { - if (!expr) return ''; - if (/^\d+$/.test(expr)) { - const secs = parseInt(expr); - if (secs < 60) return `Every ${secs} seconds`; - if (secs < 3600) return `Every ${Math.round(secs/60)} minute${secs >= 120 ? 's' : ''}`; - return `Every ${Math.round(secs/3600)} hour${secs >= 7200 ? 's' : ''}`; - } - // Standard cron expressions - const parts = expr.split(' '); - if (parts.length >= 5) { - if (expr === '* * * * *') return 'Every minute'; - if (expr === '0 * * * *') return 'Every hour'; - if (expr === '0 0 * * *') return 'Every day at midnight'; - if (parts[0].startsWith('*/')) return `Every ${parts[0].slice(2)} minute${parts[0].slice(2) !== '1' ? 's' : ''}`; - } - return expr; - }; - - const formatTimeLocal = (utcString) => { - if (!utcString) return 'Never'; - const dateStr = utcString.endsWith('Z') || utcString.includes('+') ? utcString : utcString + 'Z'; - return new Date(dateStr).toLocaleString(undefined, { - month: 'short', day: 'numeric', - hour: '2-digit', minute: '2-digit', second: '2-digit' - }); - }; - - useEffect(() => { - const loadConf = async () => { - try { - const conf = await getUserConfig(); - setUserConfig(conf); - } catch (e) {} - try { - const nList = await getUserAccessibleNodes(); - setNodes(nList); - } catch (e) {} - try { - const sList = await getSkills(); - setAllSkills(sList); - } catch (e) {} - }; - loadConf(); - }, []); - - const fetchData = async () => { - try { - // Find agent - const allAgents = await getAgents(); - const found = allAgents.find(a => a.id === agentId); - if (!found) throw new Error("Agent not found"); - setAgent(found); - - // Populate form only on first load using the agent context - setEditConfig(prev => prev || { - name: found.template?.name || "", - system_prompt: found.template?.system_prompt_content || found.template?.system_prompt_path || "", - max_loop_iterations: found.template?.max_loop_iterations || 20, - mesh_node_id: found.mesh_node_id || "", - provider_name: found.session?.provider_name || "", - restrict_skills: found.session?.restrict_skills || false, - allowed_skill_ids: found.session?.skills ? found.session.skills.map(s => s.id) : [], - is_locked: found.session?.is_locked || false, - auto_clear_history: found.session?.auto_clear_history || false, - co_worker_quality_gate: found.template?.co_worker_quality_gate || false, - rework_threshold: found.template?.rework_threshold || 80, - max_rework_attempts: found.template?.max_rework_attempts || 3 - }); - - // Fetch chat history if session exists - if (found.session_id) { - const historyResp = await getSessionMessages(found.session_id); - const formatted = (historyResp.messages || []).map(m => ({ - text: m.content, - isUser: m.sender === 'user', - reasoning: m.reasoning_content, - status: null, - sender: m.sender, - timestamp: m.created_at, - id: m.id, - tool_calls: m.tool_calls, - message_metadata: m.message_metadata - })); - setChatHistory(formatted); - - try { - const usage = await getSessionTokenStatus(found.session_id); - if (usage.error) { - setTokenError(usage.error); - setTokenUsage({ token_count: 0, token_limit: 0, percentage: 0 }); - } else { - setTokenUsage(usage); - setTokenError(null); - } - } catch(e) { - setTokenError(e.message); - } - } - - try { - const tList = await getAgentTriggers(agentId); - setTriggers(tList); - } catch(e) {} - - // Fetch Evaluation Hub data (.cortex/) - const sid = found.session?.sync_workspace_id || found.session_id; - const nodeId = found.mesh_node_id || "hub"; - - if (sid) { - try { - const cFilesListing = await getAgentCortexFiles(agentId, nodeId, sid); - const files = cFilesListing.files || []; - setCortexFiles(files); - - const fileExists = (name) => files.some(f => f.name === name || f.path === `.cortex/${name}`); - - // Stream feedback.md - if (fileExists("feedback.md")) { - try { - const feedback = await getAgentCortexFile(agentId, nodeId, sid, "feedback.md"); - setFeedbackContent(feedback?.content || ""); - } catch (e) {} - } - - // Display rubric.md - if (fileExists("rubric.md")) { - try { - const rubric = await getAgentCortexFile(agentId, nodeId, sid, "rubric.md"); - setRubricContent(rubric?.content || ""); - } catch (e) {} - } - - // Display coworker.md (Ground Truth) - try { - const coworker = await getAgentCortexFile(agentId, nodeId, sid, ".coworker.md"); - setCoworkerContent(coworker?.content || ""); - } catch (e) {} - - // Display history.log - if (fileExists("history.log")) { - try { - const logs = await getAgentCortexFile(agentId, nodeId, sid, "history.log"); - if (logs?.content) { - try { - const parsed = JSON.parse(logs.content); - setHistoryLog(Array.isArray(parsed) ? parsed : []); - } catch (e) { - // Fallback to raw lines if not JSON - setHistoryLog(logs.content.split('\n').filter(l => l.trim()).map(line => ({ message: line }))); - } - } - } catch (e) {} - } - } catch (e) {} - } - } catch (err) { - setError(err.message); - } finally { - setLoading(false); - } - }; - - useEffect(() => { - fetchData(); - const interval = setInterval(fetchData, 2500); // 2.5s is sufficient and less noisy - return () => clearInterval(interval); - }, [agentId]); - - useEffect(() => { - let interval = null; - if (agent?.status === 'active') { - // Reset timer if we just transitioned to active - if (previousStatus !== 'active') { - setRunningSeconds(0); - setLastTotalConsumption(null); - setCurrentAction(null); - setLastAction(null); - setLastActionDuration(null); - setActionStartTime(0); - } - interval = setInterval(() => { - setRunningSeconds(s => s + 1); - }, 1000); - - // Track Action/Phase changes - const rawStatus = agent.evaluation_status || 'Orchestrating task payload...'; - const cleanStatus = rawStatus.toLowerCase().includes('audit') || rawStatus.toLowerCase().includes('worker') - ? `๐Ÿ›ก๏ธ Co-Worker Audit: ${rawStatus}` - : `๐Ÿค– Main Agent: ${rawStatus}`; - - if (rawStatus !== (currentAction?.raw || '')) { - // Shift current to last - if (currentAction) { - setLastAction(currentAction); - setLastActionDuration(runningSeconds - actionStartTime); - } - setCurrentAction({ display: cleanStatus, raw: rawStatus }); - setActionStartTime(runningSeconds); - } - } else if (previousStatus === 'active' && agent?.status !== 'active') { - // Captured finished state - setLastTotalConsumption(runningSeconds); - } - setPreviousStatus(agent?.status || 'idle'); - return () => clearInterval(interval); - }, [agent?.status, agent?.evaluation_status, runningSeconds, previousStatus, currentAction, actionStartTime]); - - const [skipEval, setSkipEval] = useState(false); - - const handleInjectOverride = async (e) => { - e.preventDefault(); - if (!overrideText.trim() || !agent?.session_id) return; - - try { - await fetchWithAuth(`/agents/${agentId}/run`, { - method: "POST", - body: { prompt: overrideText, skip_coworker: skipEval } - }); - setOverrideText(""); - fetchData(); - } catch (err) { - setModalConfig({ title: 'Injection Failed', message: err.message, type: 'error' }); - } - }; - - const handleClearHistory = () => { - if (!agent?.session_id) return; - setModalConfig({ - title: 'Confirm Memory Wipe', - message: "Are you sure you want to clear the agent's memory? This cannot be undone.", - type: 'error', - confirmText: 'Clear Memory', - confirmAction: async () => { - try { - setClearing(true); - if (agent?.session?.is_locked && editConfig?.is_locked === false) { - await updateAgentConfig(agent.id, { - is_locked: false, - mesh_node_id: agent.mesh_node_id || "hub" - }); - } - await clearSessionHistory(agent.session_id); - setChatHistory([]); - setSelectedAuditId(null); - fetchData(); - } catch (err) { - setModalConfig({ title: 'Clear Failed', message: err.message, type: 'error' }); - } finally { - setClearing(false); - } - } - }); - }; - - const handleResetMetrics = () => { - if (!agent?.id) return; - setModalConfig({ - title: 'Confirm Reset Metrics', - message: "Are you sure you want to reset all execution metrics for this agent? This cannot be undone.", - type: 'error', - confirmText: 'Reset Metrics', - confirmAction: async () => { - try { - setClearing(true); // Re-use the clearing state to block duplicate clicks - await resetAgentMetrics(agent.id); - fetchData(); - } catch (err) { - setModalConfig({ title: 'Reset Failed', message: err.message, type: 'error' }); - } finally { - setClearing(false); - } - } - }); - }; - - const handleSaveConfig = async () => { - try { - setSaving(true); - const payload = { - name: editConfig.name, - system_prompt: editConfig.system_prompt, - max_loop_iterations: parseInt(editConfig.max_loop_iterations, 10) || 20, - mesh_node_id: editConfig.mesh_node_id - }; - if (editConfig.provider_name) { - payload.provider_name = editConfig.provider_name; - } - if (editConfig.restrict_skills !== undefined) { - payload.restrict_skills = editConfig.restrict_skills; - } - if (editConfig.allowed_skill_ids !== undefined) { - payload.allowed_skill_ids = editConfig.allowed_skill_ids; - } - if (editConfig.is_locked !== undefined) { - payload.is_locked = editConfig.is_locked; - } - if (editConfig.auto_clear_history !== undefined) { - payload.auto_clear_history = editConfig.auto_clear_history; - } - if (editConfig.co_worker_quality_gate !== undefined) { - payload.co_worker_quality_gate = editConfig.co_worker_quality_gate; - } - if (editConfig.rework_threshold !== undefined) { - payload.rework_threshold = parseInt(editConfig.rework_threshold, 10); - } - if (editConfig.evaluator_prompt !== undefined) { - payload.evaluator_prompt = editConfig.evaluator_prompt; - } - - // Explicitly pause the agent loop during update as requested by the user - try { - await fetchWithAuth(`/agents/${agentId}/status`, { method: "PATCH", body: { status: "idle" } }); - } catch (e) {} - - await updateAgentConfig(agentId, payload); - fetchData(); - setModalConfig({ title: 'Success', message: 'Configuration Saved Successfully!', type: 'success' }); - } catch (err) { - setModalConfig({ title: 'Save Failed', message: err.message, type: 'error' }); - } finally { - setSaving(false); - } - }; - - const handleSaveGroundTruth = async () => { - try { - setSavingGroundTruth(true); - const sid = agent.session?.sync_workspace_id || agent.session_id; - const nodeId = agent.mesh_node_id || "hub"; - - // Use the general node FS API to write .coworker.md - await fetchWithAuth(`/nodes/${nodeId}/fs/touch?X-User-ID=${userConfig?.id || 'agent_ui'}`, { - method: "POST", - body: { - path: ".coworker.md", - content: coworkerContent, - is_dir: false, - session_id: sid - } - }); - setModalConfig({ title: 'Success', message: 'Auditor Guidelines synced to node workspace.', type: 'success' }); - fetchData(); - } catch (err) { - setModalConfig({ title: 'Update Failed', message: err.message, type: 'error' }); - } finally { - setSavingGroundTruth(false); - } - }; - - const handleAddTrigger = async () => { - try { - setCreatingTrigger(true); - const payload = { - trigger_type: newTriggerType, - default_prompt: newDefaultPrompt - }; - if (newTriggerType === 'cron') payload.cron_expression = newCronValue; - if (newTriggerType === 'interval') payload.interval_seconds = parseInt(newIntervalValue) || 600; - - await createAgentTrigger(agentId, payload); - setNewDefaultPrompt(''); - setNewCronValue('0 * * * *'); - setNewIntervalValue(600); - fetchData(); - } catch (err) { - setModalConfig({ title: 'Trigger Failed', message: err.message, type: 'error' }); - } finally { - setCreatingTrigger(false); - } - }; - - const handleDeleteTrigger = async (triggerId) => { - try { - await deleteAgentTrigger(triggerId); - fetchData(); - } catch (err) { - setModalConfig({ title: 'Delete Failed', message: err.message, type: 'error' }); - } - }; - - const handleFireTrigger = async (triggerPrompt) => { - try { - await fetchWithAuth(`/agents/${agentId}/run`, { - method: 'POST', - body: { prompt: triggerPrompt } - }); - setModalConfig({ title: 'Success', message: 'Agent manual execution started successfully!', type: 'success' }); - fetchData(); - } catch (err) { - setModalConfig({ title: 'Execution Failed', message: err.message, type: 'error' }); - } - }; - - const handleFireWebhook = async (token, triggerPrompt) => { - try { - await fetchWithAuth(`/agents/${agentId}/webhook?token=${token}`, { - method: 'POST', - body: { prompt: triggerPrompt || "Manual test from UI" } - }); - setModalConfig({ title: 'Success', message: 'Webhook test trigger sent successfully!', type: 'success' }); - fetchData(); - } catch (err) { - setModalConfig({ title: 'Webhook Failed', message: err.message, type: 'error' }); - } - }; + const hookData = useAgentDrillDown(agentId); + const { + agent, chatHistory, loading, error, activeTab, setActiveTab, + editConfig, setEditConfig, saving, userConfig, tokenUsage, tokenError, + clearing, triggers, newTriggerType, setNewTriggerType, newCronValue, setNewCronValue, + newIntervalValue, setNewIntervalValue, newDefaultPrompt, setNewDefaultPrompt, + creatingTrigger, modalConfig, setModalConfig, nodes, allSkills, flippedCards, setFlippedCards, + feedbackContent, rubricContent, coworkerContent, setCoworkerContent, + historyLog, savingGroundTruth, selectedAuditId, setSelectedAuditId, + runningSeconds, lastTotalConsumption, currentAction, lastAction, lastActionDuration, + handleAction, handleClearHistory, handleSaveConfig, handleSaveGroundTruth, fetchData, + handleAddTrigger, handleDeleteTrigger, handleFireTrigger, handleFireWebhook, + handleResetMetrics, overrideText, setOverrideText, handleInjectOverride + } = hookData; if (loading && !agent) return (
@@ -470,1077 +36,171 @@ ); return ( - <>
- {/* Minimal Header */} -
-
- -
-

{agent?.id?.split('-')[0]}

-
- - {agent?.status === 'active' && } - - - Status: {agent?.status} -
-
- {agent && ( -
- -
- )} -
-
- Node: {nodes.find(n => n.id === agent?.mesh_node_id)?.name || agent?.mesh_node_id || 'unassigned'} - Jail Path: {agent?.current_workspace_jail || '/tmp'} - Synced Workspace: {agent?.session?.sync_workspace_id || agent?.session_id || 'not-bound'} -
-
+ - {/* Mobile View Toggle (Visible only on < lg screens) */} + {/* Mobile View Toggle */}
- - - - - + {['chat', 'evaluation', 'config', 'workspace', 'metrics'].map(tab => ( + + ))}
- {/* Main Content Area - Responsive Layout */} + {/* Main Content Area */}
- {/* Left Pane: Chat Tracker - Shown on Desktop or if Chat tab active on Mobile */} -
-
- -
- Live Thought Process -
- {agent?.session_id && ( -
+ + {/* Right Pane: Multi-Tab Dashboard */} +
+ {/* Desktop Tab Switcher */} +
+ {['evaluation', 'config', 'workspace', 'metrics'].map(tab => ( + - )} + ))}
- {agent?.session_id ? ( - - ) : ( -
- No session bounds established for this agent. -
- )} -
- - {/* Agent Status Indicator Label (M4 Observability) */} - {(agent?.status === 'active' || lastTotalConsumption !== null) && ( -
-
-
-
- {agent?.status === 'active' &&
} -
-
- Current Action: - - {agent?.status === 'active' - ? (agent.evaluation_status || 'Orchestrating Workflow...') - : 'Process Lifecycle Ended'} - -
-
- -
-
-
- Last Action: - - {lastAction?.display ? lastAction.display.split(':').pop().trim() : "Initialized"} - - {lastActionDuration !== null && ( - - {lastActionDuration}s - - )} -
-
-
- {agent?.status === 'active' ? ( -
-
- {runningSeconds}s -
- ) : ( -
- - {lastTotalConsumption}s tot. -
- )} -
-
- )} - - {/* Inject Prompt Override */} -
-
-
- setOverrideText(e.target.value)} - placeholder="Steer agent execution loop..." - className="w-full bg-white dark:bg-gray-950 border border-gray-300 dark:border-gray-700 text-sm rounded-xl py-3 pl-4 pr-12 focus:outline-none focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500 text-gray-900 dark:text-gray-100 transition-all font-mono" - /> - -
-
- setSkipEval(e.target.checked)} - className="rounded border-gray-300 dark:border-gray-700 text-indigo-600 focus:ring-indigo-500 w-3 h-3 cursor-pointer" - /> - -
-
-
-
- - {/* Right Pane: Multi-Tab Container - Hidden on mobile if Chat active */} -
- {/* Tab Header (Desktop Only, Mobile uses top buttons) */} -
- - - - -
- - {/* Tab Body */} -
- {activeTab === 'workspace' && ( - agent ? ( - - ) : ( -
- Initializing workspace bridge... -
- ) )} - + {activeTab === 'config' && ( -
- {/* Token Usage / Model Config */} - {tokenError && (tokenError.includes("not configured") || tokenError.includes("Model name")) ? ( -
- - - -
- Context Window โ€” Model Not Configured - - { (editConfig?.provider_name && editConfig.provider_name !== agent?.session?.provider_name && userConfig?.effective?.llm?.providers[editConfig.provider_name]?.model) - ? "Provider changed. Click 'Save Config' below to update usage tracking." - : "Select a provider with a configured model below โ†“ to track usage." - } - -
-
- ) : tokenError ? ( -
-
- Usage Tracking Error - {tokenError} -
-
- ) : ( -
-
- Session Context Window -
-
-
-
- {Math.round(tokenUsage.percentage)}% -
-
-
- )} + + )} -
-
- Agent Name - setEditConfig({...editConfig, name: e.target.value})} className="w-full bg-white dark:bg-gray-950 border border-gray-300 dark:border-gray-700 text-sm rounded-md py-2.5 px-3 focus:outline-none focus:ring-1 focus:ring-indigo-500 text-gray-900 dark:text-gray-100 shadow-sm" placeholder="e.g. Documentation Assistant" disabled={editConfig?.is_locked} /> -
-
- Active LLM Provider - -
-
-
- Target Mesh Node - -
-
- Max Iterations - setEditConfig({...editConfig, max_loop_iterations: e.target.value})} className="w-full bg-white dark:bg-gray-950 border border-gray-300 dark:border-gray-700 text-sm rounded-md py-2.5 px-3 focus:outline-none focus:ring-1 focus:ring-indigo-500 text-gray-900 dark:text-gray-100 shadow-sm" disabled={editConfig?.is_locked} /> -
-
-
- - {/* Skills and Lock Controls */} -
-
- Enabled Skills -
- - -
- System Core Skills -
- {allSkills.filter(s => s.is_system).map(skill => ( - - โœ“ {skill.name} - - ))} -
-
- - {editConfig?.restrict_skills && ( -
- User Context Skills -
- {allSkills.filter(s => !s.is_system).map(skill => ( - - ))} - {allSkills.filter(s => !s.is_system).length === 0 && ( - No user skills available - )} -
-
- )} -
-
-
- Memory Handling -
- - -
- - -
-
-
- - {/* Execution Triggers Box */} -
- Execution Triggers -
- - {/* List existing triggers */} - {triggers.length > 0 && ( -
- {triggers.map(t => ( -
-
- - {({'manual': '๐Ÿ–๏ธ MANUAL', 'cron': 'โฐ CRON', 'interval': '๐Ÿ”„ INTERVAL', 'webhook': '๐Ÿ”— WEBHOOK'})[t.trigger_type] || t.trigger_type} · {t.id.split('-')[0]} - - - {t.trigger_type === 'cron' && `Schedule: ${t.cron_expression} (${describeCron(t.cron_expression)})`} - {t.trigger_type === 'interval' && `Every ${t.interval_seconds >= 3600 ? Math.round(t.interval_seconds/3600) + 'h' : t.interval_seconds >= 60 ? Math.round(t.interval_seconds/60) + 'min' : t.interval_seconds + 's'} after completion`} - {t.trigger_type === 'webhook' && ( -
-
- URL: -
- - {`${window.location.host.includes('localhost') ? 'http://' : 'https://'}${window.location.host}/api/v1/agents/${agentId}/webhook?token=${t.webhook_secret}`} - - {/* Payload Tooltip */} -
-
- - Interface Documentation -
- -
-
- Execution Modes -
-
- ASYNC - Default · 202 Accepted · Fire & Forget -
-
- SYNC - Append &sync=true to block and get final response. -
-
-
- -
- Payload Structure -
-                                                                                            {JSON.stringify({ "prompt": "...custom prompt here..." }, null, 2)}
-                                                                                        
-
- -
- CURL (Sync Example) -
- curl -X POST "{`${window.location.host.includes('localhost') ? 'http://' : 'https://'}${window.location.host}/api/v1/agents/${agentId}/webhook?token=${t.webhook_secret}&sync=true`}" \
-       -H "Content-Type: application/json" \
-       -d '{JSON.stringify({ "prompt": "Hello!" })}' -
-
-
- -
- ๐Ÿ”’ External systems like GitHub can trigger this by sending their own event JSON. -
- - {/* Tooltip Arrow */} -
-
-
- -
- - ๐Ÿ’ก POST JSON to this URL. You can pass a custom prompt using {"{"}"prompt": "..."{"}"} in the body. - -
- )} - {t.trigger_type === 'manual' && `On-demand โ€” Ready for requests`} -
- {t.default_prompt && ( - Prompt: "{t.default_prompt.substring(0, 40)}{t.default_prompt.length > 40 ? '...' : ''}" - )} -
-
- - -
-
- ))} -
- )} - - {/* Add new trigger form */} -
-
- Type - -
- - {newTriggerType === 'cron' && ( -
- CRON Expr - setNewCronValue(e.target.value)} - className="w-full bg-gray-50 dark:bg-gray-900 border border-gray-300 dark:border-gray-700 text-sm rounded-md py-2 px-3 font-mono focus:outline-none" - /> -
- )} - - {newTriggerType === 'interval' && ( -
- Wait Seconds - setNewIntervalValue(e.target.value)} - className="w-full bg-gray-50 dark:bg-gray-900 border border-gray-300 dark:border-gray-700 text-sm rounded-md py-2 px-3 font-mono focus:outline-none" - /> -
- )} -
- -
- - {newTriggerType === 'cron' || newTriggerType === 'interval' ? 'Fixed Automation Prompt' : 'Default/Overridable Prompt'} - -
- setNewDefaultPrompt(e.target.value)} - placeholder="Trigger instruction..." - className="flex-1 bg-gray-50 dark:bg-gray-900 border border-gray-300 dark:border-gray-700 text-sm rounded-md py-2 px-3 focus:outline-none" - /> - -
-
-
-
- - {/* Co-Worker Loop Settings */} -
-
-
-
- -
-
-

Co-Worker Quality Gate

-

Enable autonomous evaluation loops for this agent

-
-
- -
- - {editConfig?.co_worker_quality_gate && ( -
-
-
- Rework Threshold - {editConfig.rework_threshold || 80}% -
- setEditConfig({...editConfig, rework_threshold: parseInt(e.target.value)})} - className="w-full h-1.5 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-indigo-500" - /> -
-
- Max Rework Cycles - setEditConfig({...editConfig, max_rework_attempts: parseInt(e.target.value) || 1})} - className="w-full bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 text-xs rounded-md py-2 px-3 font-mono focus:outline-none focus:border-indigo-500" - /> -
-
- )} -
- -
- Core System Instruction Prompt (Modifiable Live) - -

Changes to the system prompt will be immediately picked up by the agent loop on its next invocation turn.

-
-
- -
+ {activeTab === 'workspace' && ( +
+
)} - {activeTab === 'evaluation' && (() => { - const messagesWithAudit = chatHistory.filter(m => m.message_metadata?.evaluation && !m.isUser); - const selectedMessage = selectedAuditId ? chatHistory.find(m => m.id === selectedAuditId) : null; - - const displayFeedback = selectedMessage ? selectedMessage.message_metadata.evaluation.feedback : feedbackContent; - const displayRubric = selectedMessage ? selectedMessage.message_metadata.evaluation.rubric : rubricContent; - const displayHistory = selectedMessage ? selectedMessage.message_metadata.evaluation.history : historyLog; - - return ( -
- {/* Inspection Controls */} -
-
- Audit Mode -
- - -
-
- - {selectedAuditId && ( -
- Select Historical Message - -
- )} - -
- Terminal Inspector -
- {selectedAuditId ? `SNAPSHOT: MSID-${selectedAuditId}` : "LIVE: .CORTEX/"} -
-
-
- - {/* Auditor Guidelines Editor (Oversight Policy) */} -
-
-
- - - Auditor Guidelines - - Ground Truth Policy Layer -
-
- .coworker.md - -
-
-
- -
-
-
-
A
-
W
-
- - Oversight Policy: Shared between Auditor & Worker. - -
- - Autonomous Oversight ๐Ÿ›ฐ๏ธ - -
-
-
- -
- {/* Evaluation Strategy (Rubric) */} -
-
- - - Quality Rubric - - rubric.md -
-
- {displayRubric || No rubric configuration found for this agent instance.} -
-
- - {/* Live feedback loop (Feedback) */} -
-
- -
- {selectedAuditId ? "Audit Snapshot" : "Audit Stream"} -
- {selectedAuditId ? `MSID-${selectedAuditId}` : "feedback.md"} -
-
- {displayFeedback || Waiting for next evaluation cycle to populate feedback...} -
-
-
- - {/* Rework History Viewer */} -
-
- - - History Timeline - -
- - history.log -
-
-
-
- {(displayHistory && displayHistory.length > 0) ? [...displayHistory].reverse().map((entry, idx) => ( -
-
-
-
= (editConfig?.rework_threshold || 80) ? 'bg-emerald-500 shadow-[0_0_10px_rgba(16,185,129,0.6)]' : 'bg-amber-500 shadow-[0_0_10px_rgba(245,158,11,0.6)]') : 'bg-indigo-500/50'}`}>
- - {entry.type === 'attempt' ? `Attempt Round ${entry.round}` : entry.name || 'Event'} - {entry.duration && took {entry.duration}s} - -
-
- - {entry.timestamp ? new Date(entry.timestamp * (entry.timestamp < 2000000000 ? 1000 : 1)).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }) : 'Pending'} - - {entry.score !== undefined && ( -
= (editConfig?.rework_threshold || 80) ? 'bg-emerald-500/10 text-emerald-500 border border-emerald-500/20' : 'bg-amber-500/10 text-amber-500 border border-amber-500/20'}`}> - {entry.score}% PRECISION -
- )} -
-
- - {entry.type === 'attempt' ? ( -
-

- {entry.reason || entry.message} -

- - {entry.sub_events?.length > 0 && ( -
- {entry.sub_events.map((sub, sidx) => ( -
-
- {sub.name} - {sub.duration}โ‚› -
- ))} -
- )} -
- ) : ( -

- {entry.details} -

- )} -
- )) : ( -
- - No activity recorded -
- )} -
-
- {displayHistory && displayHistory.length > 0 && ( -
- Aggregate Time Cost - - {displayHistory.reduce((acc, entry) => acc + (entry.duration || 0), 0).toFixed(2)}s - -
- )} -
-
- ); - })()} {activeTab === 'metrics' && ( -
-
-

Execution Metrics

- -
- -
-
- Total Runs - {agent?.total_runs || 0} -
-
- Last Run - - {formatTimeLocal(agent?.last_heartbeat)} - -
-
- Success Rate - - {agent?.total_runs ? Math.round(((agent?.successful_runs || 0) / agent.total_runs) * 100) : 0}% - -
-
setFlippedCards(prev => ({...prev, runtime: !prev.runtime}))} - > -
-
- Total AI Run Time - {agent?.total_running_time_seconds ? agent.total_running_time_seconds.toLocaleString() + 's' : '0s'} -
-
- Avg Request Time - - {agent?.total_runs ? (agent.total_running_time_seconds / agent.total_runs).toFixed(1) : 0}s - -
-
-
- -
setFlippedCards(prev => ({...prev, tokens: !prev.tokens}))} - > -
-
- Total Tokens (In / Out) - - {agent?.total_input_tokens?.toLocaleString() || 0} - / - {agent?.total_output_tokens?.toLocaleString() || 0} - -
-
- Avg Tokens (In / Out) - - {agent?.total_runs ? Math.round(agent.total_input_tokens / agent.total_runs).toLocaleString() : 0} - / - {agent?.total_runs ? Math.round(agent.total_output_tokens / agent.total_runs).toLocaleString() : 0} - -
-
-
-
- -
-

Tool Usage breakdown

-
- - - - - - - - - - - {agent?.tool_call_counts && Object.keys(agent.tool_call_counts).length > 0 ? ( - Object.entries(agent.tool_call_counts).sort((a,b) => { - const bVal = typeof b[1] === 'object' ? b[1].calls : b[1]; - const aVal = typeof a[1] === 'object' ? a[1].calls : a[1]; - return bVal - aVal; - }).map(([tool, data]) => { - const calls = typeof data === 'object' ? data.calls : data; - const successes = typeof data === 'object' ? data.successes : data; - const failures = typeof data === 'object' ? data.failures : 0; - return ( - - - - - - - ); - }) - ) : ( - - - - )} - -
Tool NameCallsSuccessFailed
{tool}{calls}{successes}{failures > 0 ? failures : '-'}
No tool calls recorded yet
-
-
-
+ )}
-
- - {/* Modal Overlay Component */} + + {/* Modal Dialogs */} {modalConfig && ( -
-
-
-

{modalConfig.title}

-

{modalConfig.message}

-
- - {modalConfig.confirmAction && ( +
+
+
+ {modalConfig.type === 'error' ? ( + + ) : ( + + )} +
+

{modalConfig.title}

+

{modalConfig.message}

+
+ {modalConfig.confirmAction ? ( + <> + + + + ) : ( )}
@@ -1548,6 +208,5 @@
)}
- ); } diff --git a/frontend/src/features/agents/components/AgentHarnessPage.js b/frontend/src/features/agents/components/AgentHarnessPage.js index 7f3271e..71602d4 100644 --- a/frontend/src/features/agents/components/AgentHarnessPage.js +++ b/frontend/src/features/agents/components/AgentHarnessPage.js @@ -604,6 +604,20 @@
+ {isError && agent.last_error && ( +
+
+ + + + Diagnostic Error +
+

+ {agent.last_error} +

+
+ )} + {/* Triggers Summary */}
{triggers.length === 0 ? ( diff --git a/frontend/src/features/agents/components/drilldown/ChatTracker.js b/frontend/src/features/agents/components/drilldown/ChatTracker.js new file mode 100644 index 0000000..2885ad0 --- /dev/null +++ b/frontend/src/features/agents/components/drilldown/ChatTracker.js @@ -0,0 +1,112 @@ +import React from 'react'; +import ChatWindow from '../../../chat/components/ChatWindow'; + +const ChatTracker = ({ + agent, + chatHistory, + handleClearHistory, + handleAction, + clearing, + editConfig, + currentAction, + lastAction, + lastActionDuration, + runningSeconds +}) => { + return ( +
+
+ +
+ Live Thought Process +
+ {agent?.session_id && ( + + )} +
+ +
+ {/* Error Bar Overlay */} + {agent?.status === 'error_suspended' && agent?.last_error && ( +
+
+ + + +
+
+

Agent Lifecycle Suspended

+

{agent.last_error}

+
+ +
+
+
+ )} + + + + {/* Tracking HUD Overlay */} +
+ {currentAction && ( +
+
+
+
+ + {currentAction.display} + +
+
+
+
+ {runningSeconds}s +
+
+
+
+ )} +
+
+ + {/* Quick Summary Footer */} +
+
+
+ Total Loop Time + {runningSeconds}s spent in active session +
+
+
+ Memory Chunks + {chatHistory.length} messages in context +
+
+
+
+ {agent?.status || 'IDLE'} +
+
+
+
+ ); +}; + +export default ChatTracker; diff --git a/frontend/src/features/agents/components/drilldown/ConfigPanel.js b/frontend/src/features/agents/components/drilldown/ConfigPanel.js new file mode 100644 index 0000000..0dfa678 --- /dev/null +++ b/frontend/src/features/agents/components/drilldown/ConfigPanel.js @@ -0,0 +1,229 @@ +import React from 'react'; + +const ConfigPanel = ({ + editConfig, + setEditConfig, + saving, + handleSaveConfig, + nodes, + allSkills, + triggers, + handleAddTrigger, + handleDeleteTrigger, + handleFireTrigger, + handleFireWebhook, + newTriggerType, + setNewTriggerType, + newCronValue, + setNewCronValue, + newIntervalValue, + setNewIntervalValue, + newDefaultPrompt, + setNewDefaultPrompt, + creatingTrigger, + overrideText, + setOverrideText, + handleInjectOverride, + agentId +}) => { + return ( +
+ {/* Injection / Manual Run Section */} +
+
+
+ +
+
+

Manual Task Injection

+

Force the agent to process a specific prompt immediately.

+
+
+
+ setOverrideText(e.target.value)} + placeholder="e.g. 'List all files in /tmp' or 'Analyze current log'" + className="flex-1 bg-white/50 dark:bg-black/40 border border-indigo-500/20 rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500/30 transition-all font-mono" + /> + +
+
+ +
+ {/* Configuration Section */} +
+
+

Core Identity

+ +
+ +
+
+ + setEditConfig({...editConfig, name: e.target.value})} + className="w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500/20 transition-all shadow-sm" + /> +
+ +
+ + +
+
+ +
+ {/* Evaluation Strategy (Rubric) */} +
+
+ + + Quality Rubric + + rubric.md +
+
+ {displayRubric || No rubric configuration found for this agent instance.} +
+
+ + {/* Live feedback loop (Feedback) */} +
+
+ +
+ {selectedAuditId ? "Audit Snapshot" : "Audit Stream"} +
+ {selectedAuditId ? `MSID-${selectedAuditId}` : "feedback.md"} +
+
+ {displayFeedback || ( +
+
+ + {agent?.status === 'active' || agent?.status === 'starting' + ? "Consulting Co-Worker rubric and auditing results..." + : "Waiting for next evaluation cycle to populate feedback..."} + +
+ )} +
+
+
+ + {/* Rework History Viewer */} +
+
+ + + History Timeline + +
+ + history.log +
+
+
+
+ {(displayHistory && displayHistory.length > 0) ? [...displayHistory].reverse().map((entry, idx) => ( +
+
+
+
= (editConfig?.rework_threshold || 80) ? 'bg-emerald-500 shadow-[0_0_10px_rgba(16,185,129,0.6)]' : 'bg-amber-500 shadow-[0_0_10px_rgba(245,158,11,0.6)]') : 'bg-indigo-500/50'}`}>
+ + {entry.type === 'attempt' ? `Attempt Round ${entry.round}` : entry.name || 'Event'} + {entry.duration && took {entry.duration}s} + +
+
+ + {entry.timestamp ? new Date(entry.timestamp * (entry.timestamp < 2000000000 ? 1000 : 1)).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }) : 'Pending'} + + {entry.score !== undefined && ( +
= (editConfig?.rework_threshold || 80) ? 'bg-emerald-500/10 text-emerald-500 border border-emerald-500/20' : 'bg-amber-500/10 text-amber-500 border border-amber-500/20'}`}> + {entry.score}% PRECISION +
+ )} +
+
+ + {entry.type === 'attempt' ? ( +
+

+ {entry.reason || entry.message} +

+ + {entry.sub_events?.length > 0 && ( +
+ {entry.sub_events.map((sub, sidx) => ( +
+
+ {sub.name} + {sub.duration}โ‚› +
+ ))} +
+ )} +
+ ) : ( +

+ {entry.details} +

+ )} +
+ )) : ( +
+ + No activity recorded +
+ )} +
+
+ {displayHistory && displayHistory.length > 0 && ( +
+ Aggregate Time Cost + + {displayHistory.reduce((acc, entry) => acc + (entry.duration || 0), 0).toFixed(2)}s + +
+ )} +
+
+ ); +}; + +export default EvaluationPanel; diff --git a/frontend/src/features/agents/components/drilldown/MetricsPanel.js b/frontend/src/features/agents/components/drilldown/MetricsPanel.js new file mode 100644 index 0000000..4ae59ef --- /dev/null +++ b/frontend/src/features/agents/components/drilldown/MetricsPanel.js @@ -0,0 +1,124 @@ +import React from 'react'; + +const MetricsPanel = ({ agent, tokenUsage, tokenError, flippedCards, setFlippedCards, handleResetMetrics, clearing }) => { + return ( +
+
+

Execution Metrics

+ +
+ +
+ {/* Runtime / Health Card */} +
+
+
+ +
+ +
+ + {!flippedCards.runtime ? ( + <> +

Session Status

+
{agent?.status || 'idle'}
+
+ + Last seen {new Date(agent?.last_seen_at || Date.now()).toLocaleTimeString()} +
+ + ) : ( +
+
+ Mesh Node + {agent?.mesh_node_id || 'hub'} +
+
+ Last Seen + {new Date(agent?.last_seen_at || Date.now()).toLocaleString()} +
+
+ Jail Mount + {agent?.current_workspace_jail || '/tmp'} +
+
+ )} +
+ + {/* Precision / Score Card */} +
+
+
+ +
+
+

Quality Score

+
{agent?.latest_quality_score || 0}%
+
+
= 80 ? 'bg-emerald-500 shadow-[0_0_10px_rgba(16,185,129,0.5)]' : 'bg-amber-500'}`} style={{ width: `${agent?.latest_quality_score || 0}%` }}>
+
+
Verification results for last assistant message
+
+ + {/* Resource / Tokens Card */} +
+
+
+ +
+ +
+ + {tokenError ? ( +
Context state unavailable: {tokenError}
+ ) : !flippedCards.tokens ? ( + <> +

Context Pressure

+
{tokenUsage.percentage || 0}%
+
+
85 ? 'bg-rose-500 animate-pulse' : tokenUsage.percentage > 60 ? 'bg-amber-500' : 'bg-indigo-500 shadow-[0_0_10px_rgba(99,102,241,0.5)]'}`} style={{ width: `${tokenUsage.percentage || 0}%` }}>
+
+
+ {tokenUsage.token_count || 0} used + limit {tokenUsage.token_limit || 0} +
+ + ) : ( +
+
+ Total Consumption + {tokenUsage.token_count || 0} tokens +
+
+ Context Window + {tokenUsage.token_limit || 0} tokens +
+
+ Provider + {agent?.session?.provider_name || 'default'} +
+
+ )} +
+
+
+ ); +}; + +export default MetricsPanel; diff --git a/frontend/src/features/agents/hooks/useAgentDrillDown.js b/frontend/src/features/agents/hooks/useAgentDrillDown.js new file mode 100644 index 0000000..6f2111d --- /dev/null +++ b/frontend/src/features/agents/hooks/useAgentDrillDown.js @@ -0,0 +1,356 @@ +import { useState, useEffect, useCallback } from 'react'; +import { + getAgents, + getSessionMessages, + fetchWithAuth, + updateAgentConfig, + getUserConfig, + clearSessionHistory, + getSessionTokenStatus, + getAgentTriggers, + createAgentTrigger, + deleteAgentTrigger, + getUserAccessibleNodes, + getSkills, + resetAgentMetrics, + getAgentCortexFiles, + getAgentCortexFile +} from '../../../services/apiService'; + +export const useAgentDrillDown = (agentId) => { + const [agent, setAgent] = useState(null); + const [chatHistory, setChatHistory] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [overrideText, setOverrideText] = useState(""); + + // UI State + const [activeTab, setActiveTab] = useState('config'); + const [editConfig, setEditConfig] = useState(null); + const [saving, setSaving] = useState(false); + const [userConfig, setUserConfig] = useState(null); + const [tokenUsage, setTokenUsage] = useState({ token_count: 0, token_limit: 0, percentage: 0 }); + const [tokenError, setTokenError] = useState(null); + const [clearing, setClearing] = useState(false); + const [triggers, setTriggers] = useState([]); + const [newTriggerType, setNewTriggerType] = useState('cron'); + const [newCronValue, setNewCronValue] = useState('0 * * * *'); + const [newIntervalValue, setNewIntervalValue] = useState(600); + const [newDefaultPrompt, setNewDefaultPrompt] = useState(''); + const [creatingTrigger, setCreatingTrigger] = useState(false); + const [modalConfig, setModalConfig] = useState(null); + const [nodes, setNodes] = useState([]); + const [allSkills, setAllSkills] = useState([]); + const [flippedCards, setFlippedCards] = useState({ runtime: false, tokens: false }); + + // Evaluation Hub State + const [cortexFiles, setCortexFiles] = useState([]); + const [feedbackContent, setFeedbackContent] = useState(""); + const [rubricContent, setRubricContent] = useState(""); + const [coworkerContent, setCoworkerContent] = useState(""); + const [historyLog, setHistoryLog] = useState([]); + const [savingGroundTruth, setSavingGroundTruth] = useState(false); + const [selectedAuditId, setSelectedAuditId] = useState(null); + + // Monitoring & Timer States + const [runningSeconds, setRunningSeconds] = useState(0); + const [lastTotalConsumption, setLastTotalConsumption] = useState(null); + const [previousStatus, setPreviousStatus] = useState('idle'); + const [currentAction, setCurrentAction] = useState(null); + const [lastAction, setLastAction] = useState(null); + const [lastActionDuration, setLastActionDuration] = useState(null); + const [actionStartTime, setActionStartTime] = useState(0); + + const fetchData = useCallback(async () => { + try { + const allAgents = await getAgents(); + const found = allAgents.find(a => a.id === agentId); + if (!found) throw new Error("Agent not found"); + setAgent(found); + + setEditConfig(prev => prev || { + name: found.template?.name || "", + system_prompt: found.template?.system_prompt_content || found.template?.system_prompt_path || "", + max_loop_iterations: found.template?.max_loop_iterations || 20, + mesh_node_id: found.mesh_node_id || "", + provider_name: found.session?.provider_name || "", + restrict_skills: found.session?.restrict_skills || false, + allowed_skill_ids: found.session?.skills ? found.session.skills.map(s => s.id) : [], + is_locked: found.session?.is_locked || false, + auto_clear_history: found.session?.auto_clear_history || false, + co_worker_quality_gate: found.template?.co_worker_quality_gate || false, + rework_threshold: found.template?.rework_threshold || 80, + max_rework_attempts: found.template?.max_rework_attempts || 3 + }); + + if (found.session_id) { + const historyResp = await getSessionMessages(found.session_id); + const formatted = (historyResp.messages || []).map(m => ({ + text: m.content, + isUser: m.sender === 'user', + reasoning: m.reasoning_content, + status: null, + sender: m.sender, + timestamp: m.created_at, + id: m.id, + tool_calls: m.tool_calls, + message_metadata: m.message_metadata + })); + setChatHistory(formatted); + + try { + const usage = await getSessionTokenStatus(found.session_id); + if (usage.error) { + setTokenError(usage.error); + setTokenUsage({ token_count: 0, token_limit: 0, percentage: 0 }); + } else { + setTokenUsage(usage); + setTokenError(null); + } + } catch(e) { + setTokenError(e.message); + } + } + + try { + const tList = await getAgentTriggers(agentId); + setTriggers(tList); + } catch(e) {} + + const sid = found.session?.sync_workspace_id || found.session_id; + const nodeId = found.mesh_node_id || "hub"; + + if (sid) { + try { + const cFilesListing = await getAgentCortexFiles(agentId, nodeId, sid); + const files = cFilesListing.files || []; + setCortexFiles(files); + + const fileExists = (name) => files.some(f => f.name === name || f.path === `.cortex/${name}`); + + if (fileExists("feedback.md")) { + try { + const feedback = await getAgentCortexFile(agentId, nodeId, sid, "feedback.md"); + setFeedbackContent(feedback?.content || ""); + } catch (e) {} + } + + if (fileExists("rubric.md")) { + try { + const rubric = await getAgentCortexFile(agentId, nodeId, sid, "rubric.md"); + setRubricContent(rubric?.content || ""); + } catch (e) {} + } + + try { + const coworker = await getAgentCortexFile(agentId, nodeId, sid, ".coworker.md"); + setCoworkerContent(coworker?.content || ""); + } catch (e) {} + + if (fileExists("history.log")) { + try { + const logs = await getAgentCortexFile(agentId, nodeId, sid, "history.log"); + if (logs?.content) { + try { + const parsed = JSON.parse(logs.content); + setHistoryLog(Array.isArray(parsed) ? parsed : []); + } catch (e) { + setHistoryLog(logs.content.split('\n').filter(l => l.trim()).map(line => ({ message: line }))); + } + } + } catch (e) {} + } + } catch (e) {} + } + } catch (err) { + setError(err.message); + } finally { + setLoading(false); + } + }, [agentId]); + + useEffect(() => { + const loadConf = async () => { + try { + const [conf, nList, sList] = await Promise.all([ + getUserConfig(), + getUserAccessibleNodes(), + getSkills() + ]); + setUserConfig(conf); + setNodes(nList); + setAllSkills(sList); + } catch (e) {} + }; + loadConf(); + }, []); + + useEffect(() => { + fetchData(); + const interval = setInterval(fetchData, 2500); + return () => clearInterval(interval); + }, [agentId, fetchData]); + + useEffect(() => { + let timer = null; + const isRunning = agent?.status === 'active' || agent?.status === 'starting'; + + if (isRunning) { + timer = setInterval(() => { + setRunningSeconds(s => s + 1); + }, 1000); + } else if (previousStatus === 'active' || previousStatus === 'starting') { + setLastTotalConsumption(runningSeconds); + } + + setPreviousStatus(agent?.status || 'idle'); + return () => { if (timer) clearInterval(timer); }; + }, [agent?.status, previousStatus, runningSeconds]); + + useEffect(() => { + const isRunning = agent?.status === 'active' || agent?.status === 'starting'; + if (!isRunning) return; + + if (previousStatus !== 'active' && previousStatus !== 'starting') { + setRunningSeconds(0); + setLastTotalConsumption(null); + setCurrentAction(null); + setLastAction(null); + setLastActionDuration(null); + setActionStartTime(0); + } + + const rawStatus = agent?.evaluation_status || 'Orchestrating task payload...'; + + if (rawStatus !== (currentAction?.raw || '')) { + const lowStatus = rawStatus.toLowerCase(); + const hasPrefix = lowStatus.includes('agent:') || lowStatus.includes('audit:') || lowStatus.includes('co-worker:'); + + let cleanStatus = rawStatus; + if (!hasPrefix) { + cleanStatus = (lowStatus.includes('audit') || lowStatus.includes('worker') || lowStatus.includes('evaluat')) + ? `๐Ÿ›ก๏ธ Co-Worker Audit: ${rawStatus}` + : `๐Ÿค– Main Agent: ${rawStatus}`; + } + + if (currentAction) { + setLastAction(currentAction); + setLastActionDuration(runningSeconds - actionStartTime); + } + + setCurrentAction({ display: cleanStatus, raw: rawStatus }); + setActionStartTime(runningSeconds); + } + }, [agent?.status, agent?.evaluation_status, previousStatus, currentAction, runningSeconds, actionStartTime]); + + const handleAction = async (targetStatus) => { + try { + await fetchWithAuth(`/agents/${agentId}/status`, { + method: 'PATCH', + body: { status: targetStatus } + }); + fetchData(); + } catch (err) { + setModalConfig({ title: 'Status Change Failed', message: err.message, type: 'error' }); + } + }; + + const handleClearHistory = () => { + if (!agent?.session_id) return; + setModalConfig({ + title: 'Confirm Memory Wipe', + message: "Are you sure you want to clear the agent's memory? This cannot be undone.", + type: 'error', + confirmText: 'Clear Memory', + confirmAction: async () => { + try { + setClearing(true); + if (agent?.session?.is_locked && editConfig?.is_locked === false) { + await updateAgentConfig(agent.id, { + is_locked: false, + mesh_node_id: agent.mesh_node_id || "hub" + }); + } + await clearSessionHistory(agent.session_id); + setChatHistory([]); + setSelectedAuditId(null); + fetchData(); + } catch (err) { + setModalConfig({ title: 'Clear Failed', message: err.message, type: 'error' }); + } finally { + setClearing(false); + } + } + }); + }; + + const handleSaveConfig = async () => { + try { + setSaving(true); + const payload = { + name: editConfig.name, + system_prompt: editConfig.system_prompt, + max_loop_iterations: parseInt(editConfig.max_loop_iterations, 10) || 20, + mesh_node_id: editConfig.mesh_node_id, + provider_name: editConfig.provider_name, + restrict_skills: editConfig.restrict_skills, + allowed_skill_ids: editConfig.allowed_skill_ids, + is_locked: editConfig.is_locked, + auto_clear_history: editConfig.auto_clear_history, + co_worker_quality_gate: editConfig.co_worker_quality_gate, + rework_threshold: parseInt(editConfig.rework_threshold, 10), + max_rework_attempts: parseInt(editConfig.max_rework_attempts, 10) + }; + + try { + await fetchWithAuth(`/agents/${agentId}/status`, { method: "PATCH", body: { status: "idle" } }); + } catch (e) {} + + await updateAgentConfig(agentId, payload); + fetchData(); + setModalConfig({ title: 'Success', message: 'Configuration Saved Successfully!', type: 'success' }); + } catch (err) { + setModalConfig({ title: 'Save Failed', message: err.message, type: 'error' }); + } finally { + setSaving(false); + } + }; + + const handleSaveGroundTruth = async () => { + try { + setSavingGroundTruth(true); + const sid = agent.session?.sync_workspace_id || agent.session_id; + const nodeId = agent.mesh_node_id || "hub"; + + await fetchWithAuth(`/nodes/${nodeId}/fs/touch?X-User-ID=${userConfig?.id || 'agent_ui'}`, { + method: "POST", + body: { + path: ".coworker.md", + content: coworkerContent, + is_dir: false, + session_id: sid + } + }); + setModalConfig({ title: 'Success', message: 'Auditor Guidelines synced to node workspace.', type: 'success' }); + fetchData(); + } catch (err) { + setModalConfig({ title: 'Update Failed', message: err.message, type: 'error' }); + } finally { + setSavingGroundTruth(false); + } + }; + + return { + agent, chatHistory, loading, error, activeTab, setActiveTab, + editConfig, setEditConfig, saving, userConfig, tokenUsage, tokenError, + clearing, triggers, newTriggerType, setNewTriggerType, newCronValue, setNewCronValue, + newIntervalValue, setNewIntervalValue, newDefaultPrompt, setNewDefaultPrompt, + creatingTrigger, modalConfig, setModalConfig, nodes, allSkills, flippedCards, setFlippedCards, + cortexFiles, feedbackContent, rubricContent, coworkerContent, setCoworkerContent, + historyLog, savingGroundTruth, selectedAuditId, setSelectedAuditId, + runningSeconds, lastTotalConsumption, currentAction, lastAction, lastActionDuration, + handleAction, handleClearHistory, handleSaveConfig, handleSaveGroundTruth, fetchData, + handleAddTrigger, handleDeleteTrigger, handleFireTrigger, handleFireWebhook, + overrideText, setOverrideText + }; +}; diff --git a/scripts/run_web.sh b/scripts/run_web.sh index 75d967a..65a518a 100644 --- a/scripts/run_web.sh +++ b/scripts/run_web.sh @@ -17,8 +17,8 @@ # Resolve script directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -AI_HUB_DIR="$(realpath "$SCRIPT_DIR/ai-hub")" -CLIENT_DIR="$(realpath "$SCRIPT_DIR/frontend")" +AI_HUB_DIR="$(realpath "$SCRIPT_DIR/../ai-hub")" +CLIENT_DIR="$(realpath "$SCRIPT_DIR/../frontend")" AI_HUB_HOST="0.0.0.0" AI_HUB_PORT="8001" diff --git a/web_redeploy.log b/web_redeploy.log new file mode 100644 index 0000000..4903791 --- /dev/null +++ b/web_redeploy.log @@ -0,0 +1,434 @@ +--- Cleaning up existing processes --- +Defaulting to user installation because normal site-packages is not writeable +Obtaining file:///app/ai-hub + Installing build dependencies: started + Installing build dependencies: finished with status 'done' + Checking if build backend supports build_editable: started + Checking if build backend supports build_editable: finished with status 'done' + Getting requirements to build editable: started + Getting requirements to build editable: finished with status 'done' + Preparing editable metadata (pyproject.toml): started + Preparing editable metadata (pyproject.toml): finished with status 'done' +Collecting fastapi (from ai-hub==0.1.0) + Downloading fastapi-0.135.3-py3-none-any.whl.metadata (28 kB) +Collecting uvicorn[standard] (from ai-hub==0.1.0) + Downloading uvicorn-0.44.0-py3-none-any.whl.metadata (6.7 kB) +Collecting google-generativeai (from ai-hub==0.1.0) + Downloading google_generativeai-0.8.6-py3-none-any.whl.metadata (3.9 kB) +Collecting python-dotenv (from ai-hub==0.1.0) + Downloading python_dotenv-1.2.2-py3-none-any.whl.metadata (27 kB) +Collecting openai (from ai-hub==0.1.0) + Downloading openai-2.31.0-py3-none-any.whl.metadata (31 kB) +Collecting pytest (from ai-hub==0.1.0) + Downloading pytest-9.0.3-py3-none-any.whl.metadata (7.6 kB) +Collecting requests (from ai-hub==0.1.0) + Downloading requests-2.33.1-py3-none-any.whl.metadata (4.8 kB) +Collecting anyio (from ai-hub==0.1.0) + Downloading anyio-4.13.0-py3-none-any.whl.metadata (4.5 kB) +Collecting sqlalchemy (from ai-hub==0.1.0) + Downloading sqlalchemy-2.0.49-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (9.5 kB) +Collecting psycopg2-binary (from ai-hub==0.1.0) + Downloading psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl.metadata (4.9 kB) +Collecting pytest-asyncio (from ai-hub==0.1.0) + Downloading pytest_asyncio-1.3.0-py3-none-any.whl.metadata (4.1 kB) +Collecting pytest-tornasync (from ai-hub==0.1.0) + Downloading pytest_tornasync-0.6.0.post2-py3-none-any.whl.metadata (4.8 kB) +Collecting pytest-trio (from ai-hub==0.1.0) + Downloading pytest_trio-0.8.0-py3-none-any.whl.metadata (2.9 kB) +Collecting pytest-mock (from ai-hub==0.1.0) + Downloading pytest_mock-3.15.1-py3-none-any.whl.metadata (3.9 kB) +Collecting numpy (from ai-hub==0.1.0) + Downloading numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.metadata (6.6 kB) +Collecting faiss-cpu (from ai-hub==0.1.0) + Downloading faiss_cpu-1.13.2-cp310-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.metadata (7.6 kB) +Collecting aioresponses (from ai-hub==0.1.0) + Downloading aioresponses-0.7.8-py2.py3-none-any.whl.metadata (10 kB) +Collecting python-multipart (from ai-hub==0.1.0) + Downloading python_multipart-0.0.24-py3-none-any.whl.metadata (1.8 kB) +Collecting PyJWT (from ai-hub==0.1.0) + Downloading pyjwt-2.12.1-py3-none-any.whl.metadata (4.1 kB) +Collecting tenacity (from ai-hub==0.1.0) + Downloading tenacity-9.1.4-py3-none-any.whl.metadata (1.2 kB) +Collecting litellm (from ai-hub==0.1.0) + Downloading litellm-1.83.4-py3-none-any.whl.metadata (30 kB) +Collecting tiktoken (from ai-hub==0.1.0) + Downloading tiktoken-0.12.0-cp311-cp311-manylinux_2_28_aarch64.whl.metadata (6.7 kB) +Collecting grpcio (from ai-hub==0.1.0) + Downloading grpcio-1.80.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl.metadata (3.8 kB) +Collecting grpcio-tools (from ai-hub==0.1.0) + Downloading grpcio_tools-1.80.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl.metadata (5.3 kB) +Collecting grpcio-reflection (from ai-hub==0.1.0) + Downloading grpcio_reflection-1.80.0-py3-none-any.whl.metadata (1.2 kB) +Collecting croniter (from ai-hub==0.1.0) + Downloading croniter-6.2.2-py3-none-any.whl.metadata (22 kB) +Requirement already satisfied: packaging>=22.0 in /usr/local/lib/python3.11/site-packages (from aioresponses->ai-hub==0.1.0) (26.0) +Collecting aiohttp<4.0.0,>=3.3.0 (from aioresponses->ai-hub==0.1.0) + Downloading aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (8.1 kB) +Collecting aiohappyeyeballs>=2.5.0 (from aiohttp<4.0.0,>=3.3.0->aioresponses->ai-hub==0.1.0) + Downloading aiohappyeyeballs-2.6.1-py3-none-any.whl.metadata (5.9 kB) +Collecting aiosignal>=1.4.0 (from aiohttp<4.0.0,>=3.3.0->aioresponses->ai-hub==0.1.0) + Downloading aiosignal-1.4.0-py3-none-any.whl.metadata (3.7 kB) +Collecting attrs>=17.3.0 (from aiohttp<4.0.0,>=3.3.0->aioresponses->ai-hub==0.1.0) + Downloading attrs-26.1.0-py3-none-any.whl.metadata (8.8 kB) +Collecting frozenlist>=1.1.1 (from aiohttp<4.0.0,>=3.3.0->aioresponses->ai-hub==0.1.0) + Downloading frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (20 kB) +Collecting multidict<7.0,>=4.5 (from aiohttp<4.0.0,>=3.3.0->aioresponses->ai-hub==0.1.0) + Downloading multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (5.3 kB) +Collecting propcache>=0.2.0 (from aiohttp<4.0.0,>=3.3.0->aioresponses->ai-hub==0.1.0) + Downloading propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (13 kB) +Collecting yarl<2.0,>=1.17.0 (from aiohttp<4.0.0,>=3.3.0->aioresponses->ai-hub==0.1.0) + Downloading yarl-1.23.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (79 kB) +Collecting idna>=2.0 (from yarl<2.0,>=1.17.0->aiohttp<4.0.0,>=3.3.0->aioresponses->ai-hub==0.1.0) + Downloading idna-3.11-py3-none-any.whl.metadata (8.4 kB) +Requirement already satisfied: typing-extensions>=4.2 in /usr/local/lib/python3.11/site-packages (from aiosignal>=1.4.0->aiohttp<4.0.0,>=3.3.0->aioresponses->ai-hub==0.1.0) (4.15.0) +Collecting python-dateutil (from croniter->ai-hub==0.1.0) + Downloading python_dateutil-2.9.0.post0-py2.py3-none-any.whl.metadata (8.4 kB) +Collecting starlette>=0.46.0 (from fastapi->ai-hub==0.1.0) + Downloading starlette-1.0.0-py3-none-any.whl.metadata (6.3 kB) +Collecting pydantic>=2.9.0 (from fastapi->ai-hub==0.1.0) + Downloading pydantic-2.12.5-py3-none-any.whl.metadata (90 kB) +Collecting typing-inspection>=0.4.2 (from fastapi->ai-hub==0.1.0) + Downloading typing_inspection-0.4.2-py3-none-any.whl.metadata (2.6 kB) +Collecting annotated-doc>=0.0.2 (from fastapi->ai-hub==0.1.0) + Downloading annotated_doc-0.0.4-py3-none-any.whl.metadata (6.6 kB) +Collecting annotated-types>=0.6.0 (from pydantic>=2.9.0->fastapi->ai-hub==0.1.0) + Downloading annotated_types-0.7.0-py3-none-any.whl.metadata (15 kB) +Collecting pydantic-core==2.41.5 (from pydantic>=2.9.0->fastapi->ai-hub==0.1.0) + Downloading pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (7.3 kB) +Collecting google-ai-generativelanguage==0.6.15 (from google-generativeai->ai-hub==0.1.0) + Downloading google_ai_generativelanguage-0.6.15-py3-none-any.whl.metadata (5.7 kB) +Collecting google-api-core (from google-generativeai->ai-hub==0.1.0) + Downloading google_api_core-2.30.2-py3-none-any.whl.metadata (3.1 kB) +Collecting google-api-python-client (from google-generativeai->ai-hub==0.1.0) + Downloading google_api_python_client-2.194.0-py3-none-any.whl.metadata (7.0 kB) +Collecting google-auth>=2.15.0 (from google-generativeai->ai-hub==0.1.0) + Downloading google_auth-2.49.1-py3-none-any.whl.metadata (6.2 kB) +Collecting protobuf (from google-generativeai->ai-hub==0.1.0) + Downloading protobuf-7.34.1-cp310-abi3-manylinux2014_aarch64.whl.metadata (595 bytes) +Collecting tqdm (from google-generativeai->ai-hub==0.1.0) + Downloading tqdm-4.67.3-py3-none-any.whl.metadata (57 kB) +Collecting proto-plus<2.0.0dev,>=1.22.3 (from google-ai-generativelanguage==0.6.15->google-generativeai->ai-hub==0.1.0) + Downloading proto_plus-1.27.2-py3-none-any.whl.metadata (2.2 kB) +Collecting protobuf (from google-generativeai->ai-hub==0.1.0) + Downloading protobuf-5.29.6-cp38-abi3-manylinux2014_aarch64.whl.metadata (592 bytes) +Collecting googleapis-common-protos<2.0.0,>=1.63.2 (from google-api-core->google-generativeai->ai-hub==0.1.0) + Downloading googleapis_common_protos-1.74.0-py3-none-any.whl.metadata (9.2 kB) +Collecting grpcio-status<2.0.0,>=1.33.2 (from google-api-core[grpc]!=2.0.*,!=2.1.*,!=2.10.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,<3.0.0dev,>=1.34.1->google-ai-generativelanguage==0.6.15->google-generativeai->ai-hub==0.1.0) + Downloading grpcio_status-1.80.0-py3-none-any.whl.metadata (1.3 kB) +Collecting pyasn1-modules>=0.2.1 (from google-auth>=2.15.0->google-generativeai->ai-hub==0.1.0) + Downloading pyasn1_modules-0.4.2-py3-none-any.whl.metadata (3.5 kB) +Collecting cryptography>=38.0.3 (from google-auth>=2.15.0->google-generativeai->ai-hub==0.1.0) + Downloading cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl.metadata (5.7 kB) +INFO: pip is looking at multiple versions of grpcio-status to determine which version is compatible with other requirements. This could take a while. +Collecting grpcio-status<2.0.0,>=1.33.2 (from google-api-core[grpc]!=2.0.*,!=2.1.*,!=2.10.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,<3.0.0dev,>=1.34.1->google-ai-generativelanguage==0.6.15->google-generativeai->ai-hub==0.1.0) + Downloading grpcio_status-1.78.0-py3-none-any.whl.metadata (1.3 kB) + Downloading grpcio_status-1.76.0-py3-none-any.whl.metadata (1.1 kB) + Downloading grpcio_status-1.75.1-py3-none-any.whl.metadata (1.1 kB) + Downloading grpcio_status-1.75.0-py3-none-any.whl.metadata (1.1 kB) + Downloading grpcio_status-1.74.0-py3-none-any.whl.metadata (1.1 kB) + Downloading grpcio_status-1.73.1-py3-none-any.whl.metadata (1.1 kB) + Downloading grpcio_status-1.73.0-py3-none-any.whl.metadata (1.1 kB) +INFO: pip is still looking at multiple versions of grpcio-status to determine which version is compatible with other requirements. This could take a while. + Downloading grpcio_status-1.72.2-py3-none-any.whl.metadata (1.1 kB) + Downloading grpcio_status-1.72.1-py3-none-any.whl.metadata (1.1 kB) + Downloading grpcio_status-1.71.2-py3-none-any.whl.metadata (1.1 kB) +Collecting charset_normalizer<4,>=2 (from requests->ai-hub==0.1.0) + Downloading charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (40 kB) +Collecting urllib3<3,>=1.26 (from requests->ai-hub==0.1.0) + Downloading urllib3-2.6.3-py3-none-any.whl.metadata (6.9 kB) +Collecting certifi>=2023.5.7 (from requests->ai-hub==0.1.0) + Downloading certifi-2026.2.25-py3-none-any.whl.metadata (2.5 kB) +Collecting cffi>=2.0.0 (from cryptography>=38.0.3->google-auth>=2.15.0->google-generativeai->ai-hub==0.1.0) + Downloading cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl.metadata (2.6 kB) +Collecting pycparser (from cffi>=2.0.0->cryptography>=38.0.3->google-auth>=2.15.0->google-generativeai->ai-hub==0.1.0) + Downloading pycparser-3.0-py3-none-any.whl.metadata (8.2 kB) +Collecting pyasn1<0.7.0,>=0.6.1 (from pyasn1-modules>=0.2.1->google-auth>=2.15.0->google-generativeai->ai-hub==0.1.0) + Downloading pyasn1-0.6.3-py3-none-any.whl.metadata (8.4 kB) +Collecting httplib2<1.0.0,>=0.19.0 (from google-api-python-client->google-generativeai->ai-hub==0.1.0) + Downloading httplib2-0.31.2-py3-none-any.whl.metadata (2.2 kB) +Collecting google-auth-httplib2<1.0.0,>=0.2.0 (from google-api-python-client->google-generativeai->ai-hub==0.1.0) + Downloading google_auth_httplib2-0.3.1-py3-none-any.whl.metadata (3.0 kB) +Collecting uritemplate<5,>=3.0.1 (from google-api-python-client->google-generativeai->ai-hub==0.1.0) + Downloading uritemplate-4.2.0-py3-none-any.whl.metadata (2.6 kB) +Collecting pyparsing<4,>=3.1 (from httplib2<1.0.0,>=0.19.0->google-api-python-client->google-generativeai->ai-hub==0.1.0) + Downloading pyparsing-3.3.2-py3-none-any.whl.metadata (5.8 kB) +INFO: pip is looking at multiple versions of grpcio-reflection to determine which version is compatible with other requirements. This could take a while. +Collecting grpcio-reflection (from ai-hub==0.1.0) + Downloading grpcio_reflection-1.78.0-py3-none-any.whl.metadata (1.2 kB) + Downloading grpcio_reflection-1.76.0-py3-none-any.whl.metadata (1.1 kB) + Downloading grpcio_reflection-1.75.1-py3-none-any.whl.metadata (1.1 kB) + Downloading grpcio_reflection-1.75.0-py3-none-any.whl.metadata (1.0 kB) + Downloading grpcio_reflection-1.74.0-py3-none-any.whl.metadata (1.0 kB) + Downloading grpcio_reflection-1.73.1-py3-none-any.whl.metadata (1.0 kB) + Downloading grpcio_reflection-1.73.0-py3-none-any.whl.metadata (1.0 kB) +INFO: pip is still looking at multiple versions of grpcio-reflection to determine which version is compatible with other requirements. This could take a while. + Downloading grpcio_reflection-1.72.2-py3-none-any.whl.metadata (1.0 kB) + Downloading grpcio_reflection-1.72.1-py3-none-any.whl.metadata (1.0 kB) + Downloading grpcio_reflection-1.71.2-py3-none-any.whl.metadata (1.0 kB) +INFO: pip is looking at multiple versions of grpcio-tools to determine which version is compatible with other requirements. This could take a while. +Collecting grpcio-tools (from ai-hub==0.1.0) + Downloading grpcio_tools-1.78.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl.metadata (5.3 kB) + Downloading grpcio_tools-1.76.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl.metadata (5.3 kB) + Downloading grpcio_tools-1.75.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl.metadata (5.3 kB) + Downloading grpcio_tools-1.75.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl.metadata (5.3 kB) + Downloading grpcio_tools-1.74.0-cp311-cp311-manylinux_2_17_aarch64.whl.metadata (5.3 kB) + Downloading grpcio_tools-1.73.1-cp311-cp311-manylinux_2_17_aarch64.whl.metadata (5.3 kB) + Downloading grpcio_tools-1.73.0-cp311-cp311-manylinux_2_17_aarch64.whl.metadata (5.3 kB) +INFO: pip is still looking at multiple versions of grpcio-tools to determine which version is compatible with other requirements. This could take a while. + Downloading grpcio_tools-1.72.2-cp311-cp311-manylinux_2_17_aarch64.whl.metadata (5.3 kB) + Downloading grpcio_tools-1.72.1-cp311-cp311-manylinux_2_17_aarch64.whl.metadata (5.3 kB) + Downloading grpcio_tools-1.71.2-cp311-cp311-manylinux_2_17_aarch64.whl.metadata (5.3 kB) +Requirement already satisfied: setuptools in /usr/local/lib/python3.11/site-packages (from grpcio-tools->ai-hub==0.1.0) (78.1.1) +Collecting click==8.1.8 (from litellm->ai-hub==0.1.0) + Downloading click-8.1.8-py3-none-any.whl.metadata (2.3 kB) +Collecting fastuuid==0.14.0 (from litellm->ai-hub==0.1.0) + Downloading fastuuid-0.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (1.1 kB) +Collecting httpx==0.28.1 (from litellm->ai-hub==0.1.0) + Downloading httpx-0.28.1-py3-none-any.whl.metadata (7.1 kB) +Collecting importlib-metadata==8.5.0 (from litellm->ai-hub==0.1.0) + Downloading importlib_metadata-8.5.0-py3-none-any.whl.metadata (4.8 kB) +Collecting jinja2==3.1.6 (from litellm->ai-hub==0.1.0) + Downloading jinja2-3.1.6-py3-none-any.whl.metadata (2.9 kB) +Collecting jsonschema==4.23.0 (from litellm->ai-hub==0.1.0) + Downloading jsonschema-4.23.0-py3-none-any.whl.metadata (7.9 kB) +Collecting openai (from ai-hub==0.1.0) + Downloading openai-2.30.0-py3-none-any.whl.metadata (29 kB) +Collecting python-dotenv (from ai-hub==0.1.0) + Downloading python_dotenv-1.0.1-py3-none-any.whl.metadata (23 kB) +Collecting tokenizers==0.22.2 (from litellm->ai-hub==0.1.0) + Downloading tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (7.3 kB) +Collecting httpcore==1.* (from httpx==0.28.1->litellm->ai-hub==0.1.0) + Downloading httpcore-1.0.9-py3-none-any.whl.metadata (21 kB) +Collecting zipp>=3.20 (from importlib-metadata==8.5.0->litellm->ai-hub==0.1.0) + Downloading zipp-3.23.0-py3-none-any.whl.metadata (3.6 kB) +Collecting MarkupSafe>=2.0 (from jinja2==3.1.6->litellm->ai-hub==0.1.0) + Downloading markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (2.7 kB) +Collecting jsonschema-specifications>=2023.03.6 (from jsonschema==4.23.0->litellm->ai-hub==0.1.0) + Downloading jsonschema_specifications-2025.9.1-py3-none-any.whl.metadata (2.9 kB) +Collecting referencing>=0.28.4 (from jsonschema==4.23.0->litellm->ai-hub==0.1.0) + Downloading referencing-0.37.0-py3-none-any.whl.metadata (2.8 kB) +Collecting rpds-py>=0.7.1 (from jsonschema==4.23.0->litellm->ai-hub==0.1.0) + Downloading rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (4.1 kB) +Collecting distro<2,>=1.7.0 (from openai->ai-hub==0.1.0) + Downloading distro-1.9.0-py3-none-any.whl.metadata (6.8 kB) +Collecting jiter<1,>=0.10.0 (from openai->ai-hub==0.1.0) + Downloading jiter-0.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (5.2 kB) +Collecting sniffio (from openai->ai-hub==0.1.0) + Downloading sniffio-1.3.1-py3-none-any.whl.metadata (3.9 kB) +Collecting regex>=2022.1.18 (from tiktoken->ai-hub==0.1.0) + Downloading regex-2026.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (40 kB) +Collecting huggingface-hub<2.0,>=0.16.4 (from tokenizers==0.22.2->litellm->ai-hub==0.1.0) + Downloading huggingface_hub-1.9.2-py3-none-any.whl.metadata (14 kB) +Collecting h11>=0.16 (from httpcore==1.*->httpx==0.28.1->litellm->ai-hub==0.1.0) + Downloading h11-0.16.0-py3-none-any.whl.metadata (8.3 kB) +Collecting filelock>=3.10.0 (from huggingface-hub<2.0,>=0.16.4->tokenizers==0.22.2->litellm->ai-hub==0.1.0) + Downloading filelock-3.25.2-py3-none-any.whl.metadata (2.0 kB) +Collecting fsspec>=2023.5.0 (from huggingface-hub<2.0,>=0.16.4->tokenizers==0.22.2->litellm->ai-hub==0.1.0) + Downloading fsspec-2026.3.0-py3-none-any.whl.metadata (10 kB) +Collecting hf-xet<2.0.0,>=1.4.3 (from huggingface-hub<2.0,>=0.16.4->tokenizers==0.22.2->litellm->ai-hub==0.1.0) + Downloading hf_xet-1.4.3-cp37-abi3-manylinux_2_28_aarch64.whl.metadata (4.9 kB) +Collecting pyyaml>=5.1 (from huggingface-hub<2.0,>=0.16.4->tokenizers==0.22.2->litellm->ai-hub==0.1.0) + Downloading pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (2.4 kB) +Collecting typer (from huggingface-hub<2.0,>=0.16.4->tokenizers==0.22.2->litellm->ai-hub==0.1.0) + Downloading typer-0.24.1-py3-none-any.whl.metadata (16 kB) +Collecting iniconfig>=1.0.1 (from pytest->ai-hub==0.1.0) + Downloading iniconfig-2.3.0-py3-none-any.whl.metadata (2.5 kB) +Collecting pluggy<2,>=1.5 (from pytest->ai-hub==0.1.0) + Downloading pluggy-1.6.0-py3-none-any.whl.metadata (4.8 kB) +Collecting pygments>=2.7.2 (from pytest->ai-hub==0.1.0) + Downloading pygments-2.20.0-py3-none-any.whl.metadata (2.5 kB) +Collecting tornado>=5.0 (from pytest-tornasync->ai-hub==0.1.0) + Downloading tornado-6.5.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (2.8 kB) +Collecting trio>=0.22.0 (from pytest-trio->ai-hub==0.1.0) + Downloading trio-0.33.0-py3-none-any.whl.metadata (8.5 kB) +Collecting outcome>=1.1.0 (from pytest-trio->ai-hub==0.1.0) + Downloading outcome-1.3.0.post0-py2.py3-none-any.whl.metadata (2.6 kB) +Collecting sortedcontainers (from trio>=0.22.0->pytest-trio->ai-hub==0.1.0) + Downloading sortedcontainers-2.4.0-py2.py3-none-any.whl.metadata (10 kB) +Collecting six>=1.5 (from python-dateutil->croniter->ai-hub==0.1.0) + Downloading six-1.17.0-py2.py3-none-any.whl.metadata (1.7 kB) +Collecting greenlet>=1 (from sqlalchemy->ai-hub==0.1.0) + Downloading greenlet-3.4.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl.metadata (3.7 kB) +INFO: pip is looking at multiple versions of typer to determine which version is compatible with other requirements. This could take a while. +Collecting typer (from huggingface-hub<2.0,>=0.16.4->tokenizers==0.22.2->litellm->ai-hub==0.1.0) + Downloading typer-0.24.0-py3-none-any.whl.metadata (16 kB) + Downloading typer-0.23.2-py3-none-any.whl.metadata (16 kB) + Downloading typer-0.23.1-py3-none-any.whl.metadata (16 kB) +Collecting shellingham>=1.3.0 (from typer->huggingface-hub<2.0,>=0.16.4->tokenizers==0.22.2->litellm->ai-hub==0.1.0) + Downloading shellingham-1.5.4-py2.py3-none-any.whl.metadata (3.5 kB) +Collecting rich>=10.11.0 (from typer->huggingface-hub<2.0,>=0.16.4->tokenizers==0.22.2->litellm->ai-hub==0.1.0) + Downloading rich-14.3.3-py3-none-any.whl.metadata (18 kB) +Collecting markdown-it-py>=2.2.0 (from rich>=10.11.0->typer->huggingface-hub<2.0,>=0.16.4->tokenizers==0.22.2->litellm->ai-hub==0.1.0) + Downloading markdown_it_py-4.0.0-py3-none-any.whl.metadata (7.3 kB) +Collecting mdurl~=0.1 (from markdown-it-py>=2.2.0->rich>=10.11.0->typer->huggingface-hub<2.0,>=0.16.4->tokenizers==0.22.2->litellm->ai-hub==0.1.0) + Downloading mdurl-0.1.2-py3-none-any.whl.metadata (1.6 kB) +Collecting httptools>=0.6.3 (from uvicorn[standard]->ai-hub==0.1.0) + Downloading httptools-0.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (3.5 kB) +Collecting uvloop>=0.15.1 (from uvicorn[standard]->ai-hub==0.1.0) + Downloading uvloop-0.22.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (4.9 kB) +Collecting watchfiles>=0.20 (from uvicorn[standard]->ai-hub==0.1.0) + Downloading watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (4.9 kB) +Collecting websockets>=10.4 (from uvicorn[standard]->ai-hub==0.1.0) + Downloading websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (6.8 kB) +Downloading aioresponses-0.7.8-py2.py3-none-any.whl (12 kB) +Downloading aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (1.8 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 1.8/1.8 MB 5.2 MB/s 0:00:00 +Downloading multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (246 kB) +Downloading yarl-1.23.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (100 kB) +Downloading aiohappyeyeballs-2.6.1-py3-none-any.whl (15 kB) +Downloading aiosignal-1.4.0-py3-none-any.whl (7.5 kB) +Downloading attrs-26.1.0-py3-none-any.whl (67 kB) +Downloading frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (233 kB) +Downloading idna-3.11-py3-none-any.whl (71 kB) +Downloading propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (214 kB) +Downloading anyio-4.13.0-py3-none-any.whl (114 kB) +Downloading croniter-6.2.2-py3-none-any.whl (45 kB) +Downloading faiss_cpu-1.13.2-cp310-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (11.5 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 11.5/11.5 MB 7.3 MB/s 0:00:01 +Downloading numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (16.0 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 16.0/16.0 MB 11.4 MB/s 0:00:01 +Downloading fastapi-0.135.3-py3-none-any.whl (117 kB) +Downloading annotated_doc-0.0.4-py3-none-any.whl (5.3 kB) +Downloading pydantic-2.12.5-py3-none-any.whl (463 kB) +Downloading pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.9 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 1.9/1.9 MB 13.3 MB/s 0:00:00 +Downloading annotated_types-0.7.0-py3-none-any.whl (13 kB) +Downloading starlette-1.0.0-py3-none-any.whl (72 kB) +Downloading typing_inspection-0.4.2-py3-none-any.whl (14 kB) +Downloading google_generativeai-0.8.6-py3-none-any.whl (155 kB) +Downloading google_ai_generativelanguage-0.6.15-py3-none-any.whl (1.3 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 1.3/1.3 MB 14.1 MB/s 0:00:00 +Downloading google_api_core-2.30.2-py3-none-any.whl (173 kB) +Downloading google_auth-2.49.1-py3-none-any.whl (240 kB) +Downloading googleapis_common_protos-1.74.0-py3-none-any.whl (300 kB) +Downloading grpcio-1.80.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl (6.6 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 6.6/6.6 MB 14.2 MB/s 0:00:00 +Downloading grpcio_status-1.71.2-py3-none-any.whl (14 kB) +Downloading proto_plus-1.27.2-py3-none-any.whl (50 kB) +Downloading protobuf-5.29.6-cp38-abi3-manylinux2014_aarch64.whl (320 kB) +Downloading requests-2.33.1-py3-none-any.whl (64 kB) +Downloading charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (206 kB) +Downloading urllib3-2.6.3-py3-none-any.whl (131 kB) +Downloading certifi-2026.2.25-py3-none-any.whl (153 kB) +Downloading cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl (4.3 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 4.3/4.3 MB 15.1 MB/s 0:00:00 +Downloading cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl (216 kB) +Downloading pyasn1_modules-0.4.2-py3-none-any.whl (181 kB) +Downloading pyasn1-0.6.3-py3-none-any.whl (83 kB) +Downloading google_api_python_client-2.194.0-py3-none-any.whl (15.0 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 15.0/15.0 MB 16.1 MB/s 0:00:00 +Downloading google_auth_httplib2-0.3.1-py3-none-any.whl (9.5 kB) +Downloading httplib2-0.31.2-py3-none-any.whl (91 kB) +Downloading pyparsing-3.3.2-py3-none-any.whl (122 kB) +Downloading uritemplate-4.2.0-py3-none-any.whl (11 kB) +Downloading grpcio_reflection-1.71.2-py3-none-any.whl (22 kB) +Downloading grpcio_tools-1.71.2-cp311-cp311-manylinux_2_17_aarch64.whl (2.3 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 2.3/2.3 MB 15.5 MB/s 0:00:00 +Downloading litellm-1.83.4-py3-none-any.whl (16.0 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 16.0/16.0 MB 18.4 MB/s 0:00:00 +Downloading click-8.1.8-py3-none-any.whl (98 kB) +Downloading fastuuid-0.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (278 kB) +Downloading httpx-0.28.1-py3-none-any.whl (73 kB) +Downloading importlib_metadata-8.5.0-py3-none-any.whl (26 kB) +Downloading jinja2-3.1.6-py3-none-any.whl (134 kB) +Downloading jsonschema-4.23.0-py3-none-any.whl (88 kB) +Downloading openai-2.30.0-py3-none-any.whl (1.1 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 1.1/1.1 MB 4.6 MB/s 0:00:00 +Downloading python_dotenv-1.0.1-py3-none-any.whl (19 kB) +Downloading tiktoken-0.12.0-cp311-cp311-manylinux_2_28_aarch64.whl (1.1 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 1.1/1.1 MB 18.3 MB/s 0:00:00 +Downloading tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.3 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 3.3/3.3 MB 19.3 MB/s 0:00:00 +Downloading distro-1.9.0-py3-none-any.whl (20 kB) +Downloading httpcore-1.0.9-py3-none-any.whl (78 kB) +Downloading huggingface_hub-1.9.2-py3-none-any.whl (637 kB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 637.3/637.3 kB 18.2 MB/s 0:00:00 +Downloading hf_xet-1.4.3-cp37-abi3-manylinux_2_28_aarch64.whl (4.0 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 4.0/4.0 MB 20.1 MB/s 0:00:00 +Downloading jiter-0.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (354 kB) +Downloading filelock-3.25.2-py3-none-any.whl (26 kB) +Downloading fsspec-2026.3.0-py3-none-any.whl (202 kB) +Downloading h11-0.16.0-py3-none-any.whl (37 kB) +Downloading jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB) +Downloading markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (24 kB) +Downloading pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (775 kB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 775.6/775.6 kB 17.2 MB/s 0:00:00 +Downloading referencing-0.37.0-py3-none-any.whl (26 kB) +Downloading regex-2026.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (792 kB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 792.4/792.4 kB 22.5 MB/s 0:00:00 +Downloading rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (389 kB) +Downloading tqdm-4.67.3-py3-none-any.whl (78 kB) +Downloading zipp-3.23.0-py3-none-any.whl (10 kB) +Downloading psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl (4.4 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 4.4/4.4 MB 21.0 MB/s 0:00:00 +Downloading pycparser-3.0-py3-none-any.whl (48 kB) +Downloading pyjwt-2.12.1-py3-none-any.whl (29 kB) +Downloading pytest-9.0.3-py3-none-any.whl (375 kB) +Downloading pluggy-1.6.0-py3-none-any.whl (20 kB) +Downloading iniconfig-2.3.0-py3-none-any.whl (7.5 kB) +Downloading pygments-2.20.0-py3-none-any.whl (1.2 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 1.2/1.2 MB 20.2 MB/s 0:00:00 +Downloading pytest_asyncio-1.3.0-py3-none-any.whl (15 kB) +Downloading pytest_mock-3.15.1-py3-none-any.whl (10 kB) +Downloading pytest_tornasync-0.6.0.post2-py3-none-any.whl (6.6 kB) +Downloading tornado-6.5.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (448 kB) +Downloading pytest_trio-0.8.0-py3-none-any.whl (27 kB) +Downloading outcome-1.3.0.post0-py2.py3-none-any.whl (10 kB) +Downloading trio-0.33.0-py3-none-any.whl (510 kB) +Downloading sniffio-1.3.1-py3-none-any.whl (10 kB) +Downloading python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB) +Downloading six-1.17.0-py2.py3-none-any.whl (11 kB) +Downloading python_multipart-0.0.24-py3-none-any.whl (24 kB) +Downloading sortedcontainers-2.4.0-py2.py3-none-any.whl (29 kB) +Downloading sqlalchemy-2.0.49-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (3.3 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 3.3/3.3 MB 20.5 MB/s 0:00:00 +Downloading greenlet-3.4.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl (605 kB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 605.5/605.5 kB 17.4 MB/s 0:00:00 +Downloading tenacity-9.1.4-py3-none-any.whl (28 kB) +Downloading typer-0.23.1-py3-none-any.whl (56 kB) +Downloading rich-14.3.3-py3-none-any.whl (310 kB) +Downloading markdown_it_py-4.0.0-py3-none-any.whl (87 kB) +Downloading mdurl-0.1.2-py3-none-any.whl (10.0 kB) +Downloading shellingham-1.5.4-py2.py3-none-any.whl (9.8 kB) +Downloading uvicorn-0.44.0-py3-none-any.whl (69 kB) +Downloading httptools-0.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (454 kB) +Downloading uvloop-0.22.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (3.8 MB) + โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” 3.8/3.8 MB 21.3 MB/s 0:00:00 +Downloading watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (448 kB) +Downloading websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (185 kB) +Building wheels for collected packages: ai-hub + Building editable for ai-hub (pyproject.toml): started + Building editable for ai-hub (pyproject.toml): finished with status 'done' + Created wheel for ai-hub: filename=ai_hub-0.1.0-0.editable-py3-none-any.whl size=3288 sha256=bfbaac7689f7c36d7dc2ee8a562957aeb90ec0cc74af5de47c73089a756a944c + Stored in directory: /tmp/pip-ephem-wheel-cache-wblpckhw/wheels/61/cc/4c/86ebde176a08579a42ab23974de628f3db779a3ba715da5797 +Successfully built ai-hub +Installing collected packages: sortedcontainers, zipp, websockets, uvloop, urllib3, uritemplate, typing-inspection, tqdm, tornado, tenacity, sniffio, six, shellingham, rpds-py, regex, pyyaml, python-multipart, python-dotenv, pyparsing, PyJWT, pygments, pydantic-core, pycparser, pyasn1, psycopg2-binary, protobuf, propcache, pluggy, numpy, multidict, mdurl, MarkupSafe, jiter, iniconfig, idna, httptools, hf-xet, h11, grpcio, greenlet, fsspec, frozenlist, filelock, fastuuid, distro, click, charset_normalizer, certifi, attrs, annotated-types, annotated-doc, aiohappyeyeballs, yarl, uvicorn, sqlalchemy, requests, referencing, python-dateutil, pytest, pydantic, pyasn1-modules, proto-plus, outcome, markdown-it-py, jinja2, importlib-metadata, httplib2, httpcore, grpcio-tools, grpcio-reflection, googleapis-common-protos, faiss-cpu, cffi, anyio, aiosignal, watchfiles, trio, tiktoken, starlette, rich, pytest-tornasync, pytest-mock, pytest-asyncio, jsonschema-specifications, httpx, grpcio-status, cryptography, croniter, aiohttp, typer, pytest-trio, openai, jsonschema, google-auth, fastapi, aioresponses, huggingface-hub, google-auth-httplib2, google-api-core, tokenizers, google-api-python-client, litellm, google-ai-generativelanguage, google-generativeai, ai-hub + +Successfully installed MarkupSafe-3.0.3 PyJWT-2.12.1 ai-hub-0.1.0 aiohappyeyeballs-2.6.1 aiohttp-3.13.5 aioresponses-0.7.8 aiosignal-1.4.0 annotated-doc-0.0.4 annotated-types-0.7.0 anyio-4.13.0 attrs-26.1.0 certifi-2026.2.25 cffi-2.0.0 charset_normalizer-3.4.7 click-8.1.8 croniter-6.2.2 cryptography-46.0.7 distro-1.9.0 faiss-cpu-1.13.2 fastapi-0.135.3 fastuuid-0.14.0 filelock-3.25.2 frozenlist-1.8.0 fsspec-2026.3.0 google-ai-generativelanguage-0.6.15 google-api-core-2.30.2 google-api-python-client-2.194.0 google-auth-2.49.1 google-auth-httplib2-0.3.1 google-generativeai-0.8.6 googleapis-common-protos-1.74.0 greenlet-3.4.0 grpcio-1.80.0 grpcio-reflection-1.71.2 grpcio-status-1.71.2 grpcio-tools-1.71.2 h11-0.16.0 hf-xet-1.4.3 httpcore-1.0.9 httplib2-0.31.2 httptools-0.7.1 httpx-0.28.1 huggingface-hub-1.9.2 idna-3.11 importlib-metadata-8.5.0 iniconfig-2.3.0 jinja2-3.1.6 jiter-0.13.0 jsonschema-4.23.0 jsonschema-specifications-2025.9.1 litellm-1.83.4 markdown-it-py-4.0.0 mdurl-0.1.2 multidict-6.7.1 numpy-2.4.4 openai-2.30.0 outcome-1.3.0.post0 pluggy-1.6.0 propcache-0.4.1 proto-plus-1.27.2 protobuf-5.29.6 psycopg2-binary-2.9.11 pyasn1-0.6.3 pyasn1-modules-0.4.2 pycparser-3.0 pydantic-2.12.5 pydantic-core-2.41.5 pygments-2.20.0 pyparsing-3.3.2 pytest-9.0.3 pytest-asyncio-1.3.0 pytest-mock-3.15.1 pytest-tornasync-0.6.0.post2 pytest-trio-0.8.0 python-dateutil-2.9.0.post0 python-dotenv-1.0.1 python-multipart-0.0.24 pyyaml-6.0.3 referencing-0.37.0 regex-2026.4.4 requests-2.33.1 rich-14.3.3 rpds-py-0.30.0 shellingham-1.5.4 six-1.17.0 sniffio-1.3.1 sortedcontainers-2.4.0 sqlalchemy-2.0.49 starlette-1.0.0 tenacity-9.1.4 tiktoken-0.12.0 tokenizers-0.22.2 tornado-6.5.5 tqdm-4.67.3 trio-0.33.0 typer-0.23.1 typing-inspection-0.4.2 uritemplate-4.2.0 urllib3-2.6.3 uvicorn-0.44.0 uvloop-0.22.1 watchfiles-1.1.1 websockets-16.0 yarl-1.23.0 zipp-3.23.0 +--- Installing frontend dependencies --- + +up to date, audited 1537 packages in 7s + +344 packages are looking for funding + run `npm fund` for details + +46 vulnerabilities (13 low, 9 moderate, 24 high) + +To address issues that do not require attention, run: + npm audit fix + +To address all issues (including breaking changes), run: + npm audit fix --force + +Run `npm audit` for details. +--- Starting AI Hub Server, React frontend, and backend proxy --- +npm ERR! code ENOENT +npm ERR! syscall lstat +npm ERR! path /home/vscode/.npm-global/lib +npm ERR! errno -2 +npm ERR! enoent ENOENT: no such file or directory, lstat '/home/vscode/.npm-global/lib' +npm ERR! enoent This is related to npm not being able to find a file. +npm ERR! enoent + +npm ERR! A complete log of this run can be found in: +npm ERR! /home/vscode/.npm/_logs/2026-04-09T07_16_49_024Z-debug-0.log