Claude Code Claude Opus 4.6 commited on
Commit
6995f69
·
1 Parent(s): 34d15f3

Fix Cain: Add /api/state endpoint for health monitoring

Browse files

Root cause analysis:
- Cain's Space was running (HTTP 200) but didn't have /api/state endpoint
- Home Space was polling Cain's /api/state and getting 404 "Not Found"
- This caused Cain to show "syncing" state with "Cain is starting..." message
- The "Error: unknown" was from Home Space's health check seeing Cain as unresponsive

Changes:
1. Updated app.py to use FastAPI + Gradio integration
2. Added /api/state endpoint that returns proper agent state
3. Added /status, /health, and /agents endpoints for compatibility
4. Updated requirements.txt to include fastapi and uvicorn

Technical details:
- Agent state includes: state (idle), detail, progress, timestamps
- CORS enabled for cross-origin requests from monitoring systems
- Maintains backward compatibility with existing Gradio UI

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

Files changed (2) hide show
  1. app.py +78 -10
  2. requirements.txt +3 -1
app.py CHANGED
@@ -3,6 +3,10 @@ import os
3
  import sys
4
  import json
5
  import time
 
 
 
 
6
 
7
  # Try importing audio libraries, but don't crash if they fail
8
  # We'll add audio features later if the environment supports it
@@ -14,14 +18,62 @@ except ImportError:
14
  AUDIO_AVAILABLE = False
15
  print("Audio libraries not available, running in text-only mode")
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  # Placeholder for other features
18
  def transcribe(audio):
19
  if audio is None:
20
  return "No audio provided"
21
-
22
  if not AUDIO_AVAILABLE:
23
  return "Audio processing is not available in this environment"
24
-
25
  try:
26
  # Audio processing logic here
27
  return "Audio processed (placeholder)"
@@ -30,6 +82,7 @@ def transcribe(audio):
30
 
31
  def chat(message, history):
32
  # Simple chat response
 
33
  return f"Cain says: {message}"
34
 
35
  # Ensure the directory exists
@@ -39,13 +92,14 @@ os.makedirs("/data/logs", exist_ok=True)
39
  with gr.Blocks() as demo:
40
  gr.Markdown("# Cain is Alive")
41
  gr.Markdown("Status: RUNNING")
42
-
 
43
  with gr.Tab("Chat"):
44
  chat_interface = gr.ChatInterface(
45
  fn=chat,
46
  examples=["Hello", "How are you?", "Tell me a joke"]
47
  )
48
-
49
  with gr.Tab("Audio"):
50
  if AUDIO_AVAILABLE:
51
  audio_input = gr.Audio(sources=["microphone"])
@@ -59,10 +113,24 @@ with gr.Blocks() as demo:
59
  else:
60
  gr.Markdown("Audio features are disabled in this environment")
61
 
 
 
 
 
62
  # Launch the app
63
- print("Launching Gradio app on 0.0.0.0:7860...")
64
- demo.launch(
65
- server_name="0.0.0.0",
66
- server_port=7860,
67
- share=False
68
- )
 
 
 
 
 
 
 
 
 
 
 
3
  import sys
4
  import json
5
  import time
6
+ import datetime
7
+ from fastapi import FastAPI
8
+ from fastapi.middleware.cors import CORSMiddleware
9
+ import uvicorn
10
 
11
  # Try importing audio libraries, but don't crash if they fail
12
  # We'll add audio features later if the environment supports it
 
18
  AUDIO_AVAILABLE = False
19
  print("Audio libraries not available, running in text-only mode")
20
 
21
+ # Global state for the agent
22
+ AGENT_STATE = {
23
+ "state": "idle",
24
+ "detail": "Cain is running",
25
+ "progress": 100,
26
+ "updated_at": datetime.datetime.now().isoformat(),
27
+ "bubbleText": "",
28
+ "bubbleTextZh": "",
29
+ "officeName": "Cain's Office",
30
+ "agentId": "cain",
31
+ "name": "Cain",
32
+ "area": "breakroom",
33
+ "authStatus": "approved"
34
+ }
35
+
36
+ # Create FastAPI app for custom routes
37
+ fastapi_app = FastAPI()
38
+
39
+ # Add CORS middleware
40
+ fastapi_app.add_middleware(
41
+ CORSMiddleware,
42
+ allow_origins=["*"],
43
+ allow_credentials=True,
44
+ allow_methods=["*"],
45
+ allow_headers=["*"],
46
+ )
47
+
48
+ @fastapi_app.get("/api/state")
49
+ @fastapi_app.get("/status")
50
+ async def get_state():
51
+ """Return agent state for health monitoring"""
52
+ AGENT_STATE["updated_at"] = datetime.datetime.now().isoformat()
53
+ return AGENT_STATE
54
+
55
+ @fastapi_app.get("/health")
56
+ async def health_check():
57
+ """Simple health check endpoint"""
58
+ return {
59
+ "status": "healthy",
60
+ "agent": "Cain",
61
+ "timestamp": datetime.datetime.now().isoformat()
62
+ }
63
+
64
+ @fastapi_app.get("/agents")
65
+ async def get_agents():
66
+ """Return agents list (single agent for Cain)"""
67
+ return [AGENT_STATE]
68
+
69
  # Placeholder for other features
70
  def transcribe(audio):
71
  if audio is None:
72
  return "No audio provided"
73
+
74
  if not AUDIO_AVAILABLE:
75
  return "Audio processing is not available in this environment"
76
+
77
  try:
78
  # Audio processing logic here
79
  return "Audio processed (placeholder)"
 
82
 
83
  def chat(message, history):
84
  # Simple chat response
85
+ AGENT_STATE["updated_at"] = datetime.datetime.now().isoformat()
86
  return f"Cain says: {message}"
87
 
88
  # Ensure the directory exists
 
92
  with gr.Blocks() as demo:
93
  gr.Markdown("# Cain is Alive")
94
  gr.Markdown("Status: RUNNING")
95
+ gr.Markdown("State API: GET /api/state or /status")
96
+
97
  with gr.Tab("Chat"):
98
  chat_interface = gr.ChatInterface(
99
  fn=chat,
100
  examples=["Hello", "How are you?", "Tell me a joke"]
101
  )
102
+
103
  with gr.Tab("Audio"):
104
  if AUDIO_AVAILABLE:
105
  audio_input = gr.Audio(sources=["microphone"])
 
113
  else:
114
  gr.Markdown("Audio features are disabled in this environment")
115
 
116
+ # Mount Gradio app to FastAPI
117
+ # This allows both Gradio UI and custom API endpoints to work together
118
+ gradio_app = gr.mount_gradio_app(fastapi_app, demo, path="/")
119
+
120
  # Launch the app
121
+ print("Launching Gradio app with FastAPI on 0.0.0.0:7860...")
122
+ print("Available endpoints:")
123
+ print(" - / (Gradio UI)")
124
+ print(" - /api/state (Agent state)")
125
+ print(" - /status (Agent status)")
126
+ print(" - /agents (Agent list)")
127
+ print(" - /health (Health check)")
128
+
129
+ if __name__ == "__main__":
130
+ import uvicorn
131
+ uvicorn.run(
132
+ gradio_app,
133
+ host="0.0.0.0",
134
+ port=7860,
135
+ log_level="info"
136
+ )
requirements.txt CHANGED
@@ -1,2 +1,4 @@
1
  gradio>=4.0.0
2
- psutil
 
 
 
1
  gradio>=4.0.0
2
+ psutil
3
+ fastapi
4
+ uvicorn[standard]