oh-pi 0.1.38 → 0.1.40
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/package.json
CHANGED
|
@@ -79,7 +79,19 @@ function phasePipeline(status: string, theme: any): string {
|
|
|
79
79
|
export default function antColonyExtension(pi: ExtensionAPI) {
|
|
80
80
|
|
|
81
81
|
// ═══ Auto-trigger: 注入蚁群意识,LLM 自动判断何时启动 ═══
|
|
82
|
-
pi.on("before_agent_start", async () => {
|
|
82
|
+
pi.on("before_agent_start", async (ctx) => {
|
|
83
|
+
// 获取可用模型列表
|
|
84
|
+
let modelList = "";
|
|
85
|
+
try {
|
|
86
|
+
const { execSync } = await import("node:child_process");
|
|
87
|
+
const output = execSync("pi --list-models 2>/dev/null", { encoding: "utf-8", timeout: 5000 });
|
|
88
|
+
const models = output.trim().split("\n").slice(1) // skip header
|
|
89
|
+
.map(l => l.trim().split(/\s+/))
|
|
90
|
+
.filter(p => p.length >= 2)
|
|
91
|
+
.map(p => `${p[0]}/${p[1]}`);
|
|
92
|
+
if (models.length > 0) modelList = models.join(", ");
|
|
93
|
+
} catch { /* ignore */ }
|
|
94
|
+
|
|
83
95
|
return {
|
|
84
96
|
message: {
|
|
85
97
|
customType: "ant-colony-context",
|
|
@@ -90,7 +102,17 @@ You have the ant_colony tool. Use it automatically when:
|
|
|
90
102
|
- Task is complex enough that serial execution is inefficient
|
|
91
103
|
|
|
92
104
|
Do NOT ask for confirmation. Call ant_colony directly with a clear goal.
|
|
93
|
-
For simple single-file tasks, work directly without the colony
|
|
105
|
+
For simple single-file tasks, work directly without the colony.
|
|
106
|
+
${modelList ? `
|
|
107
|
+
[COLONY MODEL SELECTION]
|
|
108
|
+
Available models: ${modelList}
|
|
109
|
+
|
|
110
|
+
Strategy for choosing per-caste models:
|
|
111
|
+
- scoutModel: Use a fast/cheap model (e.g. haiku, flash, gpt-4o-mini). Scouts only read, no edits.
|
|
112
|
+
- workerModel: Use a capable model (e.g. sonnet, opus, gpt-4o). Workers make code changes.
|
|
113
|
+
- soldierModel: Use same as worker or slightly cheaper. Soldiers review but don't edit.
|
|
114
|
+
- If unsure, omit all three — defaults to current session model.
|
|
115
|
+
- Prefer latest model versions for best quality.` : ""}`,
|
|
94
116
|
display: false,
|
|
95
117
|
},
|
|
96
118
|
};
|
|
@@ -110,6 +132,9 @@ For simple single-file tasks, work directly without the colony.`,
|
|
|
110
132
|
goal: Type.String({ description: "What the colony should accomplish" }),
|
|
111
133
|
maxAnts: Type.Optional(Type.Number({ description: "Max concurrent ants (default: auto-adapt)", minimum: 1, maximum: 8 })),
|
|
112
134
|
maxCost: Type.Optional(Type.Number({ description: "Max cost budget in USD (default: unlimited)", minimum: 0.01 })),
|
|
135
|
+
scoutModel: Type.Optional(Type.String({ description: "Model for scout ants (default: current session model)" })),
|
|
136
|
+
workerModel: Type.Optional(Type.String({ description: "Model for worker ants (default: current session model)" })),
|
|
137
|
+
soldierModel: Type.Optional(Type.String({ description: "Model for soldier ants (default: current session model)" })),
|
|
113
138
|
}),
|
|
114
139
|
|
|
115
140
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
@@ -169,12 +194,18 @@ For simple single-file tasks, work directly without the colony.`,
|
|
|
169
194
|
appendFileSync(gitignorePath, `${content.length && !content.endsWith("\n") ? "\n" : ""}.ant-colony/\n`);
|
|
170
195
|
}
|
|
171
196
|
|
|
197
|
+
const modelOverrides: Record<string, string> = {};
|
|
198
|
+
if (params.scoutModel) modelOverrides.scout = params.scoutModel;
|
|
199
|
+
if (params.workerModel) modelOverrides.worker = params.workerModel;
|
|
200
|
+
if (params.soldierModel) modelOverrides.soldier = params.soldierModel;
|
|
201
|
+
|
|
172
202
|
const state = await runColony({
|
|
173
203
|
cwd: ctx.cwd,
|
|
174
204
|
goal: params.goal,
|
|
175
205
|
maxAnts: params.maxAnts,
|
|
176
206
|
maxCost: params.maxCost,
|
|
177
207
|
currentModel,
|
|
208
|
+
modelOverrides,
|
|
178
209
|
signal: signal ?? undefined,
|
|
179
210
|
callbacks,
|
|
180
211
|
});
|
|
@@ -282,11 +313,13 @@ For simple single-file tasks, work directly without the colony.`,
|
|
|
282
313
|
const taskTitle = task?.title?.slice(0, 55) || "...";
|
|
283
314
|
const dur = a.finishedAt ? formatDuration(a.finishedAt - a.startedAt) : formatDuration(Date.now() - a.startedAt);
|
|
284
315
|
const turns = a.usage.turns > 0 ? `${a.usage.turns}t` : "";
|
|
316
|
+
const model = a.model ? a.model.split("/").pop()! : "";
|
|
285
317
|
|
|
286
318
|
container.addChild(new Text(
|
|
287
319
|
theme.fg("muted", ` ${branch} `) + statusDot + " " +
|
|
288
320
|
theme.fg("accent", `@${a.id.slice(0, 20)} `) +
|
|
289
|
-
theme.fg("dim", `(${a.caste}) ${dur}${turns ? " │ " + turns : ""}`)
|
|
321
|
+
theme.fg("dim", `(${a.caste}) ${dur}${turns ? " │ " + turns : ""}`) +
|
|
322
|
+
(model ? " " + theme.fg("muted", model) : ""),
|
|
290
323
|
0, 0,
|
|
291
324
|
));
|
|
292
325
|
container.addChild(new Text(
|
|
@@ -37,6 +37,7 @@ export interface QueenOptions {
|
|
|
37
37
|
maxAnts?: number;
|
|
38
38
|
maxCost?: number;
|
|
39
39
|
currentModel: string;
|
|
40
|
+
modelOverrides?: ModelOverrides;
|
|
40
41
|
signal?: AbortSignal;
|
|
41
42
|
callbacks: QueenCallbacks;
|
|
42
43
|
}
|
|
@@ -138,6 +139,7 @@ interface WaveOptions {
|
|
|
138
139
|
cwd: string;
|
|
139
140
|
caste: AntCaste;
|
|
140
141
|
currentModel: string;
|
|
142
|
+
modelOverrides?: ModelOverrides;
|
|
141
143
|
signal?: AbortSignal;
|
|
142
144
|
callbacks: QueenCallbacks;
|
|
143
145
|
}
|
|
@@ -147,7 +149,8 @@ interface WaveOptions {
|
|
|
147
149
|
*/
|
|
148
150
|
async function runAntWave(opts: WaveOptions): Promise<"ok"> {
|
|
149
151
|
const { nest, cwd, caste, signal, callbacks, currentModel } = opts;
|
|
150
|
-
const
|
|
152
|
+
const casteModel = opts.modelOverrides?.[caste] || currentModel;
|
|
153
|
+
const config = { ...DEFAULT_ANT_CONFIGS[caste], model: casteModel };
|
|
151
154
|
|
|
152
155
|
let backoffMs = 0; // 429 退避时间
|
|
153
156
|
let consecutiveRateLimits = 0; // 连续限流计数
|
|
@@ -160,7 +163,8 @@ async function runAntWave(opts: WaveOptions): Promise<"ok"> {
|
|
|
160
163
|
|
|
161
164
|
const ant: Ant = {
|
|
162
165
|
id: "", caste, status: "idle", taskId: task.id,
|
|
163
|
-
pid: null,
|
|
166
|
+
pid: null, model: casteModel,
|
|
167
|
+
usage: { input: 0, output: 0, cost: 0, turns: 0 },
|
|
164
168
|
startedAt: Date.now(), finishedAt: null,
|
|
165
169
|
};
|
|
166
170
|
callbacks.onAntSpawn(ant, task);
|
|
@@ -311,6 +315,7 @@ export async function runColony(opts: QueenOptions): Promise<ColonyState> {
|
|
|
311
315
|
const waveBase: Omit<WaveOptions, "caste"> = {
|
|
312
316
|
nest, cwd: opts.cwd, signal, callbacks,
|
|
313
317
|
currentModel: opts.currentModel,
|
|
318
|
+
modelOverrides: opts.modelOverrides,
|
|
314
319
|
};
|
|
315
320
|
|
|
316
321
|
const cleanup = () => {
|