fix(slack): make video attachments available to agents (#45512)

This commit is contained in:
Teknium 2026-06-13 03:33:27 -07:00 committed by GitHub
parent 197337cc47
commit 2a5dc0ef3d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 274 additions and 3 deletions

View file

@ -20,6 +20,7 @@ from gateway.config import Platform, PlatformConfig
from gateway.platforms.base import (
MessageEvent,
MessageType,
SUPPORTED_VIDEO_TYPES,
is_host_excluded_by_no_proxy,
)
@ -120,6 +121,9 @@ def _redirect_cache(tmp_path, monkeypatch):
monkeypatch.setattr(
"gateway.platforms.base.DOCUMENT_CACHE_DIR", tmp_path / "doc_cache"
)
monkeypatch.setattr(
"gateway.platforms.base.VIDEO_CACHE_DIR", tmp_path / "video_cache"
)
# ---------------------------------------------------------------------------
@ -1443,6 +1447,84 @@ class TestIncomingDocumentHandling:
msg_event = adapter.handle_message.call_args[0][0]
assert msg_event.message_type == MessageType.PHOTO
@pytest.mark.asyncio
async def test_video_attachment_cached(self, adapter):
"""Video attachments should be downloaded into the video cache."""
video_bytes = b"\x00\x00\x00\x18ftypmp42fake-mp4"
with patch.object(
adapter, "_download_slack_file_bytes", new_callable=AsyncMock
) as dl:
dl.return_value = video_bytes
event = self._make_event(
text="what happens in this?",
files=[
{
"mimetype": "video/mp4",
"name": "clip.mp4",
"url_private_download": "https://files.slack.com/clip.mp4",
"size": len(video_bytes),
}
],
)
await adapter._handle_slack_message(event)
msg_event = adapter.handle_message.call_args[0][0]
assert msg_event.message_type == MessageType.VIDEO
assert len(msg_event.media_urls) == 1
assert os.path.exists(msg_event.media_urls[0])
assert msg_event.media_types == [SUPPORTED_VIDEO_TYPES[".mp4"]]
dl.assert_awaited_once_with("https://files.slack.com/clip.mp4", team_id="")
@pytest.mark.asyncio
async def test_file_shared_video_fallback_fetches_file_info(self, adapter):
"""file_shared-only video events should still reach the agent."""
video_bytes = b"\x00\x00\x00\x18ftypmp42fake-mp4"
adapter._app.client.files_info = AsyncMock(
return_value={
"ok": True,
"file": {
"id": "FVIDEO",
"mimetype": "video/mp4",
"name": "clip.mp4",
"url_private_download": "https://files.slack.com/clip.mp4",
"size": len(video_bytes),
"user": "U_USER",
"shares": {
"private": {
"D123": [
{"ts": "1234567890.000001"},
]
}
},
},
}
)
with (
patch.object(
adapter, "_download_slack_file_bytes", new_callable=AsyncMock
) as dl,
patch("asyncio.sleep", new_callable=AsyncMock),
):
dl.return_value = video_bytes
await adapter._handle_slack_file_shared(
{
"type": "file_shared",
"channel_id": "D123",
"file_id": "FVIDEO",
"user_id": "U_USER",
"event_ts": "1234567890.000002",
}
)
adapter._app.client.files_info.assert_awaited_once_with(file="FVIDEO")
msg_event = adapter.handle_message.call_args[0][0]
assert msg_event.message_type == MessageType.VIDEO
assert len(msg_event.media_urls) == 1
assert os.path.exists(msg_event.media_urls[0])
assert msg_event.media_types == [SUPPORTED_VIDEO_TYPES[".mp4"]]
@pytest.mark.asyncio
async def test_download_failure_is_surfaced_in_message_text(self, adapter):
"""Attachment download failures (401/403/HTML-body/etc.) should be

View file

@ -0,0 +1,50 @@
"""Tests for video attachment context notes in gateway turns."""
from unittest.mock import patch
import pytest
from gateway.config import GatewayConfig, Platform
from gateway.platforms.base import MessageEvent, MessageType
from gateway.session import SessionSource
def _make_runner() -> "GatewayRunner": # type: ignore[name-defined]
from gateway.run import GatewayRunner
runner = GatewayRunner.__new__(GatewayRunner)
runner.config = GatewayConfig()
runner.adapters = {}
runner._has_setup_skill = lambda: False
return runner
@pytest.mark.asyncio
async def test_video_attachment_adds_path_note_without_document_wording():
from gateway.run import _build_media_placeholder
runner = _make_runner()
source = SessionSource(platform=Platform.SLACK, chat_id="D123", chat_type="dm")
event = MessageEvent(
text="what happens here?",
message_type=MessageType.VIDEO,
source=source,
media_urls=["/tmp/video_clip.mp4"],
media_types=["video/mp4"],
)
with patch(
"tools.credential_files.to_agent_visible_cache_path",
side_effect=lambda path: path,
):
result = await runner._prepare_inbound_message_text(
event=event,
source=source,
history=[],
)
assert "video attachment" in result
assert "/tmp/video_clip.mp4" in result
assert "video analysis or media tool" in result
assert "The user sent a document" not in result
assert _build_media_placeholder(event) == "[User sent a video: /tmp/video_clip.mp4]"

View file

@ -378,12 +378,14 @@ class TestCacheDirectoryMounts:
hermes_home.mkdir()
(hermes_home / "cache" / "documents").mkdir(parents=True)
(hermes_home / "cache" / "audio").mkdir(parents=True)
(hermes_home / "cache" / "videos").mkdir(parents=True)
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
mounts = get_cache_directory_mounts()
paths = {m["container_path"] for m in mounts}
assert "/root/.hermes/cache/documents" in paths
assert "/root/.hermes/cache/audio" in paths
assert "/root/.hermes/cache/videos" in paths
def test_skips_nonexistent_dirs(self, tmp_path, monkeypatch):
"""Dirs that don't exist on disk are not returned."""