Spaces:
Sleeping
Sleeping
Claude Code Claude Opus 4.6 commited on
Commit ·
3f1c4cd
1
Parent(s): 95148ba
Claude Code: Add A2A diagnostics and fix stage reporting
Browse files- Update status file to RUNNING_A2A_READY when app starts
- Add logging to /a2a/jsonrpc endpoint for debugging
- Add /a2a/self-test endpoint for A2A diagnostics
- Fix stage mismatch between status file and health checks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
app.py
CHANGED
|
@@ -154,8 +154,10 @@ async def a2a_jsonrpc(request: Request):
|
|
| 154 |
|
| 155 |
Handles message/send requests from other agents in the HuggingClaw World family.
|
| 156 |
"""
|
|
|
|
| 157 |
try:
|
| 158 |
payload = await request.json()
|
|
|
|
| 159 |
|
| 160 |
# Validate JSON-RPC 2.0 basic structure
|
| 161 |
if payload.get("jsonrpc") != "2.0":
|
|
@@ -274,6 +276,59 @@ async def api_status():
|
|
| 274 |
}
|
| 275 |
|
| 276 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
# ============================================================================
|
| 278 |
# A2A HEALTH CHECK ENDPOINT - For agents to verify Cain's A2A availability
|
| 279 |
# ============================================================================
|
|
@@ -340,8 +395,34 @@ async def a2a_health():
|
|
| 340 |
print(">>> CAIN: FastAPI app created successfully", flush=True)
|
| 341 |
print(">>> CAIN: A2A endpoint registered at /a2a/jsonrpc", flush=True)
|
| 342 |
print(">>> CAIN: A2A health check at /a2a/health", flush=True)
|
|
|
|
| 343 |
print(">>> CAIN: A2A diagnostics at /a2a/diagnostics", flush=True)
|
| 344 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 345 |
# Verify openclaw can be imported at startup
|
| 346 |
try:
|
| 347 |
import openclaw
|
|
|
|
| 154 |
|
| 155 |
Handles message/send requests from other agents in the HuggingClaw World family.
|
| 156 |
"""
|
| 157 |
+
print(f">>> CAIN A2A: Received request at {time.time()}", flush=True)
|
| 158 |
try:
|
| 159 |
payload = await request.json()
|
| 160 |
+
print(f">>> CAIN A2A: Payload method={payload.get('method')}, id={payload.get('id')}", flush=True)
|
| 161 |
|
| 162 |
# Validate JSON-RPC 2.0 basic structure
|
| 163 |
if payload.get("jsonrpc") != "2.0":
|
|
|
|
| 276 |
}
|
| 277 |
|
| 278 |
|
| 279 |
+
# ============================================================================
|
| 280 |
+
# A2A SELF-TEST ENDPOINT - Test A2A endpoint without external agent
|
| 281 |
+
# ============================================================================
|
| 282 |
+
@app.get("/a2a/self-test")
|
| 283 |
+
async def a2a_self_test():
|
| 284 |
+
"""
|
| 285 |
+
Self-test endpoint for A2A functionality.
|
| 286 |
+
Tests brain import and returns detailed status.
|
| 287 |
+
"""
|
| 288 |
+
print(f">>> CAIN A2A: Self-test requested", flush=True)
|
| 289 |
+
test_results = {
|
| 290 |
+
"timestamp": time.time(),
|
| 291 |
+
"tests": {}
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
# Test 1: Can we import openclaw?
|
| 295 |
+
try:
|
| 296 |
+
import openclaw
|
| 297 |
+
test_results["tests"]["openclaw_import"] = {"status": "pass", "path": str(openclaw.__file__)}
|
| 298 |
+
except Exception as e:
|
| 299 |
+
test_results["tests"]["openclaw_import"] = {"status": "fail", "error": str(e)}
|
| 300 |
+
|
| 301 |
+
# Test 2: Can we import brain_minimal?
|
| 302 |
+
try:
|
| 303 |
+
from agents import brain_minimal
|
| 304 |
+
test_results["tests"]["brain_minimal_import"] = {"status": "pass"}
|
| 305 |
+
except Exception as e:
|
| 306 |
+
test_results["tests"]["brain_minimal_import"] = {"status": "fail", "error": str(e)}
|
| 307 |
+
|
| 308 |
+
# Test 3: Can we get a brain instance?
|
| 309 |
+
try:
|
| 310 |
+
from agents import brain_minimal
|
| 311 |
+
brain = brain_minimal.get_brain(agent_name="cain", legacy_mode=True)
|
| 312 |
+
test_results["tests"]["get_brain"] = {"status": "pass", "agent_name": getattr(brain, 'agent_name', 'unknown')}
|
| 313 |
+
except Exception as e:
|
| 314 |
+
test_results["tests"]["get_brain"] = {"status": "fail", "error": str(e)}
|
| 315 |
+
|
| 316 |
+
# Test 4: Can we call _conversation_process?
|
| 317 |
+
try:
|
| 318 |
+
from agents import brain_minimal
|
| 319 |
+
brain = brain_minimal.get_brain(agent_name="cain", legacy_mode=True)
|
| 320 |
+
result = brain._conversation_process("test")
|
| 321 |
+
test_results["tests"]["conversation_process"] = {"status": "pass", "success": result.get("success")}
|
| 322 |
+
except Exception as e:
|
| 323 |
+
test_results["tests"]["conversation_process"] = {"status": "fail", "error": str(e)}
|
| 324 |
+
|
| 325 |
+
# Overall status
|
| 326 |
+
all_pass = all(t.get("status") == "pass" for t in test_results["tests"].values())
|
| 327 |
+
test_results["overall_status"] = "pass" if all_pass else "partial_fail"
|
| 328 |
+
|
| 329 |
+
return test_results
|
| 330 |
+
|
| 331 |
+
|
| 332 |
# ============================================================================
|
| 333 |
# A2A HEALTH CHECK ENDPOINT - For agents to verify Cain's A2A availability
|
| 334 |
# ============================================================================
|
|
|
|
| 395 |
print(">>> CAIN: FastAPI app created successfully", flush=True)
|
| 396 |
print(">>> CAIN: A2A endpoint registered at /a2a/jsonrpc", flush=True)
|
| 397 |
print(">>> CAIN: A2A health check at /a2a/health", flush=True)
|
| 398 |
+
print(">>> CAIN: A2A self-test at /a2a/self-test", flush=True)
|
| 399 |
print(">>> CAIN: A2A diagnostics at /a2a/diagnostics", flush=True)
|
| 400 |
|
| 401 |
+
# Update status file to indicate app is ready (A2A available)
|
| 402 |
+
try:
|
| 403 |
+
import json
|
| 404 |
+
from datetime import datetime
|
| 405 |
+
status_path = os.environ.get('CAIN_STATUS_PATH', '/data/cain_status.json')
|
| 406 |
+
status_data = {
|
| 407 |
+
"current_state": "idle",
|
| 408 |
+
"stage": "RUNNING_A2A_READY",
|
| 409 |
+
"last_updated": datetime.utcnow().isoformat() + "+00:00",
|
| 410 |
+
"agent": "cain",
|
| 411 |
+
"a2a": {
|
| 412 |
+
"endpoint": "/a2a/jsonrpc",
|
| 413 |
+
"enabled": True,
|
| 414 |
+
"status": "ready"
|
| 415 |
+
}
|
| 416 |
+
}
|
| 417 |
+
# Ensure directory exists
|
| 418 |
+
from pathlib import Path
|
| 419 |
+
Path(status_path).parent.mkdir(parents=True, exist_ok=True)
|
| 420 |
+
with open(status_path, 'w') as f:
|
| 421 |
+
json.dump(status_data, f, indent=2)
|
| 422 |
+
print(f">>> CAIN: Status file updated: stage=RUNNING_A2A_READY", flush=True)
|
| 423 |
+
except Exception as e:
|
| 424 |
+
print(f">>> CAIN WARNING: Could not update status file: {e}", flush=True)
|
| 425 |
+
|
| 426 |
# Verify openclaw can be imported at startup
|
| 427 |
try:
|
| 428 |
import openclaw
|