Agentic_flow / drive_tools.py
disLodge's picture
Adding auth flow within calendar invites
6d34792
import io
import os
from langchain_core.tools import tool
from googleapiclient.discovery import build
from googleapiclient.http import MediaIoBaseDownload
from googleapiclient.errors import HttpError
from google_auth_helpers import (
AUTH_REQUIRED_PREFIX,
auth_required_message,
get_google_credentials,
is_auth_failure,
)
DOWNLOAD_DIR = os.getenv("DOWNLOAD_DIR", "/tmp/drive_downloads")
def _drive_service(user_email: str):
"""
Returns (Drive service, auth_message).
auth_message is set when the user must re-authenticate.
"""
creds, auth_msg = get_google_credentials(user_email)
if auth_msg:
return None, auth_msg
if creds is None:
return None, auth_required_message(user_email, "Google Drive")
return build("drive", "v3", credentials=creds), None
def _search_files(service, query: str, max_results: int = 5) -> list[dict]:
"""Full-text search across Drive files."""
results = service.files().list(
q=f"fullText contains '{query}' and trashed=false",
pageSize=max_results,
fields="files(id, name, mimeType, webViewLink, size)",
).execute()
return results.get("files", [])
def _download_file(service, file_id: str, file_name: str, mime_type: str) -> str:
"""Downloads a file and returns local path."""
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
local_path = os.path.join(DOWNLOAD_DIR, file_name)
export_map = {
"application/vnd.google-apps.document":
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.google-apps.spreadsheet":
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.google-apps.presentation":
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
}
fh = io.BytesIO()
if mime_type in export_map:
export_mime = export_map[mime_type]
request = service.files().export_media(fileId=file_id, mimeType=export_mime)
ext_map = {
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
"application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx",
}
local_path += ext_map.get(export_mime, "")
else:
request = service.files().get_media(fileId=file_id)
downloader = MediaIoBaseDownload(fh, request)
done = False
while not done:
_, done = downloader.next_chunk()
with open(local_path, "wb") as f:
f.write(fh.getvalue())
return local_path
@tool
def search_and_download_doc_tool(user_email: str, query: str) -> str:
"""
Searches Google Drive and downloads a document by name.
"""
service, auth_msg = _drive_service(user_email)
if auth_msg:
return auth_msg
try:
files = _search_files(service, query)
except HttpError as e:
if is_auth_failure(e):
return auth_required_message(user_email, "Google Drive", revoke=True)
return f"Drive search failed: {e}"
if not files:
return f"No files found on Google Drive matching '{query}'."
best = files[0]
file_id = best["id"]
file_name = best["name"]
mime_type = best["mimeType"]
view_link = best.get("webViewLink", "N/A")
other_matches = [f["name"] for f in files[1:]]
other_str = (
f"\n\nOther matches: {', '.join(other_matches)}" if other_matches else ""
)
try:
local_path = _download_file(service, file_id, file_name, mime_type)
return (
f"✅ Found and downloaded '{file_name}'.\n"
f"Saved to: {local_path}\n"
f"View online: {view_link}"
f"{other_str}"
)
except HttpError as e:
if is_auth_failure(e):
return auth_required_message(user_email, "Google Drive", revoke=True)
return (
f"Found '{file_name}' on Drive but download failed: {e}\n"
f"View online: {view_link}"
)