/** * Several members on one job, for as many rounds as you allow. * * Every other combinator here decides who does what: `fanOut` hands out the * subtasks, `orchestrate` has a planner write them, `chain` fixes the order. * A swarm decides none of it. The members are told the same goal, given a * {@link Board} to talk on and a {@link Claims} to take work from, and what * they divide between them is theirs. * * That makes it the one combinator whose value is an open question, so it is * built to be compared against the thing it might not beat: **with no board, no * claims and one round, a swarm is a fan-out**. That degenerate case is a test, * and it is the control arm of any experiment run with this. * * Three things are decided here rather than left to a prompt, each one measured * on a run before it was written down: * * - **Members remember.** `lifetime` defaults to `"workflow"` here and nowhere * else. A member that forgets the last round cannot build on what it saw, and * a swarm of amnesiacs is a fan-out that costs more. * - **What is new is handed over, not fetched.** A member is given what the * board has for it at the top of its task. Measured: given something that * arbitrates, members stopped reading the board entirely - they take, they are * refused, they take something else. Making them spend a call to find out what * happened would be charging them for the workflow's bookkeeping. * - **A member that is gone holds nothing.** Whatever it still had is released * when it drops out or when the swarm ends, and the result says what that was. * Measured: two of three members never released what they took, and claims * left by a member that has closed are work nobody will do and nobody can * take. */ import type { Agent } from "./../agent.ts"; import { type Board, type Claims, type Post } from "../board/index.ts"; import { type Result, type WorkflowResult } from "./../result.ts"; import { type WorkflowOptions } from "./options.ts"; /** How many of one agent stand on the board. A swarm of one is a run. */ export type MemberSpec = { /** Whose copies these are. */ agent: Agent; /** How many copies of it. Each gets its own id, its own memory, its own place. */ count: number; }; /** One member, and the last thing it said. */ export type SwarmMember = { /** The subagent id, which is also the name it posts and claims under. */ id: string; /** Which agent it is a copy of. */ agent: string; /** Its last turn. A member that dropped out early keeps the turn that failed. */ result: Result; }; /** What a member was holding when it stopped, and whether anyone took it back. */ export type SwarmClaim = { /** The thing that was taken. */ key: string; /** The member that had it when the swarm ended. */ heldBy: string; /** True when the swarm released it because the member was gone. */ released: boolean; }; /** Why the swarm stopped, apart from whether it went well. */ export type SwarmEnd = "until" | "rounds" | "members" | "signal"; /** Who is on it, what they are told, and how long they have. */ export type SwarmOptions = WorkflowOptions & { /** Who is on it, and how many of each. */ members: readonly MemberSpec[]; /** The one thing every member is told. Broad enough to admit several routes. */ goal: string; /** Turns per member. Defaults to 3, because "forever" must not be reachable. */ rounds?: number; /** Members asked at once. Defaults to 4. */ concurrency?: number; /** Defaults to a fresh in-memory board. Pass one to read it afterwards. */ board?: Board; /** What there is to take. Absent, the members are on their honour. */ claims?: Claims; /** Has the goal been reached? Read from the board, after every round. */ until?: (board: Board) => boolean; /** * How many `result`s a member may post in one turn. Absent, as many as the * board takes: a member describing files posts one per file. A debate wants * 1, because a vote posted again is not an argument. */ resultsPerTurn?: number; }; /** * What the swarm did, and what it left behind. * * As a `Result`: every member's last turn, labelled by the name it posted * under, a failed one marked as such; `ok` is false when any member failed or * was never asked, `error` the first failure's. `steps` is every turn of every * round, and `usage` their sum over the swarm's own wall time. */ export type SwarmResult = WorkflowResult & { /** One per member, in roster order, the ones that failed included. */ members: readonly SwarmMember[]; /** Every post, in order. The run's social history. */ posts: readonly Post[]; /** What was taken, and what was still held when its holder stopped. */ claims: readonly SwarmClaim[]; /** How many rounds actually ran. */ rounds: number; /** Whether `until` fired. Reaching the round cap is not success. */ converged: boolean; /** Which cap ended it. */ stoppedBy: SwarmEnd; }; /** * Runs a swarm. * * A round is one `ask` per live member, through `mapConcurrent`. A member whose * turn fails is asked again the next round; two failed turns in a row and it * drops out rather than costing every remaining round, keeping the turn that * failed as its result. * * The pool closes everything in a `finally`, cancellation included, and the * claims of whoever is gone are released before the result is built. */ export declare function swarm(options: SwarmOptions): Promise; /** * What every member last said, under the name it posted under. * * The id and not the agent, because three copies of one agent share the name * and a reader finds a member on the board by its id. */ export declare function membersOutput(members: readonly SwarmMember[]): string; //# sourceMappingURL=swarm.d.ts.map