This commit is contained in:
Taylor Wilsdon
2026-02-15 16:46:19 -05:00
parent 7cfa10f457
commit 39fe339dc8
3 changed files with 39 additions and 24 deletions

View File

@@ -846,7 +846,11 @@ async def get_gmail_attachment_content(
user_google_email: str,
) -> str:
"""
Downloads the content of a specific email attachment.
Downloads an email attachment and saves it to local disk.
In stdio mode, returns the local file path for direct access.
In HTTP mode, returns a temporary download URL (valid for 1 hour).
May re-fetch message metadata to resolve filename and MIME type.
Args:
message_id (str): The ID of the Gmail message containing the attachment.
@@ -854,19 +858,14 @@ async def get_gmail_attachment_content(
user_google_email (str): The user's Google email address. Required.
Returns:
str: Attachment metadata and base64-encoded content that can be decoded and saved.
str: Attachment metadata with either a local file path or download URL.
"""
logger.info(
f"[get_gmail_attachment_content] Invoked. Message ID: '{message_id}', Email: '{user_google_email}'"
)
# Download attachment directly without refetching message metadata.
#
# Important: Gmail attachment IDs are ephemeral and change between API calls for the
# same message. If we refetch the message here to get metadata, the new attachment IDs
# won't match the attachment_id parameter provided by the caller, causing the function
# to fail. The attachment download endpoint returns size information, and filename/mime
# type should be obtained from the original message content call that provided this ID.
# Download attachment content first, then optionally re-fetch message metadata
# to resolve filename and MIME type for the saved file.
try:
attachment = await asyncio.to_thread(
service.users()
@@ -919,11 +918,16 @@ async def get_gmail_attachment_content(
filename = None
mime_type = None
try:
# Use format="full" to get attachment parts with attachmentId
# Use format="full" with fields to limit response to attachment metadata only
message_full = await asyncio.to_thread(
service.users()
.messages()
.get(userId="me", id=message_id, format="full")
.get(
userId="me",
id=message_id,
format="full",
fields="payload(parts(filename,mimeType,body(attachmentId,size)),body(attachmentId,size),filename,mimeType)",
)
.execute
)
payload = message_full.get("payload", {})
@@ -978,7 +982,6 @@ async def get_gmail_attachment_content(
else:
download_url = get_attachment_url(result.file_id)
result_lines.append(f"\n📎 Download URL: {download_url}")
result_lines.append(f"📂 Local path: {result.path}")
result_lines.append("\nThe file will expire after 1 hour.")
result_lines.append(