$ cat node-template.py
C
Create Artifact
// Generate a knowledge-base-grounded Artifact (document) from a Drive folder using a generation template, then wait for generation to finish and output the content.
AI
Document
#artifact#document#knowledge-base#generate
template.py
1"""create-artifact — generate a KB-grounded Artifact and wait for the content.23Drives the existing www-emblema Artifact pipeline (template + Knowledge Base ->4grounded, translated, watermarked document) entirely through the gais SDK5(Gais.artifact.create); this node imports no HTTP client and never sees6USER_TOKEN — the lint deny-list blocks requests/httpx/os in template code, so7all privileged artifact IO lives in gais.backends._artifact.89Generation is ASYNCHRONOUS: the create call returns instantly with empty10content while a background task fills it in. ``wait=True`` makes the SDK poll11the generation TASK status until it COMPLETES (return content), FAILS/CANCELS12(raise), or the deadline passes (raise) — so this node blocks like the13video/image creation nodes and hands real content to the next node.1415Outputs ``artifactId`` + ``driveItemId`` as separate ports so a downstream Get16Artifact node can be wired from both (entity ports carry scalar ids, not17objects).18"""1920from __future__ import annotations2122import json23import re24import sys25import traceback26from typing import Any2728from gais import Gais2930_UUID_RE = re.compile(31 r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",32 re.IGNORECASE,33)343536def _normalize_uuid(value: Any, field: str) -> str:37 """Accept a bare UUID or a ``mention://.../<uuid>`` URI; validate as UUID.3839 The id is interpolated into the Artifact API path by the backend, so a40 value from an upstream node (which may emit ``mention://drive_folder/<uuid>``41 from the picker) is stripped to its trailing segment and UUID-checked here42 — a non-UUID can't reach the URL.43 """44 if not isinstance(value, str):45 raise ValueError(f"{field} must be a string, got {type(value).__name__}")46 s = value.strip()47 if "://" in s:48 s = s.rsplit("/", 1)[-1]49 if not _UUID_RE.match(s):50 raise ValueError(f"{field} is not a valid UUID: {value!r}")51 return s525354def process(inputs: dict[str, Any]) -> dict[str, Any]:55 drive_item_id = _normalize_uuid(inputs.get("driveItemId"), "driveItemId")56 template_id = _normalize_uuid(inputs.get("templateId"), "templateId")57 language = inputs.get("language") or "italian"58 formality = inputs.get("formality") or "formal"59 name = inputs.get("name") or None60 try:61 max_wait = float(inputs.get("maxWaitSeconds") or 300)62 except (TypeError, ValueError):63 max_wait = 300.06465 print(66 f"[create-artifact] kb={drive_item_id} template={template_id} "67 f"lang={language} wait<={max_wait:.0f}s",68 file=sys.stderr,69 )7071 result = Gais.artifact.create(72 drive_item_id=drive_item_id,73 template_id=template_id,74 name=name,75 config={"language": language, "langFormality": formality},76 wait=True,77 timeout=max_wait,78 )79 meta = result.metadata8081 print(82 f"[create-artifact] done id={meta.get('artifactId')} "83 f"status={meta.get('status')} chars={len(result.text or '')}",84 file=sys.stderr,85 )8687 return {88 "content": result.text or "",89 "artifactId": meta.get("artifactId", ""),90 "status": meta.get("status", ""),91 "format": meta.get("format", "markdown"),92 }939495def main() -> None:96 try:97 envelope = json.loads(sys.stdin.read() or "{}")98 if not isinstance(envelope, dict):99 envelope = {}100 inputs = envelope.get("inputs", {}) or {}101 json.dump(process(inputs), sys.stdout)102 except Exception as e:103 print(104 json.dumps({105 "error": str(e),106 "errorType": type(e).__name__,107 "traceback": traceback.format_exc(),108 }),109 file=sys.stderr,110 )111 sys.exit(1)112113114if __name__ == "__main__":115 main()116$ git log --oneline
v1.2.0
HEAD
2026-06-12v1.1.02026-06-12