fix: robust Slack DM reply routing to avoid channel_not_found#2284
fix: robust Slack DM reply routing to avoid channel_not_found#2284imadcat wants to merge 2 commits intoNousResearch:mainfrom
Conversation
- Add metadata for channel_type and user_id to send calls - Cache DM channel to user_id mapping for fallback - Prefer user_id over channel_id for IM sends - Prevent threading in top-level DMs - Add tests for DM reply threading
There was a problem hiding this comment.
Pull request overview
This PR aims to make Slack DM replies more reliable by avoiding channel_not_found in IM contexts, primarily by routing outbound DM messages via user_id (with a channel→user cache) and by preventing unintended threading for top-level DMs.
Changes:
- Extend gateway send/typing/error metadata to include
channel_typeanduser_idfor platform adapters. - Update Slack adapter to cache DM channel→user mappings and prefer
user_idfor IM/DM sends. - Add tests covering DM reply threading behavior (top-level vs threaded DMs).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
gateway/platforms/base.py |
Adds channel_type and user_id into metadata used for typing and error sends. |
gateway/platforms/slack.py |
Adds DM channel→user cache and routes IM/DM sends via user_id; adjusts DM threading behavior. |
tests/gateway/test_slack.py |
Adds assertions and new test suite for DM reply threading/routing behavior. |
proxy.py |
Introduces a Flask/litellm proxy endpoint (also contains a hard-coded API key). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return metadata["thread_id"] | ||
| if metadata.get("thread_ts"): | ||
| return metadata["thread_ts"] | ||
| if metadata.get("channel_type") == "im": |
There was a problem hiding this comment.
_resolve_thread_ts only treats metadata['channel_type'] == 'im' as a DM, but the gateway sets event.source.chat_type to 'dm' (and base.py forwards that as channel_type). This means top-level DMs will still get thread_ts from reply_to and replies will incorrectly thread. Normalize/lower the channel_type here and treat both 'im' and 'dm' as DM contexts (similar to send()).
| if metadata.get("channel_type") == "im": | |
| channel_type = str(metadata.get("channel_type", "")).lower() | |
| if channel_type in ("im", "dm"): | |
| # In DM contexts, only use thread_ts for actual threaded DMs. | |
| # Top-level DMs should reply inline. |
| result = await adapter.send( | ||
| "D123", | ||
| "hello back", | ||
| reply_to="1234567890.000001", | ||
| metadata={"channel_type": "im", "user_id": "U_USER"}, | ||
| ) |
There was a problem hiding this comment.
These DM reply tests use metadata channel_type='im', but in production the gateway passes event.source.chat_type (e.g. 'dm') via base.py metadata. Add coverage for channel_type='dm' (or parametrize) so the test matches the real metadata values and guards against regressions.
| import os | ||
| import litellm | ||
| from flask import Flask, request, jsonify | ||
|
|
||
| app = Flask(__name__) | ||
|
|
||
| # Configure litellm to use the given key | ||
| os.environ["GEMINI_API_KEY"] = "AQ.Ab8RN6JQg5TLwJVPYsu1tlL5L3ccowm8TXWR8c70rljJqw4tFg" | ||
|
|
||
| @app.route("/v1/chat/completions", methods=["POST"]) | ||
| def chat_completions(): | ||
| try: | ||
| data = request.get_json() | ||
| model = data.get("model", "gemini/gemini-1.5-pro") | ||
| messages = data.get("messages", []) | ||
|
|
||
| # Call litellm which handles the translation | ||
| response = litellm.completion( | ||
| model=model if model.startswith("gemini/") else f"gemini/{model}", | ||
| messages=messages, | ||
| stream=False # Keep it simple | ||
| ) | ||
| return jsonify(response.model_dump()) |
There was a problem hiding this comment.
This new Flask/litellm proxy file appears unrelated to the PR’s Slack DM routing purpose and introduces new runtime surface area/dependencies. If it’s not intended for this change, it should be removed from the PR (or moved behind an explicit feature flag / documented entrypoint).
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Summary
Fixes Slack DM replies that fail with channel_not_found by routing via user_id instead of channel_id for IM contexts.
Changes
Test Plan