Claude Code Claude Opus 4.6 commited on
Commit
1e0d510
·
1 Parent(s): bb0a3f8

Fix Cain 'unknown' error: Add comprehensive startup initialization and error logging

Browse files

- Add ensure_cain_status_file() to create/validate status file on startup
- Fix invalid 'unknown' state by resetting to 'idle'
- Add startup error/warning logging with get_startup_report()
- Add /api/agent/startup endpoint for diagnostics
- Add /api/agent/reset-status endpoint for manual status reset
- Print startup summary on initialization

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +154 -0
app.py CHANGED
@@ -46,6 +46,131 @@ sys.path.insert(0, str(BASE_DIR))
46
  # File paths
47
  CAIN_STATUS_FILE = AGENTS_DIR / "cain_status.json"
48
  REGISTRY_FILE = AGENTS_DIR / "registry.json"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  # ========== WebSocket Manager ==========
51
 
@@ -896,6 +1021,11 @@ def create_agent_office_with_ws():
896
  Create the Agent Office with WebSocket API endpoints.
897
  Returns a FastAPI app with the Gradio app mounted at root.
898
  """
 
 
 
 
 
899
  # Create FastAPI app first
900
  fastapi_app = FastAPI(title="HuggingClaw Agent Office API")
901
 
@@ -926,6 +1056,20 @@ def create_agent_office_with_ws():
926
  """Get agent registry as JSON."""
927
  return load_agent_registry()
928
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
929
  # ========== Analytics API Endpoints ==========
930
  if ANALYTICS_AVAILABLE:
931
  @fastapi_app.get("/api/analytics/stats")
@@ -982,6 +1126,16 @@ if __name__ == "__main__":
982
  print(f"[Agent Office] Base directory: {BASE_DIR}")
983
  print(f"[Agent Office] WebSocket API enabled at /api/thoughts")
984
 
 
 
 
 
 
 
 
 
 
 
985
  # Use uvicorn to serve the FastAPI app (not .launch())
986
  import uvicorn
987
  uvicorn.run(app, host=server_name, port=server_port)
 
46
  # File paths
47
  CAIN_STATUS_FILE = AGENTS_DIR / "cain_status.json"
48
  REGISTRY_FILE = AGENTS_DIR / "registry.json"
49
+ LOGS_DIR = AGENTS_DIR / "logs"
50
+
51
+ # ========== Startup Error Logger ==========
52
+
53
+ _startup_errors = []
54
+ _startup_warnings = []
55
+
56
+ def log_startup_error(component: str, error: str, details: Dict[str, Any] = None):
57
+ """Log a startup error for later reporting."""
58
+ _startup_errors.append({
59
+ "component": component,
60
+ "error": error,
61
+ "details": details or {},
62
+ "timestamp": datetime.utcnow().isoformat() + "Z"
63
+ })
64
+ print(f"[STARTUP ERROR] {component}: {error}", file=sys.stderr)
65
+ if details:
66
+ print(f" Details: {details}", file=sys.stderr)
67
+
68
+ def log_startup_warning(component: str, warning: str, details: Dict[str, Any] = None):
69
+ """Log a startup warning for later reporting."""
70
+ _startup_warnings.append({
71
+ "component": component,
72
+ "warning": warning,
73
+ "details": details or {},
74
+ "timestamp": datetime.utcnow().isoformat() + "Z"
75
+ })
76
+ print(f"[STARTUP WARNING] {component}: {warning}")
77
+
78
+ def get_startup_report() -> Dict[str, Any]:
79
+ """Get a comprehensive startup report."""
80
+ return {
81
+ "timestamp": datetime.utcnow().isoformat() + "Z",
82
+ "base_dir": str(BASE_DIR),
83
+ "agents_dir": str(AGENTS_DIR),
84
+ "cain_status_file": str(CAIN_STATUS_FILE),
85
+ "errors": _startup_errors,
86
+ "warnings": _startup_warnings,
87
+ "error_count": len(_startup_errors),
88
+ "warning_count": len(_startup_warnings)
89
+ }
90
+
91
+ # ========== Status File Initialization ==========
92
+
93
+ def ensure_cain_status_file() -> Dict[str, Any]:
94
+ """
95
+ Ensure cain_status.json exists and is properly initialized.
96
+ Creates the file if it doesn't exist with a valid default status.
97
+ Returns the current status.
98
+ """
99
+ AGENTS_DIR.mkdir(parents=True, exist_ok=True)
100
+ LOGS_DIR.mkdir(parents=True, exist_ok=True)
101
+
102
+ default_status = {
103
+ "current_state": "idle",
104
+ "last_updated": datetime.utcnow().isoformat() + "Z",
105
+ "agent": "cain",
106
+ "startup_time": datetime.utcnow().isoformat() + "Z"
107
+ }
108
+
109
+ if not CAIN_STATUS_FILE.exists():
110
+ try:
111
+ with open(CAIN_STATUS_FILE, 'w') as f:
112
+ json.dump(default_status, f, indent=2)
113
+ log_startup_warning("cain_status", "Created default cain_status.json file", {
114
+ "path": str(CAIN_STATUS_FILE),
115
+ "status": default_status
116
+ })
117
+ except Exception as e:
118
+ log_startup_error("cain_status", f"Failed to create status file: {e}", {
119
+ "path": str(CAIN_STATUS_FILE),
120
+ "error_type": type(e).__name__
121
+ })
122
+ return default_status
123
+
124
+ # File exists - validate and fix if needed
125
+ try:
126
+ with open(CAIN_STATUS_FILE, 'r') as f:
127
+ status = json.load(f)
128
+
129
+ # Ensure required fields exist
130
+ updated = False
131
+ if "current_state" not in status:
132
+ status["current_state"] = "idle"
133
+ updated = True
134
+ if "last_updated" not in status:
135
+ status["last_updated"] = datetime.utcnow().isoformat() + "Z"
136
+ updated = True
137
+ if "agent" not in status:
138
+ status["agent"] = "cain"
139
+ updated = True
140
+
141
+ # Fix invalid "unknown" state
142
+ if status.get("current_state") == "unknown":
143
+ old_state = status["current_state"]
144
+ status["current_state"] = "idle"
145
+ updated = True
146
+ log_startup_warning("cain_status", f"Fixed invalid state '{old_state}' -> 'idle'", {
147
+ "old_state": old_state,
148
+ "new_state": "idle"
149
+ })
150
+
151
+ if updated:
152
+ with open(CAIN_STATUS_FILE, 'w') as f:
153
+ json.dump(status, f, indent=2)
154
+ log_startup_warning("cain_status", "Updated cain_status.json with missing fields")
155
+
156
+ return status
157
+
158
+ except json.JSONDecodeError as e:
159
+ log_startup_error("cain_status", f"Invalid JSON in status file, recreating: {e}", {
160
+ "path": str(CAIN_STATUS_FILE)
161
+ })
162
+ try:
163
+ with open(CAIN_STATUS_FILE, 'w') as f:
164
+ json.dump(default_status, f, indent=2)
165
+ except Exception:
166
+ pass
167
+ return default_status
168
+ except Exception as e:
169
+ log_startup_error("cain_status", f"Error reading status file: {e}", {
170
+ "path": str(CAIN_STATUS_FILE),
171
+ "error_type": type(e).__name__
172
+ })
173
+ return default_status
174
 
175
  # ========== WebSocket Manager ==========
176
 
 
1021
  Create the Agent Office with WebSocket API endpoints.
1022
  Returns a FastAPI app with the Gradio app mounted at root.
1023
  """
1024
+ # ========== Startup Initialization ==========
1025
+ print("[Agent Office] Initializing startup...")
1026
+ initial_status = ensure_cain_status_file()
1027
+ print(f"[Agent Office] Initial status: {initial_status.get('current_state', 'unknown')}")
1028
+
1029
  # Create FastAPI app first
1030
  fastapi_app = FastAPI(title="HuggingClaw Agent Office API")
1031
 
 
1056
  """Get agent registry as JSON."""
1057
  return load_agent_registry()
1058
 
1059
+ @fastapi_app.get("/api/agent/startup")
1060
+ async def api_agent_startup():
1061
+ """Get startup initialization report."""
1062
+ return get_startup_report()
1063
+
1064
+ @fastapi_app.post("/api/agent/reset-status")
1065
+ async def api_reset_status():
1066
+ """Reset cain status file to idle."""
1067
+ try:
1068
+ status = ensure_cain_status_file()
1069
+ return {"success": True, "status": status}
1070
+ except Exception as e:
1071
+ return {"success": False, "error": str(e)}
1072
+
1073
  # ========== Analytics API Endpoints ==========
1074
  if ANALYTICS_AVAILABLE:
1075
  @fastapi_app.get("/api/analytics/stats")
 
1126
  print(f"[Agent Office] Base directory: {BASE_DIR}")
1127
  print(f"[Agent Office] WebSocket API enabled at /api/thoughts")
1128
 
1129
+ # Print startup report
1130
+ startup_report = get_startup_report()
1131
+ print(f"[Agent Office] Startup Report:")
1132
+ print(f" - Errors: {startup_report['error_count']}")
1133
+ print(f" - Warnings: {startup_report['warning_count']}")
1134
+ if startup_report['error_count'] > 0:
1135
+ print("[Agent Office] ERROR: Startup errors detected!")
1136
+ for err in startup_report['errors']:
1137
+ print(f" - {err['component']}: {err['error']}")
1138
+
1139
  # Use uvicorn to serve the FastAPI app (not .launch())
1140
  import uvicorn
1141
  uvicorn.run(app, host=server_name, port=server_port)