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
smart— Production: 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.learning— Paid 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-all— Paid 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 tosmart.
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 (
learningonly —learning-alldeliberately 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
completedwith that winner while the remaining waves keep attempting in the background and appending tobanana_peel.progressand the attempt table (banana_peel.learning.settled_earlymarks 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 inbanana_peel.learning.skipped_over_ceiling. - max_runners — optionally cap sweep width: after health and cost-ceiling filters, only the top
max_runnersranked 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
learningfor a cheaper teach that stops at the first successful wave, andsmartfor 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 codelearning_capacity, and aRetry-Afterheader (seconds). No run is created and nothing bills — back off and retry. Only Learning creates are affected;smarttraffic 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 with429, error coderate_limited, and aRetry-Afterheader — no run is created and nothing bills. Wait for a running probe to finish (pollGET /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_urland nothing bills (the batch fan-out never starts). - Create with
background: trueand pollGET /api/v1/responses/:id— while in flight,banana_peel.progressshows 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 settlescompletedwith that winner instead ofcancelled— winners are never discarded. - Sweep time is caller-controlled: pass
time_budget_msto 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 runcompletedwith the best winner and the partial attempt table; only a sweep with zero successes is markedfailedwith error coderun_timeoutand an honest note about what was attempted and billed.
Response fields
banana_peel.routing_strategy—learningorlearning-allbanana_peel.learning—mode,batch_size,concurrency,pool_size,pool(catalog/dispatchable/attempted),bills_every_attempt,max_cost_usd,attempt_max_cost_usd, plusno_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), andsettled_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), andexhaustedbanana_peel.learning_ranking— ordered winners from this probe (when any succeeded)banana_peel.routing_attempts— every attempted runner withstatus, itemizedcost_usd,timing_ms, and anerrorsnippet for failed / blocked attempts. Also returned when pollingGET /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
}