From e39bbaa9be85e57020be9735051e5f8044f53ed1 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 20 Feb 2026 09:54:21 +0000 Subject: [PATCH 1/4] feat(slack): add media file upload support Use files_upload_v2 API to upload media attachments in Slack messages. This enables the message tool's media parameter to work correctly when sending images or other files through the Slack channel. Requires files:write OAuth scope. --- nanobot/channels/slack.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/nanobot/channels/slack.py b/nanobot/channels/slack.py index dca5055..d29f1e1 100644 --- a/nanobot/channels/slack.py +++ b/nanobot/channels/slack.py @@ -84,11 +84,26 @@ class SlackChannel(BaseChannel): channel_type = slack_meta.get("channel_type") # Only reply in thread for channel/group messages; DMs don't use threads use_thread = thread_ts and channel_type != "im" - await self._web_client.chat_postMessage( - channel=msg.chat_id, - text=self._to_mrkdwn(msg.content), - thread_ts=thread_ts if use_thread else None, - ) + thread_ts_param = thread_ts if use_thread else None + + # Send text message if content is present + if msg.content: + await self._web_client.chat_postMessage( + channel=msg.chat_id, + text=self._to_mrkdwn(msg.content), + thread_ts=thread_ts_param, + ) + + # Upload media files if present + for media_path in msg.media or []: + try: + await self._web_client.files_upload_v2( + channel=msg.chat_id, + file=media_path, + thread_ts=thread_ts_param, + ) + except Exception as e: + logger.error(f"Failed to upload file {media_path}: {e}") except Exception as e: logger.error(f"Error sending Slack message: {e}") From b286457c854fd3d60228b680ec0dbc8d29286303 Mon Sep 17 00:00:00 2001 From: tercerapersona Date: Fri, 20 Feb 2026 11:34:50 -0300 Subject: [PATCH 2/4] add Openrouter prompt caching via cache_control --- nanobot/providers/registry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nanobot/providers/registry.py b/nanobot/providers/registry.py index 445d977..ecf092f 100644 --- a/nanobot/providers/registry.py +++ b/nanobot/providers/registry.py @@ -100,6 +100,7 @@ PROVIDERS: tuple[ProviderSpec, ...] = ( default_api_base="https://openrouter.ai/api/v1", strip_model_prefix=False, model_overrides=(), + supports_prompt_caching=True, ), # AiHubMix: global gateway, OpenAI-compatible interface. From cc04bc4dd1806f30e2f622a2628ddb40afc84fe7 Mon Sep 17 00:00:00 2001 From: Re-bin Date: Fri, 20 Feb 2026 15:14:45 +0000 Subject: [PATCH 3/4] fix: check gateway's supports_prompt_caching instead of always returning False --- nanobot/providers/litellm_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nanobot/providers/litellm_provider.py b/nanobot/providers/litellm_provider.py index edeb5c6..58c9ac2 100644 --- a/nanobot/providers/litellm_provider.py +++ b/nanobot/providers/litellm_provider.py @@ -111,7 +111,7 @@ class LiteLLMProvider(LLMProvider): def _supports_cache_control(self, model: str) -> bool: """Return True when the provider supports cache_control on content blocks.""" if self._gateway is not None: - return False + return self._gateway.supports_prompt_caching spec = find_by_model(model) return spec is not None and spec.supports_prompt_caching From 6bcfbd9610687875c53ce65e64538e7200913591 Mon Sep 17 00:00:00 2001 From: Re-bin Date: Fri, 20 Feb 2026 15:19:18 +0000 Subject: [PATCH 4/4] style: remove redundant comments and use loguru native format --- nanobot/channels/slack.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/nanobot/channels/slack.py b/nanobot/channels/slack.py index 26a3966..4fc1f41 100644 --- a/nanobot/channels/slack.py +++ b/nanobot/channels/slack.py @@ -86,7 +86,6 @@ class SlackChannel(BaseChannel): use_thread = thread_ts and channel_type != "im" thread_ts_param = thread_ts if use_thread else None - # Send text message if content is present if msg.content: await self._web_client.chat_postMessage( channel=msg.chat_id, @@ -94,7 +93,6 @@ class SlackChannel(BaseChannel): thread_ts=thread_ts_param, ) - # Upload media files if present for media_path in msg.media or []: try: await self._web_client.files_upload_v2( @@ -103,7 +101,7 @@ class SlackChannel(BaseChannel): thread_ts=thread_ts_param, ) except Exception as e: - logger.error(f"Failed to upload file {media_path}: {e}") + logger.error("Failed to upload file {}: {}", media_path, e) except Exception as e: logger.error("Error sending Slack message: {}", e)