NetLogo / NetLogo/Netlogo-LLM-Extension
feat: request throttling — bounded concurrency, jitter, and per-provider pacing
Nobody has claimed this yet.
- Dominant language
- Scala
- Stars
- 1
- Forks
- 0
- Avg merge
- 3d 1h
- Merged PRs (30d)
- 4
Description
Summary
Add per-provider request pacing and bounded concurrency. Retry-on-429 (#37) handles a rate limit after it happens; this is about not tripping it in the first place, and about keeping one agent's failure from taking down a tick.
Research/context only; design still to be done.
Problem
llm:chat-async fires one Future per call onto ExecutionContext.global with no cap (LLMExtension.scala:64, :479-509). ask turtles [ ... llm:chat-async ... ] with N turtles issues N simultaneous HTTP requests. There is no semaphore, no queue, and no per-provider connection bound.
Current retry (BaseHttpProvider.executeWithRetry, added in 2d083b6) covers 429 only — 5xx, timeouts, and connection errors fail immediately. Backoff is deterministic (1s/2s/4s) with no jitter, so a burst of rate-limited agents retries in lockstep.
Prior art: mesa-llm hit exactly this wall
mesa-llm has the same unbounded fan-out shape, and their own issue #200 reports the outcome:
"Mesa-LLM suffers from critical performance bottlenecks that make it unsuitable for large-scale agent simulations... resulting in exponential performance degradation beyond ~10 agents."
Reported: 20 agents ≈ 3 min/step, 50 agents ≈ 15+ min/step.
Verified from their source (local clone, HEAD 57a51ff):
- No concurrency limiting anywhere — grep for
Semaphore|throttle|rate_limit_delayacross their package returns zero hits. - Bare
asyncio.gatheratmesa_llm/parallel_stepping.py:31and:124, andmesa_llm/tools/tool_manager.py:388— none passreturn_exceptions=True. One agent raising kills the entire tick. That is their separate issue #220. - No response caching — grep for
lru_cache|functools.cache|redis|memoizereturns zero hits.
Their contributing factor we don't share is 2 LLM calls per agent per step. But the unbounded fan-out is identical, and it is the part that turns a slow tick into a failing one.
Design requirements this suggests
- Bounded concurrency — a configurable cap on in-flight requests, ideally per provider (limits differ a lot between e.g. a Gemini free tier and a paid OpenAI key).
- Jitter on backoff — deterministic backoff across many agents produces a thundering herd. Randomized backoff is a small change to
executeWithRetry. - Per-agent failure isolation — one agent's failed call should not abort the tick or poison other agents' futures.
- Broaden retry — 5xx and timeouts, not just 429.
- Optional pacing — a minimum interval between requests to a given provider, for free tiers with requests-per-minute limits.
Open questions
- Config surface: a
max_concurrent_requestskey, per-provider descriptor defaults, or both? - Should pacing be automatic from a known per-provider RPM limit, or purely modeler-set?
- What does the modeler see when requests are queued — nothing, or a monitor-able count?
- Interaction with
timeout_seconds: does queue wait count against the timeout budget? (It should not.)
Related
- #37 — retry with backoff (merged; this is the proactive counterpart)
- #22 — structured output
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with LLMExtension.scala:64 and :479-509 to understand the unbounded Future fan-out, then read BaseHttpProvider.executeWithRetry and related issue #37. The issue is research-only: implementation should wait until concurrency limits, pacing, retry behavior, failure isolation, and timeout semantics are designed and agreed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- scala
- Domain
- api, backend, performance
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100