Skip to content

Add chat attachment support: surface metadata and download files - #488

Merged
taylorwilsdon merged 4 commits into
taylorwilsdon:mainfrom
drewgillson:feat/chat-attachment-download
Feb 19, 2026
Merged

Add chat attachment support: surface metadata and download files#488
taylorwilsdon merged 4 commits into
taylorwilsdon:mainfrom
drewgillson:feat/chat-attachment-download

Conversation

@drewgillson

@drewgillson drewgillson commented Feb 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • get_messages and search_messages now display attachment metadata (filename, MIME type) inline when Chat messages contain images or files
  • New download_chat_attachment tool (extended tier) fetches attachment binary data via the Chat API media endpoint and saves to local disk — returns a file path in stdio mode or a temporary download URL in HTTP mode
  • No new OAuth scopes needed — uses existing chat_read scope

Test plan

  • uvx ruff check . — lint clean
  • 10 new unit tests in tests/gchat/test_chat_tools.py covering metadata display, download success, HTTP mode, error handling, and edge cases
  • All 135 tests pass
  • Manually verified: attachment metadata visible in get_messages output
  • Manually verified: download_chat_attachment successfully downloads images from Chat

Summary by CodeRabbit

  • New Features

    • Added chat reaction tool (emoji reactions).
    • Download attachments from Google Chat messages; supports multiple attachments, inline preview, saving, or temporary URL.
    • Attachment metadata (index, name, type) now shown in message listings and search results.
    • Export documents as formatted Markdown (optional comments).
  • Tests

    • Added comprehensive unit tests for attachment listing, download flows, and edge cases.
  • Style

    • Formatting refinements in document processing and test fixtures (no behavioral changes).

Previously, get_messages and search_messages completely ignored the
attachment field on Chat API messages. This adds:

- Attachment metadata (filename, type) displayed inline in get_messages
  and search_messages output
- New download_chat_attachment tool that downloads attachments via the
  Chat API media endpoint and saves to local disk

The download uses httpx with a Bearer token against the
chat.googleapis.com/v1/media endpoint (with alt=media), which works
correctly in both OAuth 2.0 and OAuth 2.1 modes. The attachment's
downloadUri field is intentionally ignored as it points to
chat.google.com which requires browser session cookies.

Key details:
- Uses attachmentDataRef.resourceName for the media endpoint URL
- No new OAuth scopes required (existing chat_read is sufficient)
- Tool registered in the extended tier
- 10 unit tests covering metadata display, download, and edge cases
@coderabbitai

coderabbitai Bot commented Feb 19, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds attachment support for Google Chat: updates tool tiers, surfaces attachment metadata in message listing/search, implements download_chat_attachment (stateless base64 preview or persistent save/URL), and adds comprehensive tests for attachment flows and edge cases.

Changes

Cohort / File(s) Summary
Configuration
core/tool_tiers.yaml
Added create_reaction to chatcore and download_chat_attachment to chatextended.
Chat feature & API
gchat/chat_tools.py
Added base64 and httpx imports; enhanced get_messages and search_messages to list attachment metadata and download hints; introduced download_chat_attachment(service, user_google_email, message_id, attachment_index=0) -> str supporting stateless (base64) preview and persistent save/URL modes with error handling and logging.
Unit tests — gchat
tests/gchat/test_chat_tools.py
New tests covering attachment metadata in listings/search, and many download_chat_attachment scenarios: missing attachments, invalid index, media endpoint/media bytes handling, HTTP URL mode, header/auth checks, fallback name, and failure paths.
Docs & formatting
gdocs/docs_markdown.py, README.md
Minor formatting/signature adjustments in docs_markdown.py; README updated to document create_reaction and download_chat_attachment tools.
Unit tests — docs/comments
tests/gdocs/test_docs_markdown.py, tests/core/test_comments.py
Test fixtures and expected structures reformatted to expanded multi-line representations; two pytest.fail messages reflowed. No behavioral changes.

Sequence Diagram

sequenceDiagram
    participant User as User/Client
    participant Tool as download_chat_attachment
    participant API as Google Chat API
    participant Storage as Storage System

    User->>Tool: download_chat_attachment(service, email, message_id, index)
    activate Tool
    Tool->>API: Fetch message & attachment metadata
    activate API
    API-->>Tool: Return attachments (+ media endpoint or dataRef)
    deactivate API

    alt Stateless preview
        Tool->>API: Request media bytes from media endpoint
        activate API
        API-->>Tool: Attachment bytes
        deactivate API
        Tool->>Tool: Encode bytes to base64
        Tool-->>User: Return base64 string
    else Persistent save / URL
        Tool->>API: Fetch attachment bytes (if needed)
        activate API
        API-->>Tool: Attachment bytes
        deactivate API
        Tool->>Storage: Save bytes / request temp URL
        activate Storage
        Storage-->>Tool: File path or download URL
        deactivate Storage
        Tool-->>User: Return file path or URL
    end
    deactivate Tool
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

enhancement

Poem

🐰 I found a file behind a chat,
A tiny byte where attachments sat.
Base64 hums or a link to send,
I fetch, I save, and then I lend.
Hooray — downloads hop right in my hat! 📎

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding chat attachment support with metadata surfacing and file download capabilities.
Description check ✅ Passed The PR description covers the summary of changes, testing approach with specific verification steps, and relevant context about OAuth scopes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
gchat/chat_tools.py (1)

520-526: Add an explicit timeout to the httpx download call.
Large attachments or slow networks can hang or exceed implicit defaults. Setting a timeout improves reliability and makes behavior predictable.

Proposed fix
-        async with httpx.AsyncClient(follow_redirects=True) as client:
+        timeout = httpx.Timeout(60.0)
+        async with httpx.AsyncClient(follow_redirects=True, timeout=timeout) as client:
             resp = await client.get(
                 download_url,
                 headers={"Authorization": f"Bearer {access_token}"},
             )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gchat/chat_tools.py` around lines 520 - 526, The httpx download call lacks an
explicit timeout causing potential hangs; update the download logic in
gchat/chat_tools.py (around the access_token = service._http.credentials.token
and async with httpx.AsyncClient(...) as client block) to pass a clear timeout
(e.g., httpx.Timeout or a numeric seconds value) to the request (either when
constructing AsyncClient or to client.get) so the GET to download_url uses a
bounded timeout and fails predictably on slow networks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@gchat/chat_tools.py`:
- Around line 403-409: The search_messages output currently builds att_suffix
from attachment contentName only; update the attachment formatting in
gchat/chat_tools.py (where attachments, att_suffix and the output.append line
are defined) to include the MIME type too (e.g., use a.get("contentType") or a
fallback like "unknown") so each attachment is rendered as "filename, MIME type"
to match get_messages and the stated requirement; ensure the new att_suffix
string interpolation uses both a.get("contentName", "unnamed") and the MIME type
when constructing the appended message.

---

Nitpick comments:
In `@gchat/chat_tools.py`:
- Around line 520-526: The httpx download call lacks an explicit timeout causing
potential hangs; update the download logic in gchat/chat_tools.py (around the
access_token = service._http.credentials.token and async with
httpx.AsyncClient(...) as client block) to pass a clear timeout (e.g.,
httpx.Timeout or a numeric seconds value) to the request (either when
constructing AsyncClient or to client.get) so the GET to download_url uses a
bounded timeout and fails predictably on slow networks.

Comment thread gchat/chat_tools.py
@taylorwilsdon
taylorwilsdon merged commit e9d4d29 into taylorwilsdon:main Feb 19, 2026
4 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Mar 19, 2026
20 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants