$ cat node-template.py

D

Drive Content Reader

// Reads the processed text content of one or more Drive files of any type: extracted text for documents (PDF, DOCX, TXT, MD) and speaker-labelled transcripts for audio and video. Multiple items are merged into a single output.

Input
Storage
#drive#storage#document#audio#video#transcript#text#content
template.py
1"""drive-content-reader — read the processed content of one or more Drive items.23Works for any processed file type: the llms.txt endpoint behind4Gais.drive.read_text returns MinerU markdown for PDF/DOCX, a WhisperX5transcript (speakers + timestamps) for audio/video, and the raw text for6TXT/MD. Formerly "Drive Document Reader" (same template id, in-place upgrade).78Privileged Drive IO is delegated to the gais.drive SDK; this node imports no9HTTP client. Multiple items are deduplicated and merged; a single item that10fails (e.g. a folder or an unprocessed file) is soft-skipped so one bad id11does not fail the whole batch.12"""1314from __future__ import annotations1516import json17import re18import sys19import traceback20from typing import Any2122from gais import Gais, RemoteError, BackendTimeout2324MAX_ITEMS = 10025# Kept as "Document Separator" for backward compatibility with downstream26# instructions that split on it.27_SEPARATOR = "\n\n--- Document Separator ---\n\n"2829_UUID_RE = re.compile(30    r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",31    re.IGNORECASE,32)333435def _normalize_drive_item_id(value: Any) -> str:36    """Accept a bare UUID or a mention://drive_{file,folder}/<uuid> URI."""37    if not isinstance(value, str):38        raise ValueError(f"Drive item id must be a string, got {type(value).__name__}")39    s = value.strip()40    for prefix in ("mention://drive_file/", "mention://drive_folder/"):41        if s.startswith(prefix):42            s = s[len(prefix):]43            break44    if not _UUID_RE.match(s):45        raise ValueError(f"Drive item id is not a valid UUID: {value!r}")46    return s474849def process(inputs: dict[str, Any]) -> dict[str, Any]:50    """Read + merge processed content for one or many items via gais.drive.read_text."""51    raw = inputs.get("driveItemId")52    if not raw:53        raise ValueError("Required input 'driveItemId' not provided")5455    if isinstance(raw, str):56        candidate_ids = [raw]57    elif isinstance(raw, list):58        candidate_ids = raw59    else:60        raise ValueError(61            f"driveItemId must be a string or array, got {type(raw).__name__}"62        )6364    # Normalize (strip mention:// prefix, validate UUID) and dedupe.65    seen: set[str] = set()66    unique_ids: list[str] = []67    for candidate in candidate_ids:68        if not candidate:69            continue70        item_id = _normalize_drive_item_id(candidate)71        if item_id not in seen:72            seen.add(item_id)73            unique_ids.append(item_id)7475    if not unique_ids:76        raise ValueError("No valid Drive item IDs provided")77    if len(unique_ids) > MAX_ITEMS:78        raise ValueError(f"Too many Drive items: {len(unique_ids)} (max {MAX_ITEMS})")7980    # Per-item soft-skip on a Drive API error (folders, unprocessed files, etc.)81    # OR a per-item timeout, so one bad/slow id does not fail the whole batch.82    # BackendTimeout has no http_status, so record "timeout" for those.83    all_contents: list[str] = []84    skipped: list[dict[str, Any]] = []85    for item_id in unique_ids:86        try:87            result = Gais.drive.read_text(item_id=item_id)88            text = (result.text or "").strip()89            if text:90                all_contents.append(text)91        except (RemoteError, BackendTimeout) as e:92            skipped.append({"id": item_id, "status": getattr(e, "http_status", "timeout")})93            continue9495    if skipped:96        print(97            json.dumps({"warning": f"Skipped {len(skipped)} item(s)", "skipped": skipped}),98            file=sys.stderr,99        )100101    if not all_contents:102        failed = [s["id"] for s in skipped] if skipped else unique_ids103        raise ValueError(f"No Drive items could be read. Failed items: {failed}")104105    if len(unique_ids) > 1:106        return {"content": _SEPARATOR.join(all_contents)}107    return {"content": all_contents[0]}108109110def main() -> None:111    try:112        envelope = json.loads(sys.stdin.read() or "{}")113        if not isinstance(envelope, dict):114            envelope = {}115        inputs = envelope.get("inputs", {}) or {}116        json.dump(process(inputs), sys.stdout)117    except Exception as e:118        print(119            json.dumps({120                "error": str(e),121                "errorType": type(e).__name__,122                "traceback": traceback.format_exc(),123            }),124            file=sys.stderr,125        )126        sys.exit(1)127128129if __name__ == "__main__":130    main()131

$ git log --oneline

v1.3.0
HEAD
2026-10-08