feat(matrix): add configurable graceful sync shutdown

This commit is contained in:
Alexander Minges
2026-02-10 12:32:17 +01:00
parent 227251649f
commit 2642009792
2 changed files with 27 additions and 5 deletions
+20 -1
View File
@@ -49,9 +49,28 @@ class MatrixChannel(BaseChannel):
self._sync_task = asyncio.create_task(self._sync_loop())
async def stop(self) -> None:
"""Stop the Matrix channel with graceful sync shutdown."""
self._running = False
if self.client:
# Request sync_forever loop to exit cleanly.
self.client.stop_sync_forever()
if self._sync_task:
self._sync_task.cancel()
try:
await asyncio.wait_for(
asyncio.shield(self._sync_task),
timeout=self.config.sync_stop_grace_seconds,
)
except asyncio.TimeoutError:
self._sync_task.cancel()
try:
await self._sync_task
except asyncio.CancelledError:
pass
except asyncio.CancelledError:
pass
if self.client:
await self.client.close()
+7 -4
View File
@@ -1,6 +1,7 @@
"""Configuration schema using Pydantic."""
from pathlib import Path
from pydantic import BaseModel, Field
from pydantic_settings import BaseSettings
@@ -53,6 +54,8 @@ class MatrixConfig(BaseModel):
access_token: str = ""
user_id: str = "" # @bot:matrix.org
device_id: str = ""
# Max seconds to wait for sync_forever to stop gracefully before cancellation fallback.
sync_stop_grace_seconds: int = 2
allow_from: list[str] = Field(default_factory=list)
class ChannelsConfig(BaseModel):
@@ -137,12 +140,12 @@ class Config(BaseSettings):
providers: ProvidersConfig = Field(default_factory=ProvidersConfig)
gateway: GatewayConfig = Field(default_factory=GatewayConfig)
tools: ToolsConfig = Field(default_factory=ToolsConfig)
@property
def workspace_path(self) -> Path:
"""Get expanded workspace path."""
return Path(self.agents.defaults.workspace).expanduser()
def _match_provider(self, model: str | None = None) -> tuple["ProviderConfig | None", str | None]:
"""Match provider config and its registry name. Returns (config, spec_name)."""
from nanobot.providers.registry import PROVIDERS
@@ -175,7 +178,7 @@ class Config(BaseSettings):
"""Get API key for the given model. Falls back to first available key."""
p = self.get_provider(model)
return p.api_key if p else None
def get_api_base(self, model: str | None = None) -> str | None:
"""Get API base URL for the given model. Applies default URLs for known gateways."""
from nanobot.providers.registry import find_by_name
@@ -190,7 +193,7 @@ class Config(BaseSettings):
if spec and spec.is_gateway and spec.default_api_base:
return spec.default_api_base
return None
class Config:
env_prefix = "NANOBOT_"
env_nested_delimiter = "__"