diff --git a/agent/model_metadata.py b/agent/model_metadata.py index 734febd3bf4..e286485a4f3 100644 --- a/agent/model_metadata.py +++ b/agent/model_metadata.py @@ -2155,6 +2155,35 @@ def get_model_context_length( return DEFAULT_FALLBACK_CONTEXT +async def get_model_context_length_async( + model: str, + base_url: str = "", + api_key: str = "", + config_context_length: int | None = None, + provider: str = "", + custom_providers: list | None = None, +) -> int: + """Async variant of get_model_context_length. + + Offloads the entire synchronous resolution chain (which contains + blocking HTTP calls via ``requests``) to a background thread so it + does not freeze the asyncio event loop and cause Discord heartbeat + timeouts. + + Shares all logic with the sync version — no code duplication. + """ + import asyncio + return await asyncio.to_thread( + get_model_context_length, + model, + base_url=base_url, + api_key=api_key, + config_context_length=config_context_length, + provider=provider, + custom_providers=custom_providers, + ) + + def estimate_tokens_rough(text: str) -> int: """Rough token estimate (~4 chars/token) for pre-flight checks. diff --git a/gateway/run.py b/gateway/run.py index 4c4a4b107b2..9a34a066659 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -9762,7 +9762,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew if "@" in message_text: try: from agent.context_references import preprocess_context_references_async - from agent.model_metadata import get_model_context_length + from agent.model_metadata import get_model_context_length_async _msg_cwd = os.environ.get("TERMINAL_CWD", os.path.expanduser("~")) _msg_runtime = _resolve_runtime_agent_kwargs() @@ -9776,7 +9776,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew _msg_config_ctx = int(_msg_raw_ctx) except Exception: pass - _msg_ctx_len = get_model_context_length( + _msg_ctx_len = await get_model_context_length_async( self._model, base_url=self._base_url or _msg_runtime.get("base_url") or "", api_key=_msg_runtime.get("api_key") or "", @@ -10114,7 +10114,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew if history and len(history) >= 4: from agent.model_metadata import ( estimate_messages_tokens_rough, - get_model_context_length, + get_model_context_length_async, ) # Read model + compression config from config.yaml. @@ -10215,7 +10215,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew pass if _hyg_compression_enabled: - _hyg_context_length = get_model_context_length( + _hyg_context_length = await get_model_context_length_async( _hyg_model, base_url=_hyg_base_url or "", api_key=_hyg_api_key or "", diff --git a/scripts/release.py b/scripts/release.py index 6383df326cb..09acd16849d 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -45,6 +45,7 @@ ACP_REGISTRY_MANIFEST = REPO_ROOT / "acp_registry" / "agent.json" # Auto-extracted from noreply emails + manual overrides AUTHOR_MAP = { + "5848605+itenev@users.noreply.github.com": "itenev", # PR #22753 salvage (asyncify model-context resolution in gateway message path so blocking requests.get can't starve Discord heartbeats) "290873280+rrevenanttt@users.noreply.github.com": "rrevenanttt", # PR #40773 salvage (close hardline rm bypass via quoted paths and ${HOME} brace form) "290871358+Vesna-9@users.noreply.github.com": "Vesna-9", # PR #41274 salvage (collapse shell line continuations before dangerous/hardline pattern matching so `rm -rf \/` can't bypass the yolo-proof hardline floor) "214165399+kernel-t1@users.noreply.github.com": "kernel-t1", # PR #41349 salvage (.env sanitizer: only split when line starts with a known KEY= and preceding values are plain tokens; keep URL/query/whitespace secrets verbatim)