Spaces:
Runtime error
Runtime error
File size: 15,544 Bytes
f7e1070 e36381e f7e1070 e36381e f7e1070 e36381e f7e1070 e36381e f7e1070 e36381e f7e1070 e36381e f7e1070 e36381e f7e1070 e36381e f7e1070 e36381e f7e1070 e36381e f7e1070 e36381e f7e1070 7fd3e2c f7e1070 e36381e f7e1070 e36381e f7e1070 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 | #!/usr/bin/env bash
# Cloud dev worker β parallel team of cloud-based coders like Claude Code multi-agent.
# Each provider picks a DIFFERENT 'ready' priority (avoid duplicating work).
# Output goes to same dir/format as qwen-coder β validator + reviewer + auto-commit reuses.
#
# Usage: dev-cloud-worker.sh <provider>
# provider = github | samba | cloudflare | groq | gemini
#
# Rate-limit aware per provider (set by cron schedule, NOT inside script).
# Cross-worker coordination: lockfile per (priority, provider) in ~/.surrogate/state/dev-locks/
# Global priority lock: 30-min window, so same priority only gets fresh attempt per provider
# every 30 min (prevents redundant work, allows tournament of implementations over time).
set -u
PROVIDER="${1:?usage: dev-cloud-worker.sh <github|samba|cloudflare|groq|gemini>}"
LOG="$HOME/.surrogate/logs/dev-cloud-$PROVIDER.log"
OUT_DIR="$HOME/.hermes/workspace/dev-cloud-$PROVIDER"
SHARED="$HOME/.hermes/workspace/swarm-shared"
LOCK_DIR="$HOME/.surrogate/state/dev-locks"
mkdir -p "$(dirname "$LOG")" "$OUT_DIR" "$LOCK_DIR"
START=$(date +%s)
# -------- Pick a priority --------
# Two modes:
# (A) Daemon-driven (queue mode): HERMES_PRIO_ID env var is set by the daemon
# after BLPOP + redis lock. We trust the daemon's lock and just pick that
# priority from priority.json β skipping the file-lock check that was
# designed for cron mode and now rejects daemon-driven work.
# (B) Cron-driven (legacy): no env var. Use file-lock selection: 30-min
# self-skip + 15-min any-provider skip. Still useful when cron fires N
# providers simultaneously.
PRIORITY=$(python3 -c "
import json, os, time, sys
try:
with open('$SHARED/priority.json') as f: d = json.load(f)
except Exception as e:
print('', file=sys.stderr); sys.exit(0)
now = time.time()
PROVIDER = '$PROVIDER'
LOCK_DIR = '$LOCK_DIR'
PINNED = os.environ.get('HERMES_PRIO_ID', '').strip()
# Mode A: daemon pinned this priority β no file-lock gating, trust redis lock.
if PINNED:
for p in d.get('priorities', []):
if p.get('id') == PINNED and p.get('status') == 'ready':
print(json.dumps(p))
# Still touch the file lock (useful for heuristics elsewhere, e.g.
# the multi-priority batch collector below which reads lock mtimes).
open(os.path.join(LOCK_DIR, f'{PINNED}_{PROVIDER}'), 'w').close()
sys.exit(0)
# Pinned priority missing/not ready β fall through to generic selection
print(f'pinned {PINNED} not found/not-ready β falling back to selection', file=sys.stderr)
# Mode B: cron-driven selection via file locks.
recent_any = set()
recent_self = set()
try:
for fn in os.listdir(LOCK_DIR):
parts = fn.rsplit('_', 1)
if len(parts) != 2: continue
prio_id, prov = parts
try:
mtime = os.path.getmtime(os.path.join(LOCK_DIR, fn))
except OSError: continue
age = now - mtime
if age < 900: # 15 min β any provider blocks
recent_any.add(prio_id)
if prov == PROVIDER and age < 1800: # 30 min β this provider re-skip
recent_self.add(prio_id)
except FileNotFoundError: pass
prov_offset = {'github':0,'samba':1,'cloudflare':2,'groq':3,'gemini':4,'qwen-local':5}.get(PROVIDER, 0)
priorities = [p for p in d.get('priorities', []) if p.get('status') == 'ready']
rotated = priorities[prov_offset:] + priorities[:prov_offset]
for p in rotated:
pid = p.get('id','')
if not pid: continue
if pid in recent_any: continue
if pid in recent_self: continue
print(json.dumps(p))
open(os.path.join(LOCK_DIR, f'{pid}_{PROVIDER}'), 'w').close()
sys.exit(0)
sys.exit(0)
" 2>>"$LOG")
if [[ -z "$PRIORITY" ]]; then
echo "[$(date '+%H:%M:%S')] no free priority (all locked or none ready)" >> "$LOG"
exit 0
fi
# Multi-priority per run: each worker handles up to 3 priorities in parallel subshells
# Env var MULTI_PRIORITY_COUNT=3 (override via env if needed)
MULTI_COUNT=${MULTI_PRIORITY_COUNT:-3}
# Collect up to MULTI_COUNT priorities (first one already picked above)
PRIORITIES_JSON=$(python3 -c "
import json, os, time
ps = json.loads('''$PRIORITY'''.replace('\n', ' '))
# Already picked one β fetch more from priority.json
priorities = [ps]
try:
with open('$SHARED/priority.json') as f: d = json.load(f)
ready = [p for p in d.get('priorities', []) if p.get('status') == 'ready' and p.get('id') != ps.get('id')]
# Check locks
lock_dir = '$LOCK_DIR'
now = time.time()
for extra in ready:
pid = extra.get('id','')
if not pid: continue
# Skip if any provider locked in last 15 min
blocked = False
try:
for fn in os.listdir(lock_dir):
if fn.startswith(pid + '_') and now - os.path.getmtime(os.path.join(lock_dir, fn)) < 900:
blocked = True
break
except FileNotFoundError: pass
if blocked: continue
priorities.append(extra)
# Touch lock
open(os.path.join(lock_dir, f'{pid}_$PROVIDER'), 'w').close()
if len(priorities) >= $MULTI_COUNT: break
except Exception: pass
print(json.dumps(priorities))
" 2>/dev/null || echo "[$PRIORITY]")
echo "[$(date '+%H:%M:%S')] $PROVIDER batch: $(echo "$PRIORITIES_JSON" | python3 -c "import json,sys; print(len(json.loads(sys.stdin.read())))") priorities" >> "$LOG"
# Work on first one (serially inside this script β parallel at provider level via cron)
# Future: subshell per priority β for now, 1 per invocation but worker fires multiple
PRIORITY=$(echo "$PRIORITIES_JSON" | python3 -c "import json,sys; p = json.loads(sys.stdin.read())[0]; print(json.dumps(p))")
PRIO_ID=$(echo "$PRIORITY" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['id'])")
PRIO_TITLE=$(echo "$PRIORITY" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['title'])")
PRIO_PROJECT=$(echo "$PRIORITY" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('project','?'))")
echo "[$(date '+%H:%M:%S')] $PROVIDER picked $PRIO_ID ($PRIO_PROJECT: ${PRIO_TITLE:0:60})" >> "$LOG"
# -------- Rich context injection (B: enrich with repo + similar funcs + few-shot + deltas) --------
source "$HOME/.surrogate/bin/lib/context_builder.sh"
build_rich_context "$PRIO_PROJECT" "$PRIO_ID" "$PRIO_TITLE"
# Sets: REPO_MAP, SIMILAR_FUNCS, RAG_EXAMPLES, SEMANTIC_RAG, FEWSHOT_ACCEPTED, ANTI_PATTERNS, PROMPT_DELTAS, PRIO_SPEC
# Build prompt via temp file (avoids bash double-interpretation of backticks in command substitution)
PROMPT_FILE=$(/usr/bin/mktemp)
trap "rm -f $PROMPT_FILE" EXIT
# Build prompt. When a detailed spec exists (>500 chars), it's authoritative β
# drop the fuzzy RAG/SIMILAR/FEWSHOT signals that would bloat the prompt past
# the tightest provider's request-size cap (Groq free tier ~20KB).
HAS_SPEC=0
[[ ${#PRIO_SPEC} -gt 500 ]] && HAS_SPEC=1
cat > "$PROMPT_FILE" <<EOF
You are $PROVIDER-cloud-dev. Team: github(Codestral), samba(DeepSeek-V3.1), cloudflare(R1-distill-32B), groq(Qwen3-32B), cerebras(Qwen3-235B), nvidia(Llama-3.3-70B), qwen-local(Qwen-Coder-7B). Output goes to tournament where best-of-N is synthesized by Sonnet.
β οΈ HARD QUALITY RULES (reviewer blocks if violated β no exceptions):
### Code completeness
- NEVER truncate lines. If \`raise HTTPException(...\` appears, write it completely with status_code, detail, every arg.
- NEVER use \`# TODO\` or \`# ...\` in production code. If unsure, write the simplest working version.
- EVERY function has a complete body; no stubs unless the spec explicitly says "scaffold".
### Imports (most-frequent reviewer bug)
- Use ABSOLUTE imports matching the project's layout: \`from app.models.finding import Finding\`, NOT \`from .models import Finding\` (relative imports break when tests run from repo root).
- Verify every imported symbol EXISTS in REPO MAP below. If the symbol is not mapped, do NOT import it β ask via comment "# need to verify <symbol> exists" or pick an alternate.
- Common name collisions: never shadow stdlib (\`uuid.py\`, \`logging.py\`, \`typing.py\` as module names).
### Async patterns (second-most-frequent bug)
- \`boto3\` is SYNC. To call it from async code, wrap with \`await asyncio.to_thread(client.method, ...)\`. The callable is \`client.method\`; the args come AFTER (do NOT do \`asyncio.to_thread(client.method(...))\`).
- Paginators: \`paginator = client.get_paginator('op'); async for page in aiter(paginator.paginate(...))\` OR manually loop pages inside a to_thread wrapper. Do NOT wrap \`.paginate(...)\` itself in to_thread β \`.paginate()\` returns an iterator, not a coroutine.
- \`await\` only on awaitables. \`async for\` only on async iterators. Use \`aiter()\` to bridge syncβasync iterators.
### Pydantic / SQLAlchemy
- Pydantic v2: fields with enums MUST be typed as the Enum, not \`str\` (type hints drive validation).
- SQLAlchemy models: every column has explicit type + nullable/default. Use \`Mapped[str | None]\` syntax (v2), not \`Column(...)\`.
- ORM β Pydantic: use \`model_config = ConfigDict(from_attributes=True)\` (v2) not deprecated \`from_orm\`.
### Test files
- Absolute imports in tests: \`from app.services.X import Y\` (tests run via \`pytest backend/tests/\` from project root).
- Use existing fixtures from \`conftest.py\` β do NOT re-define \`async_session\`, \`test_client\`, etc.
- Each test independent + self-seeding. NO \`pytest.mark.order\` chaining unless spec says so.
### Validator gates (enforced automatically, 0/100 if fail)
1. \`ast.parse\` β syntax OK
2. \`import <top-level-dep>\` β only stdlib + real deps (boto3, pydantic, sqlalchemy, fastapi, etc.)
3. \`pytest -x\` on embedded tests β must PASS (not just collect)
4. No secrets (AWS keys, tokens) in output
### Anti-hallucination
- Imports: ONLY stdlib OR packages actually in the project's pyproject.toml/package.json OR ones you verified via WebFetch
- APIs: every function/class must exist in REPO MAP or be a well-known library method
- If unsure whether an API exists β write a stub with \`raise NotImplementedError("verify <api>")\` so reviewer sees it explicitly, rather than hallucinating a signature
$([ -n "$PROMPT_DELTAS" ] && echo "=== ACTIVE-LEARNED RULES (avoid these past mistakes) ===" && echo "$PROMPT_DELTAS")
=== TASK ===
PROJECT: $PRIO_PROJECT
PRIORITY_ID: $PRIO_ID
TITLE: $PRIO_TITLE
EOF
if [[ $HAS_SPEC -eq 1 ]]; then
# Authoritative path: spec + repo map + authoritative sources (NEW) + anti-patterns.
cat >> "$PROMPT_FILE" <<EOF
=== FULL SPEC (AUTHORITATIVE β follow exactly) ===
$PRIO_SPEC
=== REPO MAP (existing files/symbols β do not invent new modules) ===
$REPO_MAP
$([ -n "$AUTHORITATIVE_CONTEXT" ] && echo "=== AUTHORITATIVE KNOWLEDGE (from task-routed sources β use as ground truth) ===" && echo "$AUTHORITATIVE_CONTEXT")
$([ -n "$HERMES_RECALL" ] && echo "=== HERMES HANDLED SIMILAR BEFORE (learn from past decisions) ===" && echo "$HERMES_RECALL")
$([ -n "$GRAPH_CONTEXT" ] && echo "=== RELATED PRIORITIES + LEARNED RULES (graph) ===" && echo "$GRAPH_CONTEXT")
$([ -n "$ANTI_PATTERNS" ] && echo "=== DO NOT REPEAT THESE BUGS (from recent rejections) ===" && echo "$ANTI_PATTERNS")
EOF
else
# No spec β use full RAG scaffolding to give the model grounding.
cat >> "$PROMPT_FILE" <<EOF
=== REPO MAP (all files + exported symbols) ===
$REPO_MAP
=== EXISTING SIMILAR FUNCTIONS IN THIS PROJECT (match their style) ===
$SIMILAR_FUNCS
=== RAG (FTS keyword-matched project patterns) ===
$RAG_EXAMPLES
$([ -n "$AUTHORITATIVE_CONTEXT" ] && echo "=== AUTHORITATIVE KNOWLEDGE (task-routed: scraped experts/docs) ===" && echo "$AUTHORITATIVE_CONTEXT")
$([ -n "$HERMES_RECALL" ] && echo "=== HERMES HANDLED SIMILAR BEFORE ===" && echo "$HERMES_RECALL")
=== SEMANTIC RAG (embedding-matched related knowledge) ===
$SEMANTIC_RAG
$([ -n "$FEWSHOT_ACCEPTED" ] && echo "=== GOLD EXAMPLE (previously accepted output, quality β₯ 7) ===" && echo "$FEWSHOT_ACCEPTED")
$([ -n "$ANTI_PATTERNS" ] && echo "=== DO NOT REPEAT THESE BUGS (from recent rejections) ===" && echo "$ANTI_PATTERNS")
EOF
fi
if [[ -n "$ANTI_PATTERNS" ]]; then
echo "=== ANTI-PATTERNS (DO NOT repeat these bugs from recent rejections) ===" >> "$PROMPT_FILE"
echo "$ANTI_PATTERNS" >> "$PROMPT_FILE"
fi
cat >> "$PROMPT_FILE" <<'OUTPUT_SPEC'
=== OUTPUT (strict structure) ===
## Implementation Plan
- 3-5 bullets in dependency order
## Code
```<language>
# complete runnable, verified imports, no TODO stubs
```
## Tests
```<language>
# 3 cases: happy + edge + error
```
## Acceptance Criteria
- 3 bullets with exact commands/assertions
Total under 2000 tokens.
OUTPUT_SPEC
PROMPT=$(cat "$PROMPT_FILE")
# -------- Route to appropriate cloud bridge --------
case "$PROVIDER" in
github)
# Codestral-2501 is Mistral's dedicated code model β free via PAT, top-tier for code tasks.
# Better than gpt-4o-mini for coding specifically. Budget-aware: falls through if HALT.
RESULT=$(echo "$PROMPT" | "$HOME/.surrogate/bin/github-bridge.sh" --model codestral 2>>"$LOG")
;;
samba|sambanova)
RESULT=$(echo "$PROMPT" | "$HOME/.surrogate/bin/sambanova-bridge.sh" --model deepseek 2>>"$LOG")
;;
cloudflare|cf)
RESULT=$(echo "$PROMPT" | "$HOME/.surrogate/bin/cloudflare-bridge.sh" --model deepseek 2>>"$LOG")
;;
groq)
RESULT=$(echo "$PROMPT" | "$HOME/.surrogate/bin/groq-bridge.sh" --model qwen 2>>"$LOG")
;;
gemini)
# Use ai-fallback's gemini path
RESULT=$(echo "$PROMPT" | "$HOME/.surrogate/bin/ai-fallback.sh" --force gemini 2>>"$LOG")
;;
cerebras)
# Wafer-scale β fastest inference on planet (~2000 tok/s). Qwen3 235B excellent for code.
RESULT=$(echo "$PROMPT" | "$HOME/.surrogate/bin/cerebras-bridge.sh" --model big 2>>"$LOG")
;;
nvidia|nim)
# NVIDIA NIM β Llama 3.3 70B, diverse model pool (Nemotron, DeepSeek-R1, Qwen-Coder)
RESULT=$(echo "$PROMPT" | "$HOME/.surrogate/bin/nvidia-bridge.sh" --model qwen 2>>"$LOG")
;;
chutes_DISABLED_402)
# Chutes.ai aggregator β free tier needs activation; currently may 402
RESULT=$(echo "$PROMPT" | "$HOME/.surrogate/bin/chutes-bridge.sh" --model deepseek 2>>"$LOG")
;;
surrogate|surrogate-1)
# ΰΈΰΉΰΈΰΈ β local Ollama, Ashira-personalized (Qwen2.5-Coder-7B + Thai/DevSecOps prompt)
# Will be upgraded with LoRA adapter after RunPod training.
RESULT=$(echo "$PROMPT" | "$HOME/.surrogate/bin/surrogate-bridge.sh" 2>>"$LOG")
;;
*)
echo "[$(date '+%H:%M:%S')] unknown provider $PROVIDER" >> "$LOG"
exit 1
;;
esac
DUR=$(( $(date +%s) - START ))
if [[ -z "$RESULT" ]]; then
echo "[$(date '+%H:%M:%S')] $PROVIDER failed after ${DUR}s" >> "$LOG"
# Remove lock so another provider can try
/bin/rm -f "$LOCK_DIR/${PRIO_ID}_${PROVIDER}" 2>/dev/null
exit 1
fi
# -------- Save output (same schema as qwen-coder for unified review pipeline) --------
DATE=$(date +%Y-%m-%d_%H-%M)
OUT="$OUT_DIR/${PRIO_ID}_${DATE}.md"
cat > "$OUT" <<EOF
---
priority_id: $PRIO_ID
project: $PRIO_PROJECT
title: $PRIO_TITLE
model: $PROVIDER-cloud
worker: dev-cloud-$PROVIDER
ran_at: $(date -u +%Y-%m-%dT%H:%M:%SZ)
duration_s: $DUR
reviewed: false
---
$RESULT
EOF
echo "[$(date '+%H:%M:%S')] β
$PROVIDER β $OUT (${DUR}s, $(wc -c < "$OUT") bytes)" >> "$LOG"
|