@irythill/orq 0.7.0 → 0.8.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/README.pt-BR.md +1 -1
- package/dist/pick.js +53 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -135,7 +135,7 @@ roles = ["impl", "fix", "review"]
|
|
|
135
135
|
quota = ["codex"] # name in agent-quota's output
|
|
136
136
|
[agents.codex.models.impl]
|
|
137
137
|
simple = "gpt-6-sol@low" # model@effort, per issue level
|
|
138
|
-
medium = "gpt-6-sol@medium"
|
|
138
|
+
medium = ["gpt-6-sol@medium", "gpt-6-luna@medium"] # several: each (agent, model) pair is scored on its own history
|
|
139
139
|
heavy = "gpt-6.1-sol@high"
|
|
140
140
|
[agents.codex.models.review]
|
|
141
141
|
simple = "gpt-6-sol@low" # reviews scale with the issue too
|
package/README.pt-BR.md
CHANGED
|
@@ -138,7 +138,7 @@ roles = ["impl", "fix", "review"]
|
|
|
138
138
|
quota = ["codex"] # name in agent-quota's output
|
|
139
139
|
[agents.codex.models.impl]
|
|
140
140
|
simple = "gpt-6-sol@low" # model@effort, per issue level
|
|
141
|
-
medium = "gpt-6-sol@medium"
|
|
141
|
+
medium = ["gpt-6-sol@medium", "gpt-6-luna@medium"] # several: each (agent, model) pair is scored on its own history
|
|
142
142
|
heavy = "gpt-6.1-sol@high"
|
|
143
143
|
[agents.codex.models.review]
|
|
144
144
|
simple = "gpt-6-sol@low" # reviews scale with the issue too
|
package/dist/pick.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Picks agent, model, effort and account for a role (port of orq-pick, plus fixed roles).
|
|
2
2
|
// fixed role: the first agent of [agent, ...fallback] that passes the filters wins — no scoring, never swapped
|
|
3
3
|
// for an agent the user didn't list.
|
|
4
|
-
// auto role: every
|
|
4
|
+
// auto role: every (agent, model) pair that passes is scored (a level may list several models):
|
|
5
5
|
// quality * delivery + tokens * economy + speed * quickness − rotation * (picks in the last `window`)
|
|
6
6
|
// − reserve (agents with reserve = true) + explore (fewer than `min_samples` runs, fading with n, none after
|
|
7
7
|
// `explore_streak` picks in a row). Each axis is (prior·k + observed·n)/(k + n), k = min_samples.
|
|
@@ -73,17 +73,25 @@ export function limitsFor(pol, level) {
|
|
|
73
73
|
const five = { ...FIVE, ...(pol.quota_5h ?? {}) };
|
|
74
74
|
return [Math.min(Number(five[level] ?? five.medium), Number(pol.quota_limit ?? 90)), Number(pol.quota_week ?? 95)];
|
|
75
75
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const models = a.models;
|
|
76
|
+
/** The agent's models section for a role, with the same fallbacks as the roles (fix → impl, watchdog → opinion → review). */
|
|
77
|
+
function roleModels(a, role) {
|
|
78
|
+
const models = (a.models ?? {});
|
|
80
79
|
const lr = legacyRole(role);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
return models[lr] ?? (lr === "fix" ? models.impl : lr === "watchdog" ? models.opinion ?? models.review : undefined) ?? {};
|
|
81
|
+
}
|
|
82
|
+
const specList = (spec) => (Array.isArray(spec) ? spec : spec ? [spec] : []).map(String).filter(Boolean)
|
|
83
|
+
.map((x) => { const [model, effort] = x.split("@"); return [model, effort || undefined]; });
|
|
84
|
+
/** Every candidate model for the role/level, in the order configured; undefined when none is. */
|
|
85
|
+
export function modelsFor(a, role, level) {
|
|
86
|
+
if (a.models === undefined)
|
|
87
|
+
return [["", undefined]]; // no models configured: the CLI's default
|
|
88
|
+
const ms = roleModels(a, role);
|
|
89
|
+
const list = specList(ms[level] ?? ms.default ?? ms.medium ?? ms.simple);
|
|
90
|
+
return list.length ? list : undefined;
|
|
91
|
+
}
|
|
92
|
+
/** The first candidate model for the role/level. */
|
|
93
|
+
export function modelFor(a, role, level) {
|
|
94
|
+
return modelsFor(a, role, level)?.[0];
|
|
87
95
|
}
|
|
88
96
|
// --- per-model quota ---------------------------------------------------------------------------------------------------
|
|
89
97
|
// A collector may add lines "<account>/<model> 5h=.. week=.." (antigravity: one bucket per model group). The model a pick is about
|
|
@@ -98,20 +106,19 @@ export function modelKey(model, keys) {
|
|
|
98
106
|
}
|
|
99
107
|
/** The models of an agent's role section lighter than `level`, the closest first, without repeats of `tried`. */
|
|
100
108
|
export function lighterModels(a, role, level, tried) {
|
|
101
|
-
|
|
102
|
-
if (!models)
|
|
109
|
+
if (!a.models)
|
|
103
110
|
return [];
|
|
104
|
-
const
|
|
105
|
-
const ms = models[lr] ?? (lr === "fix" ? models.impl : lr === "watchdog" ? models.opinion ?? models.review : undefined) ?? {};
|
|
111
|
+
const ms = roleModels(a, role);
|
|
106
112
|
const rank = LEVEL_RANK[level] ?? 1, seen = new Set(tried);
|
|
107
113
|
const out = [];
|
|
108
114
|
for (const [, spec] of Object.entries(ms).filter(([lv]) => (LEVEL_RANK[lv] ?? 1) < rank)
|
|
109
115
|
.sort((x, y) => (LEVEL_RANK[y[0]] ?? 1) - (LEVEL_RANK[x[0]] ?? 1))) {
|
|
110
|
-
const [model, effort]
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
for (const [model, effort] of specList(spec)) {
|
|
117
|
+
if (seen.has(model))
|
|
118
|
+
continue;
|
|
119
|
+
seen.add(model);
|
|
120
|
+
out.push([model, effort]);
|
|
121
|
+
}
|
|
115
122
|
}
|
|
116
123
|
return out;
|
|
117
124
|
}
|
|
@@ -187,8 +194,8 @@ export function pick(inp) {
|
|
|
187
194
|
out(name, m("ignores prompts (a review tool)", "ignora prompts (ferramenta de revisão)"));
|
|
188
195
|
continue;
|
|
189
196
|
}
|
|
190
|
-
const
|
|
191
|
-
if (!
|
|
197
|
+
const mfs = modelsFor(a, role, level);
|
|
198
|
+
if (!mfs) {
|
|
192
199
|
out(name, m(`no model for ${role}/${level}`, `sem modelo para ${role}/${level}`));
|
|
193
200
|
continue;
|
|
194
201
|
}
|
|
@@ -247,8 +254,8 @@ export function pick(inp) {
|
|
|
247
254
|
return { pace: paces.length ? Math.max(...paces) : undefined,
|
|
248
255
|
hot: penalty > 0 && ((p5 !== undefined && p5 > limit5) || (pw !== undefined && pw > limitWeek)) };
|
|
249
256
|
};
|
|
250
|
-
// The chosen account's per-model buckets: an exhausted
|
|
251
|
-
let mfx =
|
|
257
|
+
// The chosen account's per-model buckets: an exhausted candidate drops out; with none left, a lighter model of the agent.
|
|
258
|
+
let mfx = mfs;
|
|
252
259
|
const acc0 = usable.length ? [...usable].sort((x, y) => x[1] - y[1])[0][0] : undefined;
|
|
253
260
|
const mkeys = acc0 ? Object.keys(inp.windows ?? {}).filter((k) => k.startsWith(`${acc0}/`)).map((k) => k.slice(acc0.length + 1)) : [];
|
|
254
261
|
if (acc0 && mkeys.length) {
|
|
@@ -263,23 +270,25 @@ export function pick(inp) {
|
|
|
263
270
|
return `${model} ${five ? `5h ${q.five}%` : `${m("week", "semana")} ${q.week}%`}`
|
|
264
271
|
+ (at.length ? ` (${m("back", "volta")} ${humanReset(Math.max(...at))})` : "");
|
|
265
272
|
};
|
|
266
|
-
const
|
|
267
|
-
let found;
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
+
const skipped = [];
|
|
274
|
+
let found = mfs.filter((c) => { const why = over(c[0]); if (why)
|
|
275
|
+
skipped.push(why); return !why; });
|
|
276
|
+
if (!found.length) {
|
|
277
|
+
for (const cand of lighterModels(a, role, level, mfs.map((c) => c[0]))) {
|
|
278
|
+
const why = over(cand[0]);
|
|
279
|
+
if (!why) {
|
|
280
|
+
found = [cand];
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
skipped.push(why);
|
|
273
284
|
}
|
|
274
|
-
tried.push(cand[0]);
|
|
275
|
-
skipped.push(why);
|
|
276
285
|
}
|
|
277
|
-
if (!found) {
|
|
286
|
+
if (!found.length) {
|
|
278
287
|
out(name, `${m("every model over its quota", "todo modelo acima da cota")}: ${skipped.join(", ")}`);
|
|
279
288
|
continue;
|
|
280
289
|
}
|
|
281
290
|
if (skipped.length)
|
|
282
|
-
out(name, `${m("quota exhausted", "cota esgotada")}: ${skipped.join(", ")}; ${m("using", "usando")} ${found[0]}`);
|
|
291
|
+
out(name, `${m("quota exhausted", "cota esgotada")}: ${skipped.join(", ")}; ${m("using", "usando")} ${found.map((c) => c[0]).join(", ")}`);
|
|
283
292
|
mfx = found;
|
|
284
293
|
}
|
|
285
294
|
const ranked = usable.map(([n, v]) => ({ n, v: v, ...paceOf(n) }))
|
|
@@ -287,9 +296,12 @@ export function pick(inp) {
|
|
|
287
296
|
const chosen = ranked[0];
|
|
288
297
|
const acct = qn.length > 1 ? chosen?.n : undefined;
|
|
289
298
|
const statRole = role === "fix" || role === "worker" ? "impl" : role;
|
|
290
|
-
|
|
291
|
-
const
|
|
292
|
-
|
|
299
|
+
// One candidate per (agent, model) pair, each scored on its own stats row; a fixed role takes the first model.
|
|
300
|
+
for (const [model, effort] of res.mode === "fixed" ? mfx.slice(0, 1) : mfx) {
|
|
301
|
+
const stOf = (sr) => inp.stats.find((r) => r.role === sr && r.agent === name && r.model === (model.split("/").pop() || "default"));
|
|
302
|
+
const st = stOf(statRole) ?? (role === "watchdog" ? stOf("review") : undefined); // no watchdog runs yet: its review record
|
|
303
|
+
pass.push({ agent: name, model, effort, acct, a, st, pace: chosen?.pace, hot: Boolean(chosen?.hot) });
|
|
304
|
+
}
|
|
293
305
|
if (res.mode === "fixed")
|
|
294
306
|
break; // the first that passes wins
|
|
295
307
|
}
|
|
@@ -324,9 +336,10 @@ export function pick(inp) {
|
|
|
324
336
|
const t = axis(m("economy", "economia"), prior, c.st?.new && refTok ? Math.min(1, refTok / c.st.new) : undefined, n);
|
|
325
337
|
const s = axis(m("speed", "rapidez"), prior, c.st?.min && refMin ? Math.min(1, refMin / Math.max(c.st.min, 0.1)) : undefined, n);
|
|
326
338
|
const used = inp.recent.filter((p) => p.agent === c.agent).length;
|
|
327
|
-
// Exploration fades as samples come in and stops after explore_streak picks of the
|
|
339
|
+
// Exploration fades as samples come in and stops after explore_streak picks of the pair in a row (rotation stays per agent).
|
|
340
|
+
const same = (p) => p.agent === c.agent && (p.model === undefined || p.model === c.model);
|
|
328
341
|
let streak = 0;
|
|
329
|
-
for (let i = inp.recent.length - 1; i >= 0 && inp.recent[i]
|
|
342
|
+
for (let i = inp.recent.length - 1; i >= 0 && same(inp.recent[i]); i--)
|
|
330
343
|
streak++;
|
|
331
344
|
const capped = few(c.st) && streak >= w.explore_streak;
|
|
332
345
|
const explore = few(c.st) && !capped ? w.explore * (k - n) / k : 0;
|