mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-31 08:13:11 +03:00
fix(agent): default request concurrency to unlimited (#5572)
* fix(agent): default request concurrency to unlimited * test(agent): clarify session serialization coverage
This commit is contained in:
@@ -188,7 +188,7 @@ These variables are process-level switches. Set them in the same terminal, servi
|
|||||||
|
|
||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
|----------|---------|-------------|
|
|----------|---------|-------------|
|
||||||
| `NANOBOT_MAX_CONCURRENT_REQUESTS` | `3` | Maximum concurrently running inbound agent requests. Must be an integer; set `0` or a negative value for unlimited. |
|
| `NANOBOT_MAX_CONCURRENT_REQUESTS` | Unlimited | Maximum concurrently running inbound agent requests. Set a positive integer to apply a cap; unset, `0`, or a negative value means unlimited. |
|
||||||
| `NANOBOT_LLM_TIMEOUT_S` | `300` | Wall-clock timeout, in seconds. Ordinary requests use this value; streaming requests use the greater of 300 seconds or twice this value. Set `0` to disable. Sustained-goal turns bypass this wall-clock cap. |
|
| `NANOBOT_LLM_TIMEOUT_S` | `300` | Wall-clock timeout, in seconds. Ordinary requests use this value; streaming requests use the greater of 300 seconds or twice this value. Set `0` to disable. Sustained-goal turns bypass this wall-clock cap. |
|
||||||
| `NANOBOT_STREAM_IDLE_TIMEOUT_S` | `90` | Streaming idle timeout, in seconds, used by streaming providers. Invalid or non-positive values are ignored; values above `3600` are clamped. |
|
| `NANOBOT_STREAM_IDLE_TIMEOUT_S` | `90` | Streaming idle timeout, in seconds, used by streaming providers. Invalid or non-positive values are ignored; values above `3600` are clamped. |
|
||||||
| `NANOBOT_OPENAI_COMPAT_TIMEOUT_S` | `120` | HTTP request timeout, in seconds, for OpenAI-compatible providers. Invalid or non-positive values are ignored. |
|
| `NANOBOT_OPENAI_COMPAT_TIMEOUT_S` | `120` | HTTP request timeout, in seconds, for OpenAI-compatible providers. Invalid or non-positive values are ignored. |
|
||||||
|
|||||||
@@ -430,8 +430,8 @@ class AgentLoop:
|
|||||||
("cron", self._cron_turns),
|
("cron", self._cron_turns),
|
||||||
("local trigger", self._local_trigger_turns),
|
("local trigger", self._local_trigger_turns),
|
||||||
)
|
)
|
||||||
# NANOBOT_MAX_CONCURRENT_REQUESTS: <=0 means unlimited; default 3.
|
# NANOBOT_MAX_CONCURRENT_REQUESTS: unset or <=0 means unlimited.
|
||||||
_max = int(os.environ.get("NANOBOT_MAX_CONCURRENT_REQUESTS", "3"))
|
_max = int(os.environ.get("NANOBOT_MAX_CONCURRENT_REQUESTS", "0"))
|
||||||
self._concurrency_gate: asyncio.Semaphore | None = (
|
self._concurrency_gate: asyncio.Semaphore | None = (
|
||||||
asyncio.Semaphore(_max) if _max > 0 else None
|
asyncio.Semaphore(_max) if _max > 0 else None
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
def _provider() -> MagicMock:
|
||||||
|
provider = MagicMock()
|
||||||
|
provider.get_default_model.return_value = "test-model"
|
||||||
|
provider.generation = SimpleNamespace(
|
||||||
|
max_tokens=4096,
|
||||||
|
temperature=0.1,
|
||||||
|
reasoning_effort=None,
|
||||||
|
)
|
||||||
|
return provider
|
||||||
|
|
||||||
|
|
||||||
|
def test_request_concurrency_is_unlimited_by_default(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
loop_factory,
|
||||||
|
) -> None:
|
||||||
|
monkeypatch.delenv("NANOBOT_MAX_CONCURRENT_REQUESTS", raising=False)
|
||||||
|
|
||||||
|
loop = loop_factory(provider=_provider(), patch_deps=True)
|
||||||
|
|
||||||
|
assert loop._concurrency_gate is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_positive_request_concurrency_keeps_explicit_cap(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
loop_factory,
|
||||||
|
) -> None:
|
||||||
|
monkeypatch.setenv("NANOBOT_MAX_CONCURRENT_REQUESTS", "2")
|
||||||
|
loop = loop_factory(provider=_provider(), patch_deps=True)
|
||||||
|
gate = loop._concurrency_gate
|
||||||
|
|
||||||
|
assert gate is not None
|
||||||
|
for _ in range(2):
|
||||||
|
await gate.acquire()
|
||||||
|
try:
|
||||||
|
assert gate.locked()
|
||||||
|
finally:
|
||||||
|
for _ in range(2):
|
||||||
|
gate.release()
|
||||||
@@ -254,7 +254,7 @@ class TestDispatch:
|
|||||||
assert isinstance(second.event, StreamEndEvent)
|
assert isinstance(second.event, StreamEndEvent)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_processing_lock_serializes(self):
|
async def test_same_session_dispatches_serialize(self):
|
||||||
from nanobot.bus.events import InboundMessage, OutboundMessage
|
from nanobot.bus.events import InboundMessage, OutboundMessage
|
||||||
|
|
||||||
loop, bus = _make_loop()
|
loop, bus = _make_loop()
|
||||||
|
|||||||
Reference in New Issue
Block a user