@nbardy/oompa 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/agentnet/src/agentnet/cli.clj +79 -30
- package/agentnet/src/agentnet/worker.clj +6 -3
- package/bin/test-models +78 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -99,7 +99,7 @@ This spawns:
|
|
|
99
99
|
|
|
100
100
|
| Field | Required | Description |
|
|
101
101
|
|-------|----------|-------------|
|
|
102
|
-
| `model` | yes | `harness:model` (e.g. `
|
|
102
|
+
| `model` | yes | `harness:model` or `harness:model:reasoning` (e.g. `codex:o3:low`, `claude:opus-4.6`) |
|
|
103
103
|
| `prompt` | no | String or array of paths — concatenated into one prompt |
|
|
104
104
|
| `iterations` | no | Max iterations per worker (default: 10) |
|
|
105
105
|
| `count` | no | Number of workers with this config (default: 1) |
|
|
@@ -132,34 +132,75 @@
|
|
|
132
132
|
;; Commands
|
|
133
133
|
;; =============================================================================
|
|
134
134
|
|
|
135
|
+
(declare cmd-swarm)
|
|
136
|
+
|
|
137
|
+
(defn- probe-model
|
|
138
|
+
"Send 'say ok' to a model via its harness CLI. Returns true if model responds."
|
|
139
|
+
[harness model]
|
|
140
|
+
(try
|
|
141
|
+
(let [cmd (case harness
|
|
142
|
+
:claude ["claude" "--model" model "-p" "say ok" "--max-turns" "1"]
|
|
143
|
+
:codex ["codex" "exec" "--model" model "--" "say ok"])
|
|
144
|
+
result (process/sh cmd {:out :string :err :string :timeout 30000})]
|
|
145
|
+
(zero? (:exit result)))
|
|
146
|
+
(catch Exception _ false)))
|
|
147
|
+
|
|
148
|
+
(defn- validate-models!
|
|
149
|
+
"Probe each unique harness:model pair. Prints results and exits if any fail."
|
|
150
|
+
[worker-configs review-model]
|
|
151
|
+
(let [models (cond-> (set (map (fn [wc]
|
|
152
|
+
(parse-model-string (:model wc)))
|
|
153
|
+
worker-configs))
|
|
154
|
+
review-model (conj review-model))
|
|
155
|
+
_ (println "Validating models...")
|
|
156
|
+
results (pmap (fn [{:keys [harness model]}]
|
|
157
|
+
(let [ok (probe-model harness model)]
|
|
158
|
+
(println (format " %s:%s %s"
|
|
159
|
+
(name harness) model
|
|
160
|
+
(if ok "OK" "FAIL")))
|
|
161
|
+
{:harness harness :model model :ok ok}))
|
|
162
|
+
models)
|
|
163
|
+
failures (filter (complement :ok) results)]
|
|
164
|
+
(when (seq failures)
|
|
165
|
+
(println)
|
|
166
|
+
(println "ERROR: The following models are not accessible:")
|
|
167
|
+
(doseq [{:keys [harness model]} failures]
|
|
168
|
+
(println (format " %s:%s" (name harness) model)))
|
|
169
|
+
(println)
|
|
170
|
+
(println "Fix model names in oompa.json and retry.")
|
|
171
|
+
(System/exit 1))
|
|
172
|
+
(println)))
|
|
173
|
+
|
|
135
174
|
(defn cmd-run
|
|
136
|
-
"Run orchestrator
|
|
175
|
+
"Run orchestrator — uses oompa.json if present, otherwise simple mode"
|
|
137
176
|
[opts args]
|
|
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
|
-
|
|
177
|
+
(if (.exists (io/file "oompa.json"))
|
|
178
|
+
(cmd-swarm opts (or (seq args) ["oompa.json"]))
|
|
179
|
+
(let [swarm-id (make-swarm-id)]
|
|
180
|
+
(if-let [specs (:worker-specs opts)]
|
|
181
|
+
;; Mixed worker specs: --workers claude:5 codex:4
|
|
182
|
+
(let [workers (mapcat
|
|
183
|
+
(fn [spec]
|
|
184
|
+
(let [{:keys [harness count]} spec]
|
|
185
|
+
(map-indexed
|
|
186
|
+
(fn [idx _]
|
|
187
|
+
(worker/create-worker
|
|
188
|
+
{:id (format "%s-%d" (name harness) idx)
|
|
189
|
+
:swarm-id swarm-id
|
|
190
|
+
:harness harness
|
|
191
|
+
:model (:model opts)
|
|
192
|
+
:iterations 1}))
|
|
193
|
+
(range count))))
|
|
194
|
+
specs)]
|
|
195
|
+
(println (format "Running once with mixed workers (swarm %s):" swarm-id))
|
|
196
|
+
(doseq [spec specs]
|
|
197
|
+
(println (format " %dx %s" (:count spec) (name (:harness spec)))))
|
|
198
|
+
(println)
|
|
199
|
+
(worker/run-workers! workers))
|
|
200
|
+
;; Simple mode
|
|
201
|
+
(do
|
|
202
|
+
(println (format "Swarm ID: %s" swarm-id))
|
|
203
|
+
(orchestrator/run-once! (assoc opts :swarm-id swarm-id)))))))
|
|
163
204
|
|
|
164
205
|
(defn cmd-loop
|
|
165
206
|
"Run orchestrator N times"
|
|
@@ -280,11 +321,15 @@
|
|
|
280
321
|
(if available? "✓ available" "✗ not found"))))))
|
|
281
322
|
|
|
282
323
|
(defn- parse-model-string
|
|
283
|
-
"Parse
|
|
324
|
+
"Parse model string into {:harness :model :reasoning}.
|
|
325
|
+
Formats: 'harness:model', 'harness:model:reasoning', or just 'model'."
|
|
284
326
|
[s]
|
|
285
327
|
(if (and s (str/includes? s ":"))
|
|
286
|
-
(let [
|
|
287
|
-
|
|
328
|
+
(let [parts (str/split s #":" 3)]
|
|
329
|
+
(case (count parts)
|
|
330
|
+
2 {:harness (keyword (first parts)) :model (second parts)}
|
|
331
|
+
3 {:harness (keyword (first parts)) :model (second parts) :reasoning (nth parts 2)}
|
|
332
|
+
{:harness :codex :model s}))
|
|
288
333
|
{:harness :codex :model s}))
|
|
289
334
|
|
|
290
335
|
(defn cmd-swarm
|
|
@@ -320,12 +365,13 @@
|
|
|
320
365
|
;; Convert to worker format
|
|
321
366
|
workers (map-indexed
|
|
322
367
|
(fn [idx wc]
|
|
323
|
-
(let [{:keys [harness model]} (parse-model-string (:model wc))]
|
|
368
|
+
(let [{:keys [harness model reasoning]} (parse-model-string (:model wc))]
|
|
324
369
|
(worker/create-worker
|
|
325
370
|
{:id (str "w" idx)
|
|
326
371
|
:swarm-id swarm-id
|
|
327
372
|
:harness harness
|
|
328
373
|
:model model
|
|
374
|
+
:reasoning reasoning
|
|
329
375
|
:iterations (or (:iterations wc) 10)
|
|
330
376
|
:prompts (:prompt wc)
|
|
331
377
|
:can-plan (:can_plan wc)
|
|
@@ -348,6 +394,9 @@
|
|
|
348
394
|
(if (:prompt wc) (str ", " (:prompt wc)) "")))))
|
|
349
395
|
(println)
|
|
350
396
|
|
|
397
|
+
;; Preflight: probe each unique model before launching workers
|
|
398
|
+
(validate-models! worker-configs review-model)
|
|
399
|
+
|
|
351
400
|
;; Run workers using new worker module
|
|
352
401
|
(worker/run-workers! workers))))
|
|
353
402
|
|
|
@@ -106,8 +106,9 @@
|
|
|
106
106
|
(defn create-worker
|
|
107
107
|
"Create a worker config.
|
|
108
108
|
:prompts is a string or vector of strings — paths to prompt files.
|
|
109
|
-
:can-plan when false, worker waits for tasks before starting (backpressure).
|
|
110
|
-
|
|
109
|
+
:can-plan when false, worker waits for tasks before starting (backpressure).
|
|
110
|
+
:reasoning reasoning effort level (e.g. \"low\", \"medium\", \"high\") — codex only."
|
|
111
|
+
[{:keys [id swarm-id harness model iterations prompts can-plan reasoning review-harness review-model]}]
|
|
111
112
|
{:id id
|
|
112
113
|
:swarm-id swarm-id
|
|
113
114
|
:harness (or harness :codex)
|
|
@@ -118,6 +119,7 @@
|
|
|
118
119
|
(string? prompts) [prompts]
|
|
119
120
|
:else [])
|
|
120
121
|
:can-plan (if (some? can-plan) can-plan true)
|
|
122
|
+
:reasoning reasoning
|
|
121
123
|
:review-harness review-harness
|
|
122
124
|
:review-model review-model
|
|
123
125
|
:completed 0
|
|
@@ -144,7 +146,7 @@
|
|
|
144
146
|
|
|
145
147
|
(defn- run-agent!
|
|
146
148
|
"Run agent with prompt, return {:output string, :done? bool, :exit int}"
|
|
147
|
-
[{:keys [id swarm-id harness model prompts]} worktree-path context]
|
|
149
|
+
[{:keys [id swarm-id harness model prompts reasoning]} worktree-path context]
|
|
148
150
|
(let [;; 1. Task header (always, from package)
|
|
149
151
|
task-header (or (load-prompt "config/prompts/_task_header.md") "")
|
|
150
152
|
|
|
@@ -175,6 +177,7 @@
|
|
|
175
177
|
"--skip-git-repo-check"
|
|
176
178
|
"-C" abs-worktree]
|
|
177
179
|
model (into ["--model" model])
|
|
180
|
+
reasoning (into ["-c" (str "reasoning_effort=\"" reasoning "\"")])
|
|
178
181
|
true (conj "--" full-prompt))
|
|
179
182
|
:claude (cond-> ["claude" "-p" "--dangerously-skip-permissions"
|
|
180
183
|
"--session-id" session-id]
|
package/bin/test-models
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test-models — probe all models in oompa.json with a hello-world check
|
|
3
|
+
#
|
|
4
|
+
# Usage: test-models [path/to/oompa.json]
|
|
5
|
+
#
|
|
6
|
+
# Sends "say ok" to each unique model via its harness CLI.
|
|
7
|
+
# Reports pass/fail for each. Exits non-zero if any fail.
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
CONFIG="${1:-oompa.json}"
|
|
12
|
+
|
|
13
|
+
if [ ! -f "$CONFIG" ]; then
|
|
14
|
+
echo "Config not found: $CONFIG"
|
|
15
|
+
echo "Usage: test-models [path/to/oompa.json]"
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Extract unique model strings (harness:model) from workers[] and review_model
|
|
20
|
+
MODELS=$(python3 -c "
|
|
21
|
+
import json, sys
|
|
22
|
+
with open('$CONFIG') as f:
|
|
23
|
+
cfg = json.load(f)
|
|
24
|
+
models = set()
|
|
25
|
+
if cfg.get('review_model'):
|
|
26
|
+
models.add(cfg['review_model'])
|
|
27
|
+
for w in cfg.get('workers', []):
|
|
28
|
+
if 'model' in w:
|
|
29
|
+
models.add(w['model'])
|
|
30
|
+
for m in sorted(models):
|
|
31
|
+
print(m)
|
|
32
|
+
")
|
|
33
|
+
|
|
34
|
+
if [ -z "$MODELS" ]; then
|
|
35
|
+
echo "No models found in $CONFIG"
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
echo "Probing models from $CONFIG"
|
|
40
|
+
echo ""
|
|
41
|
+
|
|
42
|
+
PASS=0
|
|
43
|
+
FAIL=0
|
|
44
|
+
|
|
45
|
+
while IFS= read -r model; do
|
|
46
|
+
HARNESS="${model%%:*}"
|
|
47
|
+
MODEL_NAME="${model#*:}"
|
|
48
|
+
|
|
49
|
+
printf " %-30s " "$model"
|
|
50
|
+
|
|
51
|
+
case "$HARNESS" in
|
|
52
|
+
claude)
|
|
53
|
+
OUTPUT=$(claude --model "$MODEL_NAME" -p "say ok" --max-turns 1 2>&1) && EXIT=$? || EXIT=$?
|
|
54
|
+
;;
|
|
55
|
+
codex)
|
|
56
|
+
OUTPUT=$(codex exec --model "$MODEL_NAME" -- "say ok" 2>&1) && EXIT=$? || EXIT=$?
|
|
57
|
+
;;
|
|
58
|
+
*)
|
|
59
|
+
echo "SKIP (unknown harness)"
|
|
60
|
+
continue
|
|
61
|
+
;;
|
|
62
|
+
esac
|
|
63
|
+
|
|
64
|
+
if [ $EXIT -eq 0 ]; then
|
|
65
|
+
echo "OK"
|
|
66
|
+
PASS=$((PASS + 1))
|
|
67
|
+
else
|
|
68
|
+
echo "FAIL"
|
|
69
|
+
# Print first line of error for context
|
|
70
|
+
echo "$OUTPUT" | head -3 | sed 's/^/ /'
|
|
71
|
+
FAIL=$((FAIL + 1))
|
|
72
|
+
fi
|
|
73
|
+
done <<< "$MODELS"
|
|
74
|
+
|
|
75
|
+
echo ""
|
|
76
|
+
echo "$PASS passed, $FAIL failed"
|
|
77
|
+
|
|
78
|
+
[ "$FAIL" -eq 0 ]
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nbardy/oompa",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Git-worktree multi-agent swarm orchestrator for Codex and Claude",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"bin": {
|
|
8
|
-
"oompa": "bin/oompa.js"
|
|
8
|
+
"oompa": "bin/oompa.js",
|
|
9
|
+
"oompa-test-models": "bin/test-models"
|
|
9
10
|
},
|
|
10
11
|
"files": [
|
|
11
12
|
"bin/",
|