arcade-mcp/toolkits/google/arcade_google/tools/file_picker.py
Sam Partee b6b4cd0a4c
🏗️ Restructure: Multi-Package Architecture + uv Migration (#412)
### Overview
Major restructuring from monolithic `arcade-ai` package to modular
library architecture with standardized uv-based dependency management.

![arcade-ai Monorepo
(2)](https://github.com/user-attachments/assets/25f102b0-bb87-4a04-9701-d227d05664b1)

### New Package Structure
- **`arcade-tdk`** - Lightweight toolkit development kit (core
decorators, auth)
- **`arcade-core`** - Core execution engine and catalog functionality  
- **`arcade-serve`** - FastAPI/MCP server components
- **`arcade-ai`** - Meta package that includes CLI functionality.
Optionally include evals via the `evals` extra. Optionally include all
packages via the `all` extra.

### Key Benefits
- **Lighter Dependencies**: Toolkits now depend only on `arcade-tdk` (~2
deps) vs full `arcade-ai` (~30+ deps)
- **Faster Builds**: uv provides 10-100x faster dependency resolution
and installation
- **Better Modularity**: Clear separation of concerns, consumers import
only what they need
- **Standard Tooling**: Eliminates custom poetry scripts, uses standard
Python packaging

### Migration Impact
- All 20 toolkits converted from poetry → uv with `arcade-tdk`
dependencies plus `arcade-ai[evals]` and `arcade-serve` dev
dependencies. When developing locally, devs should install toolkits via
`make install-local`.
- Modern Python 3.10+ type hints throughout
- Standardized build system with hatchling backend
- Enhanced Makefile with robust toolkit management commands
- Removed `arcade dev` CLI command
- Reduce the number of files created by `arcade new` and add an option
to not generate a tests and evals folder.

This foundation enables faster development cycles and cleaner dependency
chains for the growing toolkit ecosystem.

### Todo After this PR is merged
- [ ] Post-merge workflow(s) (release & publish containers, etc)
- [ ] Release order plan. @EricGustin suggests releasing in the
following order:
    1. `arcade-core` version 0.1.0
    2. `arcade-serve` version 0.1.0 and `arcade-tdk` version 0.1.0
    3. `arcade-ai` version 2.0.0
4. Patch release for all toolkits (all changes in toolkits are internal
refactors)
- [ ] [Update docs](https://github.com/ArcadeAI/docs/pull/318)

---------

Co-authored-by: Eric Gustin <eric@arcade.dev>
Co-authored-by: Eric Gustin <34000337+EricGustin@users.noreply.github.com>
2025-06-11 16:48:17 -07:00

54 lines
2.1 KiB
Python

import base64
import json
from typing import Annotated
from arcade_tdk import ToolContext, ToolMetadataKey, tool
from arcade_tdk.auth import Google
from arcade_tdk.errors import ToolExecutionError
@tool(
requires_auth=Google(),
requires_metadata=[ToolMetadataKey.CLIENT_ID, ToolMetadataKey.COORDINATOR_URL],
)
def generate_google_file_picker_url(
context: ToolContext,
) -> Annotated[dict, "Google File Picker URL for user file selection and permission granting"]:
"""Generate a Google File Picker URL for user-driven file selection and authorization.
This tool generates a URL that directs the end-user to a Google File Picker interface where
where they can select or upload Google Drive files. Users can grant permission to access their
Drive files, providing a secure and authorized way to interact with their files.
This is particularly useful when prior tools (e.g., those accessing or modifying
Google Docs, Google Sheets, etc.) encountered failures due to file non-existence
(Requested entity was not found) or permission errors. Once the user completes the file
picker flow, the prior tool can be retried.
"""
client_id = context.get_metadata(ToolMetadataKey.CLIENT_ID)
client_id_parts = client_id.split("-")
if not client_id_parts:
raise ToolExecutionError(
message="Invalid Google Client ID",
developer_message=f"Google Client ID '{client_id}' is not valid",
)
app_id = client_id_parts[0]
cloud_coordinator_url = context.get_metadata(ToolMetadataKey.COORDINATOR_URL).strip("/")
config = {
"auth": {
"client_id": client_id,
"app_id": app_id,
},
}
config_json = json.dumps(config)
config_base64 = base64.urlsafe_b64encode(config_json.encode("utf-8")).decode("utf-8")
url = f"{cloud_coordinator_url}/google/drive_picker?config={config_base64}"
return {
"url": url,
"llm_instructions": (
"Instruct the user to click the following link to open the Google Drive File Picker. "
"This will allow them to select files and grant access permissions: {url}"
),
}