Learning mode

Learning and learning-all exist to teach the router the best path: they calibrate the routing brain on a domain/task so later smart runs pick the winning runner. That is the only reason these modes exist — not as a production default. After a learning run on a domain, subsequent routing: smart traffic should pick the winning runner. A captcha or anti-bot wall is recorded as bot_challenge / captcha on the routing brain so later smart traffic stops picking the same runner on that domain.

API and MCP: routing: "learning" or routing: "learning-all". Run learning (or once, learning-all) on a new domain/task to teach the brain, then switch back to smart. Do not use learning-all for every production job.

Which mode when

  • smartProduction: the router uses the brain to pick a runner and fall back. Exploit learned rankings with light explore and parallel fallback on failure. Default for production traffic.
  • learningPaid calibration: probe the curated live pool (~54 single runners) on this task/URL so the brain learns which runners win. $20 default ceiling. Not the default for production traffic. Evaluate the top 9 of the live operational pool (~54 single runners), then the next 9, and stop after the first wave containing a success (or when the pool is exhausted).
  • learning-allPaid thorough calibration: sweep the full dispatchable catalog (250+), more expensive, $20 ceiling. Do not use for every production job. Same wave machinery over the full catalog (250+ options), but successes never stop the sweep — every dispatchable option attempts exactly once; coming-soon catalog rows are skipped (never billed). Run it once per domain to seed the scoreboard, then switch to smart.

How batches work

Runners are ordered by current domain × task scores (when there is no history yet, learning starts from the smart default chain plus the other live base runners (~54); learning-all then adds live driver×model combos and the remaining catalog). Each batch evaluates up to 9 runners with at most 8 in flight — enough parallelism to finish a batch quickly without tripping provider rate limits. The rule is always the same: evaluate the top 9, then the next 9.

  • Success — objective task success. All successes in the batch finish so latency and cost can be compared (not first-win cancel).
  • Soft failure — blocked / antibot, capacity, rate limit, hang. If every runner in the batch is soft, Learning advances to the next batch.
  • Hard failure — task looks impossible for this instruction. Probing stops so the rest of the pool is not burned (learning only — learning-all deliberately completes the sweep).

Runners the health layer has suspended (provider account hard-down, e.g. persistent 402s) are skipped before any wave starts — they are never attempted or billed, and the skip is disclosed in banana_peel.routing_note and banana_peel.learning.skipped_suspended.

learning-all — teach more thoroughly (not production)

routing: learning-all is the most expensive routing mode by design: every dispatchable option in the full catalog (250+ advertised runner combinations — single runners plus distinct driver×model combos) attempts the task exactly once, regardless of how many already succeeded, and every attempt bills. Catalog-only coming-soon rows are listed under banana_peel.learning.skipped_no_adapter and never launch. A 250-wide sweep will typically hit the $20 default ceiling — the echo discloses stopped early (budget) after N of M. The result is the best success (same fastest + cheapest winner selection as learning) plus a complete per-runner attempt table — one run that fully teaches the routing brain the best path for a domain. Treat it as a one-time teach per domain, never as a production routing mode: after one sweep the scoreboard is seeded and smart exploits it at a fraction of the cost.

  • No early stop, but an early result — successes and hard failures do not end the sweep. The run still settles for callers as soon as the first genuine success lands: status flips to completed with that winner while the remaining waves keep attempting in the background and appending to banana_peel.progress and the attempt table (banana_peel.learning.settled_early marks such runs). Stop polling when you have your answer, or keep polling for the full scoreboard.
  • $20 default ceiling (same as learning) —max_cost_usdmay lower it but cannot raise it. A tight ceiling turns learning-all into “sweep as much of the pool as this budget allows”.
  • Per-attempt cost ceiling — runners whose catalog price projects above ~$2.00 per attempt (env BP_ATTEMPT_MAX_COST_USD) are skipped before dispatch — never attempted, never billed — and disclosed in banana_peel.learning.skipped_over_ceiling.
  • max_runners — optionally cap sweep width: after health and cost-ceiling filters, only the top max_runners ranked runners attempt. The cheapest way to try learning-all on a budget.
  • Same admission caps — counts against the per-account Learning concurrency limit and the per-instance execution ceiling below.
  • When to use — teach a domain thoroughly ONCE before production traffic. Prefer learning for a cheaper teach that stops at the first successful wave, and smart for everyday runs. Do not use learning-all for every production job.

Ranking & smart handoff

Among successes, Banana Peel ranks by fastest + cheapest (normalized latency and cost). #1 becomes the preferred primary for that domain; #2 and #3 are fallbacks. The ranking is persisted and applied on later smart requests — that is the teach: subsequent smart runs on that domain should route better. Continuous outcomes still update domain rankings and the routing brain.

Cost — paid calibration, not production

Learning-family runs are a paid calibration sweep: every runner attempt that executes is billed (successes, failures, and blocked attempts — same policy as smart fan-out; not success-only). A single Learning request can start dozens of runners across batches. Prefer smart after you have taught the brain.

Spend ceiling — $20 defaults, max_cost_usd to override

Every Learning-family run has a dollar ceiling: $20 by default for learning and learning-all, or the request’s max_cost_usd(learning may override in either direction up to $100; learning-all may lower the default but cannot raise it). Once executed attempts’ spend reaches the ceiling, no new attempts or batches launch; attempts already in flight finish and bill. The run then ends honestly with error code budget_exhausted and partial results under banana_peel.routing_attempts (and a persisted ranking when any attempt succeeded). The active ceiling is echoed under banana_peel.budget and reflected by POST /api/v1/estimate. All other routing modes honor max_cost_usd the same way (they just have no implicit default).

On top of the run ceiling, each attempt has its own cost ceiling (default ~$2.00, env BP_ATTEMPT_MAX_COST_USD): runners whose catalog price projects above it are skipped before dispatch and listed in banana_peel.learning.skipped_over_ceiling — never attempted, never billed. An attempt whose metered cost settles above the ceiling still bills (it already executed) but its attempt row is flagged over_attempt_ceiling so the bill stays explainable.

Async creates & concurrency ceiling

Learning and learning-all creates always return 202 immediately and run in the background — poll GET /api/v1/responses/:id (same contract as background: true). Creation is cheap and parallel-safe; the expensive fan-out is what is limited:

  • Execution ceiling — at most 6 Learning fan-outs execute concurrently per server instance (each fan-out already runs up to 8 runners in parallel). Additional accepted runs wait in a short queue (up to 12 per instance) and start automatically when a slot frees.
  • Machine-readable back-pressure — when the ceiling and queue are both full, the create is refused with 429, error code learning_capacity, and a Retry-After header (seconds). No run is created and nothing bills — back off and retry. Only Learning creates are affected; smart traffic never hits this limit.
  • Per-account concurrency — an account may have at most 3 Learning-family runs (learning + learning-all) in flight at once. Creates beyond that are refused with 429, error code rate_limited, and a Retry-After header — no run is created and nothing bills. Wait for a running probe to finish (poll GET /api/v1/responses/:id) or cancel one, then retry.

Watch it and stop it

  • Both Learning modes require a resolvable target URL — without one the request is rejected up front with 400 missing_url and nothing bills (the batch fan-out never starts).
  • Create with background: true and poll GET /api/v1/responses/:id — while in flight, banana_peel.progress shows attempts so far, the current batch ({ current, total, size }), and spend to date.
  • Stop any time with POST /api/v1/responses/:id/cancel — attempts that already executed still bill; no further attempts or batches start. If the sweep already produced a genuine success, the run settles completed with that winner instead of cancelled — winners are never discarded.
  • Sweep time is caller-controlled: pass time_budget_ms to stop dispatching new batches once it elapses (in-flight attempts finish and bill). Omitted (the default), the sweep is uncapped — waves run until the pool or the spend ceiling is exhausted, and the reaper only expires a sweep whose executor died (no progress heartbeat for the whole pool-scaled window — roughly 12 minutes per expected wave on top of the base, capped at 150 minutes) or that hit the 24-hour safety ceiling. When a sweep is stopped that way, any genuine success already on the scoreboard settles the run completed with the best winner and the partial attempt table; only a sweep with zero successes is marked failed with error code run_timeout and an honest note about what was attempted and billed.

Response fields

  • banana_peel.routing_strategy learning or learning-all
  • banana_peel.learning mode, batch_size, concurrency, pool_size, pool (catalog / dispatchable / attempted), bills_every_attempt, max_cost_usd, attempt_max_cost_usd, plus no_early_stop (learning-all), max_runners (when the request capped the pool), skipped_suspended (health suspensions shrank the pool), skipped_over_ceiling (catalog price above the per-attempt ceiling), skipped_no_adapter (catalog-only coming-soon options, never billed), and settled_early / settled_early_at (the run completed with a winner while the sweep kept exploring)
  • banana_peel.budget — the spend ceiling that governed dispatch: max_cost_usd, source (request / learning_default / learning_all_default / plan), and exhausted
  • banana_peel.learning_ranking — ordered winners from this probe (when any succeeded)
  • banana_peel.routing_attempts — every attempted runner with status, itemized cost_usd, timing_ms, and an error snippet for failed / blocked attempts. Also returned when polling GET /v1/responses/:id.

Examples

{
  "model": "banana-peel",
  "input": "Download the latest PDF bill",
  "url": "https://portal.example.com",
  "routing": "learning"
}

Full-pool sweep with a tightened ceiling (stops launching new attempts at $10 instead of the $20 default) and capped width (only the top 12 ranked runners attempt):

{
  "model": "banana-peel",
  "input": "Download the latest PDF bill",
  "url": "https://portal.example.com",
  "routing": "learning-all",
  "max_cost_usd": 10,
  "max_runners": 12
}

See also

Command Palette

Search for a command to run...