$ cat node-template.py

D

Drive Document Reader

// Reads one or more documents from Drive and returns their text content. Multiple documents are merged into a single output.

Input
Storage
#drive#storage#document#text
template.py
1"""drive-document-reader — read one or more documents' text from Drive.23Migrated from the legacy monolithic template to the new multi-file format.4Privileged Drive IO is delegated to the gais.drive SDK (Gais.drive.read_text);5this node imports no HTTP client. Multiple documents are deduplicated and6merged; a single item that fails (e.g. a folder or an unprocessed doc) is7soft-skipped so one bad id does not fail the whole batch.8"""910from __future__ import annotations1112import json13import re14import sys15import traceback16from typing import Any1718from gais import Gais, RemoteError, BackendTimeout1920MAX_DOCUMENTS = 10021_SEPARATOR = "\n\n--- Document Separator ---\n\n"2223_UUID_RE = re.compile(24    r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",25    re.IGNORECASE,26)272829def _normalize_drive_item_id(value: Any) -> str:30    """Accept a bare UUID or a mention://drive_{file,folder}/<uuid> URI."""31    if not isinstance(value, str):32        raise ValueError(f"Drive item id must be a string, got {type(value).__name__}")33    s = value.strip()34    for prefix in ("mention://drive_file/", "mention://drive_folder/"):35        if s.startswith(prefix):36            s = s[len(prefix):]37            break38    if not _UUID_RE.match(s):39        raise ValueError(f"Drive item id is not a valid UUID: {value!r}")40    return s414243def process(inputs: dict[str, Any]) -> dict[str, Any]:44    """Read + merge text for one or many documents via gais.drive.read_text."""45    raw = inputs.get("driveItemId")46    if not raw:47        raise ValueError("Required input 'driveItemId' not provided")4849    if isinstance(raw, str):50        candidate_ids = [raw]51    elif isinstance(raw, list):52        candidate_ids = raw53    else:54        raise ValueError(55            f"driveItemId must be a string or array, got {type(raw).__name__}"56        )5758    # Normalize (strip mention:// prefix, validate UUID) and dedupe.59    seen: set[str] = set()60    unique_ids: list[str] = []61    for candidate in candidate_ids:62        if not candidate:63            continue64        item_id = _normalize_drive_item_id(candidate)65        if item_id not in seen:66            seen.add(item_id)67            unique_ids.append(item_id)6869    if not unique_ids:70        raise ValueError("No valid document IDs provided")71    if len(unique_ids) > MAX_DOCUMENTS:72        raise ValueError(f"Too many documents: {len(unique_ids)} (max {MAX_DOCUMENTS})")7374    # Per-item soft-skip on a Drive API error (folders, unprocessed docs, etc.)75    # OR a per-item timeout, so one bad/slow id does not fail the whole batch.76    # BackendTimeout has no http_status, so record "timeout" for those.77    all_contents: list[str] = []78    skipped: list[dict[str, Any]] = []79    for item_id in unique_ids:80        try:81            result = Gais.drive.read_text(item_id=item_id)82            text = (result.text or "").strip()83            if text:84                all_contents.append(text)85        except (RemoteError, BackendTimeout) as e:86            skipped.append({"id": item_id, "status": getattr(e, "http_status", "timeout")})87            continue8889    if skipped:90        print(91            json.dumps({"warning": f"Skipped {len(skipped)} item(s)", "skipped": skipped}),92            file=sys.stderr,93        )9495    if not all_contents:96        failed = [s["id"] for s in skipped] if skipped else unique_ids97        raise ValueError(f"No documents could be read. Failed items: {failed}")9899    if len(unique_ids) > 1:100        return {"content": _SEPARATOR.join(all_contents)}101    return {"content": all_contents[0]}102103104def main() -> None:105    try:106        envelope = json.loads(sys.stdin.read() or "{}")107        if not isinstance(envelope, dict):108            envelope = {}109        inputs = envelope.get("inputs", {}) or {}110        json.dump(process(inputs), sys.stdout)111    except Exception as e:112        print(113            json.dumps({114                "error": str(e),115                "errorType": type(e).__name__,116                "traceback": traceback.format_exc(),117            }),118            file=sys.stderr,119        )120        sys.exit(1)121122123if __name__ == "__main__":124    main()

$ git log --oneline

v1.2.0
HEAD
2026-08-18
v1.0.22026-05-22
v1.0.12026-05-07
v1.0.02026-04-09