fix: complete OPC system implementation
Deploy Dashboard (Dev) / deploy-dev (push) Failing after 10m4s
Run Tests / Frontend Tests (push) Failing after 14m28s
Run Tests / Backend Tests (push) Failing after 14m50s
Run Tests / Test Summary (push) Has been cancelled

- Add database connection retry logic (3 attempts, 5s delay)
- Add missing database functions: get_task_by_id, get_agent_execution, approve_agent_execution, update_agent_config, update_execution_status
- Complete agent execution approval workflow
- Fix agent executor to handle approved executions
- Fix inefficient task queries (use direct ID lookup)
- Add error action handling in agent executor
- Improve LiteLLM error handling with specific exception types
- Reduce LiteLLM timeout from 60s to 30s
- Fix execution status update to only set started_at once
- Consolidate WebSocket datetime serialization with helper function
- Implement update agent endpoint
This commit is contained in:
Gan, Jimmy
2026-04-03 22:30:51 +08:00
parent a42e3d3050
commit 5da2ae01a0
5 changed files with 333 additions and 56 deletions
+23 -11
View File
@@ -19,6 +19,7 @@ class AgentUpdate(BaseModel):
class ExecutionApproval(BaseModel):
approved: bool
feedback: Optional[str] = None
@router.get("/agents")
@@ -53,8 +54,17 @@ async def update_agent(
):
"""Update agent configuration (admin only)"""
user: User = request.state.user
# This would need an update_agent function in opc_db
raise HTTPException(status_code=501, detail="Not implemented yet")
try:
agent = await opc_db.update_agent_config(
agent_id=agent_id,
name=updates.name,
description=updates.description,
system_prompt=updates.system_prompt,
enabled=updates.enabled
)
return agent
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
@router.post("/agents/{agent_id}/execute")
@@ -99,8 +109,7 @@ async def get_execution(
):
"""Get execution details"""
user: User = request.state.user
executions = await opc_db.get_agent_executions(limit=1000)
execution = next((e for e in executions if e["id"] == execution_id), None)
execution = await opc_db.get_agent_execution(execution_id)
if not execution:
raise HTTPException(status_code=404, detail="Execution not found")
return execution
@@ -114,10 +123,13 @@ async def approve_execution(
):
"""Approve or reject agent execution (for CxO level actions)"""
user: User = request.state.user
# This would need an approve_execution function in opc_db
# For now, return placeholder
return {
"execution_id": execution_id,
"approved": approval.approved,
"approved_by": user.username
}
try:
execution = await opc_db.approve_agent_execution(
execution_id=execution_id,
approved=approval.approved,
approved_by=user.username,
feedback=approval.feedback
)
return execution
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
+20 -21
View File
@@ -2,7 +2,8 @@
OPC WebSocket Router - Real-time updates for tasks and agent executions
"""
from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Depends
from typing import Set
from typing import Set, Any, Dict
from datetime import datetime
import json
import asyncio
import logging
@@ -16,6 +17,18 @@ router = APIRouter()
active_connections: Set[WebSocket] = set()
def serialize_datetime(obj: Any) -> Any:
"""Recursively serialize datetime objects to ISO format strings"""
if isinstance(obj, datetime):
return obj.isoformat()
elif isinstance(obj, dict):
return {key: serialize_datetime(value) for key, value in obj.items()}
elif isinstance(obj, list):
return [serialize_datetime(item) for item in obj]
else:
return obj
class ConnectionManager:
"""Manages WebSocket connections"""
@@ -74,44 +87,30 @@ async def websocket_endpoint(websocket: WebSocket):
async def broadcast_task_update(task_id: int, action: str, task_data: dict):
"""Broadcast task update to all connected clients"""
# Serialize datetime objects to ISO format
serialized_data = task_data.copy()
for key in ["created_at", "updated_at", "completed_at", "due_date"]:
if serialized_data.get(key):
serialized_data[key] = serialized_data[key].isoformat() if hasattr(serialized_data[key], 'isoformat') else serialized_data[key]
timestamp = task_data.get("updated_at")
if timestamp and hasattr(timestamp, 'isoformat'):
timestamp = timestamp.isoformat()
# Serialize all datetime objects recursively
serialized_data = serialize_datetime(task_data)
message = {
"type": "task_update",
"task_id": task_id,
"action": action, # created, updated, moved, deleted
"data": serialized_data,
"timestamp": timestamp
"timestamp": datetime.utcnow().isoformat()
}
await manager.broadcast(message)
async def broadcast_agent_execution(execution_id: int, status: str, execution_data: dict):
"""Broadcast agent execution update"""
# Serialize datetime objects to ISO format
serialized_data = execution_data.copy()
for key in ["started_at", "completed_at"]:
if serialized_data.get(key):
serialized_data[key] = serialized_data[key].isoformat() if hasattr(serialized_data[key], 'isoformat') else serialized_data[key]
timestamp = execution_data.get("started_at") or execution_data.get("completed_at")
if timestamp and hasattr(timestamp, 'isoformat'):
timestamp = timestamp.isoformat()
# Serialize all datetime objects recursively
serialized_data = serialize_datetime(execution_data)
message = {
"type": "agent_execution",
"execution_id": execution_id,
"status": status, # pending, running, completed, failed, pending_approval
"data": serialized_data,
"timestamp": timestamp
"timestamp": datetime.utcnow().isoformat()
}
await manager.broadcast(message)