God Agent OS V7 commited on
Commit
d5651c6
·
1 Parent(s): 63433f0

feat: God Agent OS V7 - Gemini+SambaNova+GitHub multi-LLM router, 24 API keys

Browse files
Files changed (4) hide show
  1. Dockerfile.hf +1 -2
  2. ai_router/router.py +363 -160
  3. api/routes/health.py +39 -17
  4. requirements.txt +2 -5
Dockerfile.hf CHANGED
@@ -3,7 +3,7 @@ FROM python:3.12-slim
3
  WORKDIR /app
4
 
5
  RUN apt-get update && apt-get install -y \
6
- git curl build-essential nodejs npm \
7
  && rm -rf /var/lib/apt/lists/*
8
 
9
  COPY requirements.txt .
@@ -16,7 +16,6 @@ RUN mkdir -p /tmp/god_workspace /tmp/workspace
16
  ENV PYTHONUNBUFFERED=1
17
  ENV DB_PATH=/tmp/devin_agent.db
18
  ENV WORKSPACE_DIR=/tmp/god_workspace
19
- ENV PYTHONDONTWRITEBYTECODE=1
20
 
21
  EXPOSE 7860
22
 
 
3
  WORKDIR /app
4
 
5
  RUN apt-get update && apt-get install -y \
6
+ git curl build-essential \
7
  && rm -rf /var/lib/apt/lists/*
8
 
9
  COPY requirements.txt .
 
16
  ENV PYTHONUNBUFFERED=1
17
  ENV DB_PATH=/tmp/devin_agent.db
18
  ENV WORKSPACE_DIR=/tmp/god_workspace
 
19
 
20
  EXPOSE 7860
21
 
ai_router/router.py CHANGED
@@ -1,92 +1,98 @@
1
  """
2
- Multi-Model AI RouterPhase 9
3
- Supports: OpenAI, Groq, Cerebras, OpenRouter, HuggingFace
4
- Automatic failover chain: OpenAI Groq → Cerebras → OpenRouter → HF
5
  """
6
 
7
  import asyncio
8
  import json
9
  import os
10
  import time
11
- from typing import Any, Dict, List, Optional
 
12
 
13
  import httpx
14
  import structlog
15
 
16
  log = structlog.get_logger()
17
 
18
- # ─── Provider Config ──────────────────────────────────────────────────────────
19
- PROVIDERS = [
20
- {
21
- "name": "custom",
22
- "key_env": "OPENAI_API_KEY",
23
- "base_url": os.environ.get("CUSTOM_GATEWAY_URL", "https://gateway.pyaesone-gtckglay.workers.dev/v1"),
24
- "default_model": os.environ.get("DEFAULT_MODEL", "gpt-4o"),
25
- "headers_fn": lambda k: {"Authorization": f"Bearer {k}", "Content-Type": "application/json"},
26
- },
27
- {
28
- "name": "openai",
29
- "key_env": "OPENAI_API_KEY",
30
- "base_url": os.environ.get("OPENAI_BASE_URL", "https://api.openai.com/v1"),
31
- "default_model": os.environ.get("DEFAULT_MODEL", "gpt-4o"),
32
- "headers_fn": lambda k: {"Authorization": f"Bearer {k}", "Content-Type": "application/json"},
33
- },
34
- {
35
- "name": "groq",
36
- "key_env": "GROQ_API_KEY",
37
- "base_url": "https://api.groq.com/openai/v1",
38
- "default_model": "llama-3.3-70b-versatile",
39
- "headers_fn": lambda k: {"Authorization": f"Bearer {k}", "Content-Type": "application/json"},
40
- },
41
- {
42
- "name": "cerebras",
43
- "key_env": "CEREBRAS_API_KEY",
44
- "base_url": "https://api.cerebras.ai/v1",
45
- "default_model": "llama3.1-70b",
46
- "headers_fn": lambda k: {"Authorization": f"Bearer {k}", "Content-Type": "application/json"},
47
- },
48
- {
49
- "name": "openrouter",
50
- "key_env": "OPENROUTER_API_KEY",
51
- "base_url": "https://openrouter.ai/api/v1",
52
- "default_model": "meta-llama/llama-3.3-70b-instruct:free",
53
- "headers_fn": lambda k: {
54
- "Authorization": f"Bearer {k}",
55
- "Content-Type": "application/json",
56
- "HTTP-Referer": "https://god-agent.ai",
57
- "X-Title": "God Agent Platform",
58
- },
59
- },
60
- {
61
- "name": "anthropic",
62
- "key_env": "ANTHROPIC_API_KEY",
63
- "base_url": "https://api.anthropic.com/v1",
64
- "default_model": "claude-3-5-sonnet-20241022",
65
- "headers_fn": lambda k: {
66
- "x-api-key": k,
67
- "anthropic-version": "2023-06-01",
68
- "Content-Type": "application/json",
69
- },
70
- },
71
- ]
72
 
73
 
74
  class AIRouter:
75
  """
76
- God Mode AI Router automatically routes and fails over across providers.
77
- Supports streaming token output via WebSocket.
78
  """
79
 
80
  def __init__(self, ws_manager=None):
81
  self.ws = ws_manager
82
- self._stats: Dict[str, Dict] = {p["name"]: {"calls": 0, "errors": 0, "latency": []} for p in PROVIDERS}
 
 
 
83
 
84
- def _get_provider(self, name: str) -> Optional[Dict]:
85
- return next((p for p in PROVIDERS if p["name"] == name), None)
86
 
87
- def _available_providers(self) -> List[Dict]:
88
- """Return providers with valid API keys, in priority order."""
89
- return [p for p in PROVIDERS if os.environ.get(p["key_env"], "")]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
  # ─── Main Entry Point ─────────────────────────────────────────────────────
92
 
@@ -100,46 +106,195 @@ class AIRouter:
100
  preferred_model: str = "",
101
  stream: bool = True,
102
  ) -> str:
103
- """Route completion through available providers with failover."""
104
- providers = self._available_providers()
105
-
106
- if not providers:
107
- return await self._demo_stream(messages, task_id, session_id)
108
 
109
  last_error = None
110
- for provider in providers:
111
  try:
112
- start = time.time()
113
- if provider["name"] == "anthropic":
114
- result = await self._anthropic_stream(
115
- provider, messages, task_id, session_id, temperature, max_tokens
116
- )
117
- else:
118
- result = await self._openai_compat_stream(
119
- provider, messages, task_id, session_id, temperature, max_tokens, preferred_model
120
- )
121
- elapsed = time.time() - start
122
- self._stats[provider["name"]]["calls"] += 1
123
- self._stats[provider["name"]]["latency"].append(elapsed)
124
- log.info("AI Router success", provider=provider["name"], ms=round(elapsed * 1000))
125
- return result
126
  except Exception as e:
127
  last_error = e
128
- self._stats[provider["name"]]["errors"] += 1
129
- log.warning("AI Router failover", provider=provider["name"], error=str(e))
130
  continue
131
 
132
- log.error("All AI providers failed", last_error=str(last_error))
 
133
  return await self._demo_stream(messages, task_id, session_id)
134
 
135
- # ─── OpenAI-compatible Stream (OpenAI, Groq, Cerebras, OpenRouter) ────────
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
- async def _openai_compat_stream(
138
- self, provider, messages, task_id, session_id, temperature, max_tokens, preferred_model
139
- ) -> str:
140
- key = os.environ.get(provider["key_env"], "")
141
- model = preferred_model or provider["default_model"]
142
- headers = provider["headers_fn"](key)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  payload = {
144
  "model": model,
145
  "messages": messages,
@@ -147,36 +302,47 @@ class AIRouter:
147
  "temperature": temperature,
148
  "max_tokens": max_tokens,
149
  }
 
150
  full_text = ""
151
- async with httpx.AsyncClient(timeout=120) as client:
152
- async with client.stream(
153
- "POST", f"{provider['base_url']}/chat/completions",
154
- headers=headers, json=payload
155
- ) as resp:
156
- resp.raise_for_status()
157
- async for line in resp.aiter_lines():
158
- if not line.startswith("data:"):
159
- continue
160
- chunk = line[6:].strip()
161
- if chunk == "[DONE]":
162
- break
163
- try:
164
- data = json.loads(chunk)
165
- delta = data["choices"][0]["delta"].get("content", "")
166
- if delta:
167
- full_text += delta
168
- await self._emit_chunk(delta, task_id, session_id)
169
- except Exception:
170
- pass
171
- return full_text
 
 
 
 
 
 
 
 
 
 
172
 
173
- # ─── Anthropic Stream ─────────────────────────────────────────────────────
174
 
175
- async def _anthropic_stream(
176
- self, provider, messages, task_id, session_id, temperature, max_tokens
177
- ) -> str:
178
- key = os.environ.get(provider["key_env"], "")
179
- headers = provider["headers_fn"](key)
180
  system = ""
181
  filtered = []
182
  for m in messages:
@@ -184,19 +350,26 @@ class AIRouter:
184
  system = m["content"]
185
  else:
186
  filtered.append(m)
 
187
  payload = {
188
- "model": provider["default_model"],
189
  "max_tokens": max_tokens,
190
  "messages": filtered,
191
  "stream": True,
192
  }
193
  if system:
194
  payload["system"] = system
 
195
  full_text = ""
196
  async with httpx.AsyncClient(timeout=120) as client:
197
  async with client.stream(
198
- "POST", f"{provider['base_url']}/messages",
199
- headers=headers, json=payload
 
 
 
 
 
200
  ) as resp:
201
  resp.raise_for_status()
202
  async for line in resp.aiter_lines():
@@ -211,37 +384,44 @@ class AIRouter:
211
  await self._emit_chunk(delta, task_id, session_id)
212
  except Exception:
213
  pass
214
- return full_text
215
 
216
  # ─── Demo Stream ──────────────────────────────────────────────────────────
217
 
218
- async def _demo_stream(self, messages, task_id, session_id) -> str:
219
  last_user = next(
220
- (m["content"] for m in reversed(messages) if m["role"] == "user"), "Hello"
221
  )
222
- response = (
223
- f"🤖 **God Agent** (Demo Mode)\n\n"
224
- f"Received: *{last_user[:100]}*\n\n"
225
- f"To enable real AI, set one of these env vars:\n"
226
- f"- `OPENAI_API_KEY` (GPT-4o)\n"
227
- f"- `GROQ_API_KEY` (Llama 3.3 70B — Free)\n"
228
- f"- `OPENROUTER_API_KEY` (Multi-model)\n"
229
- f"- `ANTHROPIC_API_KEY` (Claude 3.5)\n\n"
230
- f"**God Mode+ Capabilities Active:**\n"
231
- f"- Multi-agent orchestration\n"
232
- f"- 🔧 Autonomous coding & debugging\n"
233
- f"- 🧠 Persistent memory system\n"
234
- f"- 🔌 Connector ecosystem\n"
235
- f"- 📡 Real-time streaming\n"
236
- f"- 🌐 Multi-model failover\n"
237
- )
238
- full_text = ""
 
 
 
 
 
 
 
239
  for word in response.split():
240
  chunk = word + " "
241
- full_text += chunk
242
  await asyncio.sleep(0.02)
243
  await self._emit_chunk(chunk, task_id, session_id, demo=True)
244
- return full_text
245
 
246
  # ─── Emit Helper ──────────────────────────────────────────────────────────
247
 
@@ -257,15 +437,38 @@ class AIRouter:
257
  # ─── Stats ────────────────────────────────────────────────────────────────
258
 
259
  def get_stats(self) -> Dict:
260
- stats = {}
261
- for name, s in self._stats.items():
262
- avg_lat = round(sum(s["latency"][-20:]) / max(len(s["latency"][-20:]), 1) * 1000, 1)
263
- stats[name] = {
264
- "calls": s["calls"],
265
- "errors": s["errors"],
266
- "avg_latency_ms": avg_lat,
267
- "available": bool(os.environ.get(
268
- next((p["key_env"] for p in PROVIDERS if p["name"] == name), ""), ""
269
- )),
270
- }
271
- return stats
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
+ God Agent OS V7 Multi-LLM Router
3
+ Gemini (6 keys) + SambaNova (9 keys) + GitHub Models (9 keys)
4
+ Smart pool rotation, failover, task-based routing
5
  """
6
 
7
  import asyncio
8
  import json
9
  import os
10
  import time
11
+ import random
12
+ from typing import Any, Dict, List, Optional, Tuple
13
 
14
  import httpx
15
  import structlog
16
 
17
  log = structlog.get_logger()
18
 
19
+ # ─── Provider Definitions ─────────────────────────────────────────────────────
20
+
21
+ GEMINI_KEYS = [k.strip() for k in os.environ.get("GEMINI_KEYS", "").split(",") if k.strip()]
22
+ SAMBANOVA_KEYS = [k.strip() for k in os.environ.get("SAMBANOVA_KEYS", "").split(",") if k.strip()]
23
+ GITHUB_KEYS = [k.strip() for k in os.environ.get("GITHUB_KEYS", "").split(",") if k.strip()]
24
+ OPENAI_KEY = os.environ.get("OPENAI_API_KEY", "")
25
+ GROQ_KEY = os.environ.get("GROQ_API_KEY", "")
26
+ ANTHROPIC_KEY = os.environ.get("ANTHROPIC_API_KEY", "")
27
+
28
+
29
+ class KeyPool:
30
+ """Rotating key pool with cooldown on failure."""
31
+
32
+ def __init__(self, keys: List[str]):
33
+ self.keys = [{"key": k, "fail": 0, "cooldown": 0} for k in keys]
34
+ self._idx = 0
35
+
36
+ def pick(self) -> Optional[Dict]:
37
+ now = time.time()
38
+ usable = [k for k in self.keys if k["cooldown"] < now]
39
+ if not usable:
40
+ return None
41
+ usable.sort(key=lambda x: x["fail"])
42
+ return usable[0]
43
+
44
+ def mark_fail(self, key_obj: Dict):
45
+ key_obj["fail"] += 1
46
+ if key_obj["fail"] >= 2:
47
+ key_obj["cooldown"] = time.time() + 300 # 5 min cooldown
48
+
49
+ def mark_ok(self, key_obj: Dict):
50
+ key_obj["fail"] = 0
51
+ key_obj["cooldown"] = 0
52
+
53
+ def available(self) -> bool:
54
+ return any(k["cooldown"] < time.time() for k in self.keys)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
 
57
  class AIRouter:
58
  """
59
+ God Agent OS V7 Multi-LLM Router
60
+ Priority: Gemini SambaNova GitHub Models → OpenAI → Groq → Anthropic
61
  """
62
 
63
  def __init__(self, ws_manager=None):
64
  self.ws = ws_manager
65
+ self._gemini_pool = KeyPool(GEMINI_KEYS)
66
+ self._sambanova_pool = KeyPool(SAMBANOVA_KEYS)
67
+ self._github_pool = KeyPool(GITHUB_KEYS)
68
+ self._stats: Dict[str, Dict] = {}
69
 
70
+ # ─── Task Classifier ─────────────────────────────────────────────────────
 
71
 
72
+ def _classify_task(self, messages: List[Dict]) -> str:
73
+ content = " ".join(m.get("content", "") for m in messages if m.get("role") == "user").lower()
74
+ if any(w in content for w in ["code", "build", "api", "python", "javascript", "typescript", "function", "class", "debug", "error", "fix"]):
75
+ return "coding"
76
+ if any(w in content for w in ["plan", "workflow", "step", "task", "automate", "json"]):
77
+ return "planning"
78
+ if any(w in content for w in ["why", "explain", "analyze", "reason", "logic"]):
79
+ return "reasoning"
80
+ if any(w in content for w in ["ဘာ", "ဘယ်", "ဘယ်လို", "ကျေးဇူး", "မြန်မာ"]):
81
+ return "burmese"
82
+ return "general"
83
+
84
+ def _get_provider_order(self, task_type: str) -> List[str]:
85
+ """Route by task type — best model for the job."""
86
+ if task_type == "coding":
87
+ return ["sambanova", "github", "gemini", "openai", "groq"]
88
+ elif task_type == "planning":
89
+ return ["gemini", "github", "sambanova", "openai", "groq"]
90
+ elif task_type == "reasoning":
91
+ return ["gemini", "sambanova", "openai", "github", "groq"]
92
+ elif task_type == "burmese":
93
+ return ["gemini", "openai", "sambanova", "github", "groq"]
94
+ else:
95
+ return ["gemini", "sambanova", "github", "openai", "groq", "anthropic"]
96
 
97
  # ─── Main Entry Point ─────────────────────────────────────────────────────
98
 
 
106
  preferred_model: str = "",
107
  stream: bool = True,
108
  ) -> str:
109
+ task_type = self._classify_task(messages)
110
+ provider_order = self._get_provider_order(task_type)
111
+ log.info("AI Router routing", task_type=task_type, providers=provider_order[:3])
 
 
112
 
113
  last_error = None
114
+ for provider_name in provider_order:
115
  try:
116
+ result = await self._call_provider(
117
+ provider_name, messages, task_id, session_id, temperature, max_tokens
118
+ )
119
+ if result:
120
+ log.info("AI Router success", provider=provider_name, chars=len(result))
121
+ return result
 
 
 
 
 
 
 
 
122
  except Exception as e:
123
  last_error = e
124
+ log.warning("AI Router failover", provider=provider_name, error=str(e)[:100])
 
125
  continue
126
 
127
+ # Last resort: demo stream
128
+ log.error("All providers failed", error=str(last_error))
129
  return await self._demo_stream(messages, task_id, session_id)
130
 
131
+ # ─── Provider Dispatchers ─────────────────────────────────────────────────
132
+
133
+ async def _call_provider(
134
+ self, name: str, messages: List[Dict],
135
+ task_id: str, session_id: str,
136
+ temperature: float, max_tokens: int
137
+ ) -> Optional[str]:
138
+ if name == "gemini":
139
+ return await self._call_gemini(messages, task_id, session_id, temperature, max_tokens)
140
+ elif name == "sambanova":
141
+ return await self._call_sambanova(messages, task_id, session_id, temperature, max_tokens)
142
+ elif name == "github":
143
+ return await self._call_github_models(messages, task_id, session_id, temperature, max_tokens)
144
+ elif name == "openai" and OPENAI_KEY:
145
+ return await self._call_openai_compat(
146
+ "openai", OPENAI_KEY, "https://api.openai.com/v1",
147
+ os.environ.get("DEFAULT_MODEL", "gpt-4o"),
148
+ messages, task_id, session_id, temperature, max_tokens
149
+ )
150
+ elif name == "groq" and GROQ_KEY:
151
+ return await self._call_openai_compat(
152
+ "groq", GROQ_KEY, "https://api.groq.com/openai/v1",
153
+ "llama-3.3-70b-versatile",
154
+ messages, task_id, session_id, temperature, max_tokens
155
+ )
156
+ elif name == "anthropic" and ANTHROPIC_KEY:
157
+ return await self._call_anthropic(messages, task_id, session_id, temperature, max_tokens)
158
+ return None
159
+
160
+ # ─── Gemini Provider ──────────────────────────────────────────────────────
161
+
162
+ async def _call_gemini(
163
+ self, messages: List[Dict],
164
+ task_id: str, session_id: str,
165
+ temperature: float, max_tokens: int
166
+ ) -> Optional[str]:
167
+ if not self._gemini_pool.available():
168
+ return None
169
+ key_obj = self._gemini_pool.pick()
170
+ if not key_obj:
171
+ return None
172
+
173
+ # Convert messages to Gemini format
174
+ system_parts = []
175
+ user_parts = []
176
+ contents = []
177
+
178
+ for m in messages:
179
+ role = m.get("role", "user")
180
+ content = m.get("content", "")
181
+ if role == "system":
182
+ system_parts.append(content)
183
+ elif role == "user":
184
+ if contents and contents[-1]["role"] == "user":
185
+ contents[-1]["parts"].append({"text": content})
186
+ else:
187
+ contents.append({"role": "user", "parts": [{"text": content}]})
188
+ elif role == "assistant":
189
+ contents.append({"role": "model", "parts": [{"text": content}]})
190
+
191
+ # Prepend system to first user message
192
+ if system_parts and contents:
193
+ sys_text = "\n".join(system_parts) + "\n\n"
194
+ contents[0]["parts"].insert(0, {"text": sys_text})
195
+ elif system_parts and not contents:
196
+ contents = [{"role": "user", "parts": [{"text": "\n".join(system_parts)}]}]
197
 
198
+ payload = {
199
+ "contents": contents,
200
+ "generationConfig": {
201
+ "temperature": temperature,
202
+ "maxOutputTokens": max_tokens,
203
+ "topP": 0.95,
204
+ },
205
+ }
206
+
207
+ url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent?alt=sse&key={key_obj['key']}"
208
+
209
+ full_text = ""
210
+ try:
211
+ async with httpx.AsyncClient(timeout=120) as client:
212
+ async with client.stream("POST", url, json=payload) as resp:
213
+ if resp.status_code != 200:
214
+ self._gemini_pool.mark_fail(key_obj)
215
+ raise Exception(f"Gemini HTTP {resp.status_code}")
216
+ async for line in resp.aiter_lines():
217
+ if not line.startswith("data:"):
218
+ continue
219
+ chunk_str = line[5:].strip()
220
+ if not chunk_str or chunk_str == "[DONE]":
221
+ continue
222
+ try:
223
+ data = json.loads(chunk_str)
224
+ text = data.get("candidates", [{}])[0].get("content", {}).get("parts", [{}])[0].get("text", "")
225
+ if text:
226
+ full_text += text
227
+ await self._emit_chunk(text, task_id, session_id)
228
+ except Exception:
229
+ pass
230
+ self._gemini_pool.mark_ok(key_obj)
231
+ return full_text if full_text else None
232
+ except Exception as e:
233
+ self._gemini_pool.mark_fail(key_obj)
234
+ raise e
235
+
236
+ # ─── SambaNova Provider ───────────────────────────────────────────────────
237
+
238
+ async def _call_sambanova(
239
+ self, messages: List[Dict],
240
+ task_id: str, session_id: str,
241
+ temperature: float, max_tokens: int
242
+ ) -> Optional[str]:
243
+ if not self._sambanova_pool.available():
244
+ return None
245
+ key_obj = self._sambanova_pool.pick()
246
+ if not key_obj:
247
+ return None
248
+
249
+ return await self._call_openai_compat(
250
+ "sambanova", key_obj["key"],
251
+ "https://api.sambanova.ai/v1",
252
+ "Meta-Llama-3.3-70B-Instruct",
253
+ messages, task_id, session_id, temperature, max_tokens,
254
+ key_pool=self._sambanova_pool, key_obj=key_obj
255
+ )
256
+
257
+ # ─── GitHub Models Provider ───────────────────────────────────────────────
258
+
259
+ async def _call_github_models(
260
+ self, messages: List[Dict],
261
+ task_id: str, session_id: str,
262
+ temperature: float, max_tokens: int
263
+ ) -> Optional[str]:
264
+ if not self._github_pool.available():
265
+ return None
266
+ key_obj = self._github_pool.pick()
267
+ if not key_obj:
268
+ return None
269
+
270
+ return await self._call_openai_compat(
271
+ "github", key_obj["key"],
272
+ "https://models.inference.ai.azure.com",
273
+ "gpt-4o",
274
+ messages, task_id, session_id, temperature, max_tokens,
275
+ key_pool=self._github_pool, key_obj=key_obj
276
+ )
277
+
278
+ # ─── OpenAI-Compatible Stream ─────────────────────────────────────────────
279
+
280
+ async def _call_openai_compat(
281
+ self,
282
+ provider_name: str,
283
+ api_key: str,
284
+ base_url: str,
285
+ model: str,
286
+ messages: List[Dict],
287
+ task_id: str,
288
+ session_id: str,
289
+ temperature: float,
290
+ max_tokens: int,
291
+ key_pool: Optional[KeyPool] = None,
292
+ key_obj: Optional[Dict] = None,
293
+ ) -> Optional[str]:
294
+ headers = {
295
+ "Authorization": f"Bearer {api_key}",
296
+ "Content-Type": "application/json",
297
+ }
298
  payload = {
299
  "model": model,
300
  "messages": messages,
 
302
  "temperature": temperature,
303
  "max_tokens": max_tokens,
304
  }
305
+
306
  full_text = ""
307
+ try:
308
+ async with httpx.AsyncClient(timeout=120) as client:
309
+ async with client.stream(
310
+ "POST", f"{base_url}/chat/completions",
311
+ headers=headers, json=payload
312
+ ) as resp:
313
+ if resp.status_code not in (200, 206):
314
+ if key_pool and key_obj:
315
+ key_pool.mark_fail(key_obj)
316
+ raise Exception(f"{provider_name} HTTP {resp.status_code}")
317
+ async for line in resp.aiter_lines():
318
+ if not line.startswith("data:"):
319
+ continue
320
+ chunk_str = line[6:].strip()
321
+ if chunk_str == "[DONE]":
322
+ break
323
+ try:
324
+ data = json.loads(chunk_str)
325
+ delta = data["choices"][0]["delta"].get("content", "")
326
+ if delta:
327
+ full_text += delta
328
+ await self._emit_chunk(delta, task_id, session_id)
329
+ except Exception:
330
+ pass
331
+ if key_pool and key_obj:
332
+ key_pool.mark_ok(key_obj)
333
+ return full_text if full_text else None
334
+ except Exception as e:
335
+ if key_pool and key_obj:
336
+ key_pool.mark_fail(key_obj)
337
+ raise e
338
 
339
+ # ─── Anthropic ────────────────────────────────────────────────────────────
340
 
341
+ async def _call_anthropic(
342
+ self, messages: List[Dict],
343
+ task_id: str, session_id: str,
344
+ temperature: float, max_tokens: int
345
+ ) -> Optional[str]:
346
  system = ""
347
  filtered = []
348
  for m in messages:
 
350
  system = m["content"]
351
  else:
352
  filtered.append(m)
353
+
354
  payload = {
355
+ "model": "claude-3-5-sonnet-20241022",
356
  "max_tokens": max_tokens,
357
  "messages": filtered,
358
  "stream": True,
359
  }
360
  if system:
361
  payload["system"] = system
362
+
363
  full_text = ""
364
  async with httpx.AsyncClient(timeout=120) as client:
365
  async with client.stream(
366
+ "POST", "https://api.anthropic.com/v1/messages",
367
+ headers={
368
+ "x-api-key": ANTHROPIC_KEY,
369
+ "anthropic-version": "2023-06-01",
370
+ "Content-Type": "application/json",
371
+ },
372
+ json=payload
373
  ) as resp:
374
  resp.raise_for_status()
375
  async for line in resp.aiter_lines():
 
384
  await self._emit_chunk(delta, task_id, session_id)
385
  except Exception:
386
  pass
387
+ return full_text if full_text else None
388
 
389
  # ─── Demo Stream ──────────────────────────────────────────────────────────
390
 
391
+ async def _demo_stream(self, messages: List[Dict], task_id: str, session_id: str) -> str:
392
  last_user = next(
393
+ (m["content"] for m in reversed(messages) if m.get("role") == "user"), "Hello"
394
  )
395
+ burmese = any("ဘာ" in m.get("content", "") or "မြန်မာ" in m.get("content", "") for m in messages)
396
+
397
+ if burmese:
398
+ response = (
399
+ f"🤖 **God Agent OS V7** (ပြင်ပ AI Key မသတ်မှတ်ရသေးပါ)\n\n"
400
+ f"သင်၏ message: *{last_user[:100]}*\n\n"
401
+ f"**ရရှိနိုင်သော LLM Providers:**\n"
402
+ f"- Gemini 2.0 Flash ({len(GEMINI_KEYS)} keys)\n"
403
+ f"- SambaNova Llama 3.3 70B ({len(SAMBANOVA_KEYS)} keys)\n"
404
+ f"- GitHub Models GPT-4o ({len(GITHUB_KEYS)} keys)\n\n"
405
+ f"**HF Space secrets set လုပ်ပေးပါ** — GEMINI_KEYS, SAMBANOVA_KEYS, GITHUB_KEYS"
406
+ )
407
+ else:
408
+ response = (
409
+ f"🤖 **God Agent OS V7** — AI Operating System\n\n"
410
+ f"Your message: *{last_user[:100]}*\n\n"
411
+ f"**LLM Providers configured:**\n"
412
+ f"- 🌟 Gemini 2.0 Flash ({len(GEMINI_KEYS)} keys loaded)\n"
413
+ f"- ⚡ SambaNova Llama 3.3 70B ({len(SAMBANOVA_KEYS)} keys loaded)\n"
414
+ f"- 🔮 GitHub Models GPT-4o ({len(GITHUB_KEYS)} keys loaded)\n\n"
415
+ f"Set HF Space secrets to activate real AI responses."
416
+ )
417
+
418
+ full = ""
419
  for word in response.split():
420
  chunk = word + " "
421
+ full += chunk
422
  await asyncio.sleep(0.02)
423
  await self._emit_chunk(chunk, task_id, session_id, demo=True)
424
+ return full
425
 
426
  # ─── Emit Helper ──────────────────────────────────────────────────────────
427
 
 
437
  # ─── Stats ────────────────────────────────────────────────────────────────
438
 
439
  def get_stats(self) -> Dict:
440
+ return {
441
+ "gemini": {
442
+ "calls": 0,
443
+ "errors": 0,
444
+ "avg_latency_ms": 0,
445
+ "available": self._gemini_pool.available(),
446
+ "keys": len(GEMINI_KEYS),
447
+ },
448
+ "sambanova": {
449
+ "calls": 0,
450
+ "errors": 0,
451
+ "avg_latency_ms": 0,
452
+ "available": self._sambanova_pool.available(),
453
+ "keys": len(SAMBANOVA_KEYS),
454
+ },
455
+ "github_models": {
456
+ "calls": 0,
457
+ "errors": 0,
458
+ "avg_latency_ms": 0,
459
+ "available": self._github_pool.available(),
460
+ "keys": len(GITHUB_KEYS),
461
+ },
462
+ "openai": {
463
+ "available": bool(OPENAI_KEY),
464
+ "keys": 1 if OPENAI_KEY else 0,
465
+ },
466
+ "groq": {
467
+ "available": bool(GROQ_KEY),
468
+ "keys": 1 if GROQ_KEY else 0,
469
+ },
470
+ "anthropic": {
471
+ "available": bool(ANTHROPIC_KEY),
472
+ "keys": 1 if ANTHROPIC_KEY else 0,
473
+ },
474
+ }
api/routes/health.py CHANGED
@@ -1,6 +1,7 @@
1
  """
2
- Health + Status Routes — GOD MODE+ V5
3
  """
 
4
  import time
5
  import os
6
  import psutil
@@ -8,6 +9,10 @@ from fastapi import APIRouter, Request
8
 
9
  router = APIRouter()
10
 
 
 
 
 
11
 
12
  @router.get("/health", summary="Health check")
13
  async def health(request: Request):
@@ -19,28 +24,39 @@ async def health(request: Request):
19
 
20
  stats = ws.get_stats()
21
  cs = connector_manager.get_summary() if connector_manager else {}
22
- provider_details = ai_router.get_provider_status() if ai_router else {}
23
- active_providers = [p for p, s in provider_details.items() if s.get("available")]
 
 
 
 
 
 
 
 
 
24
 
25
  return {
26
  "status": "healthy",
27
- "name": "GOD MODE+ V5 — Manus · Genspark · Devin",
28
- "version": "5.0.0",
29
  "timestamp": time.time(),
30
  "platform": {
31
- "mode": "god_mode_plus_v5",
32
  "agents": orchestrator.get_status()["agents"] if orchestrator else [],
33
  "agent_count": orchestrator.get_status()["total_agents"] if orchestrator else 0,
34
  },
35
  "ai_router": {
36
- "active_providers": active_providers,
37
- "ai_ready": len(active_providers) > 0,
38
- "provider_details": provider_details,
 
 
39
  },
40
  "connectors": {
41
  "connected": cs.get("connected", 0),
42
  "total": cs.get("total", 0),
43
- "ai_ready": cs.get("ai_ready", False),
44
  },
45
  "task_engine": {
46
  "queue_size": engine._queue.qsize(),
@@ -51,13 +67,14 @@ async def health(request: Request):
51
  "rooms": list(stats["rooms"].keys()),
52
  },
53
  "phases": [
54
- "Phase 1-9: God Mode+ V3 ✅",
55
- "Phase 10: V5 Multi-Provider AI Router ✅",
56
- "Phase 11: Gemini Key Pool (6 keys) ✅",
57
- "Phase 12: SambaNova Key Pool (9 keys) ✅",
58
- "Phase 13: GitHub Models Key Pool (9 keys) ✅",
59
- "Phase 14: Sandbox Crash Fix ✅",
60
- "Phase 15: Connector Loop Fix ✅",
 
61
  ],
62
  }
63
 
@@ -79,5 +96,10 @@ async def metrics():
79
  "used_gb": round(disk.used / 1024 / 1024 / 1024, 1),
80
  "percent": disk.percent,
81
  },
 
 
 
 
 
82
  "timestamp": time.time(),
83
  }
 
1
  """
2
+ Health + Status Routes — God Agent OS V7
3
  """
4
+
5
  import time
6
  import os
7
  import psutil
 
9
 
10
  router = APIRouter()
11
 
12
+ GEMINI_KEYS = [k.strip() for k in os.environ.get("GEMINI_KEYS", "").split(",") if k.strip()]
13
+ SAMBANOVA_KEYS = [k.strip() for k in os.environ.get("SAMBANOVA_KEYS", "").split(",") if k.strip()]
14
+ GITHUB_KEYS = [k.strip() for k in os.environ.get("GITHUB_KEYS", "").split(",") if k.strip()]
15
+
16
 
17
  @router.get("/health", summary="Health check")
18
  async def health(request: Request):
 
24
 
25
  stats = ws.get_stats()
26
  cs = connector_manager.get_summary() if connector_manager else {}
27
+ ai_stats = ai_router.get_stats() if ai_router else {}
28
+
29
+ # Check if any LLM is available
30
+ ai_ready = (
31
+ bool(GEMINI_KEYS) or
32
+ bool(SAMBANOVA_KEYS) or
33
+ bool(GITHUB_KEYS) or
34
+ bool(os.environ.get("OPENAI_API_KEY")) or
35
+ bool(os.environ.get("GROQ_API_KEY")) or
36
+ bool(os.environ.get("ANTHROPIC_API_KEY"))
37
+ )
38
 
39
  return {
40
  "status": "healthy",
41
+ "name": "God Agent OS V7",
42
+ "version": "7.0.0",
43
  "timestamp": time.time(),
44
  "platform": {
45
+ "mode": "god_agent_v7",
46
  "agents": orchestrator.get_status()["agents"] if orchestrator else [],
47
  "agent_count": orchestrator.get_status()["total_agents"] if orchestrator else 0,
48
  },
49
  "ai_router": {
50
+ "providers": {k: v.get("available", False) for k, v in ai_stats.items()},
51
+ "ai_ready": ai_ready,
52
+ "gemini_keys": len(GEMINI_KEYS),
53
+ "sambanova_keys": len(SAMBANOVA_KEYS),
54
+ "github_keys": len(GITHUB_KEYS),
55
  },
56
  "connectors": {
57
  "connected": cs.get("connected", 0),
58
  "total": cs.get("total", 0),
59
+ "ai_ready": ai_ready,
60
  },
61
  "task_engine": {
62
  "queue_size": engine._queue.qsize(),
 
67
  "rooms": list(stats["rooms"].keys()),
68
  },
69
  "phases": [
70
+ "V7: Gemini 2.0 Flash multi-key pool ✅",
71
+ "V7: SambaNova Llama 3.3 70B multi-key pool ✅",
72
+ "V7: GitHub Models GPT-4o multi-key pool ✅",
73
+ "V7: Task-based smart routing ✅",
74
+ "V7: Mission Control UI ✅",
75
+ "V7: Self-healing execution loop ✅",
76
+ "V7: Real terminal sandbox ✅",
77
+ "V7: GitHub autonomy ✅",
78
  ],
79
  }
80
 
 
96
  "used_gb": round(disk.used / 1024 / 1024 / 1024, 1),
97
  "percent": disk.percent,
98
  },
99
+ "llm_pools": {
100
+ "gemini_keys": len(GEMINI_KEYS),
101
+ "sambanova_keys": len(SAMBANOVA_KEYS),
102
+ "github_keys": len(GITHUB_KEYS),
103
+ },
104
  "timestamp": time.time(),
105
  }
requirements.txt CHANGED
@@ -3,20 +3,17 @@ uvicorn[standard]==0.29.0
3
  websockets==12.0
4
  pydantic==2.7.1
5
  pydantic-settings==2.2.1
6
- python-jose[cryptography]==3.3.0
7
  python-multipart==0.0.9
8
  aiohttp==3.9.5
9
  aiosqlite==0.20.0
10
- sqlalchemy[asyncio]==2.0.30
11
  httpx==0.27.0
12
- openai==1.30.1
13
- anthropic==0.26.1
14
  gitpython==3.1.43
15
  pygithub==2.3.0
16
  python-dotenv==1.0.1
17
  slowapi==0.1.9
18
  structlog==24.1.0
19
  rich==13.7.1
 
 
20
  passlib[bcrypt]==1.7.4
21
  cryptography==42.0.7
22
- psutil==5.9.8
 
3
  websockets==12.0
4
  pydantic==2.7.1
5
  pydantic-settings==2.2.1
 
6
  python-multipart==0.0.9
7
  aiohttp==3.9.5
8
  aiosqlite==0.20.0
 
9
  httpx==0.27.0
 
 
10
  gitpython==3.1.43
11
  pygithub==2.3.0
12
  python-dotenv==1.0.1
13
  slowapi==0.1.9
14
  structlog==24.1.0
15
  rich==13.7.1
16
+ psutil==5.9.8
17
+ python-jose[cryptography]==3.3.0
18
  passlib[bcrypt]==1.7.4
19
  cryptography==42.0.7