# Documentation Documentation for the Griptape Nodes engine: scripting (retained mode), the project system, custom node development, the built-in node reference, and the engine MCP surface. Use these files to ground an agent in the engine's actual API rather than scraping rendered HTML or fetching unrenderered raw markdown from `main`. # Overview # Unleash Your AI Superpowers with Griptape Nodes Imagine being able to harness the full power of today's most advanced AI models without writing a single line of complex code. That's exactly what Griptape Nodes delivers. What if you could: - Create an AI that automatically summarizes your research papers - Build a visual art generator that brings your wildest ideas to life - Design a virtual assistant that knows exactly how you like things done - Construct intelligent workflows that do in minutes what used to take days With Griptape Nodes, you can connect visual building blocks that represent powerful AI capabilities, drag-and-drop them into place, and watch as they work together to accomplish extraordinary things. It's like having a visual superpower for orchestrating AI. Whether you're a creative who wants to explore new AI-powered possibilities, a knowledge worker looking to automate tedious tasks, or a curious mind wanting to experiment with cutting-edge technology, Griptape Nodes opens doors you might have thought were locked to all but professional programmers. And for those who want to go deeper? A little Python knowledge goes a long way here. Our [simple scripting interface](https://docs.griptapenodes.com/retained_mode/index.md) lets you customize and extend what's possible, turning your big ideas into working solutions without requiring an engineering degree. Griptape Nodes is where your imagination meets AI capability. Let's build something amazing together. # For Agents This page documents the machine-readable surface that [docs.griptapenodes.com](https://docs.griptapenodes.com/) exposes for AI coding agents, MCP skills, and any tool that wants to ground its work in the engine's actual API. The same files that render this site are also published as post-processed markdown so an agent can fetch them directly. Snippets, macros, and mkdocstrings output are already expanded, which `raw.githubusercontent.com` does not give you. ## Surface - [`/llms.txt`](https://docs.griptapenodes.com/en/stable/llms.txt) is the curated index per the [llms.txt convention](https://llmstxt.org/). It groups the high-value pages (scripting, the project system, custom node development, MCP integration, and the node reference) under named sections with short descriptions and absolute URLs. - [`/llms-full.txt`](https://docs.griptapenodes.com/en/stable/llms-full.txt) is the full concatenated post-processed markdown of every page in the nav. Use this when you want to drop the entire engine docs into a single context window. - Every doc page is also available as standalone markdown next to its HTML rendering. The URL is the rendered page URL with `/index.md` appended, for example [`/developing_nodes/comprehensive_guide/index.md`](https://docs.griptapenodes.com/en/stable/developing_nodes/comprehensive_guide/index.md) and [`/retained_mode/index.md`](https://docs.griptapenodes.com/en/stable/retained_mode/index.md). Top-level pages live at `//index.md`; nested pages mirror the rendered URL. ## When to use which - Reach for **`/llms.txt`** to discover what's available without pulling the whole corpus. It's small enough to skim and the section descriptions tell you which page covers which topic. - Reach for **`/llms-full.txt`** when you want a single-shot grounding document and have the context budget for it. It is the same content as the per-page markdown files, concatenated in the order declared in the llms.txt sections. - Reach for **a single per-page `.md`** when you already know which page you need (e.g. you're writing a custom node and want the comprehensive guide, or you're scripting and want `retained_mode.md`). ## High-value pages for engine grounding If you are pointing an agent at a small set of pages to bootstrap, these five cover most of the engine's first-party API surface: - [Scripting (retained mode)](https://docs.griptapenodes.com/en/stable/retained_mode/index.md) - [Comprehensive node development guide](https://docs.griptapenodes.com/en/stable/developing_nodes/comprehensive_guide/index.md) - [Getting started with node development](https://docs.griptapenodes.com/en/stable/developing_nodes/getting_started/index.md) - [Project system overview](https://docs.griptapenodes.com/en/stable/projects/index.md) - [MCP integration overview](https://docs.griptapenodes.com/en/stable/how_to/mcp/index.md) ## Stability - The surface tracks `main` via the existing docs deploy pipeline. There is one live surface; versioned URLs (e.g. `/v0.40/llms.txt`) are not currently published. - The set of pages is driven by the `llmstxt` plugin block in `mkdocs.yml`. Adding a new doc page does not automatically include it in `/llms.txt` or `/llms-full.txt`; the page must be added under a section there. Reference pages under `nodes/` are picked up via a glob. # Frequently Asked Questions ## Where is my workspace (where do my files save)? Files such as saved workflows, etc., are saved in a the Workspace Directory. The path for the Workspace Directory can be found in the Griptape Nodes Editor: 1. Open the Griptape Nodes Editor. 1. Open an existing workflow or create a blank one. 1. Click "Settings". 1. Select "Configuration Editor". 1. Click "Griptape Nodes Settings" on the leftmost column, if not already selected. 1. The path is listed under "Workspace Directory". If you are not running the Editor, run this command and it will report back your Workspace Directory: ``` gtn config show | grep workspace ``` ## Can I run the Engine on a different machine than the Editor? The Engine and Editor can run on completely separate machines. Remember that any files you save or libraries you register will be stored on the machine where the Engine is running. So if you're looking for your files and can't find them right away, double-check which machine the Engine is running on. ## Where is Griptape Nodes installed? Looking for the exact installation location of your Griptape Nodes? This command will show you precisely where it's installed: === "macOS / Linux" ```` ```bash dirname $(dirname $(readlink -f $(which griptape-nodes))) ```` ``` === "Windows (PowerShell)" ``` ```powershell Split-Path (Split-Path (Get-Command griptape-nodes).Source) ``` ``` ## Can I see or edit my config file? To get a path to the file, go to the top Settings menu in the Editor, and select **Copy Path to Settings**. That will copy the config file path to your clipboard. If you prefer working in the command line, you can also use: ``` gtn config show ``` ## How do I install the Advanced Media Library after Initial Setup? If you initially declined to install the Advanced Media Library during setup but now want to add it, you can do so by running: ``` gtn init ``` This will restart the configuration process. You can press Enter to keep your existing workspace and Griptape Cloud API Key settings. When prompted with: ``` Register Advanced Media Library? [y/n] (n): ``` Press **y** to install the Advanced Media Library, or **n** to skip installation. Note Some nodes in the Advanced Media Library require specific models to function properly. You will need to install these models separately. Refer to each node's documentation to determine which nodes need which models; they each have links to specific requirements. ## What happened to deprecated nodes from the Advanced Media Library? Version 0.64.0 removed deprecated nodes from the Advanced Media Library. These nodes were previously marked for deprecation and have been replaced with more flexible alternatives. If you have workflows that use deprecated nodes, please refer to the [MIGRATION.md](https://github.com/griptape-ai/griptape-nodes/blob/main/MIGRATION.md) guide. This comprehensive guide provides: - A complete list of removed nodes and their replacements - Step-by-step migration instructions - Visual examples of replacement nodes - Details about the new Diffusion Pipeline Builder system The migration guide includes replacements for all deprecated image processing, diffusion pipeline, upscaling, and LoRA nodes. ## How do I uninstall Griptape Nodes? ``` griptape-nodes self uninstall ``` To reinstall, follow the instructions on the [installation](https://docs.griptapenodes.com/installation/index.md) page. ## How do I update Griptape Nodes? Griptape Nodes will automatically check if it needs to update every time it runs. If it does, you will be prompted to answer with a (y/n) response. Respond with a y and it will automatically update to the latest version of the Engine. If you would like to *manually* update, you can always use either of these commands: ``` griptape-nodes self update griptape-nodes libraries sync ``` or ``` gtn self update gtn libraries sync ``` ## I'm seeing `failed to locate pyvenv.cfg: The system cannot find the file specified.` - What should I do? It is possible, that during a previous uninstall things were not *fully* uninstalled. Simply perform an uninstall again, and then [re-install](https://docs.griptapenodes.com/installation/index.md). ## I'm seeing `Attempted to create a Flow with a parent 'None', but no parent with that name could be found.` - What should I do? The good news is, this is usually harmless, and you can usually disregard it. If you're getting it in a way that stops work, please restart your engine, and that should take care of it. That said, we apologize for this elusive bug. We're working to catch and fix it as soon as possible. If you are so inclined, we'd be grateful if you wanted to [log a bug](https://github.com/griptape-ai/griptape-nodes/issues/new?template=bug_report.yml&title=Attempted%20to%20create%20flow%20with%20a%20parent%20%27None%27) and provide any context around what may have led to the issue when you see it! ## I'm receiving an error when trying to run Griptape Nodes: `ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1000)` - What should I do? The Python installation on your machine may not have access to verified SSL certificates. To remedy: 1. Reinstall Python using the python.org installer. As of this writing, Griptape Nodes requires Python 3.12. 2. At the end of the installation, select to "Install Certificates". 1. If not available in the installer, run `/Applications/Python\ 3.12/Install\ Certificates.command` ## Where can I provide feedback or ask questions? You can connect with us through several channels: - [Website](https://www.griptape.ai) - Visit our homepage for general information - [Discord](https://discord.gg/gnWRz88eym) - Join our community for questions and discussions - [GitHub](https://github.com/griptape-ai/griptape-nodes) - Submit issues or contribute to the codebase These same links are also available as the three icons in the footer (bottom right) of every documentation page. ## How can I test out unreleased features? If you're interested in testing out unreleased features, you can install the pre-release builds of Griptape Nodes. Updates are now published to the [latest](https://github.com/griptape-ai/griptape-nodes/releases/tag/latest) tag twice a day. Warning Pre-release builds are not guaranteed to be stable and may contain bugs or incomplete features. Use them at your own risk. To switch to the pre-release update channel, run the following commands: ``` uv tool uninstall griptape-nodes uv tool install git+https://github.com/griptape-ai/griptape-nodes.git@latest --reinstall --force --python 3.12 ``` This will uninstall the current version of Griptape Nodes and install the latest pre-release build from the GitHub repository. Info Uninstalling using `uv tool uninstall griptape-nodes` will not remove your existing projects or settings. It only removes the Griptape Nodes engine itself. You can confirm it went through by running `gtn self version`. Your version number should show a reference to a git commit: ``` gtn self version v0.31.0 (git - e172e80) ``` To switch back to the stable release channel, run the following commands: ``` uv tool uninstall griptape-nodes uv tool install griptape-nodes ``` ``` # Griptape Nodes Glossary for Artists & Creators ## What is Griptape Nodes? Griptape Nodes is a toolkit that enables artists and creators to build AI-powered projects without the need for deep technical expertise. You can think of Griptape Nodes as a set of building blocks that you can connect together to create art, generate images, process text, or even build other workflow-centric applications. ### Core Griptape Nodes Concepts - **Workflow**: A document containing nodes, connections, and values. While technically a Workflow is also a Script, avoid calling them Scripts, so that the term Script can communicate a clearly different thing than the term Workflow. This is also what we call the saved file. - **Workflow Editor**: The workspace where nodes are added, connected, and configured. - **Flow**: A collection of connected nodes that form a functional unit. A Workflow can contain 0-n Flows. - **Sub-Flow**: A contained set of nodes that executes within a Loop, Branch, Reference (and sometimes Groups). This refers to any logically distinct, internally-contained portion of a flow that functions as a cohesive unit. - **Reference**: A link to another Flow, embedding it as a Sub-Flow within the current context. - **Group**: A visual clustering of nodes. A Group may act as a Sub-Flow if it is executed as a unit, but grouping alone does not imply execution and may simply be a subjectively related set of nodes. - **Script**: A Python script that runs code. This term should be avoided when describing a Workflow; instead, script refers to tools, macros, or flow-building aids. - **Libraries**: Collections of node definitions and/or scripts that extend functionality - **Node**: A single piece of the puzzle in your workflow. Nodes are like LEGO blocks that you can connect to create something bigger. Each node does one specific thing (like generating an image or processing text). There are **types** of nodes: - **DataNode**: A node that handles information — it can transform data from one form to another, or simply hold data you set. - **ControlNode**: A node that will "do some work". Control Nodes usually take longer to run than DataNodes because, they're usually doing more work. - **DriverNode**: A node that connects your project to outside services (like image generators or search engines). - **Tool**: A ready-to-use component that performs a specific function. Tools are like brushes in your digital toolkit — each designed for a specific purpose. - **CalculatorTool**: Does math calculations for you. - **DateTimeTool**: Works with dates and times. - **FileManagerTool**: Helps manage files (saving, loading, etc.). - **WebScraperTool**: Collects information from websites. - **PromptSummaryTool**: Creates summaries of longer text. - **Driver**: The connector between your project and external AI services or databases. Drivers are like adapters that let your project talk to specialized services. - **PromptDriver**: Communicates with AI text generators (like GPT models). - **WebSearchDriver**: Connects to search engines to find information. - **EmbeddingDriver**: Transforms words and concepts into numerical form that AI can understand. - **ImageGenerationDriver**: Connects to AI image generators (like DALL-E). - **AudioTranscriptionDriver**: Converts spoken audio to written text. - **Engine**: The powerhouse that processes your creative requests. Engines are specialized components that transform inputs into creative outputs. - **RagEngine**: Processes questions and generates answers based on provided information. - **PromptSummaryEngine**: Creates concise summaries of longer text. - **CsvExtractionEngine**: Pulls information from spreadsheet-like files. - **JsonExtractionEngine**: Pulls information from structured data files. - **Artifact**: A piece of content created during your project, like a generated image or text. Artifacts are the outputs of your creative process. - **ErrorArtifact**: A notification when something goes wrong. - **Agent**: A helper that can perform tasks on your behalf, often using a combination of tools. Agents are like assistants that can navigate a sequence of operations for you. - **Ruleset**: A set of guidelines that control how your project behaves. Rulesets are like recipes that tell your project how to respond in different situations. - **Rule**: A single instruction within a ruleset, like "if the user asks for an image, generate one." ### Node Contents and Activities - **Parameter**: A setting or value you can adjust on a node. Parameters are like the knobs and sliders in a music synthesizer — they let you fine-tune how things work. - **ParameterMode**: Describes if a parameter is for input, output, internal, or any combination. - **ParameterValue**: The "internal" value for a parameter (this is the data the node works with internally) - **ParameterOutputValues**: The results or "output" value for your parameters (this is the data that results from what the node did) - **ParameterUIOptions**: Settings for how parameters appear in the user interface. - **Port**: The circular indicators displayed on the left and/or right sides of a parameter. Ports on the left side indicate the parameter can accept incoming connections, while ports on the right side indicate the parameter supports outgoing connections. - **Pin**: Interchangeable with "Port" - **Default Value**: The pre-set value a parameter has before you change it. This is like the factory settings on a device. - **Parameter Validation**: A check that ensures the values you enter make sense. This prevents errors like trying to use text where a number is needed. - **Off-prompt**: A Griptape secret-sauce way to keep some information private, even when working with LLMs. - **Stream Mode**: A continuous processing mode, like a live video feed rather than taking separate photos. - **Type Hints**: Labels that suggest what kind of information a parameter expects. These are like labels on art supply containers telling you what's inside. - **Any**: A type hint meaning a parameter can accept any kind of information. - **List**: A type hint for a collection of items (like an array of colors or shapes). - **Literal**: A type hint indicating a parameter only accepts specific preset values. - **Union type**: A type hint showing a parameter can accept multiple specific types of information. - **Connection**: The link that allows nodes to communicate with each other. Connections are like the cables connecting different pieces of equipment. ## Technical Terms Made Simple - **Data Types**: - **Dictionary (dict)**: A way to store information as pairs of labels and values. Dictionaries are like organized containers where each item has a unique label. - **Key-Value Pair**: A label (key) paired with its corresponding information (value). Dictionaries are just a collection of Key-Value Pairs. - **Integer (int)**: Whole numbers without decimal points, like 5, -10, or 1000. - **Float (float)**: Numbers with decimal points, like 3.14, -0.5, or 2.0. - **String (str)**: Text enclosed in quotes, like "hello", 'Python', or "123". - **Boolean (bool)**: Represents either True or False. - **List (list)**: An ordered collection of items that can be modified, like [1, 2, 3] or ["apple", "banana", "cherry"]. - **Tuple (tuple)**: An ordered collection of items that cannot be modified after creation, like (1, 2, 3) or ("red", "green", "blue"). - **Set (set)**: An unordered collection of unique items, like {1, 2, 3} or {"apple", "banana", "cherry"}. - **None (NoneType)**: Represents the absence of a value or a null value. - **Environment Variable**: Like secret notes that your computer keeps to help programs know important information. - **API Key**: A special password that grants your project access to external services like AI image generators. Think of it like a membership card that lets you use specific online services. - **Secret Keys**: Private credentials such as API tokens, passwords, and access keys that need to be kept secure but are required for certain operations. - **Secrets Manager**: This is the actual mechanism by which your secret keys are handled so that they *stay* secret. - **Configuration Settings**: Parameters and options that control how the Griptape Nodes engine and its components operate. - **Project Files**: Documents, scripts, and other resources that make up a Griptape project. - **Generated Assets**: Files and data produced by the Griptape Nodes engine during execution, such as outputs, reports, or visualizations. - **.env**: A special file used to store environment variables, particularly sensitive information like API keys. ## AI Terms for Artists - **Temperature Control**: A setting that controls how creative or predictable an AI's responses will be. Lower temperature means more predictable, higher temperature means more creative and varied. - **Embedding Model**: An AI tool that converts words, images, or other content into numbers that capture their meaning. This helps AI understand relationships between different concepts. - **LLM (Large Language Model)**: A type of AI system trained on vast amounts of text data to understand and generate human-like language. LLMs can write text, answer questions, summarize information, and even generate creative content. - **NLP (Natural Language Processing)**: The field of AI focused on helping computers understand and generate human language. - **Prompt**: The input text you provide to guide an AI model. For artists, this is similar to a creative brief or instructions to a collaborator. - **Vector Store**: A specialized database that stores information in a way that captures relationships and meaning, not just the information itself. - **Diffusion Models**: AI systems that create images by gradually transforming random noise into detailed visuals based on text descriptions. Popular examples include Stable Diffusion and Midjourney. - **Text-to-Image Generation**: Technology that creates images based on written descriptions, allowing artists to visualize concepts through text prompts. - **Style Transfer**: AI technique that applies the visual style of one image (like a famous painting) to the content of another image. - **Inpainting/Outpainting**: Tools that can fill in missing parts of an image or extend it beyond its original borders based on surrounding context. - **Fine-tuning**: The process of adapting a pre-trained AI model to recognize and generate specific styles, subjects, or artistic elements. - **Latent Space**: A mathematical representation where AI models organize concepts; artists can explore this space to find creative variations and transitions between ideas. - **Tokens**: The basic units that AI language models process text in, similar to words or parts of words. Understanding token limits helps artists craft effective prompts. - **Multimodal AI**: Systems that can work with multiple types of content (text, images, audio) simultaneously, enabling more integrated creative workflows. - **LoRA (Low-Rank Adaptation)**: A technique that allows artists to train AI models on their specific style without extensive computing resources. - **Sampling Methods**: Different algorithms (like DDIM, Euler, etc.) that control how AI generates images, affecting detail levels, creativity, and coherence. ## AI Companies and Models (Updated Apr 9,2025) ### Anthropic - **Claude 3 Opus**: Anthropic's most capable model for complex creative tasks and detailed content generation - **Claude 3.5 Sonnet**: Balanced model offering strong creative capabilities with faster performance - **Claude 3.7 Sonnet**: Advanced reasoning-focused model for complex creative problem-solving - **Claude 3.5 Haiku**: Fastest model for rapid ideation and real-time creative collaboration ### OpenAI - **GPT-4o**: OpenAI's most advanced multimodal model for text, image, and audio processing - **GPT-4 Turbo**: Powerful large language model for sophisticated text generation and creative writing - **GPT-3.5 Turbo**: More economical model for standard creative text tasks - **DALL-E 3**: Text-to-image generation model for creating visual art from descriptions - **Whisper**: Speech recognition model that can transcribe audio for creative projects ### Stability AI - **Stable Diffusion XL**: Text-to-image model with high-quality image generation capabilities - **Stable Diffusion 3**: Latest generation text-to-image model with improved coherence - **Stable Video**: Text-to-video generation model for creating short animated sequences ### Google - **Gemini Pro**: Google's advanced model for text generation and reasoning - **Gemini Ultra**: Google's most capable model for complex creative tasks - **Imagen**: Text-to-image generation model with photorealistic capabilities ### Cohere - **Command**: Specialized for instruction following and precise creative tasks - **Embed**: Creates semantic representations of text for organizing creative content ### Midjourney - **Midjourney V6**: Image generation model popular among artists for its distinctive aesthetic ### Meta - **Llama 3**: Open-weight foundation model adaptable for various creative applications - **Llama 3.1**: More advanced version with improved capabilities - **Segment Anything**: AI model for precise image segmentation in visual arts ### Ollama - **Local model hosting service**: Allows running various open-source AI models locally for creative projects - - ## Helpful Programming Concepts - **Callback**: A function that automatically runs when something specific happens. This is like setting up a camera to take a photo when motion is detected. - **Custom Parameter Types**: Specialized settings you can define for your specific project needs. - **Exception Handling**: The way your project deals with errors. This is like having backup plans for when things go wrong. - **Authentication Error**: An error that occurs when your project can't prove it has permission to use a service. - **KeyError**: An error when your project tries to access information using an incorrect label. - **SyntaxError**: An error caused by incorrect formatting in code. - **ValueError**: An error when your project tries to use an inappropriate value. - **Metadata**: Extra information about your content or components. This is like the information stored in digital photo files (camera type, date taken, etc.). ## Additional Terms - **Python**: A popular programming language that Griptape is built with. Python is known for being relatively easy to read and understand compared to other programming languages. - **API (Application Programming Interface)**: A set of rules that allows different software applications to communicate with each other. For artists, this is like the standardized connections that allow different audio equipment to work together. - **JSON (JavaScript Object Notation)**: A common format for storing and transmitting data. This is like a standardized template for organizing information. - **CSV (Comma-Separated Values)**: A simple file format used to store tabular data, such as spreadsheet data. Each line represents a row, and commas separate the values in each column. - **UI (User Interface)**: The visual elements and controls that allow users to interact with software. This includes buttons, sliders, text fields, and other interactive elements. In Griptape Nodes, we call our UI the "Editor" - **Framework**: A pre-built set of code that provides structure and functionality to build applications more easily. Frameworks are like art supply kits that come with the basic materials and tools you need. # Installing Griptape Nodes Griptape Nodes is comprised of two components - an Editor and an Engine. The Editor is served from the web, so there is no need to install that, and you'll interact with your Engine through the Editor in your web browser. The Engine can be installed on your local machine, whether that runs MacOS, Windows or Linux. If you'd rather not install the Engine locally, perhaps because you want it to be able to access more resources than you have available on your laptop, it is easy to run it in on a separate machine. The instructions that follow will work the same for either approach. The Editor and the Engine are decoupled and communicate with each other through an event service. There is no locally-hosted Editor option #### Summary There are just 4 steps required to install Griptape Nodes: 1. Sign up or Log in 1. Install the Griptape Nodes Engine 1. Configuration 1. Start Your Engine ## 1. Sign up or Log in To get started, visit and click the sign-in button. You'll be presented with a Griptape account authorization form. > If you've already signed up for [Griptape Cloud](https://cloud.griptape.ai), your existing credentials will work here! Once you've logged in, you'll be presented with a setup screen which walks you through installing the Engine. ## 2. Install the Griptape Nodes Engine ### Griptape Nodes Desktop (Recommended) Download [Griptape Nodes Desktop](https://www.griptapenodes.com/griptape-nodes-desktop) — a bundled app that includes both the engine and the editor. No further steps are needed; Desktop handles everything for you. ### Advanced: Manual Engine Install If you prefer to install the engine yourself: 1. Install [uv](https://docs.astral.sh/uv/getting-started/installation/) if you don't already have it. 1. Run the following command to install the Griptape Nodes Engine: ``` uv tool install griptape-nodes ``` After installation, run `griptape-nodes` or `gtn` in the terminal *for the first time* and you will be asked a series of configuration questions. ## 3. Configuration **First**, you'll be prompted to set your *workspace directory*. Your workspace directory is where the Griptape Nodes engine will save [project files](https://docs.griptapenodes.com/glossary/#project-files), and [generated assets](https://docs.griptapenodes.com/glossary/#generated-assets). It will also contain a [.env](https://docs.griptapenodes.com/glossary/#.env) for your Griptape Nodes [secret keys](https://docs.griptapenodes.com/glossary/#secret-keys). ``` ╭───────────────────────────────────────────────────────────────────╮ │ Workspace Directory │ │ Select the workspace directory. This is the root for your │ │ projects, saved workflows, and potentially project-specific │ │ settings. By default, this will be set to │ │ "/GriptapeNodes", which is the │ │ directory from which you run the 'gtn' command. │ │ You may enter a custom directory or press Return to accept │ │ the default workspace directory. │ ╰───────────────────────────────────────────────────────────────────╯ Workspace Directory (/Users/user/Documents/local-dev/nodes-test-eng/GriptapeNodes) ``` Pressing Enter will use the default: `/GriptapeNodes`, where `` is the directory from which you're running the `gtn` command. Alternatively, you can specify any location you prefer. > You can always return to this dialog using the `gtn init` command if you need to make changes in the future. **Second**, you'll be prompted for your Griptape Cloud API Key. 1. Return to the web browser and open **Step 3: Generate an API Key**. 1. Click the *Generate API Key* button. 1. Copy the key that is generated and enter it in the next step. Note that the API key will not be saved. ``` Workspace directory set to: /Users/user/Documents/local-dev/nodes-test-eng/GriptapeNodes ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ Griptape API Key │ │ A Griptape API Key is needed to proceed. │ │ This key allows the Griptape Nodes Engine to communicate with the Griptape Nodes Editor. │ │ In order to get your key, return to the https://nodes.griptape.ai tab in your browser and click the button │ │ "Generate API Key". │ │ Once the key is generated, copy and paste its value here to proceed. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ Griptape API Key (YOUR-KEY-HERE): ``` Info If you've previously run `gtn init` your key might be presented to you in this dialog. You can accept it by pressing Enter or use a different value as required. ## 4. Start Your Engine Your installation is now complete and you're ready to proceed to creating your first Workflow or trying out one of the sample workflows. To get started, return to your terminal and run `griptape-nodes` or `gtn`, then return to your browser. Your browser tab at will update to show *Create from scratch*, allowing you to start from a blank canvas, together with several sample Griptape Nodes workflows that you can experiment with! Next, on to learning how to actually work inside Griptape Nodes! [Begin](https://docs.griptapenodes.com/ftue/FTUE/index.md) # Configuration and CLI # Engine Configuration When running Griptape Nodes engine on your own machine, you are provided with utilities to manage configuration settings. Understanding how the configuration settings are loaded is important as you build out and manage more complicated projects or share projects with your team members. > During installation, `gtn init` was run automatically. ## Configuration Loading Griptape Nodes employs a specific search order to load settings from environment variables and configuration files. Understanding this process is key to managing your setup. 1. **Environment Variables (`.env`)** Environment variables are used to securely store sensitive secrets like API keys. Griptape Nodes automatically loads env files, making these secrets available to the application. - The primary `.env` file is loaded from the system-wide user configuration directory: `xdg_config_home() / "griptape_nodes" / ".env"` (commonly `~/.config/griptape_nodes/.env`). - This file is intended for secrets like `GT_CLOUD_API_KEY`, `OPENAI_API_KEY`. > You shouldn't interact with these files directly. Griptape Nodes manages your environment variables through its Settings dialog. 1. **Configuration Files (`griptape_nodes_config.json`)** Configuration files hold information important for Griptape Nodes operation, such as where to locate Node Libraries, as well as user preferences to customize the Griptape Nodes experience. - If no configuration files are found, Griptape Nodes will run using built-in default values. - Settings are loaded from up to four `griptape_nodes_config.json` files and merged in priority order: - **Load Order (lower numbers are loaded first; higher numbers override):** 1. **Built-in defaults** — values baked into the application. 1. **User config** — `~/.config/griptape_nodes/griptape_nodes_config.json`. Global settings for this machine. 1. **Project-adjacent config** — `/griptape_nodes_config.json`. Loaded when a project is set as active. Use this to distribute shared defaults alongside a project file. 1. **Workspace config** — `/griptape_nodes_config.json`. Loaded after the workspace is resolved. Use this for per-user overrides that take precedence over the shared project config. When the workspace directory is the same as the project directory, this file is the same as the project-adjacent config and is not loaded twice. 1. **Environment variables** — `GTN_CONFIG_*` prefix (highest priority). See below. - **Override Priority:** Settings in files loaded later override settings from files loaded earlier. - **Per-project workspace overrides** — The `project_workspaces` key in your user config maps project file paths to local workspace directories. See [Workspace](https://docs.griptapenodes.com/projects/workspace/#per-project-workspace-overrides) for details. 1. **Defaults and Merging** Griptape Nodes comes with built-in default settings for various options, including the default workspace directory. These defaults are used unless overridden by settings loaded from discovered configuration files. - Settings loaded from the first found configuration file override the built-in default values. - If no configuration file is found in any of the search paths, the application uses only the built-in defaults. - One key default is `workspace_directory`, which defaults to `/GriptapeNodes` if not specified in a loaded configuration file. 1. **Runtime Management (`ConfigManager`)** After initial settings are loaded, the `ConfigManager` handles runtime operations using the final resolved configuration, particularly the workspace directory. It's responsible for saving user-specific changes, like registered workflows, back to a configuration file within the workspace. - Once settings are loaded, the `ConfigManager` uses the final resolved `workspace_directory`. - Modifications made at runtime (e.g., registering custom workflows) are typically saved by the `ConfigManager` into a `griptape_nodes_config.json` file located within this resolved `workspace_directory`. ## Loading Examples Here are a few scenarios to illustrate how configuration files are located and loaded: **Scenario 1: Using Defaults** - You run `gtn init` and accept the default settings. - `gtn init` creates `~/.config/griptape_nodes/griptape_nodes_config.json` and `~/.config/griptape_nodes/.env`. It sets `workspace_directory` inside the `.json` file to point to `/GriptapeNodes`. - You later run `gtn` from `/home/user/my_project/`. - **File Structure:** ``` /home/user/ my_project/ <-- CWD when running 'gtn' GriptapeNodes/ <-- Default Workspace (may contain runtime saved config) my_flow.graph.json .config/ griptape_nodes/ .env # Loaded for environment variables griptape_nodes_config.json # Contains workspace_directory = /home/user/my_project/GriptapeNodes ``` - **Loading Process:** 1. Checks `/etc/xdg/griptape_nodes/` (Assume not found). 1. Checks `~/.config/griptape_nodes/griptape_nodes_config.json` (Found!). 1. **Result:** The application loads settings from `~/.config/griptape_nodes/griptape_nodes_config.json`. The `workspace_directory` is set to `/home/user/my_project/GriptapeNodes`. Subsequent runtime changes managed by `ConfigManager` will be saved to `/home/user/my_project/GriptapeNodes/griptape_nodes_config.json`. **Scenario 2: Custom Workspace** - You run `gtn init --workspace-directory /data/gtn_work`. - `gtn init` creates `~/.config/griptape_nodes/griptape_nodes_config.json` (setting `workspace_directory = "/data/gtn_work"`) and `~/.config/griptape_nodes/.env`. - You might manually create `/data/gtn_work/griptape_nodes_config.yaml` to store project-specific settings. - You run `gtn` from `/home/user/some_dir/`. - **File Structure:** ``` /home/user/ some_dir/ <-- CWD when running 'gtn' .config/ griptape_nodes/ .env # Loaded for environment variables griptape_nodes_config.json # Contains workspace_directory = /data/gtn_work /data/ gtn_work/ <-- Custom Workspace griptape_nodes_config.yaml # Manually created / runtime saved config project_flows/ ``` - **Loading Process:** 1. Checks `/etc/xdg/griptape_nodes/` (Assume not found). 1. Checks `~/.config/griptape_nodes/griptape_nodes_config.json` (Found!). 1. **Result:** The application *initially* loads settings from `~/.config/griptape_nodes/griptape_nodes_config.json`. The `workspace_directory` is set to `/data/gtn_work`. Even though `/data/gtn_work/griptape_nodes_config.yaml` exists, it's not checked during initial load because a higher priority file was found. Runtime changes will be saved back to `/data/gtn_work/griptape_nodes_config.json` (overwriting/merging with the YAML potentially, depending on `ConfigManager`'s save logic). **Scenario 3: Config in Current Directory (No System Config)** - You haven't run `gtn init`, or you deleted `~/.config/griptape_nodes/`. - You create a project-specific config file directly in your project folder. - You run `gtn` from `/home/user/my_project/`. - **File Structure:** ``` /home/user/ my_project/ <-- CWD when running 'gtn' griptape_nodes_config.toml # User-created config GriptapeNodes/ <-- Potential default workspace location my_flow.graph.json ``` - **Loading Process:** 1. Checks `/etc/xdg/griptape_nodes/` (Assume not found). 1. Checks `~/.config/griptape_nodes/` (Assume not found). 1. Checks `/home/user/my_project/GriptapeNodes/` (Assume not found). 1. Checks `/home/user/my_project/griptape_nodes_config.toml` (Found!). 1. **Result:** The application loads settings from `/home/user/my_project/griptape_nodes_config.toml`. If this file specifies a `workspace_directory`, that path is used. If not, the default (`/GriptapeNodes` = `/home/user/my_project/GriptapeNodes`) is used. ## Environment Variable Overrides Any configuration value can be set or overridden using an environment variable with the `GTN_CONFIG_` prefix. The key is the config setting name in uppercase: ``` GTN_CONFIG_= ``` Environment variable overrides have the **highest priority** — they win over user config files, project-adjacent config files, and built-in defaults. Examples: | Setting | Environment variable | | --------------------- | -------------------------------- | | `workspace_directory` | `GTN_CONFIG_WORKSPACE_DIRECTORY` | | `project_file` | `GTN_CONFIG_PROJECT_FILE` | | `log_level` | `GTN_CONFIG_LOG_LEVEL` | | `storage_backend` | `GTN_CONFIG_STORAGE_BACKEND` | This is useful for scripted environments, containers, and CI/CD pipelines where you want to inject configuration without modifying any config files: ``` GTN_CONFIG_PROJECT_FILE=/shared/studio-project.yml gtn ``` ## Workspace Directory During `gtn init`, you specify a Workspace Directory. This is the root for your projects, saved flows, and potentially project-specific settings. While `gtn init` might suggest `/GriptapeNodes` as a default, you can choose any location. Griptape Nodes uses the exact path you provide, which is then stored in the system `griptape_nodes_config.json`. It does **not** automatically search within a hardcoded `GriptapeNodes` subdirectory; it relies solely on the configured path. ## Static File Server Configuration When running Griptape Nodes, a local static file server hosts media assets (images, videos, audio) generated by your workflows. The `static_server_base_url` setting controls what base URL is used when generating links to these files. By default, it uses `http://localhost:8124`, but you can override this when using tunnels, proxies, or deploying in containers. ### When to Override This Setting You'll need to configure `static_server_base_url` in these scenarios: - **Tunneling Services**: Using ngrok, cloudflare tunnels, or similar services to expose your local server - **Docker/Kubernetes**: Running in containers where the internal address differs from the external access point - **Reverse Proxies**: Running behind nginx, Apache, or other reverse proxies - **Remote Development**: Working on a remote machine and accessing the UI from your local browser - **Team Collaboration**: Sharing your running instance with team members who need to access generated media ### How to Configure The static server base URL is configured using the `static_server_base_url` setting in the Griptape Nodes UI Settings dialog. If not explicitly set, it defaults to `http://localhost:8124` (or respects `STATIC_SERVER_HOST` and `STATIC_SERVER_PORT` environment variables if they are set). After updating this setting, you'll need to restart the Griptape Nodes engine for the changes to take effect. ### Example Scenarios **Scenario 1: Local Development with ngrok** You're testing webhook integrations that need to access generated media files. 1. Start ngrok tunnel: `ngrok http 8124` 1. Copy the generated URL (e.g., `https://abc123.ngrok.app`) 1. Open Griptape Nodes UI Settings dialog 1. Update the `static_server_base_url` setting with your ngrok URL 1. Restart the Griptape Nodes engine: `gtn` Now when workflows generate media: - Local access: Works via the tunnel URL - External services: Can fetch media via the ngrok URL - CORS: Automatically configured for the tunnel URL # Griptape Nodes Command Line Interface (CLI) If you're new to command-line interfaces (CLIs), a CLI is a text-based way to interact with software by typing commands instead of clicking buttons in a UI. Griptape Nodes provides a CLI with the the `griptape-nodes` (or `gtn`) command. This enables you to interact with Griptape Nodes in the terminal. `griptape-nodes` (or its shorthand alias `gtn`) is a command-line tool specifically designed to launch and manage the Griptape Nodes Engine installation on your computer. This tool handles tasks like initializing your workspace, managing configuration settings, and starting the engine that powers the web-based Griptape Nodes editor. The actual creation and editing of workflows happens in the web interface that opens when you run the engine. ## Basic Usage ``` griptape-nodes [options] [COMMAND] ``` If no command is specified, the tool defaults to the `engine` command. ## Commands ### `engine` (Default Command) Run the Griptape Nodes engine. ``` griptape-nodes engine ``` This will start the Griptape Nodes engine and open the web interface at https://nodes.griptape.ai. ### `init` Initialize a new workspace for Griptape Nodes. ``` griptape-nodes init [options] ``` Options: - `--api-key` - Directly specify your Griptape API key without being prompted - `--workspace-directory` - Directly specify your workspace directory without being prompted ### `config` Manage your Griptape Nodes configuration. ``` griptape-nodes config SUBCOMMAND ``` Subcommands: - `show` - Show the current configuration settings - `list` - List all configuration files in order of precedence - `reset` - Reset your configuration to default values ### `self` Manage the CLI installation itself. ``` griptape-nodes self SUBCOMMAND ``` Subcommands: - `uninstall` - Uninstall the CLI, removing configuration and data directories - `version` - Display the current version of the CLI ### `libraries` Manage local libraries. ``` griptape-nodes libraries SUBCOMMAND ``` Subcommands: - `sync` - Sync libraries with your current engine version ## Configuration Griptape Nodes stores its configuration in the following locations: - Configuration directory: `~/.config/griptape_nodes` (Linux/macOS) or `%APPDATA%\griptape_nodes` (Windows) - Data directory: `~/.local/share/griptape_nodes` (Linux/macOS) or `%LOCALAPPDATA%\griptape_nodes` (Windows) - Configuration file: `griptape_nodes_config.json` in the configuration directory - Environment file: `.env` in the configuration directory ## Workflow Typical usage flow: 1. Run `griptape-nodes init` to set up your workspace and API key 1. Run `griptape-nodes` to start the engine 1. Use the web interface to create and manage your workflows # Scripting (retained mode) # Griptape Nodes Retained Mode Scripting "Retained Mode" provides a Python scripting interface for interacting with Griptape Nodes. This allows users to create, modify, and manage nodes, parameters, connections, and flows through a simplified Python API. > **Note:** The actual import command for RetainedMode is: > > ``` > from griptape_nodes.retained_mode import RetainedMode as cmd > ``` > > However, for convenience, in the script editor of the GUI, this import is already done for you automatically, so you can freely use `cmd.` directly. ## Flow Operations ### create_flow Creates a new flow within the Griptape system. ``` cmd.create_flow(flow_name=None, parent_flow_name=None) ``` #### Arguments | Name | Argument Type | Required | | ---------------- | ------------- | -------- | | flow_name | string | ⚪ | | parent_flow_name | string | ⚪ | #### Return Value ResultPayload object with flow creation status #### Description Creates a new flow with the specified name. If parent_flow_name is provided, the new flow will be created as a child of the specified parent flow. ______________________________________________________________________ ### delete_flow ``` cmd.delete_flow(flow_name) ``` Deletes an existing flow. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | flow_name | string | 🟢 | #### Return Value ResultPayload object with flow deletion status #### Description Removes the specified flow from the system. ______________________________________________________________________ ### get_flows ``` cmd.get_flows(parent_flow_name=None) ``` Lists all flows within a parent flow. #### Arguments | Name | Argument Type | Required | | ---------------- | ------------- | -------- | | parent_flow_name | string | ⚪ | #### Return Value ResultPayload object containing a list of flows #### Description Returns all flows within the specified parent flow. If no parent_flow_name is provided, returns all top-level flows. ______________________________________________________________________ ### get_nodes_in_flow ``` cmd.get_nodes_in_flow(flow_name) ``` Lists all nodes within a flow. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | flow_name | string | 🟢 | #### Return Value ResultPayload object containing a list of node names #### Description Returns all nodes within the specified flow. ______________________________________________________________________ ### run_flow ``` cmd.run_flow(flow_name) ``` Executes a flow. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | flow_name | string | 🟢 | #### Return Value ResultPayload object with flow execution status #### Description Starts the execution of the specified flow. ______________________________________________________________________ ### reset_flow ``` cmd.reset_flow(flow_name) ``` Resets a flow to its initial state. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | flow_name | string | 🟢 | #### Return Value ResultPayload object with flow reset status #### Description Unresolves all nodes in the flow, returning it to its initial state. ______________________________________________________________________ ### get_flow_state ``` cmd.get_flow_state(flow_name) ``` Returns the current state of a flow. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | flow_name | string | 🟢 | #### Return Value ResultPayload object containing flow state information #### Description Gets the current execution state of the specified flow. ______________________________________________________________________ ### cancel_flow ``` cmd.cancel_flow(flow_name) ``` Cancels the execution of a flow. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | flow_name | string | 🟢 | #### Return Value ResultPayload object with flow cancellation status #### Description Stops the execution of the specified flow. ______________________________________________________________________ ### single_step ``` cmd.single_step(flow_name) ``` Executes a single node step in a flow. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | flow_name | string | 🟢 | #### Return Value ResultPayload object with step execution status #### Description Executes a single node in the specified flow. ______________________________________________________________________ ### single_execution_step ``` cmd.single_execution_step(flow_name) ``` Executes a single execution step in a flow. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | flow_name | string | 🟢 | #### Return Value ResultPayload object with execution step status #### Description Executes a single execution step in the specified flow. ______________________________________________________________________ ### continue_flow ``` cmd.continue_flow(flow_name) ``` Continues the execution of a paused flow. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | flow_name | string | 🟢 | #### Return Value ResultPayload object with flow continuation status #### Description Continues the execution of a flow that was previously paused. ## Node Operations ### create_node ``` cmd.create_node(node_type, specific_library_name=None, node_name=None, parent_flow_name=None, metadata=None) ``` Creates a new node. #### Arguments | Name | Argument Type | Required | | --------------------- | ------------- | -------- | | node_type | string | 🟢 | | specific_library_name | string | ⚪ | | node_name | string | ⚪ | | parent_flow_name | string | ⚪ | | metadata | dict | ⚪ | #### Return Value Node name or ResultPayload object with node creation status #### Description Creates a new node of the specified type. Optional parameters allow specifying the library, node name, parent flow, and metadata. ______________________________________________________________________ ### delete_node ``` cmd.delete_node(node_name) ``` Deletes an existing node. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | node_name | string | 🟢 | #### Return Value ResultPayload object with node deletion status #### Description Removes the specified node from the system. ______________________________________________________________________ ### run_node ``` cmd.run_node(node_name) ``` Executes a single node. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | node_name | string | 🟢 | #### Return Value ResultPayload object with node execution status #### Description Resolves and executes the specified node. ______________________________________________________________________ ### get_resolution_state_for_node ``` cmd.get_resolution_state_for_node(node_name) ``` Returns the resolution state of a node. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | node_name | string | 🟢 | #### Return Value ResultPayload object containing node resolution state #### Description Gets the current resolution state of the specified node. ______________________________________________________________________ ### get_metadata_for_node ``` cmd.get_metadata_for_node(node_name) ``` Returns the metadata for a node. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | node_name | string | 🟢 | #### Return Value ResultPayload object containing node metadata #### Description Gets the metadata associated with the specified node. ______________________________________________________________________ ### set_metadata_for_node ``` cmd.set_metadata_for_node(node_name, metadata) ``` Sets the metadata for a node. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | node_name | string | 🟢 | | metadata | dict | 🟢 | #### Return Value ResultPayload object with metadata update status #### Description Sets the metadata for the specified node. ______________________________________________________________________ ### exists ``` cmd.exists(node) ``` Checks if a node exists. #### Arguments | Name | Argument Type | Required | | ---- | ------------- | -------- | | node | string | 🟢 | #### Return Value Boolean indicating whether the node exists #### Description Returns True if the specified node exists, False otherwise. ______________________________________________________________________ ### ls ``` cmd.ls(**kwargs) ``` Lists objects in the system. #### Arguments | Name | Argument Type | Required | | ---------- | ------------- | -------- | | \*\*kwargs | dict | ⚪ | #### Return Value List of object names matching the filter criteria #### Description Lists objects in the system, optionally filtered by the provided criteria. ## Parameter Operations ### list_params ``` cmd.list_params(node) ``` Lists all parameters on a node. #### Arguments | Name | Argument Type | Required | | ---- | ------------- | -------- | | node | string | 🟢 | #### Return Value List of parameter names #### Description Returns a list of all parameters on the specified node. ______________________________________________________________________ ### add_param ``` cmd.add_param(node_name, parameter_name, default_value, tooltip, type=None, input_types=None, output_type=None, edit=False, tooltip_as_input=None, tooltip_as_property=None, tooltip_as_output=None, ui_options=None, mode_allowed_input=True, mode_allowed_property=True, mode_allowed_output=True, **kwargs) ``` Adds a parameter to a node. #### Arguments | Name | Argument Type | Required | | --------------------- | ------------------ | -------- | | node_name | string | 🟢 | | parameter_name | string | 🟢 | | default_value | any | 🟢 | | tooltip | string or list | 🟢 | | type | string | ⚪ | | input_types | list of strings | ⚪ | | output_type | string | ⚪ | | edit | boolean | ⚪ | | tooltip_as_input | string or list | ⚪ | | tooltip_as_property | string or list | ⚪ | | tooltip_as_output | string or list | ⚪ | | ui_options | ParameterUIOptions | ⚪ | | mode_allowed_input | boolean | ⚪ | | mode_allowed_property | boolean | ⚪ | | mode_allowed_output | boolean | ⚪ | #### Return Value ResultPayload object with parameter addition status #### Description Adds a parameter to the specified node with the given configuration. If edit=True, modifies an existing parameter instead. ______________________________________________________________________ ### del_param ``` cmd.del_param(node_name, parameter_name) ``` Removes a parameter from a node. #### Arguments | Name | Argument Type | Required | | -------------- | ------------- | -------- | | node_name | string | 🟢 | | parameter_name | string | 🟢 | #### Return Value ResultPayload object with parameter removal status #### Description Removes the specified parameter from the node. ______________________________________________________________________ ### param_info ``` cmd.param_info(node, param) or cmd.param_info("node.param") ``` Gets information about a parameter. #### Arguments | Name | Argument Type | Required | | ----- | ------------- | -------- | | node | string | 🟢 | | param | string | 🟢 | #### Return Value ResultPayload object containing parameter details #### Description Returns detailed information about the specified parameter. Accepts either separate node and param arguments or a single "node.param" string. ______________________________________________________________________ ### get_value ``` cmd.get_value(node, param) or cmd.get_value("node.param") ``` Gets the value of a parameter. #### Arguments | Name | Argument Type | Required | | ----- | ------------- | -------- | | node | string | 🟢 | | param | string | 🟢 | #### Return Value The value of the parameter or a failure result #### Description Returns the current value of the specified parameter. Supports indexed access for container types (e.g., "node.param[0]"). ______________________________________________________________________ ### set_value ``` cmd.set_value(node, param, value) or cmd.set_value("node.param", value) ``` Sets the value of a parameter. #### Arguments | Name | Argument Type | Required | | ----- | ------------- | -------- | | node | string | 🟢 | | param | string | 🟢 | | value | any | 🟢 | #### Return Value ResultPayload object with value update status #### Description Sets the value of the specified parameter. Supports indexed access for container types (e.g., "node.param[0]"). ## Connection Operations ### connect ``` cmd.connect(source, destination) ``` Creates a connection between two parameters. #### Arguments | Name | Argument Type | Required | | ----------- | ------------- | -------- | | source | string | 🟢 | | destination | string | 🟢 | #### Return Value ResultPayload object with connection creation status #### Description Creates a connection from the source parameter to the destination parameter. Both arguments should be in the format "node.param". ______________________________________________________________________ ### exec_chain ``` cmd.exec_chain(*node_names) ``` Creates execution connections between a sequence of nodes. #### Arguments | Name | Argument Type | Required | | ------------ | ------------- | -------- | | \*node_names | string(s) | 🟢 | #### Return Value Dictionary with results of each connection attempt #### Description Creates exec_out -> exec_in connections between a sequence of nodes, effectively chaining them for execution in sequence. ______________________________________________________________________ ### delete_connection ``` cmd.delete_connection(source_node_name, source_param_name, target_node_name, target_param_name) ``` Deletes a connection between parameters. #### Arguments | Name | Argument Type | Required | | ----------------- | ------------- | -------- | | source_node_name | string | 🟢 | | source_param_name | string | 🟢 | | target_node_name | string | 🟢 | | target_param_name | string | 🟢 | #### Return Value ResultPayload object with connection deletion status #### Description Removes the connection between the specified source and target parameters. ______________________________________________________________________ ### get_connections_for_node ``` cmd.get_connections_for_node(node_name) ``` Lists all connections for a node. #### Arguments | Name | Argument Type | Required | | --------- | ------------- | -------- | | node_name | string | 🟢 | #### Return Value ResultPayload object containing connection information #### Description Returns all connections involving the specified node, both incoming and outgoing. ## Library Operations ### get_available_libraries ``` cmd.get_available_libraries() ``` Lists all available node libraries. #### Arguments None #### Return Value ResultPayload object containing a list of library names #### Description Returns all registered node libraries in the system. ______________________________________________________________________ ### get_node_types_in_library ``` cmd.get_node_types_in_library(library_name) ``` Lists all node types in a library. #### Arguments | Name | Argument Type | Required | | ------------ | ------------- | -------- | | library_name | string | 🟢 | #### Return Value ResultPayload object containing a list of node type names #### Description Returns all node types available in the specified library. ______________________________________________________________________ ### get_node_metadata_from_library ``` cmd.get_node_metadata_from_library(library_name, node_type_name) ``` Gets metadata for a node type. #### Arguments | Name | Argument Type | Required | | -------------- | ------------- | -------- | | library_name | string | 🟢 | | node_type_name | string | 🟢 | #### Return Value ResultPayload object containing node type metadata #### Description Returns the metadata for the specified node type in the given library. ## Config Operations ### get_config_value ``` cmd.get_config_value(category_and_key) ``` Gets a configuration value. #### Arguments | Name | Argument Type | Required | | ---------------- | ------------- | -------- | | category_and_key | string | 🟢 | #### Return Value ResultPayload object containing the configuration value #### Description Returns the value for the specified configuration key. ______________________________________________________________________ ### set_config_value ``` cmd.set_config_value(category_and_key, value) ``` Sets a configuration value. #### Arguments | Name | Argument Type | Required | | ---------------- | ------------- | -------- | | category_and_key | string | 🟢 | | value | any | 🟢 | #### Return Value ResultPayload object with configuration update status #### Description Sets the value for the specified configuration key. ______________________________________________________________________ ### get_config_category ``` cmd.get_config_category(category=None) ``` Gets all configuration values in a category. #### Arguments | Name | Argument Type | Required | | -------- | ------------- | -------- | | category | string | ⚪ | #### Return Value ResultPayload object containing category configuration values #### Description Returns all configuration values in the specified category. If no category is provided, returns all configuration values. ______________________________________________________________________ ### set_config_category ``` cmd.set_config_category(category=None, contents={}) ``` Sets configuration values for a category. #### Arguments | Name | Argument Type | Required | | -------- | ------------- | -------- | | category | string | ⚪ | | contents | dict | ⚪ | #### Return Value ResultPayload object with category configuration update status #### Description Sets all configuration values for the specified category with the provided contents. ## Utility Operations ### run_arbitrary_python ``` cmd.run_arbitrary_python(python_str) ``` Executes arbitrary Python code. #### Arguments | Name | Argument Type | Required | | ---------- | ------------- | -------- | | python_str | string | 🟢 | #### Return Value ResultPayload object with execution status and results #### Description Executes the provided Python code string in the Griptape environment. ## Examples ### Creating a Flow and Nodes ``` # Create a new flow flow = cmd.create_flow(flow_name="MyFlow") # Create two nodes in the flow node1 = cmd.create_node(node_type="CreateText", node_name="MyText", parent_flow_name="MyFlow") node2 = cmd.create_node(node_type="RunAgent", node_name="MyAgent", parent_flow_name="MyFlow") ``` ### Setting Parameter Values and Creating Connections ``` # Set a parameter value cmd.set_value("MyText.text", "This is a sample text to summarize.") # Connect two nodes cmd.connect("MyText.text", "MyAgent.prompt") ``` ### Running a Flow ``` # Run the flow cmd.run_flow("MyFlow") # Get the result summary = cmd.get_value("MyAgent.output") print(summary) # ``` ### Working with Parameters ``` # Add a new parameter to a node cmd.add_param( node_name="MyAgent", parameter_name="sunglasses", default_value=100, tooltip="The coolness of sunglasses", type=["int"] ) # Set the value of the new parameter cmd.set_value("MyAgent.sunglasses", 50) # Change the parameter cmd.add_param( node_name="MyAgent", parameter_name="sunglasses", tooltip="The coolness of sunglasses if halved", edit=1 ) ``` ### Listing and Querying ``` # List all nodes in a flow nodes = cmd.get_nodes_in_flow("MyFlow") print(nodes) # ["MyText","MyAgent"] # List all parameters on a node params = cmd.list_params("MyAgent") print(params) # ["agent", "prompt_driver", "tools", "rulesets", "prompt", "prompt_context", "output"] # Check if a node exists if cmd.exists("MyText"): print("MyText node exists") # True ``` # Project system # Project The project system controls how files are organized, named, and saved when you work in Griptape Nodes. Every time a node saves an image, copies an uploaded file, or downloads a URL, the project system decides where that file goes and what it's called. ## Why it exists Without a project system, every file save operation would require a hard-coded path. The project system replaces hard-coded paths with named templates called **macros** and named file-saving scenarios called **situations**. This lets you customize your entire project's file layout in one place, and every node that saves files automatically follows that layout. ## How the pieces fit together ``` workspace/ ← workspace directory (your root) griptape-nodes-project.yml ← optional: your customizations my_workflow/ ← workflow directory inputs/ ← default inputs directory outputs/ ← default outputs directory temp/ ← default temp directory .griptape-nodes-previews/ ← default previews directory ``` The conceptual hierarchy is: ``` workspace └── project template ├── situations (where and how to save files in each scenario) ├── directories (logical name → relative path mappings) └── environment (custom key-value variables) ``` **Workspace** is the root directory for all work in this project. It is configured in your settings. **Project template** is the configuration file that governs this project. It is loaded at startup — first the system defaults, then any overrides you've placed in `griptape-nodes-project.yml` inside your workspace. The project template is what lets you control where files land and how they're named. **Situations** are named scenarios for when a file is read or written. Each has a macro template that determines the file path and a policy that determines what happens when a file already exists. **Directories** are logical name-to-path mappings. Each directory has a short name (like `outputs`) that you use as a variable in macros. That name maps to a real path on disk — by default `outputs` maps to an `outputs` subfolder. Directory paths can be relative or absolute, and can themselves contain macro variables or environment variable references. **Environment** is a bag of custom key-value pairs that can be referenced in macros. **Macros** are the template strings used in situations and directories to generate concrete file paths. ## Pages in this section - [Workspace](https://docs.griptapenodes.com/projects/workspace/index.md) — the root working context and how relative paths resolve - [Projects](https://docs.griptapenodes.com/projects/projects/index.md) — the project file format and the merge model - [Macros](https://docs.griptapenodes.com/projects/macros/index.md) — template syntax reference for generating file paths - [Directories](https://docs.griptapenodes.com/projects/directories/index.md) — logical name-to-path mappings - [Situations](https://docs.griptapenodes.com/projects/situations/index.md) — named file-saving scenarios with policies - [Environment & Builtin Variables](https://docs.griptapenodes.com/projects/environment/index.md) — custom variables and system-provided values - [File Extension Directories](https://docs.griptapenodes.com/projects/file_extension_directories/index.md) — route files to folders by extension - [Customization Guide](https://docs.griptapenodes.com/projects/customization/index.md) — practical examples for common customizations # Workspace The workspace is the root directory for all your work. It is the starting point from which relative file paths are resolved. ## Config files Engine configuration is stored in files named `griptape_nodes_config.json`. Three locations are meaningful: | File | Purpose | | ----------------------------------------------------- | -------------------------------------------------------------------------------- | | `~/.config/griptape_nodes/griptape_nodes_config.json` | User config — global settings for this machine | | `/griptape_nodes_config.json` | Project-adjacent config — shared defaults distributed alongside the project file | | `/griptape_nodes_config.json` | Workspace config — per-user overrides for the active project | ## Config resolution order Settings are resolved in this order (later entries win): 1. Built-in defaults 1. User config (`~/.config/griptape_nodes/griptape_nodes_config.json`) 1. Project-adjacent config (`/griptape_nodes_config.json`) 1. Workspace config (`/griptape_nodes_config.json`) 1. Environment variable (`GTN_CONFIG_*`) When the workspace directory is the same as the project directory (self-contained project), layers 3 and 4 point to the same file — it is loaded only once. ## Workspace resolution The workspace directory is determined in this order (later entries win): 1. Built-in default — `GriptapeNodes/` inside the engine's working directory at startup 1. User config `workspace_directory` key 1. Project directory auto-default — when a project is loaded and no other source sets the workspace, it defaults to the directory containing the project file 1. Project-adjacent config `workspace_directory` key 1. `project_workspaces` entry in user config (see below) 1. Environment variable `GTN_CONFIG_WORKSPACE_DIRECTORY` ## Per-project workspace overrides The `project_workspaces` setting in your user config maps project file paths to workspace directory overrides. Use this when a shared project needs to resolve to a different local workspace on each machine. ``` { "project_workspaces": { "//NAS/Projects/ProjectA/griptape-nodes-project.yml": "/Users/collin/ProjectA/", "//NAS/Projects/ProjectB/griptape-nodes-project.yml": "/Users/collin/ProjectB/" } } ``` Keys are resolved absolute paths to project files. When a project is loaded, if its resolved path matches a key, the corresponding value is used as the workspace directory. ## Example scenarios ### Scenario 1: Solo developer, no project file No project file, no project-adjacent or workspace config. Workspace comes from user config or the built-in default. ``` ~/.config/griptape_nodes/ griptape_nodes_config.json <- workspace_directory: ~/GriptapeNodes ~/GriptapeNodes/ <- workspace griptape_nodes_config.json <- optional workspace-level overrides ``` ### Scenario 2: Self-contained portable project Project and config live in the same directory. The workspace auto-defaults to the project directory. Move the folder to a thumb drive or a different machine — no changes needed. ``` /My_Indie_Short/ griptape-nodes-project.yml griptape_nodes_config.json <- project-adjacent and workspace config (same file) inputs/ outputs/ ``` ### Scenario 3: Shared project, per-user workspaces A project file lives on a shared network drive. Each user maps it to their own local workspace via `project_workspaces` in their user config. Each user's workspace can have its own `griptape_nodes_config.json` for personal overrides. ``` //NAS/Projects/ProjectA/ griptape-nodes-project.yml griptape_nodes_config.json <- shared studio defaults (e.g. model preferences) ``` Collin's user config: ``` { "project_workspaces": { "//NAS/Projects/ProjectA/griptape-nodes-project.yml": "/Users/collin/ProjectA/" } } ``` James' user config: ``` { "project_workspaces": { "//NAS/Projects/ProjectA/griptape-nodes-project.yml": "C:\\Projects\\ProjectA\\" } } ``` Each user can place a `griptape_nodes_config.json` in their local workspace directory for personal overrides that take precedence over the shared project-adjacent config. ### Scenario 4: Studio-mandated workspace The project-adjacent config sets a shared workspace. Artists without a `project_workspaces` entry all get the studio default. A workspace config on the shared drive can hold additional shared settings. ``` //NAS/Projects/ProjectA/ griptape-nodes-project.yml griptape_nodes_config.json <- workspace_directory: //NAS/Workspaces/ProjectA/ //NAS/Workspaces/ProjectA/ griptape_nodes_config.json <- shared workspace-level settings ``` Render farm machines can override the workspace via `GTN_CONFIG_WORKSPACE_DIRECTORY` without touching any shared files. ### Scenario 5: User overrides shared engine config The studio ships `log_level: "WARNING"` in the project-adjacent config. A developer wants `DEBUG` locally. They put the override in their workspace config — workspace config (layer 4) beats project-adjacent (layer 3). ``` /Users/dev/ProjectA/ griptape_nodes_config.json <- {"log_level": "DEBUG"} ``` ### Scenario 6: Multiple projects, different workspaces `project_workspaces` maps each shared project to a distinct local workspace. Switching active projects switches workspace and reloads the workspace config. ``` { "project_workspaces": { "//NAS/ProjectA/griptape-nodes-project.yml": "/Users/dev/ProjectA/", "//NAS/ProjectB/griptape-nodes-project.yml": "/Users/dev/ProjectB/" } } ``` ### Scenario 7: Workspace auto-discovery Griptape Nodes looks for `griptape-nodes-project.yml` in the workspace directory on startup. If found, the project is loaded automatically. This is the same as scenario 2 — workspace and project directory are the same, so the single `griptape_nodes_config.json` serves as both project-adjacent and workspace config. ## How paths resolve All relative paths in the project system resolve against the **workspace directory**. If your workspace is `/Users/you/workspace/` and a situation macro resolves to `outputs/render_001.png`, the final absolute path is `/Users/you/workspace/outputs/render_001.png`. The **project base directory** (the folder containing `griptape-nodes-project.yml`) is exposed as the `{project_dir}` builtin variable but is not used as the resolution base for relative paths. It is used as a fallback when the path manager maps an absolute path back to a macro form and the path falls inside the project folder but outside any named directory. ## Workspace and the project file When Griptape Nodes starts, it looks for `griptape-nodes-project.yml` in your workspace directory. If found, the file is merged on top of the system defaults to produce the active project template. If not found, system defaults are used. See [Projects](https://docs.griptapenodes.com/projects/projects/index.md) for details on the project file and merge model. ## Summary | Setting | Description | | -------------------------------------------- | --------------------------------------------------- | | `workspace_directory` | The root directory for your work | | `project_workspaces` | Per-project workspace overrides in user config | | `griptape-nodes-project.yml` | Optional project template file | | `/griptape_nodes_config.json` | Optional shared config distributed with the project | | `/griptape_nodes_config.json` | Optional per-user workspace config | | `GTN_CONFIG_WORKSPACE_DIRECTORY` | Environment variable override (highest priority) | # Projects A project template is a configuration that defines how files are organized and saved. It is the combination of [situations](https://docs.griptapenodes.com/projects/situations/index.md), [directories](https://docs.griptapenodes.com/projects/directories/index.md), and [environment variables](https://docs.griptapenodes.com/projects/environment/index.md) that every node consults when it needs to save a file. By default, Griptape Nodes ships with a built-in project template that handles the common cases — saving images, audio, and other outputs to an `outputs` folder in your workspace. A project file lets you change any of that: redirect where a particular kind of file is saved, add a new named location (like `renders/4k`), or inject environment variables specific to your project. You only write down the things you want to change; everything else continues to use the [default situations](https://docs.griptapenodes.com/projects/situations/#default-situations) and [default directories](https://docs.griptapenodes.com/projects/directories/#default-directories). ## The project file Your workspace-level customizations live in a file named: ``` griptape-nodes-project.yml ``` Place this file in your workspace directory. It is optional — if absent, the [system defaults](#the-system-defaults) apply. ## Project file structure ``` project_template_schema_version: "0.1.0" name: "My Project" description: "Optional description" directories: outputs: path_macro: "renders" # override the outputs directory path renders_4k: # add a new directory path_macro: "renders/4k" situations: save_node_output: # override an existing situation macro: "{outputs}/{workflow_name?:_}{file_name_base}{_index?:03}.{file_extension}" archive_render: # add a new situation macro: "{renders_4k}/{workflow_name?:_}{file_name_base}.{file_extension}" policy: on_collision: overwrite create_dirs: true environment: PROJECT_CODENAME: "aurora" file_extension_directories: png: "images" mp4: "{outputs}/videos" ``` ### Fields reference | Field | Required | Description | | --------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `project_template_schema_version` | Yes | Must match the supported version (`"0.3.0"`) | | `name` | Yes | Human-readable name for this project | | `description` | No | Optional description | | `situations` | No | Dict of situation overrides and additions | | `directories` | No | Dict of directory overrides and additions | | `environment` | No | Dict of custom key-value variables | | `file_extension_directories` | No | Extension-to-folder routing; see [File Extension Directories](https://docs.griptapenodes.com/projects/file_extension_directories/index.md) | ### Situation fields Each entry under `situations` is keyed by situation name. You can provide any subset of these fields: | Field | Required for new | Description | | --------------------- | ---------------- | -------------------------------------------------- | | `macro` | Yes | Macro template string for the file path | | `policy` | Yes | Must include both `on_collision` and `create_dirs` | | `policy.on_collision` | Yes | One of: `create_new`, `overwrite`, `fail` | | `policy.create_dirs` | Yes | `true` to create missing directories automatically | | `fallback` | No | Name of another situation to use if this one fails | | `description` | No | Human-readable description | When *modifying* an existing situation, you only need to provide the fields you want to change. When *adding* a new situation, `macro` and `policy` are required. ### Directory fields Each entry under `directories` is keyed by the logical name: | Field | Required for new | Description | | ------------ | ---------------- | ------------------------------------------------------------------ | | `path_macro` | Yes | Path string, may contain macros or environment variable references | ## The merge model The project system uses a two-layer model: 1. **System defaults** — a complete, built-in project template that ships with Griptape Nodes. It defines all default situations and directories and is always loaded first. 1. **Workspace overlay** — the contents of `griptape-nodes-project.yml`, if present. This file is merged *on top of* the system defaults. The merge behavior is additive and field-level: - Situations and directories from the overlay are merged into the defaults. An overlay situation with the same name as a default situation changes only the fields you specify (e.g., just the macro, or just the policy). An overlay situation with a new name is added alongside the defaults. - Environment entries in the overlay override entries with the same key in the defaults. New keys are added. - `file_extension_directories` entries merge per-key the same way as environment entries. A `null` value tombstones the base entry. - The `name` field is always taken from the overlay (required). You never need to repeat default values. Your project file only needs to contain the things you want to change. ## Validation status When a project file is loaded, it receives one of four statuses: | Status | Meaning | | ---------- | ---------------------------------------------------------------------- | | `GOOD` | Loaded and fully valid | | `FLAWED` | Loaded but has warnings (e.g., schema version mismatch) — still usable | | `UNUSABLE` | Errors prevent the template from being used | | `MISSING` | The project file was not found | When a project file has `UNUSABLE` or `MISSING` status, Griptape Nodes falls back to the system defaults. You can inspect validation problems (with field paths and line numbers) to diagnose issues. ## The system defaults The system defaults define these situations and directories out of the box. See [Situations](https://docs.griptapenodes.com/projects/situations/index.md) and [Directories](https://docs.griptapenodes.com/projects/directories/index.md) for full details. # Macros A macro is a template string that generates a file path by substituting named variables. Macros are used in situation templates and directory definitions. Before diving into the full syntax, here are two examples that show what macros look like in practice: ``` Template: {outputs}/{node_name?:_}{file_name_base}{_index?:03}.{file_extension} With all variables: outputs="outputs", node_name="ImageGen", file_name_base="render", _index=2, file_extension="png" → outputs/ImageGen_render002.png With optional variables omitted: outputs="outputs", file_name_base="render", file_extension="png" → outputs/render.png ``` `{outputs}` is a directory name that the project system supplies automatically. `{node_name?:_}` is optional — when present, its value is followed by `_`; when absent, the block disappears entirely. `{_index?:03}` is optional and zero-padded to three digits when present. ## Variable syntax reference ### Required variable ``` {variable_name} ``` The variable must be provided. If it is missing when the macro is resolved, resolution fails with an error. ### Optional variable ``` {variable_name?} ``` The `?` marks the variable as optional. If the variable is not provided, the `{}` block — and any format spec — is omitted entirely from the output. The rest of the macro continues normally. ### Separator format ``` {variable_name:separator} ``` Appends `separator` after the variable's value. Any text that is not a recognized keyword (see transformations below) and not a numeric padding is treated as a separator. This is most useful for building path prefixes that disappear cleanly when the variable is absent. For example, `{node_name?:_}` adds `node_name_` before a filename when the node name is known, but produces nothing at all when it isn't: ``` {node_name?:_}{file_name_base} node_name="ImageGen", file_name_base="render" → ImageGen_render node_name not provided, file_name_base="render" → render ``` Path separators work the same way — `{sub_dirs?:/}` adds a subdirectory prefix only when sub-directories are specified: ``` {outputs}/{sub_dirs?:/}{file_name_base}.{file_extension} sub_dirs="lighting/pass_a", file_name_base="render", file_extension="exr" → outputs/lighting/pass_a/render.exr sub_dirs not provided, file_name_base="render", file_extension="exr" → outputs/render.exr ``` ### Numeric padding ``` {variable_name:03} ``` Zero-pads the value to the specified width. The variable must hold an integer value. ``` {_index:03} with _index = 5 → "005" {_index:04} with _index = 12 → "0012" ``` Used with `?` for auto-incrementing filenames: `{_index?:03}` is absent on the first save, then becomes `001`, `002`, and so on as needed. ### String transformations ``` {variable_name:lower} → lowercase {variable_name:upper} → UPPERCASE {variable_name:slug} → slug-form (spaces to hyphens, safe chars only) ``` For example, if `workflow_name` is `"My Autumn Shoot"`: ``` {workflow_name:lower} → "my autumn shoot" {workflow_name:slug} → "my-autumn-shoot" ``` ### Default value ``` {variable_name|default_value} ``` If the variable is not provided, `default_value` is used instead. ``` {workflow_name|untitled} → uses "untitled" if workflow_name is not provided ``` ### Chaining format specs Multiple format specs are separated by `:` and applied left to right. If a separator is used, it must come first: ``` {variable_name:_:lower} → lowercase value with underscore appended {variable_name:lower:slug} → lowercase, then slug ``` ### Quoted separators If your separator text matches a keyword like `lower` or `upper`, wrap it in single quotes to treat it as a literal separator: ``` {variable_name:'lower'} → appends the text "lower" as a separator ``` ## Resolution When a macro is resolved, directory names and builtin variables are supplied automatically by the project system. You only need to provide the variables specific to your operation (like `file_name_base` and `file_extension`). For example, resolving the `save_node_output` situation macro: ``` Template: {outputs}/{sub_dirs?:/}{node_name?:_}{file_name_base}{_index?:03}.{file_extension} Automatic: outputs → resolved from the "outputs" directory definition → "outputs" Provided: node_name="StyleTransfer", file_name_base="portrait", _index=3, file_extension="png" Result: outputs/StyleTransfer_portrait003.png ``` Directory names (like `outputs`) are automatically resolved to their configured paths. See [Directories](https://docs.griptapenodes.com/projects/directories/index.md). Builtin variables (like `workflow_name`, `project_dir`) are also supplied automatically. See [Environment & Builtin Variables](https://docs.griptapenodes.com/projects/environment/index.md). ## Reverse matching The macro system can also work in reverse: given an actual path and a macro template, it can extract the values of the variables. This is used when the system needs to identify whether a file belongs to a known project directory and what metadata is encoded in its name. For example: ``` Template: {outputs}/{node_name?:_}{file_name_base}{_index?:03}.{file_extension} Path: outputs/StyleTransfer_portrait003.png Extracted: outputs="outputs", node_name="StyleTransfer", file_name_base="portrait", _index=3, file_extension="png" ``` Numeric padding is reversed by parsing the number (`"003"` → integer `3`). Case transformations and slugification cannot be reliably reversed and return the value as-is. ## Syntax errors The macro parser reports syntax errors with a position number to help you find the problem: - Unclosed brace: `{variable_name` (no closing `}`) - Unmatched closing brace: `variable}name` - Nested braces: `{outer{inner}}` - Empty variable: `{}` # Directories Directories are logical name-to-path mappings. Instead of hard-coding a path like `outputs/renders` everywhere, you give it a name (`outputs`) and use that name in macros. If you later change where outputs should go, you update the directory definition in one place. ## Default directories The system defaults define four directories: | Name | Default path | Description | | ---------- | -------------------------- | ------------------------------------------------------------- | | `inputs` | `inputs` | Files brought into the project (uploaded, copied, downloaded) | | `outputs` | `outputs` | Files generated by nodes | | `temp` | `temp` | Temporary working files | | `previews` | `.griptape-nodes-previews` | Preview thumbnails (hidden folder) | All default paths are relative and resolve against the project base directory. ## Using directory names in macros Directory names are available as variables in any macro. When a macro is resolved, the project system automatically substitutes the directory name with its configured path: ``` Template: {outputs}/{file_name_base}.{file_extension} ↓ Resolved: outputs/my_image.png ``` You do not supply directory values yourself — they come from the directory definitions. Directory names are **reserved**: if you try to pass a variable with the same name as a directory, the system will reject it with an error to prevent ambiguity. ## Customizing directory paths Override a directory's path in your `griptape-nodes-project.yml`: ``` project_template_schema_version: "0.1.0" name: "My Project" directories: outputs: path_macro: "renders/final" ``` Now `{outputs}` in any macro resolves to `renders/final` instead of `outputs`. ## Adding new directories Add a directory that doesn't exist in the defaults: ``` directories: deliverables: path_macro: "client_deliverables" ``` Once added, `{deliverables}` is available in any macro. ## Directory paths with macros The `path_macro` field supports tilde (`~`) expansion, macro syntax, and environment variable references: ``` directories: downloads: path_macro: "~/Downloads" ``` This maps the `downloads` directory to the current user's Downloads folder, regardless of the machine it runs on. ``` directories: outputs: path_macro: "$OUTPUT_BASE/renders" ``` If `$OUTPUT_BASE` is set in your environment (or in the project's `environment` section), it will be substituted when the path is resolved. You can also reference builtin variables in directory paths: ``` directories: outputs: path_macro: "{workflow_dir}/renders" ``` This makes the `outputs` directory relative to the current workflow's location rather than the project base directory. ## Reserved names Directory names are reserved across the entire variable namespace. You cannot use a directory name as a user-supplied variable in a macro call — the system will return an error if you try. This protects against accidentally overriding a directory path through a variable name collision. The builtin variables (`project_dir`, `workspace_dir`, `workflow_name`, `workflow_dir`, `static_files_dir`) are also reserved and cannot be overridden. See [Environment & Builtin Variables](https://docs.griptapenodes.com/projects/environment/index.md). # Situations A situation is a named file-saving scenario. It defines: - **Where** the file goes (via a macro template) - **How** to handle the case when a file already exists there (via a collision policy) - **What to do** if saving fails (via an optional fallback situation) When a node needs to save a file, it names the situation it's in (for example, `save_node_output`) and the project system resolves the path using that situation's macro and applies the policy. ## Collision policies | Policy | Behavior | | ------------ | ----------------------------------------------------------------------------------------------------------- | | `create_new` | Increment a counter in the filename until a non-colliding name is found. Requires `{_index?}` in the macro. | | `overwrite` | Replace the existing file without asking. | | `fail` | Stop and report an error if the file already exists. | The `create_dirs` field controls whether intermediate parent directories are created automatically (`true`, like `mkdir -p`) or whether a missing parent directory causes an error (`false`). ## Fallbacks A situation can name a fallback situation. If the primary situation cannot resolve its macro (for example, because required variables are missing), the system tries the fallback. The default `save_file` situation is a minimal fallback used by most other situations. ## Default situations ### `save_file` ``` macro: {file_name_base}{_index?:03}.{file_extension} policy: create_new, create_dirs: true ``` Generic file save at the project root (or wherever the caller's path context puts it). This is the fallback for most other situations. The `{_index?:03}` variable is zero-padded and optional — omitted on the first save, then incremented to avoid overwriting existing files. ### `copy_external_file` ``` macro: {inputs}/{node_name?:_}{parameter_name?:_}{file_name_base}{_index?:03}.{file_extension} policy: create_new, create_dirs: true fallback: save_file ``` Used when a user copies or drags an external file into the project. The file is placed in the `inputs` directory. The node name and parameter name are prepended as optional prefixes to help identify the file's origin. **Example:** ``` node_name="LoadImage", parameter_name="source", file_name_base="photo", file_extension="jpg" → inputs/LoadImage_source_photo.jpg node_name not provided, file_name_base="photo", file_extension="jpg" → inputs/photo.jpg ``` ### `download_url` ``` macro: {inputs}/{sanitized_url} policy: overwrite, create_dirs: true fallback: save_file ``` Used when a node downloads a file from a URL. The URL is sanitized into a safe filename. Files downloaded from the same URL are overwritten rather than duplicated. ### `save_node_output` ``` macro: {outputs}/{sub_dirs?:/}{node_name?:_}{file_name_base}{_index?:03}.{file_extension} policy: create_new, create_dirs: true fallback: save_file ``` Used when a node generates and saves output. Files go into the `outputs` directory. Optional sub-directories (`{sub_dirs?:/}`) allow nesting within outputs. The node name is an optional prefix. **Example:** ``` outputs="outputs", node_name="ImageGen", file_name_base="render", _index=1, file_extension="png" → outputs/ImageGen_render001.png sub_dirs="lighting/pass_a", node_name="ImageGen", file_name_base="render", file_extension="exr" → outputs/lighting/pass_a/ImageGen_render.exr ``` ### `save_preview` ``` macro: {previews}/{drive_volume_mount?:/}{source_relative_path?:/}{source_file_name}.{preview_format} policy: overwrite, create_dirs: true fallback: save_file ``` Used to generate preview thumbnails. Previews mirror the directory hierarchy of the source file so that each source file has exactly one preview. Previews are overwritten rather than versioned. The `previews` directory defaults to `.griptape-nodes-previews` (a hidden folder). ### `save_static_file` ``` macro: {workflow_dir?:/}{static_files_dir}/{file_name_base}.{file_extension} policy: overwrite, create_dirs: true fallback: save_file ``` Used by the static files manager to save static assets. Files go into the `static_files_dir` subdirectory of the current workflow's directory. These files are overwritten when regenerated. ## How nodes use situations Nodes that save files use a `ProjectFileParameter` to declare which situation they operate in. The node provides its situation-specific variables (like `file_name_base` and `file_extension`), and the project system supplies everything else (directory paths, builtin variables). To use a custom situation from your project file, configure the node's situation parameter to match the name of your custom situation. ## Adding custom situations See [Customization Guide](https://docs.griptapenodes.com/projects/customization/index.md) for examples. # Environment & Builtin Variables ## Environment The `environment` section of a project file holds custom key-value pairs. These values are available for use in macros and directory `path_macro` fields. ``` environment: RENDER_STYLE: "realistic" CLIENT_CODE: "ACME" ``` ### Overlay behavior Environment entries from your project file are merged on top of the system defaults. If a key exists in the defaults, your value replaces it. New keys are added. ### Referencing other variables from an environment value Environment values are macros themselves. A value can reference a builtin, a directory, another project environment variable, or a shell environment variable by using `{NAME}`: Assume the shell that launched Griptape Nodes has `SHARED_DRIVE=/mnt/renders` exported. Then: ``` directories: outputs: # Reference a shell env var directly — no need to declare it under `environment:` path_macro: "{SHARED_DRIVE}/outputs" environment: CLIENT_CODE: "ACME" # Reference a builtin PROJECT_RENDERS: "{project_dir}/renders" # Compose a shell env var, a project env var, and literal text CLIENT_RENDERS: "{SHARED_DRIVE}/{CLIENT_CODE}/renders" ``` References are resolved recursively, following the priority order in [Variable priority](#variable-priority). Cycles (for example, `A: "{B}"` and `B: "{A}"`) are detected and surfaced as macro resolution errors. #### Legacy `$VAR` syntax For backwards compatibility, an environment value that is **exactly** `$NAME` (no surrounding text, no suffix, no other macros) is expanded using the operating-system environment **when the value is consumed by a macro**: ``` environment: OUTPUT_ROOT: "$RENDER_FARM_SHARE" # works in macros: {OUTPUT_ROOT} -> /mnt/renders ``` Important limitations — the `$VAR` form is narrow and has known gaps: - **Whole value only.** Anything appended (for example `"$SHARED_DRIVE/outputs"`) or any adjacent text is **not** expanded and is treated as a literal string. Macro resolution will fail looking up the entire trailing string as a secret name. - **Macro-only, not process.** `$VAR` is only expanded at macro resolution time. It is **not** expanded when the value is written to `os.environ` — subprocesses and nodes calling `os.environ.get("OUTPUT_ROOT")` will see the literal string `"$RENDER_FARM_SHARE"`, not the expanded value. - **Cannot be composed.** A `$`-prefixed value cannot reference another project env var, a builtin, or a directory. Prefer the `{NAME}` form for all new projects — it composes cleanly, expands consistently in macros **and** `os.environ`, and works in both `environment` values and directory `path_macro` fields. ## Builtin variables Builtin variables are automatically available in all macros. You do not define them — the system provides their values at runtime. They cannot be overridden. | Variable | Type | Description | | ------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `project_dir` | directory | Absolute path to the project base directory (the folder containing `griptape-nodes-project.yml`, or the workspace directory when no project file is present) | | `workspace_dir` | directory | Absolute path to the workspace directory (defaults to the project directory when no explicit workspace is configured; see [Workspace](https://docs.griptapenodes.com/projects/workspace/#config-resolution-order)) | | `workflow_name` | string | Name of the currently running workflow | | `workflow_dir` | directory | Absolute path to the directory containing the current workflow file | | `static_files_dir` | string | Name of the static files subdirectory (from settings, defaults to `staticfiles`) | ### How builtins are resolved Builtins are resolved at the moment a macro is evaluated — not when the project file is loaded. This means: - `workflow_name` and `workflow_dir` reflect whichever workflow is currently executing - `project_dir` reflects the actual path of the loaded project file - `workspace_dir` reflects the project directory when no explicit workspace is configured, otherwise the value from the project-adjacent config or environment variable If a builtin variable is required but cannot be resolved (for example, `workflow_name` when no workflow is running), macro resolution fails with an error. If the variable is optional (marked with `?`), the block is silently omitted instead. ### Builtin variables in situation macros The `save_static_file` situation uses `workflow_dir` and `static_files_dir`: ``` {workflow_dir?:/}{static_files_dir}/{file_name_base}.{file_extension} ``` If `workflow_dir` is available, the static files go into a subdirectory of the workflow folder. If not (the workflow hasn't been saved yet), the `{workflow_dir?:/}` block is omitted. ## Variable priority When a macro is resolved, variables are supplied from these sources in priority order: 1. **Builtin variables** — always win; cannot be overridden by any other source 1. **Directory names** — resolved from the project's directory definitions; cannot be overridden by caller-supplied variables 1. **Caller-supplied variables** — values passed by the node or operation requesting path resolution 1. **Derived variables** — computed from the variables above plus project state, and injected before the situation macro is resolved. A derived variable abstains when the caller has already supplied its value, so caller-supplied entries still win. 1. **Project environment variables** — values from the project's `environment:` block. Recursively resolved, so a project env value can reference builtins, directory names, other project env vars, or shell env vars. 1. **Shell environment variables** — final fallback. Any variable set in the shell that launched Griptape Nodes (including `HOME`, `USER`, or anything the user exported) can be referenced with `{NAME}` in a macro. A project env var of the same name always wins. Reserved names (builtins, directories) silently win over the shell, since shells have many incidental variables that shouldn't shadow project state. If a caller tries to supply a value for a builtin or directory name that differs from the system value, the resolution fails with a `RESERVED_NAME_COLLISION` error. ### Derived variables | Variable | Derived from | Source of truth | | -------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | | `file_extension_directory` | `file_extension` plus the project's `file_extension_directories` mapping | See [File Extension Directories](https://docs.griptapenodes.com/projects/file_extension_directories/index.md) | # File Extension Directories `file_extension_directories` is a project-template mapping from file extension to a folder fragment used to route files by type. When a situation macro references the derived variable `{file_extension_directory}`, the project system looks up the file's extension in this table and substitutes the associated value. Typical use: keep images, videos, audio, and documents in separate subfolders under `outputs/` without writing a separate situation for each type. ## Quick example ``` project_template_schema_version: "0.3.0" name: "My Project" file_extension_directories: png: "images" jpg: "images" mp4: "videos" wav: "audio" situations: save_node_output: macro: "{outputs}/{file_extension_directory?:/}{node_name?:_}{file_name_base}{_index?:03}.{file_extension}" ``` With this configuration: ``` file_extension="png" → outputs/images/Node_render.png file_extension="mp4" → outputs/videos/Node_render.mp4 file_extension="xyz" → outputs/Node_render.xyz (unmapped: slot collapses) ``` The `?:/` on `{file_extension_directory?:/}` makes the slot optional and adds a trailing `/` when the value is present — so unmapped extensions land at the situation's directory root instead of failing. ## Two value forms A value can be either a **plain name** or a **macro**. ### Plain name ``` file_extension_directories: png: "images" ``` The string is used verbatim. No resolution happens. This is the common case and has zero overhead. ### Macro value ``` file_extension_directories: mp4: "{outputs}/videos" wav: "{workspace_dir}/shared/audio" ``` A value containing `{...}` is resolved against the project's builtins, directory definitions, and any caller-supplied context (like `node_name`) before being substituted into the situation macro. Macro values are what let a single `file_extension_directories` table reroute certain types to entirely different roots — e.g. video to a share drive — without writing a separate situation per type. ### What a macro value can reference | Source | Example | Available? | | ----------------------- | -------------------------------------------------------------------------------------------- | ---------------------------------------- | | Builtin variables | `{workspace_dir}`, `{workflow_dir}`, `{project_dir}`, `{project_name}`, `{static_files_dir}` | Yes | | Directory definitions | `{outputs}`, `{inputs}`, `{temp}`, any custom directory | Yes | | Caller-supplied context | `{node_name}`, `{parameter_name}`, `{sub_dirs}`, `{_index}` | Yes | | Filename parts | `{file_name_base}`, `{file_extension}` | **No** — routing is not a filename layer | Filename parts are excluded intentionally: `file_extension_directories` is a *routing* layer that decides which folder a file lands in. Filenames belong to the situation macro's filename section. ## How resolution works `file_extension_directory` is a **derived variable**. It is not a builtin, and callers don't supply it directly. Instead, the project system runs a small derivation rule whenever the situation's macro template references it: 1. The caller names a situation and provides variables (including `file_extension`). 1. Before the situation macro is resolved, the derivation rule fires: - If `file_extension_directory` is already set by the caller, the rule abstains (caller wins). - Otherwise, the rule looks up `file_extension` (case-insensitive) in the current project's `file_extension_directories` table. - If the value is plain, it becomes the variable's value directly. - If the value is a macro, it is resolved to a concrete path string first. 1. The resulting value is injected into the variable bag and the situation macro is resolved as usual. If lookup fails (empty extension, no project loaded, unmapped extension, or resolution error) the variable is simply not set. Situation macros that use the optional form `{file_extension_directory?:/}` degrade cleanly to no folder prefix; macros using the required form `{file_extension_directory}` fail resolution, as they would for any missing required variable. ## Interaction with the situation macro There is no special-case engine logic that prepends or reparents the routing prefix. What you get is exactly what the situation macro template says you get. | Situation macro shape | Routing behavior | | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | | `{outputs}/{file_extension_directory?:/}{file_name_base}.{ext}` | Routing is a **subfolder** under `{outputs}`. Values must be relative. | | `{file_extension_directory?:/}{file_name_base}.{ext}` | Routing dictates the **root**. Values may be absolute to redirect away from `{outputs}` entirely. | | `{outputs}/{file_extension_directory?:/}...` with an absolute value | String concatenation — `outputs//Volumes/share/videos/foo.mp4` — not what you want. | Choose the situation-macro shape that matches the kind of routing you need. ## Overlay merge behavior `file_extension_directories` merges entry-by-entry, identically to `environment`: - Keys not present in the overlay are inherited from the base. - Keys present in the overlay override the base entry for that extension. - Keys with a `null` value in the overlay are tombstoned — the base entry is dropped. ``` # Inherit the base's image routing, send mp4 somewhere else, drop csv routing. file_extension_directories: mp4: "{workspace_dir}/shared/videos" csv: null ``` ## Caller override Any caller can pre-populate `file_extension_directory` in the variables bag. When it's already set, the derivation rule abstains and the caller's value is used as-is. This is how UI-level settings (like an explicit output-folder override) bypass the taxonomy while still participating in the same situation macro. ## What's in the defaults The system defaults ship with entries for common image, video, audio, text, and Python source extensions, routing each to `images`, `videos`, `audio`, `text`, and `python` subfolders respectively. You can override individual entries, add new ones, or tombstone any you don't want. # Customization Guide All customizations go in `griptape-nodes-project.yml` in your workspace directory. You only need to include the things you want to change — everything else inherits from the system defaults. ## Changing the outputs directory path Move all node outputs to a folder called `renders`: ``` project_template_schema_version: "0.1.0" name: "My Project" directories: outputs: path_macro: "renders" ``` All situations that use `{outputs}` will now save to the `renders` folder instead of `outputs`. ## Changing the inputs directory path ``` project_template_schema_version: "0.1.0" name: "My Project" directories: inputs: path_macro: "source_files" ``` ## Adding a custom directory Add a directory for client deliverables: ``` project_template_schema_version: "0.1.0" name: "My Project" directories: deliverables: path_macro: "client/final" ``` Now `{deliverables}` is available as a variable in any macro. ## Adding a custom situation Add a situation that saves final renders to the deliverables directory with overwrite policy: ``` project_template_schema_version: "0.1.0" name: "My Project" directories: deliverables: path_macro: "client/final" situations: save_deliverable: macro: "{deliverables}/{workflow_name?:_}{file_name_base}.{file_extension}" policy: on_collision: overwrite create_dirs: true description: "Final deliverable for client" ``` ## Modifying an existing situation Change `save_node_output` to always include the workflow name in the filename: ``` project_template_schema_version: "0.1.0" name: "My Project" situations: save_node_output: macro: "{outputs}/{workflow_name:_}{file_name_base}{_index?:03}.{file_extension}" ``` Only the `macro` field is replaced. The policy, fallback, and description are inherited from the default. ## Adding environment variables Define custom values for use in macros: ``` project_template_schema_version: "0.1.0" name: "My Project" environment: CLIENT_CODE: "ACME" SEASON: "S03" ``` Reference them in a directory path or situation macro: ``` directories: outputs: path_macro: "{CLIENT_CODE}/{SEASON}/renders" ``` ## Routing files by extension Use `file_extension_directories` to land files of different types in different subfolders without writing a separate situation for each type. See [File Extension Directories](https://docs.griptapenodes.com/projects/file_extension_directories/index.md) for the full reference. ``` project_template_schema_version: "0.3.0" name: "My Project" file_extension_directories: png: "images" jpg: "images" mp4: "videos" wav: "audio" situations: save_node_output: macro: "{outputs}/{file_extension_directory?:/}{node_name?:_}{file_name_base}{_index?:03}.{file_extension}" ``` Extensions can also resolve to a macro. To route videos to a share drive while everything else stays under `outputs`, use the situation macro that lets the routing value dictate the root: ``` project_template_schema_version: "0.3.0" name: "My Project" file_extension_directories: png: "{outputs}/images" mp4: "{workspace_dir}/shared/videos" situations: save_node_output: macro: "{file_extension_directory?:/}{node_name?:_}{file_name_base}{_index?:03}.{file_extension}" ``` ## Referencing OS environment variables Pull values from the operating system environment: ``` project_template_schema_version: "0.1.0" name: "My Project" environment: SHARED_DRIVE: "$STUDIO_SHARED_STORAGE" directories: outputs: path_macro: "{SHARED_DRIVE}/renders" ``` If the OS environment variable `STUDIO_SHARED_STORAGE` is set to `/mnt/studio`, then `{outputs}` resolves to `/mnt/studio/renders`. ## Putting it all together A complete customized project file for a visual effects project: ``` project_template_schema_version: "0.1.0" name: "VFX Pipeline" description: "Customized layout for VFX production" environment: SHOW_CODE: "AURORA" directories: inputs: path_macro: "source" outputs: path_macro: "renders" plates: path_macro: "source/plates" deliverables: path_macro: "deliverables/{SHOW_CODE}" situations: save_node_output: macro: "{outputs}/{workflow_name?:_}{file_name_base}{_index?:03}.{file_extension}" save_deliverable: macro: "{deliverables}/{workflow_name?:_}{file_name_base}.{file_extension}" policy: on_collision: overwrite create_dirs: true fallback: save_file description: "Final deliverable" ``` # Custom nodes and scripts # Developing Nodes This section provides comprehensive documentation for developers building custom nodes for Griptape Nodes. For AI Assistants & Coding Agents All documentation in this section is available as post-processed markdown for AI coding assistants. The site exposes a full machine-readable surface; see [For Agents](https://docs.griptapenodes.com/for_agents/index.md) for the index. - **Getting Started**: [Markdown](https://docs.griptapenodes.com/en/stable/developing_nodes/getting_started/index.md) - **Comprehensive Guide**: [Markdown](https://docs.griptapenodes.com/en/stable/developing_nodes/comprehensive_guide/index.md) - **Example Code**: [View Python Example](https://raw.githubusercontent.com/griptape-ai/griptape-nodes/main/docs/developing_nodes/example_control_node.py) **Usage:** Point your AI assistant to these URLs with instructions like: `"Read this node development guide: [URL] and help me build a custom node"` ## Getting Started If you're new to developing nodes, start with the [Getting Started Guide](https://docs.griptapenodes.com/developing_nodes/getting_started/index.md). This guide provides a beginner-friendly introduction to the node development ecosystem and walks you through building your first node. ## Comprehensive Reference For detailed technical information, see the [Comprehensive Node Development Guide](https://docs.griptapenodes.com/developing_nodes/comprehensive_guide/index.md). This exhaustive reference covers: - Node base classes (`DataNode`, `ControlNode`, `StartNode`, `EndNode`, etc.) - Parameters, traits, containers, and lifecycle callbacks - Async patterns (`AsyncResult`) - Advanced UI/UX and error-handling guidance - Creating and distributing node libraries - Custom widget components - Production best practices ## Practical Examples - [Example Control Node](https://docs.griptapenodes.com/developing_nodes/example_control_node.py) - A complete working example demonstrating best practices for building control nodes ## Quick Links - **Quick Start**: [Making Custom Nodes](https://docs.griptapenodes.com/how_to/making_custom_nodes/index.md) - Template for rapid node creation - **Custom Scripts**: [Making Custom Scripts](https://docs.griptapenodes.com/how_to/making_custom_scripts/index.md) - Build custom node execution scripts ## Documentation Structure 1. **[Getting Started](https://docs.griptapenodes.com/developing_nodes/getting_started/index.md)** - Your first node and essential concepts 1. **[Comprehensive Guide](https://docs.griptapenodes.com/developing_nodes/comprehensive_guide/index.md)** - Complete technical reference 1. **[Example Code](https://docs.griptapenodes.com/developing_nodes/example_control_node.py)** - Practical implementation patterns # Getting Started with Node Development For AI Assistants & Coding Agents This guide is available as post-processed markdown for AI coding assistants. The site exposes a full machine-readable surface; see [For Agents](https://docs.griptapenodes.com/for_agents/index.md) for the index. - **Getting Started** (this page): [Markdown](https://docs.griptapenodes.com/en/stable/developing_nodes/getting_started/index.md) - **Comprehensive Guide**: [Markdown](https://docs.griptapenodes.com/en/stable/developing_nodes/comprehensive_guide/index.md) - **Example Code**: [View Python Example](https://raw.githubusercontent.com/griptape-ai/griptape-nodes/main/docs/developing_nodes/example_control_node.py) **Usage:** Point your AI assistant to these URLs with instructions like: `"Read this node development guide: [URL] and help me build a custom node"` This page is for developers who are **new to the Griptape Nodes ecosystem** and want to build custom nodes with confidence. It’s a beginner-friendly “front door” to the deeper, exhaustive technical material in the [Comprehensive Node Development Guide](https://docs.griptapenodes.com/developing_nodes/comprehensive_guide/index.md). ## What you’ll build (mentally) before you write code At a high level: - A **Node** is a Python class that defines **parameters** (inputs/outputs/properties) and a `process()` method. - A **Workflow (Flow)** is a graph of nodes connected by parameters. - Parameters are both: - **UI elements** (what a user sees/edits/connects), and - **type-checked connection points** (what can connect to what). ### Choose the right base node type - **`DataNode`**: use when your node processes data and doesn’t need to branch execution. - **`ControlNode`**: use when your node needs explicit execution flow (control in/out). - **`SuccessFailureNode`**: use when you want separate control-flow outputs for success vs failure. - **Iterative loop nodes**: the engine’s loop primitives are built on `BaseIterativeStartNode` / `BaseIterativeEndNode`. If you’re unsure, start with a `DataNode` and only graduate to `ControlNode`/loop nodes when you need it. ## Quick start (recommended path) If you’ve never built a Griptape Node before, this is the fastest path to a working node: - Start from the template: [Making Custom Nodes](https://docs.griptapenodes.com/how_to/making_custom_nodes/index.md) - Build a single `DataNode` first (no control flow). - Prefer the built-in `Parameter*` helper constructs for common types. - Validate inputs with `validate_before_node_run()`. - Add secrets through `GriptapeNodes.SecretsManager()` when needed. ## Your first node (minimal example) This is the smallest useful node you can build: read a string, transform it, emit a string. ``` from griptape_nodes.exe_types.core_types import Parameter, ParameterMode from griptape_nodes.exe_types.node_types import DataNode class UppercaseText(DataNode): def __init__(self, **kwargs) -> None: # Always call the parent constructor so the engine can initialize # the node’s internal state and register the node context. super().__init__(**kwargs) # add_parameter(...) registers a Parameter with the node. # Parameters define: # - what users can configure (PROPERTY mode), # - what can be connected from other nodes (INPUT mode), # - what can be connected to other nodes (OUTPUT mode). self.add_parameter( Parameter( name="text", # A parameter's "type" is its primary data type in the engine. # It influences UI defaults and connection type checking. type="str", # input_types controls what types can connect INTO this parameter. # You can allow multiple incoming types if you need flexible wiring. input_types=["str"], # default_value is used when nothing is connected and the user # hasn't set a value in the UI. default_value="Hello Griptape Nodes", allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY}, tooltip="Input text", ) ) self.add_parameter( Parameter( name="uppercased", type="str", output_type="str", allowed_modes={ParameterMode.OUTPUT}, tooltip="Uppercased output", ) ) def process(self) -> None: # process() is called when the node executes in a flow. # Read inputs via get_parameter_value(...) and write outputs via parameter_output_values. text = self.get_parameter_value("text") or "" self.parameter_output_values["uppercased"] = text.upper() ``` ### Test as you go - After adding/editing a node, run it in a simple flow and verify: - the parameter UI looks correct (inputs, properties, outputs) - output values update in the UI - validation errors are actionable ## Parameters: the practical model Every parameter can be used in three “modes”: - **Input**: accepts a connection from another node - **Output**: provides a connection to another node - **Property**: user-configurable value in the node UI ### Use `Parameter*` helpers for common cases The core engine ships parameter helper constructs under `griptape_nodes.exe_types.param_types.*` such as: - `ParameterString`, `ParameterInt`, `ParameterFloat`, `ParameterBool` - `ParameterJson`, `ParameterDict`, `ParameterRange` - `ParameterImage`, `ParameterAudio`, `ParameterVideo`, `Parameter3D` - `ParameterButton` These helpers are useful because they: - hard-set the intended `type` / `output_type` and common `ui_options` - often support `accept_any=True` to convert values safely - expose several UI options as Python properties for runtime updates If you need a quick reference, see the **Parameter helper constructs** section in the [Comprehensive Node Development Guide](https://docs.griptapenodes.com/developing_nodes/comprehensive_guide/index.md). ### Containers: `ParameterList` and `ParameterDictionary` - **`ParameterList`**: use when you want “many of the same thing” in a node UI. - Retrieval: `get_parameter_list_value()` flattens nested iterables. - Note: the current implementation drops falsey items (e.g. `0`, `False`). Preserve those by using `get_parameter_value()` and flattening manually. - **`ParameterDictionary`**: use when you want ordered key/value entries in the UI. ### Traits: UI behaviors and validation Traits are attached to parameters to add UI and behavior. Common traits you’ll use: - `Options(...)`: dropdowns (choices are stored in `ui_options` for serialization stability) - `Slider(min_val, max_val)`: slider UI + range validation - `FileSystemPicker(...)`: file/directory picking UI (with filters and workspace constraints) Example: a numeric slider parameter: ``` from griptape_nodes.exe_types.core_types import Parameter, ParameterMode from griptape_nodes.traits.slider import Slider self.add_parameter( Parameter( name="temperature", type="float", default_value=0.7, tooltip="Sampling temperature (higher = more random)", # This can be a pure PROPERTY, or INPUT+PROPERTY if you want to allow wiring. allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY}, # Common pattern in the codebase: attach traits inline via the `traits` argument. traits={Slider(min_val=0.0, max_val=2.0)}, ) ) ``` ## Validation, error handling, and user experience For newcomers, a good default is: - Use `validate_before_node_run()` for parameter validation - Fail early with actionable messages (tell the user what to connect or set) - If the node can fail but you want the workflow to continue, use `SuccessFailureNode` and route failure explicitly Examples you can reference in the docs: - [Start Flow](https://docs.griptapenodes.com/nodes/execution/start_flow/index.md) and [End Flow](https://docs.griptapenodes.com/nodes/execution/end_flow/index.md) for control-flow concepts and status reporting. ## Common gotchas - **`get_parameter_list_value()` drops falsey items**: if your list can contain `0` or `False`, use `get_parameter_value()` and flatten manually. - **`ui_options` conflicts**: if you pass both `hide=...` and `ui_options={"hide": ...}`, the `ui_options` value wins. - **Secrets**: do not hardcode API keys. Use `GriptapeNodes.SecretsManager().get_secret(...)`. ## Secrets and configuration When a node needs an API key or other secret: - Register secrets in the library configuration (`griptape_nodes_library.json`) - Read secrets via `GriptapeNodes.SecretsManager().get_secret("NAME")` ## Where to look for real examples - **Standard library nodes**: `libraries/griptape_nodes_library/griptape_nodes_library/` - **Engine internals** (advanced): `src/griptape_nodes/` ## Next steps - Read the deeper technical guide: [Comprehensive Node Development Guide](https://docs.griptapenodes.com/developing_nodes/comprehensive_guide/index.md) - Browse a few nodes in the standard library and copy patterns that match your use case # Comprehensive Node Development Guide For AI Assistants & Coding Agents This guide is available as post-processed markdown for AI coding assistants. The site exposes a full machine-readable surface; see [For Agents](https://docs.griptapenodes.com/for_agents/index.md) for the index. - **Comprehensive Guide** (this page): [Markdown](https://docs.griptapenodes.com/en/stable/developing_nodes/comprehensive_guide/index.md) - **Getting Started**: [Markdown](https://docs.griptapenodes.com/en/stable/developing_nodes/getting_started/index.md) - **Example Code**: [View Python Example](https://raw.githubusercontent.com/griptape-ai/griptape-nodes/main/docs/developing_nodes/example_control_node.py) **Usage:** Point your AI assistant to these URLs with instructions like: `"Read this node development guide: [URL] and help me build a custom node"` ## Table of Contents 1. [Introduction](#introduction) 1. [Core Concepts](#core-concepts) 1. [Setting Up](#setting-up) 1. [Creating a Node](#creating-a-node) 1. [Parameters](#parameters) 1. [Advanced Parameter Patterns](#advanced-parameter-patterns) 1. [Lifecycle Callbacks](#lifecycle-callbacks) 1. [Best Practices](#best-practices) 1. [Working with the Project System](#working-with-the-project-system) 1. [Advanced Topics](#advanced-topics) 1. [Modern UI/UX Patterns](#modern-uiux-patterns) 1. [Production Error Handling](#production-error-handling) 1. [Logging Best Practices](#logging-best-practices) 1. [Flexible Artifact Processing](#flexible-artifact-processing) 1. [Creating Node Libraries](#creating-node-libraries) 1. [Custom Widget Components](#custom-widget-components) 1. [Library Structure with uv Dependency Management](#library-structure-with-uv-dependency-management) 1. [Two-Mode UI Pattern (Simple + Custom)](#two-mode-ui-pattern-simple-custom) 1. [Music/Audio Generation API Patterns](#musicaudio-generation-api-patterns) 1. [Documentation Patterns for Node Libraries](#documentation-patterns-for-node-libraries) 1. [Contributing to the Standard Library](#contributing-to-the-standard-library) 1. [Appendix](#appendix) ## Introduction Griptape Nodes are modular workflow components that enable users to build complex AI workflows through visual programming. This guide covers both fundamental concepts and advanced patterns for creating robust, user-friendly nodes. Nodes inherit from BaseNode subclasses: - **DataNode**: For data processing tasks - **ControlNode**: For flow control with exec_in/out - **StartNode**: For workflow initialization - **EndNode**: For workflow termination ## Core Concepts ### Base Classes - **DataNode**: Processes data without execution flow control. Use for nodes that transform or pass through data synchronously — they process immediately when their inputs are satisfied. - **ControlNode**: Manages execution flow with exec_in/exec_out connections. Use for nodes that make external API calls, perform long-running operations, or need `AsyncResult` to yield work to a background thread. If your node calls an API and polls for results, it must be a ControlNode. - **StartNode**: Entry points for workflows - **EndNode**: Terminal points for workflows ### Parameters Define inputs, outputs, and properties via the Parameter class. Parameters support: - Type validation - UI customization - Connection constraints - Default values - Traits (Options, Slider, Button, ColorPicker) ### Process Method The `process()` method contains core logic. Set outputs in `self.parameter_output_values`. ### Node States - **UNRESOLVED**: Initial state - **RESOLVING**: Currently processing - **RESOLVED**: Processing complete ### Connections Managed via lifecycle callbacks for validation and handling. ### Events Use `on_griptape_event` for reacting to workflow events. ## Setting Up 1. Install griptape-nodes 1. Use virtual environments for isolation 1. Structure projects with simple folder hierarchies 1. Import from `griptape_nodes.exe_types.*` and `griptape_nodes_library.utils.*` ## Creating a Node ### Basic Node Structure ``` from typing import Any from griptape_nodes.exe_types.core_types import Parameter, ParameterMode from griptape_nodes.exe_types.node_types import DataNode class MyNode(DataNode): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self.category = "Category" self.description = "Description" self.add_parameter(Parameter( name="input", input_types=["str"], type="str", tooltip="Input parameter" )) self.add_parameter(Parameter( name="output", output_type="str", tooltip="Output parameter" )) def process(self) -> None: val = self.get_parameter_value("input").upper() self.parameter_output_values["output"] = val ``` ## Parameters ### Parameter Attributes All Parameter attributes: - **name**: str, unique identifier, no whitespace - **tooltip**: str or list[dict] for UI help text - **default_value**: Any default value - **type**: str (e.g., "str", "list[str]", ParameterTypeBuiltin.STR.value) - **input_types**: list[str] for incoming connection types - **output_type**: str for outgoing connection type - **allowed_modes**: set[ParameterMode] {INPUT, OUTPUT, PROPERTY} - **ui_options**: dict for UI customization - **converters**: list\[Callable\[[Any], Any\]\] for value transformation - **validators**: list\[Callable\[[Parameter, Any], None\]\] for validation - **hide/hide_label/hide_property**: common UI flags (also available via `ui_options`; `ui_options` wins on conflict) - **allow_input/allow_property/allow_output**: convenience flags for configuring modes (ignored if `allowed_modes` is explicitly set) - **settable**: bool (default True) - False for computed/output parameters - **serializable**: bool (default True) - set False for non-serializable values (drivers, file handles, etc.) - **user_defined**: bool (default False) - **private**: bool (default False) - hide from general user editing (library/internal use) - **parent_container_name**: str|None — assigns this parameter as a child of a `ParameterContainer` (i.e. a `ParameterList` or `ParameterDictionary`). Used for list-like ownership. - **parent_element_name**: str|None — nests this parameter under a `ParameterGroup` (a UI grouping element). Used for visual grouping in the node UI. ### Traits Add functionality via `add_trait()`: - **Options**: `Options(choices=list[str] | list[tuple[str, Any]], show_search: bool = True, search_filter: str = "")` - **Slider**: `Slider(min_val: float, max_val: float)` - **Button**: `Button(label: str = "", variant=..., size=..., button_link=... | on_click=..., get_button_state=...)` - **ColorPicker**: `ColorPicker(format="hex")` - **FileSystemPicker**: `FileSystemPicker(...)` (file/directory selection UI) ### Parameter helper constructs (`ParameterString`, `ParameterInt`, ...) Griptape Nodes includes a set of convenience Parameter subclasses under `griptape_nodes.exe_types.param_types.*`. They exist to make common parameter patterns **simple, consistent, and runtime-mutable** (many expose UI options as Python properties). #### Quick reference table | Helper | Enforced `type` / `output_type` | Default `input_types` behavior | Key UI convenience args | Notes | | ----------------- | --------------------------------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------- | | `ParameterString` | `"str"` / `"str"` | `accept_any=True` → `["any"]` with converter to `str` | `markdown`, `multiline`, `placeholder_text`, `is_full_width` | `type`, `output_type`, and `input_types` constructor args are ignored | | `ParameterBool` | `"bool"` / `"bool"` | `accept_any=True` → `["any"]` with converter to `bool` | `on_label`, `off_label` | Converts common strings like `"true"/"false"`, `"yes"/"no"` | | `ParameterInt` | `"int"` / `"int"` | `accept_any=True` → `["any"]` with converter to `int` | `step`, `slider`, `min_val`, `max_val`, `validate_min_max` | Adds constraint traits (Clamp/MinMax/Slider) based on args | | `ParameterFloat` | `"float"` / `"float"` | `accept_any=True` → `["any"]` with converter to `float` | `step`, `slider`, `min_val`, `max_val`, `validate_min_max` | Adds constraint traits (Clamp/MinMax/Slider) based on args | | `ParameterDict` | `"dict"` / `"dict"` | `accept_any=True` → `["any"]` with converter to `dict` | (none) | Uses `griptape_nodes.utils.dict_utils.to_dict()` for conversion | | `ParameterJson` | `"json"` / `"json"` | `accept_any=True` → `["any"]` with converter to JSON | `button`, `button_label`, `button_icon` | Uses `json_repair.repair_json()` for robust string → JSON | | `ParameterRange` | `"list"` / `"list"` | `accept_any=True` → `["any"]` with converter to `list` | `range_slider` + `min_val/max_val/step`, labels | Range slider is only meaningful when the value is a 2-number list | | `ParameterImage` | `"ImageUrlArtifact"` / `"ImageUrlArtifact"` | `accept_any=True` → `["any"]` (no conversion) | `clickable_file_browser`, `webcam_capture_image`, `edit_mask`, `pulse_on_run` | Mostly UI convenience; add converters if you need type coercion | | `ParameterAudio` | `"AudioUrlArtifact"` / `"AudioUrlArtifact"` | `accept_any=True` → `["any"]` (no conversion) | `clickable_file_browser`, `microphone_capture_audio`, `edit_audio`, `pulse_on_run` | Mostly UI convenience; add converters if you need type coercion | | `ParameterVideo` | `"VideoUrlArtifact"` / `"VideoUrlArtifact"` | `accept_any=True` → `["any"]` (no conversion) | `clickable_file_browser`, `webcam_capture_video`, `edit_video`, `pulse_on_run` | Mostly UI convenience; add converters if you need type coercion | | `Parameter3D` | `"ThreeDUrlArtifact"` / `"ThreeDUrlArtifact"` | `accept_any=True` → `["any"]` (no conversion) | `clickable_file_browser`, `expander`, `pulse_on_run` | Mostly UI convenience; add converters if you need type coercion | | `ParameterButton` | `"button"` / `"str"` | `["str", "any"]` | `label`, `variant`, `size`, `icon`, `state`, `href` / `on_click` | **Label is display text; `default_value` is stored value** | #### Shared behavior across helpers - All helpers forward the standard `Parameter` constructor knobs (`allowed_modes` or `allow_input/allow_property/allow_output`, `hide/hide_label/hide_property`, `settable`, `serializable`, etc.). - Many helpers default `accept_any=True`. When enabled, the helper typically sets `input_types=["any"]` and prepends a converter (e.g. `ParameterString` converts any input to `str`). Turn this off if you want strict typing. - If you provide both an explicit convenience parameter (e.g. `hide=True`) and the same key in `ui_options` (e.g. `ui_options={"hide": False}`), **`ui_options` wins** and Griptape Nodes will warn about the conflict. #### Detailed helper notes ##### `ParameterString` - Enforces `type="str"` and `output_type="str"`. - `accept_any=True` converts `None` → `""` and otherwise uses `str(value)`. - UI convenience: `markdown`, `multiline`, `placeholder_text`, `is_full_width` (all are runtime-settable properties). ##### `ParameterBool` - Enforces `type="bool"` and `output_type="bool"`. - `accept_any=True` converts common string representations (e.g. `"true"`, `"yes"`, `"on"`, `"1"`) to `True` and (`"false"`, `"no"`, `"off"`, `"0"`) to `False`. - UI convenience: `on_label`, `off_label` (runtime-settable properties). ##### `ParameterInt` / `ParameterFloat` (via `ParameterNumber`) - Enforces numeric `type` / `output_type` and can prepend a converter when `accept_any=True`. - `step`: stored in `ui_options["step"]` and validated (value must be a multiple of the current step). - `slider`, `min_val`, `max_val`, `validate_min_max`: adds one of these constraint traits based on priority: - `Slider(min_val, max_val)` if `slider=True` - `MinMax(min_val, max_val)` if `validate_min_max=True` - `Clamp(min_val, max_val)` if `min_val` and `max_val` are provided ##### `ParameterJson` - Enforces `type="json"` and `output_type="json"`. - `accept_any=True` attempts to repair/parse JSON strings using `json_repair.repair_json()` (and will also attempt to stringify non-string inputs). - UI convenience: optional editor button (`button`, `button_label`, `button_icon`). ##### `ParameterDict` - Enforces `type="dict"` and `output_type="dict"`. - `accept_any=True` uses `to_dict(...)` to coerce common inputs into a dict. ##### `ParameterRange` - Enforces `type="list"` and `output_type="list"`. - `accept_any=True` coerces `None` → `[]`, list → list, and any other value → `[value]`. - UI convenience: `range_slider` (a nested `ui_options["range_slider"]` object) with `min_val/max_val/step` and label visibility options. - The range slider UI is only applicable when the value is a list of exactly two numeric values. ##### `ParameterImage` (Recommended for Image Parameters) **Always use `ParameterImage` instead of generic `Parameter` for image inputs/outputs.** It provides: - Automatic `type="ImageUrlArtifact"` and `output_type="ImageUrlArtifact"` - Built-in UI options for file browser, webcam capture, and mask editing - Consistent behavior across all image-handling nodes **Basic Usage:** ``` from griptape_nodes.exe_types.param_types.parameter_image import ParameterImage # Input image parameter self.add_parameter( ParameterImage( name="input_image", tooltip="Input image for processing", allow_output=False, # Input only ) ) # Output image parameter self.add_parameter( ParameterImage( name="output_image", tooltip="Generated image result", allow_input=False, # Output only allow_property=False, ) ) ``` **Available UI Options:** - `clickable_file_browser`: Enable file browser for image selection - `webcam_capture_image`: Enable webcam capture - `edit_mask`: Enable mask editing overlay - `pulse_on_run`: Visual feedback when image updates **Dynamic Visibility Pattern:** For parameters that should only appear for certain model types: ``` def __init__(self, **kwargs) -> None: super().__init__(**kwargs) # Add image parameter (hidden by default) self.add_parameter( ParameterImage( name="input_image", tooltip="Input image for image-to-image generation", allow_output=False, ) ) # Initialize visibility based on default model self._initialize_parameter_visibility() def _initialize_parameter_visibility(self) -> None: """Initialize parameter visibility based on default model.""" model = self.get_parameter_value("model") or "default" if model in ["model-with-image-support", "another-model"]: self.show_parameter_by_name("input_image") else: self.hide_parameter_by_name("input_image") def after_value_set(self, parameter: Parameter, value: Any) -> None: """Update visibility when model changes.""" if parameter.name == "model": if value in ["model-with-image-support", "another-model"]: self.show_parameter_by_name("input_image") else: self.hide_parameter_by_name("input_image") self.set_parameter_value("input_image", None) # Clear when hiding return super().after_value_set(parameter, value) ``` **Why Use `ParameterImage` Over Generic `Parameter`:** | Aspect | Generic `Parameter` | `ParameterImage` | | ----------- | --------------------------------- | ------------------------------------------- | | Type safety | Manual `type`/`input_types` setup | Automatic artifact types | | UI features | Manual `ui_options` configuration | Built-in file browser, webcam, mask editing | | Consistency | Varies by implementation | Standardized across nodes | | Maintenance | More boilerplate code | Less code, cleaner | **Legacy Pattern (Avoid):** ``` # ❌ Don't do this - use ParameterImage instead self.add_parameter( Parameter( name="input_image", input_types=["ImageArtifact", "ImageUrlArtifact", "str"], type="ImageArtifact", default_value=None, tooltip="Input image", allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY}, ui_options={"display_name": "Input Image"}, ) ) ``` **Why `ParameterImage` is better:** - **Standardized type conversion logic:** Handles ImageArtifact, ImageUrlArtifact, and string inputs consistently - **Built-in UI features:** File browser, webcam capture, mask editing - **Less boilerplate:** Automatically configures types and options - **Robust error handling:** Gracefully handles various input formats (URLs, file paths, data URIs) **Recommended Pattern:** ``` # ✅ Use ParameterImage for cleaner, more maintainable code self.add_parameter( ParameterImage( name="input_image", tooltip="Input image", allow_output=False, ) ) ``` `ParameterImage` standardizes how your node handles different image input types, reducing conversion errors and improving reliability. ##### `ParameterAudio` / `ParameterVideo` / `Parameter3D` - Enforce their corresponding `*UrlArtifact` `type` / `output_type` (e.g., `AudioUrlArtifact`, `VideoUrlArtifact`, `ThreeDUrlArtifact`). - These helpers primarily provide UI options (file browser / capture / editing / expanders). If you need coercion from e.g. `str` → artifact, supply `converters` and/or handle it in your node's `before_value_set()` / `process()` logic. - Follow the same patterns as `ParameterImage` for these media types. ##### `ParameterButton` Buttons provide interactive UI elements that trigger actions when clicked, such as updating parameters, performing calculations, or navigating between states. **Basic Properties:** - Enforces `type="button"` and `output_type="str"` - By default, it's a **property-only** UI element (`allow_property=True`, `allow_input=False`, `allow_output=False`) - Accepts either `href="..."` (simple link) or `on_click=...` (custom callback) parameters - `label` is the display text shown on the button - `icon` adds a visual icon (optional) - `icon_position` controls icon placement ("left" or "right", defaults to "left") **Important:** The `on_click` handler and `href` are passed **directly to `ParameterButton`** as parameters, not via the `Button` trait. **Implementation Pattern:** Buttons must be wrapped in a `ParameterButtonGroup` container: ``` from griptape_nodes.exe_types.core_types import ParameterButtonGroup from griptape_nodes.exe_types.param_types.parameter_button import ParameterButton from griptape_nodes.traits.button import Button, ButtonDetailsMessagePayload class MyNode(DataNode): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) # Create button group with context manager with ParameterButtonGroup(name="my_button_group") as button_group: ParameterButton( name="update_button", label="Update Date/Time", icon="calendar", on_click=self._handle_button_click, # Pass on_click directly ) self.add_node_element(button_group) # Add parameter that will be updated by button self.add_parameter( Parameter( name="display_value", tooltip="Value updated by button", type=ParameterTypeBuiltin.STR.value, allowed_modes={ParameterMode.PROPERTY}, ui_options={ "display_name": "Display Value", "readonly": True, # Prevent manual editing }, default_value="Click button to update", ) ) def _handle_button_click( self, button: Button, button_payload: ButtonDetailsMessagePayload, ) -> None: """Button click handler. Args: button: The Button trait instance button_payload: Contains click event details """ # Update parameter value new_value = "Updated at " + datetime.now().strftime("%H:%M:%S") self.set_parameter_value("display_value", new_value) ``` **Multiple Buttons in a Group:** ``` with ParameterButtonGroup(name="navigation_buttons") as nav_buttons: ParameterButton( name="previous", label="Previous", icon="arrow-left", on_click=self._previous_item, # Pass on_click directly ) ParameterButton( name="next", label="Next", icon="arrow-right", icon_position="right", on_click=self._next_item, # Pass on_click directly ) self.add_node_element(nav_buttons) ``` **Common Use Cases:** 1. **Update Display Values** ``` def _update_datetime(self, button: Button, button_payload: ButtonDetailsMessagePayload) -> None: """Update datetime display when button is clicked.""" current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") self.set_parameter_value("datetime_display", current_time) ``` 1. **Navigate Through Items** ``` def _next_image(self, button: Button, button_payload: ButtonDetailsMessagePayload) -> None: """Increment index and update display.""" current_index = self.get_parameter_value("image_index") self.set_parameter_value("image_index", current_index + 1) self._update_display() ``` 1. **Trigger Calculations** ``` def _calculate(self, button: Button, button_payload: ButtonDetailsMessagePayload) -> None: """Perform calculation and update result parameter.""" input_value = self.get_parameter_value("input") result = self._perform_complex_calculation(input_value) self.set_parameter_value("result", result) ``` 1. **Reset to Defaults** ``` def _reset(self, button: Button, button_payload: ButtonDetailsMessagePayload) -> None: """Reset parameters to default values.""" self.set_parameter_value("counter", 0) self.set_parameter_value("display", "") ``` **Link Buttons (Alternative to `on_click`):** For simple navigation to external URLs: ``` ParameterButton( name="docs_link", label="View Documentation", icon="external-link", href="https://docs.griptape.ai", # Pass href directly ) ``` **Best Practices:** - Use descriptive button labels that clearly indicate the action - Choose appropriate icons that match the action (e.g., "calendar" for date/time, "arrow-left"/"arrow-right" for navigation) - Keep button handlers simple and focused on a single action - Use read-only parameters for values that should only be updated by buttons - Avoid triggering expensive operations directly in button handlers (consider using flags that `process()` checks instead) - Group related buttons together in a single `ParameterButtonGroup` **Common Patterns:** | Pattern | Button Action | Updated Parameter Type | Use Case | | -------------- | ---------------------------------- | --------------------------- | -------------------------------- | | Update Display | Updates a read-only text parameter | `PROPERTY` (readonly) | Show current time, status, count | | Navigation | Increments/decrements an index | Hidden `PROPERTY` parameter | Image carousel, list browsing | | Toggle State | Switches between states | `PROPERTY` parameter | Enable/disable features | | Trigger Action | Sets a flag checked by `process()` | Hidden `PROPERTY` parameter | Refresh data, recalculate | **Complete Example:** See `example_control_node.py` and `image_carousel.py` for working implementations that demonstrate: - Button creation with icons - Button group usage - Updating read-only parameters - Handler method signatures - Navigation patterns - Locale-appropriate datetime formatting ### Containers - **ParameterList**: A container parameter that owns multiple child `Parameter` items (use `get_parameter_list_value()` to flatten values) - **ParameterDictionary**: A container parameter that owns ordered key/value pairs (distinct from `ParameterDict`, which is a `dict`-typed value parameter helper) - **ParameterGroup**: For UI grouping **Container semantics (important):** - Container parameters are represented as `ParameterContainer` objects in the engine. They are **always truthy**, even when empty (they override `__bool__()` to avoid bugs with stale cached values). - `ParameterList` supports several UI convenience options (e.g. `collapsed`, grid display, and column count) that are merged into `ui_options` at runtime. - `ParameterDictionary` is an ordered collection of key/value pair children (internally represented as a list to preserve order). **`parent_container_name` vs `parent_element_name` — critical distinction:** Parameters have two separate parent-pointer attributes that serve different purposes: | Attribute | Points to | Purpose | | ----------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `parent_container_name` | `ParameterContainer` (`ParameterList`, `ParameterDictionary`) | **Ownership.** The parameter is a child of a list/dictionary container. The engine uses this for `add_parameter()`, child cleanup, value aggregation, and serialization/reload. | | `parent_element_name` | `ParameterGroup` | **UI grouping.** The parameter is visually nested under a collapsible group in the node UI. The engine uses this for `add_parameter()` placement, `_remove_existing_*()` lookups, and serialization/reload. | **Do NOT confuse them.** If you use `parent_container_name` when you should be using `parent_element_name` (or vice versa), the parameter will: 1. Appear at the node root instead of inside the intended group/container 1. Not be cleaned up between runs (e.g. stale outputs persist) 1. Fail to restore after save/reload — the reload handler looks for a `ParameterContainer` or `ParameterGroup` by the name you specified, and if the type doesn't match, the parameter is silently dropped before its saved values are applied **Rule of thumb:** - Putting a parameter inside a **`ParameterList`** or **`ParameterDictionary`**? → use `parent_container_name` - Putting a parameter inside a **`ParameterGroup`** for visual organization? → use `parent_element_name` ``` # ✅ CORRECT: Nesting under a ParameterGroup for UI grouping param = ParameterImage( name="cell_0_0", parent_element_name=self._grid_cells_group.name, # ParameterGroup ... ) # ❌ WRONG: Using parent_container_name for a ParameterGroup param = ParameterImage( name="cell_0_0", parent_container_name=self._grid_cells_group.name, # BUG: this is a ParameterGroup, not a ParameterContainer ... ) ``` ### ParameterList Pattern For parameters accepting multiple inputs of the same type: ``` self.add_parameter( ParameterList( name="tools", input_types=["Tool", "list[Tool]"], default_value=[], tooltip="Connect individual tools or a list of tools", allowed_modes={ParameterMode.INPUT}, ) ) # Retrieve in process method tools = self.get_parameter_list_value("tools") # Always returns a list for tool in tools: # Process each tool ``` **Benefits:** - Multiple connection points in UI - Automatic aggregation of inputs - Flexible workflow design - Follows Griptape design patterns **Important behavior note:** `get_parameter_list_value()` flattens nested iterables and **drops falsey items** (e.g. `0`, `False`, `""`, empty dicts/lists). If you need to preserve falsey values, use `get_parameter_value()` and handle flattening yourself. ### Common Parameter Patterns #### Search Input with Placeholder ``` Parameter( name="search_query", input_types=["str"], type="str", tooltip="Search term to find models", allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY}, ui_options={"placeholder_text": "e.g., llama, bert, stable-diffusion"} ) ``` #### Full-Width List Output ``` Parameter( name="results", output_type="list[dict]", type="list[dict]", tooltip="Search results with full information", allowed_modes={ParameterMode.OUTPUT}, ui_options={"is_full_width": True} ) ``` #### Multiline Text Input ``` Parameter( name="prompt", input_types=["str"], type="str", tooltip="Description of desired output", allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY}, ui_options={"multiline": True, "placeholder_text": "Describe what you want..."} ) ``` #### File Upload with Browser ``` Parameter( name="image", input_types=["ImageArtifact", "ImageUrlArtifact", "str"], type="ImageArtifact", tooltip="Input image file", allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY}, ui_options={"clickable_file_browser": True} ) ``` ## Advanced Parameter Patterns ### Dynamic Parameter Visibility Use `after_value_set()` callback to create context-aware UIs: ``` def after_value_set(self, parameter: Parameter, value: Any) -> None: """Update parameter visibility based on model selection.""" if parameter.name == "model": if value == "text-to-image": self.hide_parameter_by_name("input_image") self.show_parameter_by_name("prompt") elif value == "image-to-image": self.show_parameter_by_name("input_image") self.show_parameter_by_name("prompt") return super().after_value_set(parameter, value) ``` ### Dynamic Options Updates Update parameter choices at runtime: ``` from griptape_nodes.traits.options import Options def _update_option_choices(self, param_name: str, choices: list, default_value: str): """Update Options trait choices dynamically.""" param = self.get_parameter_by_name(param_name) if not param: return # Traits are stored as child elements on the Parameter # (most commonly, you'll be updating an Options trait) for trait in param.find_elements_by_type(Options): trait.choices = choices break self.set_parameter_value(param_name, default_value) ``` ### Advanced ParameterList Usage Include both individual and list types for maximum flexibility: ``` self.add_parameter( ParameterList( name="images", input_types=[ "ImageArtifact", "ImageUrlArtifact", "str", "list", "list[ImageArtifact]", "list[ImageUrlArtifact]", ], default_value=[], tooltip="Input images (up to 10 images total)", allowed_modes={ParameterMode.INPUT}, ui_options={"expander": True, "display_name": "Input Images"}, ) ) ``` ### Controlling Parameter Order in the UI Parameters appear in the UI in the order they are added via `add_parameter()`. This matters for user experience - related parameters should be grouped logically. **Problem**: Base classes like `BaseImageProcessor` automatically add parameters (e.g., `input_image`) in their `__init__`, which may not be the order you want. **Solution**: Extend `SuccessFailureNode` directly instead of `BaseImageProcessor` to gain full control over parameter ordering: ``` from griptape_nodes.exe_types.node_types import SuccessFailureNode from griptape_nodes.exe_types.param_types.parameter_image import ParameterImage class ColorMatch(SuccessFailureNode): """Transfer colors from a reference image to a target image.""" def __init__(self, name: str, metadata: dict[Any, Any] | None = None) -> None: super().__init__(name, metadata) # Reference image FIRST - the source of the color palette self.add_parameter( ParameterImage( name="reference_image", tooltip="Reference image - the source of the color palette to transfer", ui_options={"clickable_file_browser": True, "expander": True}, ) ) # Target image SECOND - the image to modify self.add_parameter( ParameterImage( name="target_image", tooltip="Target image - the image to apply the color transfer to", ui_options={"clickable_file_browser": True, "expander": True}, ) ) # Additional parameters in desired order... ``` **When to Use This Pattern**: - Two-image nodes where the semantic order matters (reference → target) - Nodes requiring specific parameter groupings not provided by base classes - When base class parameter order conflicts with your UX goals **Trade-off**: You lose helper methods from specialized base classes, but gain complete control over the node's UI structure. ### Two-Image Processing Node Pattern For nodes that process two images together (blending, color matching, compositing), use this pattern: ``` from typing import Any, ClassVar from PIL import Image from griptape.artifacts import ImageUrlArtifact from griptape_nodes.exe_types.core_types import Parameter from griptape_nodes.exe_types.node_types import SuccessFailureNode from griptape_nodes.exe_types.param_types.parameter_image import ParameterImage from griptape_nodes_library.utils.image_utils import ( dict_to_image_url_artifact, load_pil_from_url, save_pil_image_with_named_filename, ) from griptape_nodes_library.utils.file_utils import generate_filename class TwoImageProcessor(SuccessFailureNode): """Base pattern for nodes processing two images.""" CATEGORY: ClassVar[str] = "image" def __init__(self, name: str, metadata: dict[Any, Any] | None = None) -> None: super().__init__(name, metadata) # First image input self.add_parameter( ParameterImage( name="image_a", tooltip="First input image", ui_options={"clickable_file_browser": True, "expander": True}, ) ) # Second image input self.add_parameter( ParameterImage( name="image_b", tooltip="Second input image", ui_options={"clickable_file_browser": True, "expander": True}, ) ) # Output image self.add_parameter( ParameterImage( name="output_image", tooltip="Processed result", allowed_modes={ParameterMode.OUTPUT}, ) ) def _get_image_artifact(self, param_name: str) -> ImageUrlArtifact | None: """Convert parameter value to ImageUrlArtifact.""" value = self.get_parameter_value(param_name) if value is None: return None if isinstance(value, dict): return dict_to_image_url_artifact(value) return value def _process_images(self) -> None: """Process both images and set output.""" image_a_artifact = self._get_image_artifact("image_a") image_b_artifact = self._get_image_artifact("image_b") if not image_a_artifact or not image_b_artifact: return # Load as PIL images pil_a = load_pil_from_url(image_a_artifact.value) pil_b = load_pil_from_url(image_b_artifact.value) # Process images (override in subclass) result_pil = self._do_processing(pil_a, pil_b) # Save result filename = generate_filename(self.name, suffix="processed") output_artifact = save_pil_image_with_named_filename(result_pil, filename) self.parameter_output_values["output_image"] = output_artifact def _do_processing(self, image_a: Image.Image, image_b: Image.Image) -> Image.Image: """Override this method with actual processing logic.""" raise NotImplementedError def after_value_set(self, parameter: Parameter, value: Any) -> None: """Trigger live preview when both images are available.""" if parameter.name in ("image_a", "image_b"): image_a = self.get_parameter_value("image_a") image_b = self.get_parameter_value("image_b") if image_a and image_b: self._process_images() return super().after_value_set(parameter, value) def process(self) -> None: """Main processing entry point.""" self._process_images() ``` **Key Utilities Used**: - `dict_to_image_url_artifact()`: Converts dict representation to artifact - `load_pil_from_url()`: Loads PIL Image from URL (including localhost) - `save_pil_image_with_named_filename()`: Saves PIL Image and returns artifact - `generate_filename()`: Creates consistent filenames with node name ## Lifecycle Callbacks All callbacks are overridable: - **allow_incoming_connection**, **allow_outgoing_connection**: Return bool for connection validation - **after_incoming_connection**, **after_outgoing_connection**: Handle post-connection logic - **after_incoming_connection_removed**, **after_outgoing_connection_removed**: Handle disconnection - **before_value_set**: Return modified value before setting - **after_value_set**: React to parameter value changes - **validate_before_workflow_run**, **validate_before_node_run**: Return list[Exception]|None - **on_griptape_event**: Handle workflow events - **initialize_spotlight**: Setup spotlight functionality - **get_next_control_output**: Return Parameter|None for control flow ### Helper Methods - `hide_parameter_by_name()`, `show_parameter_by_name()` - `append_value_to_parameter()` - `publish_update_to_parameter()` - `show_message_by_name()`, `hide_message_by_name()`, `get_message_by_name_or_element_id()` ## Best Practices ### Core Principles - **Descriptive names and tooltips** - **Robust error handling with validators** - **Single responsibility per node** - **Use `SecretsManager` for API keys and secrets** - **Import all dependencies at module level** - **Idempotent process methods** ### Secrets Management Use `GriptapeNodes.SecretsManager()` to access API keys and secrets: ``` from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes class MyNode(DataNode): SERVICE_NAME = "MyService" API_KEY_NAME = "MY_SERVICE_API_KEY" def _validate_api_key(self) -> str: api_key = GriptapeNodes.SecretsManager().get_secret(self.API_KEY_NAME) if not api_key: raise ValueError(f"Missing {self.API_KEY_NAME}") return api_key ``` **Key Points:** - Import `GriptapeNodes` at module level, not inside functions - Use `SecretsManager().get_secret()` to retrieve secrets - Define `API_KEY_NAME` as a class constant for consistency - Always validate that the secret exists before using it ### Import Best Practices **Always import dependencies at module level, not inside functions:** ❌ **Bad** - Conditional/lazy imports: ``` def _get_image_data(self, image_artifact): try: from PIL import Image # Don't do this from io import BytesIO img = Image.open(BytesIO(image_bytes)) ``` ✅ **Good** - Module-level imports: ``` # At top of file from PIL import Image from io import BytesIO def _get_image_data(self, image_artifact): img = Image.open(BytesIO(image_bytes)) ``` **Why?** - Makes dependencies clear and visible - Avoids redundant imports throughout the file - Follows Python best practices (PEP 8) - Easier to catch missing dependencies early - Better IDE support and code completion **Exception**: Only use conditional imports for truly optional dependencies that may not be installed: ``` def process(self) -> None: try: from huggingface_hub import HfApi except ImportError: error_msg = "huggingface_hub library not installed" self.parameter_output_values["output"] = None raise ImportError(error_msg) ``` ### Import Organization Organize imports in standard order with blank lines between groups: ``` # Standard library imports import base64 import logging from typing import Any # Third-party imports import requests from PIL import Image # Local/Griptape imports from griptape_nodes.exe_types.core_types import Parameter, ParameterMode from griptape_nodes.exe_types.node_types import DataNode from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes ``` ### Type Checking for Third-Party Libraries When importing third-party libraries, you may encounter type checking errors. Use the appropriate `type: ignore` comment based on the situation: #### Scenario 1: Library Installed but Missing Type Stubs For libraries that are installed but lack type annotations (like `sklearn`, `ultralytics`, `supervision`): ``` # ✅ Library exists but has no type stubs from sklearn.cluster import KMeans # type: ignore[import-untyped] from ultralytics import YOLO # type: ignore[import-untyped] from supervision import Detections # type: ignore[import-untyped] ``` #### Scenario 2: Library Not Installed in CI Type Checking Environment For libraries that are runtime dependencies but not installed in the CI type checking environment (like `color-matcher`, specialized processing libraries): ``` # ✅ Library not installed in type checking environment from color_matcher import ColorMatcher # type: ignore[reportMissingImports] from color_matcher.normalizations import norm_img_to_uint8 # type: ignore[reportMissingImports] ``` #### When to Use Which | Error Type | Comment | Use When | | ---------------------- | -------------------------------------- | -------------------------------- | | `import-untyped` | `# type: ignore[import-untyped]` | Library installed, no type stubs | | `reportMissingImports` | `# type: ignore[reportMissingImports]` | Library not in CI environment | **General guidance:** - `import-untyped` is preferred when both work - it's more precise - `reportMissingImports` is necessary when the library isn't available during type checking - Check CI logs to determine which error you're actually getting ### Function Parameter Management Keep function argument counts low (under 6) by using dataclasses: ❌ **Bad** - Too many parameters: ``` def process_bbox(self, x: int, y: int, width: int, height: int, dilation_percent: float, img_width: int, img_height: int): # Process bounding box ``` ✅ **Good** - Use dataclass: ``` from dataclasses import dataclass @dataclass class BoundingBox: x: int y: int width: int height: int dilation_percent: float img_width: int img_height: int def process_bbox(self, bbox: BoundingBox): # Process bounding box using bbox.x, bbox.y, etc. ``` **Benefits:** - Improved readability - Type safety - Easier to maintain - Self-documenting code ### Code Quality **Additional linting best practices:** - Remove trailing whitespace from all lines (including blank lines) - Use consistent indentation (spaces only, no tabs) - Keep lines under 120 characters when possible - Use descriptive variable names - Avoid adding unnecessary Python packaging scaffolding. Create `__init__.py` files only when you actually want a package (or need them for your chosen packaging approach). #### Pre-commit checks (required) Before committing in `griptape-nodes`, run formatting and checks and fix any errors: ``` make format make check/lint make check/types ``` #### Node docs + navigation When adding a new node to the core library, also add node reference documentation: - Create a docs page at: `docs/nodes//.md` - Add it to `mkdocs.yml` under: `nav -> Nodes Reference -> ` #### Common gotchas - Repo-wide lint/type checks can surface issues in **untracked** files too. Avoid leaving untracked folders/files in the repo (for example, copied scratch folders) when running checks or preparing a PR. - If ruff flags function complexity (e.g., `C901`, `PLR0912`), prefer refactoring into smaller helpers over suppressing. - **`parent_container_name` ≠ `parent_element_name`**: These two `Parameter` attributes look similar but serve completely different purposes. `parent_container_name` is for `ParameterContainer` (list/dictionary ownership), `parent_element_name` is for `ParameterGroup` (UI grouping). Mixing them up causes parameters to land at the node root, skip cleanup between runs, and silently vanish on save/reload. See the [Containers](#containers) section for the full distinction. ## Working with the Project System The **project system** is Griptape Nodes' centralized file management framework that handles file organization, naming, and saving across all workflows. It eliminates hard-coded file paths and provides a consistent, configurable approach to file operations. ### Overview Before the project system, nodes used `StaticFilesManager.save_static_file()` with UUID-based filenames scattered across various locations. The project system replaces this with: - **Centralized configuration**: File organization rules defined in `griptape-nodes-project.yml` - **Named situations**: Semantic contexts for file operations (e.g., "save_node_output", "copy_external_file") - **Template-based paths**: Dynamic path generation using macros like `{outputs}/{node_name}_{file_name_base}.{file_extension}` - **Consistent behavior**: All nodes automatically follow the same file layout ### Key Components #### Workspace The root directory containing all project work. Configured in Griptape Nodes settings, it serves as the base for relative path resolution. ``` workspace/ ├── griptape-nodes-project.yml # Optional customizations ├── my_workflow/ │ ├── inputs/ │ ├── outputs/ │ ├── temp/ │ └── .griptape-nodes-previews/ ``` #### Situations Named scenarios that define: 1. **Where** files are saved (via macro templates) 1. **How** to handle collisions (create_new, overwrite, fail) 1. **Fallback** behavior if saving fails Common situations include: | Situation | Purpose | Default Macro Pattern | | -------------------- | ---------------------- | ----------------------------------------------------------------------- | | `save_node_output` | Generated node outputs | `{outputs}/{node_name?:_}{file_name_base}{_index?:03}.{file_extension}` | | `copy_external_file` | External file imports | `{inputs}/{node_name?:_}{parameter_name?:_}{file_name_base}...` | | `download_url` | Downloaded files | `{inputs}/{sanitized_url}` | | `save_preview` | Thumbnail generation | `{previews}/{source_relative_path?:/}...` | #### Macros Template strings in situations and directories that generate concrete file paths dynamically. Examples: - `{outputs}` - resolves to the outputs directory path - `{node_name}` - current node's name - `{file_name_base}` - filename without extension - `{file_extension}` - file extension - `{_index?:03}` - auto-incrementing counter (3-digit format) #### Directories Logical name-to-path mappings that can be referenced in macros: ``` directories: outputs: path_macro: "outputs" custom_renders: path_macro: "my_custom_path/{workflow_name}" ``` ### Using the Project System in Nodes There are two main patterns for working with project files in nodes: #### Pattern 1: ProjectFileParameter (Recommended for Node Outputs) Use `ProjectFileParameter` when your node has a configurable output file parameter that users might want to customize. ``` from griptape_nodes.exe_types.param_components.project_file_parameter import ProjectFileParameter from griptape_nodes.exe_types.core_types import Parameter, ParameterMode from griptape.artifacts.video_url_artifact import VideoUrlArtifact class MyVideoNode(ControlNode): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) # Add regular output parameter self.add_parameter(Parameter( name="output_video", output_type="VideoUrlArtifact", tooltip="Generated video", allowed_modes={ParameterMode.OUTPUT} )) # Add project file parameter for output file configuration self._output_video_file = ProjectFileParameter( node=self, name="output_video_file", default_filename="output_video.mp4", ) self._output_video_file.add_parameter() def process(self) -> None: # ... generate video_bytes ... # Use build_file() to get a ProjectFileDestination dest = self._output_video_file.build_file() saved = dest.write_bytes(video_bytes) # Set the output parameter with the saved location self.parameter_output_values["output_video"] = VideoUrlArtifact(saved.location) ``` **Key Points:** - `ProjectFileParameter` creates a UI parameter that users can configure - Call `build_file()` to get a `ProjectFileDestination` instance - Use `write_bytes()` to save the file - Access the saved file's URL/path via `saved.location` **❌ Common Mistake: Not Capturing write_bytes() Return Value** ``` # WRONG - Don't do this: dest = self._output_video_file.build_file() dest.write_bytes(video_bytes) # ❌ Return value not captured artifact = VideoUrlArtifact(dest.location) # Using dest, not saved file # This will fail with: "Failed because missing required variables: file_extension, file_name_base" ``` **Why it fails:** Macro variables like `{file_extension}` and `{file_name_base}` are resolved when `write_bytes()` saves the file and returns the saved file object, not by `build_file()`. Using `dest.location` before writing causes macro resolution errors. ``` # CORRECT: dest = self._output_video_file.build_file() saved = dest.write_bytes(video_bytes) # ✅ Capture return value artifact = VideoUrlArtifact(saved.location) # Use saved file's resolved location ``` The `saved` object contains the fully resolved file path with all macros filled in. #### Pattern 2: ProjectFileDestination Directly (For Utility Functions) Use `ProjectFileDestination.from_situation()` directly in utility functions or when you don't need user configuration. ``` from griptape_nodes.files.project_file import ProjectFileDestination from griptape.artifacts.video_url_artifact import VideoUrlArtifact def frames_to_video_artifact(frames: list, fps: int = 30, video_format: str = "mp4") -> VideoUrlArtifact: """Convert a list of frames to a VideoUrlArtifact.""" # ... process frames into video_bytes ... # Save using project file system dest = ProjectFileDestination.from_situation( filename=f"video.{video_format}", situation="save_node_output" ) saved = dest.write_bytes(video_bytes) return VideoUrlArtifact(saved.location) ``` **Key Points:** - Use `from_situation()` to create a destination with a named situation - The `filename` parameter is the base filename (will be transformed by the situation's macro) - The situation (e.g., "save_node_output") determines the final path and collision behavior ### Migration from StaticFilesManager #### Old Pattern (Deprecated) ``` from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes import uuid def old_save_video(video_bytes: bytes) -> VideoUrlArtifact: filename = f"{uuid.uuid4()}.mp4" url = GriptapeNodes.StaticFilesManager().save_static_file(video_bytes, filename) return VideoUrlArtifact(url) ``` #### New Pattern ``` from griptape_nodes.files.project_file import ProjectFileDestination def new_save_video(video_bytes: bytes) -> VideoUrlArtifact: dest = ProjectFileDestination.from_situation( filename="video.mp4", situation="save_node_output" ) saved = dest.write_bytes(video_bytes) return VideoUrlArtifact(saved.location) ``` **Migration Benefits:** - No more UUID generation required - Consistent file organization across all nodes - User-configurable file paths via project templates - Better file tracking and management - Automatic handling of name collisions ### Common Situations and When to Use Them - **`save_node_output`**: Primary situation for files generated by nodes (images, videos, audio, etc.) - **`copy_external_file`**: When importing/copying files from external sources - **`download_url`**: When downloading files from URLs - **`save_preview`**: For generating thumbnail or preview images - **`save_static_file`**: For static assets that don't change between runs ### Advanced Configuration Users can customize the project system by creating a `griptape-nodes-project.yml` file in their workspace: ``` project_template_schema_version: "0.1.0" name: "My Custom Project" directories: outputs: path_macro: "final_outputs/{workflow_name}" situations: save_node_output: macro: "{outputs}/{node_name}_{file_name_base}_{_index:04}.{file_extension}" policy: on_collision: create_new create_dirs: true ``` Your nodes automatically respect these customizations without any code changes. ### Best Practices 1. **Always use the project system** for saving files - never use hard-coded paths 1. **Choose the right pattern**: Use `ProjectFileParameter` for user-configurable outputs, `ProjectFileDestination` for utility functions 1. **Use semantic situations**: Pick the situation that best describes your operation 1. **Let macros handle naming**: Don't generate UUIDs or timestamps yourself - let the situation's macro and collision policy handle it 1. **Handle temporary files properly**: Use Python's `tempfile` for intermediate processing, only save final results via the project system 1. **Clean up temporary files**: Always clean up temporary files after copying to the project system ### Example: Complete Video Processing Node ``` import tempfile from pathlib import Path from typing import Any from griptape.artifacts.video_url_artifact import VideoUrlArtifact from griptape_nodes.exe_types.core_types import Parameter, ParameterMode from griptape_nodes.exe_types.node_types import ControlNode, AsyncResult from griptape_nodes.exe_types.param_components.project_file_parameter import ProjectFileParameter from griptape_nodes.files.file import File class ProcessVideo(ControlNode): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self.add_parameter(Parameter( name="input_video", input_types=["VideoUrlArtifact"], type="VideoUrlArtifact", tooltip="Input video to process" )) self.add_parameter(Parameter( name="output_video", output_type="VideoUrlArtifact", tooltip="Processed video", allowed_modes={ParameterMode.OUTPUT} )) # Add project file parameter for output self._output_video_file = ProjectFileParameter( node=self, name="output_video_file", default_filename="processed_video.mp4", ) self._output_video_file.add_parameter() def process(self) -> AsyncResult: yield lambda: self._process() def _process(self) -> None: # Get input video input_artifact = self.get_parameter_value("input_video") input_bytes = File(input_artifact.value).read_bytes() # Process in temporary location with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_file: temp_path = Path(temp_file.name) try: # Write input to temp file temp_path.write_bytes(input_bytes) # ... perform processing on temp_path ... # Read processed result output_bytes = temp_path.read_bytes() # Save using project system dest = self._output_video_file.build_file() saved = dest.write_bytes(output_bytes) # Set output self.parameter_output_values["output_video"] = VideoUrlArtifact(saved.location) finally: # Clean up temporary file if temp_path.exists(): temp_path.unlink() ``` ## Advanced Topics ### REST API vs SDK Integration **Problem**: Python SDKs often lag behind REST APIs in supporting new features. Parameters available in REST API documentation may not be exposed in SDK libraries. **When to Use REST API Directly**: - SDK missing documented API features (e.g., `image_config` for Gemini) - Need immediate access to new API parameters - SDK has bugs or limitations - Want lighter dependencies **REST API Implementation Pattern**: ``` import base64 import requests from google.oauth2 import service_account from google.auth.transport.requests import Request # Authentication credentials = service_account.Credentials.from_service_account_file( service_account_file, scopes=['https://www.googleapis.com/auth/cloud-platform'] ) def _get_access_token(self, credentials) -> str: """Get access token from credentials.""" if not credentials.valid: credentials.refresh(Request()) return credentials.token # Build JSON payload matching REST API spec payload = { "contents": { "role": "USER", "parts": [ {"text": prompt}, { "inline_data": { "mime_type": "image/jpeg", "data": base64.b64encode(image_bytes).decode('utf-8') } } ] }, "generation_config": { "temperature": 1.0, "topP": 0.95, "candidateCount": 1, "response_modalities": ["TEXT", "IMAGE"], "image_config": { # Feature not in SDK! "aspect_ratio": "16:9" } } } # Make authenticated request access_token = self._get_access_token(credentials) headers = { "Authorization": f"Bearer {access_token}", "Content-Type": "application/json" } api_endpoint = f"https://{location}-aiplatform.googleapis.com/v1/projects/{project_id}/locations/{location}/publishers/google/models/{model}:generateContent" response = requests.post(api_endpoint, headers=headers, json=payload, timeout=120) response.raise_for_status() response_data = response.json() # Parse JSON response (handle both camelCase and snake_case) candidates = response_data.get("candidates", []) for cand in candidates: parts_list = cand.get("content", {}).get("parts", []) for part in parts_list: if "inlineData" in part or "inline_data" in part: inline_data = part.get("inlineData") or part.get("inline_data", {}) mime = inline_data.get("mimeType") or inline_data.get("mime_type") data_b64 = inline_data.get("data", "") data_bytes = base64.b64decode(data_b64) ``` **Key Considerations**: 1. **Dependencies**: Use `google-auth` instead of full SDK (`google-cloud-aiplatform`, `google-genai`) 1. **Regional Availability**: Some models only work in specific regions (e.g., `us-central1`), not `global` 1. **Model Names**: Check for `-preview` suffix differences between preview and stable models 1. **Authentication Scopes**: Use `https://www.googleapis.com/auth/cloud-platform` for Vertex AI 1. **Response Format**: Handle both camelCase (API) and snake_case (some SDKs) field names 1. **Base64 Encoding**: REST API expects base64-encoded strings for binary data 1. **Error Handling**: Parse JSON error responses for detailed error messages **Trade-offs**: - ✅ Immediate access to all API features - ✅ Lighter dependencies - ✅ Full control over requests - ❌ More implementation work - ❌ Must handle auth/tokens manually - ❌ Need to track API changes yourself ### Complex Type Management Systems For nodes that need sophisticated type negotiation between multiple parameters: ``` class IfElse(BaseNode): def __init__(self, name: str, metadata: dict[Any, Any] | None = None) -> None: super().__init__(name, metadata) # Sophisticated connection tracking for type management self._possibility_space: list[str] = [] # Types acceptable to output target self._locked_type: str | None = None # Specific type locked by input self._connected_inputs: set[str] = set() # Track input connections self._output_connected: bool = False # Track output connections def _update_parameter_types(self) -> None: """Update all parameter types based on current state.""" if self._locked_type: # Locked to specific type - everything uses that type self.output_if_true.input_types = [self._locked_type] self.output_if_false.input_types = [self._locked_type] self.output.output_type = self._locked_type elif self._possibility_space: # Flexible within possibility space self.output_if_true.input_types = self._possibility_space.copy() self.output_if_false.input_types = self._possibility_space.copy() self.output.output_type = ParameterTypeBuiltin.ALL.value else: # Default state - accept any type self.output_if_true.input_types = ["any"] self.output_if_false.input_types = ["any"] self.output.output_type = ParameterTypeBuiltin.ALL.value ``` **Best Practice**: Use sophisticated type management for nodes that route data between multiple inputs and outputs. ### Agentic Nodes Inherit from ControlNode for agent management: ``` from griptape.structures import Agent from griptape_nodes.exe_types.node_types import ControlNode class MyAgentNode(ControlNode): def __init__(self, **kwargs): super().__init__(**kwargs) self.add_parameter(Parameter( name="agent_in", input_types=["Agent"], type="Agent" )) self.add_parameter(Parameter( name="agent_out", output_type="Agent" )) def process(self) -> None: agent_state = self.get_parameter_value("agent_in") agent = Agent.from_dict(agent_state) if agent_state else Agent() # Process with agent self.parameter_output_values["agent_out"] = agent.to_dict() ``` ### Abstract Base Classes for Node Families Create abstract base classes for related nodes to share common functionality: ``` from abc import abstractmethod from typing import Any from griptape_nodes.exe_types.base_iterative_nodes import BaseIterativeStartNode class BaseCustomIterativeStartNode(BaseIterativeStartNode): """Base class for a family of custom iterative start nodes.""" @abstractmethod def _get_compatible_end_classes(self) -> set[type]: """Return the set of End node classes that this Start node can connect to.""" @abstractmethod def _get_parameter_group_name(self) -> str: """Return the name for the parameter group containing iteration data.""" @abstractmethod def _get_exec_out_display_name(self) -> str: """Return the display name for the exec_out parameter.""" @abstractmethod def _get_exec_out_tooltip(self) -> str: """Return the tooltip for the exec_out parameter.""" @abstractmethod def _get_iteration_items(self) -> list[Any]: """Get the list of items to iterate over.""" @abstractmethod def is_loop_finished(self) -> bool: """Return True if the loop has completed all iterations.""" ``` **Best Practice**: Use abstract base classes to share common logic across node families while enforcing implementation of specific methods. ### Caching Use ClassVar for shared resources: ``` from typing import ClassVar, Any class CachedModelNode(DataNode): _cache: ClassVar[dict[str, Any]] = {} def get_model(self, model_id: str) -> Any: if model_id not in self._cache: self._cache[model_id] = load_model(model_id) return self._cache[model_id] ``` ### Hub Integration (e.g., HuggingFace) ``` # Gated model detection is_gated = getattr(model, 'gated', False) model_dict['gated'] = is_gated # Status updates for gated models if getattr(model_info, 'gated', False): self.publish_update_to_parameter( "status", "🔒 GATED MODEL - May require approval" ) ``` ### ParameterMessage for External Links and Status Updates ``` from griptape_nodes.exe_types.core_types import ParameterMessage # External link example ParameterMessage( name="model_card_link", title="Model Card", variant="info", value="View model documentation", button_link=f"https://huggingface.co/{model_id}", button_text="View on HuggingFace" ) # Dynamic status message example class MyIterativeNode(BaseIterativeStartNode): def __init__(self, name: str, metadata: dict[Any, Any] | None = None) -> None: super().__init__(name, metadata) # Status message parameter for real-time updates self.status_message = ParameterMessage( name="status_message", variant="info", value="", ) self.add_node_element(self.status_message) def _update_status_message(self, status_type: str = "normal") -> None: """Update status message based on current state.""" if self._total_iterations == 0: status = "Completed 0 (of 0)" elif status_type == "break": status = f"Stopped at {self._current_iteration_count} (of {self._total_iterations}) - Break" elif self.is_loop_finished(): status = f"Completed {self._total_iterations} (of {self._total_iterations})" else: status = f"Processing {self._current_iteration_count} (of {self._total_iterations})" self.status_message.value = status ``` **Best Practice**: Use ParameterMessage for static external links, dynamic status updates, and deprecation notices. For a full walkthrough of the deprecation notice pattern (hidden message + `before_value_set` auto-migration), see [Deprecated Model Migration and User Notification](#deprecated-model-migration-and-user-notification). ## Modern UI/UX Patterns ### Advanced UI Options ``` ui_options={ "hide_property": True, # Hide from property panel "pulse_on_run": True, # Visual feedback during execution "expander": True, # Collapsible parameter groups "is_full_width": True, # Full-width display "multiline": True, # Multi-line text input "placeholder_text": "...", # Input placeholder "display_name": "...", # Custom display name "markdown": True, # Markdown rendering "compare": True, # Comparison mode "clickable_file_browser": True, # File browser integration "hide": True, # ⭐ CORRECT: Completely hide parameter "collapsed": True, # Start parameter groups collapsed "edit_mask": True, # Enable mask editing for images } ``` #### Hidden Parameters Best Practice Use `"hide": True` to hide parameters from the UI (for advanced/expert settings): ``` # ✅ CORRECT: Hidden parameter with slider num_images_param = Parameter( name="num_images", input_types=["int"], type="int", default_value=1, tooltip="Number of images to generate (1-9)", allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY}, ui_options={ "display_name": "Number of Images", "hide": True # ⭐ CORRECT pattern }, ) num_images_param.add_trait(Slider(min_val=1, max_val=9)) self.add_parameter(num_images_param) # ⚠️ LEGACY: "hidden": True exists but is rare (3 instances vs 47) ui_options={"hidden": True} # Less common, prefer "hide": True ``` **Common Use Cases for Hidden Parameters:** - Advanced/expert configuration options - Internal control signals - Debug parameters - Optional advanced features - Parameters that should only be set programmatically ### Success/Failure Node Pattern For nodes that can succeed or fail, inherit from SuccessFailureNode: ``` from griptape_nodes.exe_types.node_types import SuccessFailureNode class LoadImage(SuccessFailureNode): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) # Add status parameters using the helper method self._create_status_parameters( result_details_tooltip="Details about the image loading operation result", result_details_placeholder="Details on the load attempt will be presented here.", ) def process(self) -> None: # Reset execution state at start self._clear_execution_status() # Clear output values to prevent stale data on errors self.parameter_output_values["image"] = None try: # Processing logic here result = load_image() self.parameter_output_values["image"] = result # Success case success_details = f"Image loaded successfully from {source}" self._set_status_results(was_successful=True, result_details=f"SUCCESS: {success_details}") except Exception as e: error_details = f"Failed to load image: {e}" self._set_status_results(was_successful=False, result_details=f"FAILURE: {error_details}") self._handle_failure_exception(e) ``` **Best Practice**: Use SuccessFailureNode for operations that can fail and need to report status to users. ### Parameter Initialization Initialize parameter visibility on node creation: ``` def _initialize_parameter_visibility(self) -> None: """Initialize parameter visibility based on default values.""" default_model = self.get_parameter_value("model") or "default" if default_model == "text-only": self.hide_parameter_by_name("image_input") else: self.show_parameter_by_name("image_input") ``` ### Artifact Path Tethering Pattern For nodes that work with files, use the artifact tethering pattern to keep path and artifact parameters synchronized: ``` from griptape_nodes_library.utils.artifact_path_tethering import ( ArtifactPathTethering, ArtifactTetheringConfig, ) class LoadImage(SuccessFailureNode): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) # Configuration for artifact tethering self._tethering_config = ArtifactTetheringConfig( dict_to_artifact_func=dict_to_image_url_artifact, extract_url_func=self._extract_url_from_image_value, supported_extensions=self.SUPPORTED_EXTENSIONS, default_extension="png", url_content_type_prefix="image/", ) # Create artifact parameter self.image_parameter = Parameter( name="image", input_types=["ImageUrlArtifact", "ImageArtifact", "str"], type="ImageUrlArtifact", output_type="ImageUrlArtifact", ui_options={"clickable_file_browser": True}, ) # Create path parameter using tethering utility self.path_parameter = ArtifactPathTethering.create_path_parameter( name="path", config=self._tethering_config, display_name="File Path or URL", ) # Tethering helper keeps parameters in sync self._tethering = ArtifactPathTethering( node=self, artifact_parameter=self.image_parameter, path_parameter=self.path_parameter, config=self._tethering_config, ) def after_value_set(self, parameter: Parameter, value: Any) -> None: # Delegate tethering logic to helper self._tethering.on_after_value_set(parameter, value) return super().after_value_set(parameter, value) ``` **Best Practice**: Use artifact tethering for seamless file/URL parameter synchronization. ## Production Error Handling ### Comprehensive Validation Use `validate_before_node_run()` for complex validation: ``` def validate_before_node_run(self) -> list[Exception] | None: """Validate parameters before running the node.""" exceptions = [] model = self.get_parameter_value("model") if model == "advanced": images = self.get_parameter_list_value("images") or [] if len(images) > MAX_IMAGES: exceptions.append(ValueError( f"{self.name}: Maximum {MAX_IMAGES} images allowed, got {len(images)}" )) return exceptions if exceptions else None ``` ### Connection Validation Patterns For complex nodes with multiple connection requirements: ``` def _validate_iterative_connections(self) -> list[Exception]: """Validate that all required connections are properly established.""" errors = [] node_type = self._get_base_node_type_name() # Check if exec_out has outgoing connections if not _outgoing_connection_exists(self.name, self.exec_out.name): errors.append( Exception( f"{self.name}: Missing required connection from 'On Each Item'. " f"REQUIRED ACTION: Connect {node_type} Start to interior loop nodes. " "The start node must connect to other nodes to execute the loop body." ) ) # Check if loop has outgoing connection to End if self.end_node is None: errors.append( Exception( f"{self.name}: Missing required tethering connection. " f"REQUIRED ACTION: Connect {node_type} Start 'Loop End Node' to {node_type} End 'Loop Start Node'. " "This establishes the explicit relationship between start and end nodes." ) ) return errors ``` **Best Practice**: Provide detailed, actionable error messages that tell users exactly what connections are missing and how to fix them. ### Safe Defaults Pattern Always set safe defaults before raising exceptions: ``` def _set_safe_defaults(self) -> None: """Set safe default values for all outputs.""" self.parameter_output_values["result"] = None self.parameter_output_values["status"] = "error" self.parameter_output_values["count"] = 0 def process(self) -> None: try: # Processing logic result = process_data() self.parameter_output_values["result"] = result except Exception as e: self._set_safe_defaults() raise RuntimeError(f"Processing failed: {str(e)}") from e ``` ### URL Construction Use `urllib.parse.urljoin()` for safe URL building: ``` from urllib.parse import urljoin import os def __init__(self, **kwargs): super().__init__(**kwargs) # Safe URL construction base = os.getenv("API_BASE_URL", "https://api.example.com") base_slash = base if base.endswith("/") else base + "/" api_base = urljoin(base_slash, "api/") self._endpoint = urljoin(api_base, "v1/process/") ``` ## Logging Best Practices ### Safe Logging Pattern Prevent logging failures from breaking execution: ``` from contextlib import suppress import logging logger = logging.getLogger(__name__) def _log(self, message: str) -> None: """Safe logging with exception suppression.""" with suppress(Exception): logger.info(message) ``` ### Request Sanitization Sanitize sensitive data in logs: ``` from copy import deepcopy import json PROMPT_TRUNCATE_LENGTH = 100 def _log_request(self, payload: dict[str, Any]) -> None: """Log request with sanitized sensitive data.""" with suppress(Exception): sanitized_payload = deepcopy(payload) # Truncate long prompts prompt = sanitized_payload.get("prompt", "") if len(prompt) > PROMPT_TRUNCATE_LENGTH: sanitized_payload["prompt"] = prompt[:PROMPT_TRUNCATE_LENGTH] + "..." # Redact base64 image data if "image" in sanitized_payload: image_data = sanitized_payload["image"] if isinstance(image_data, str) and image_data.startswith("data:image/"): parts = image_data.split(",", 1) header = parts[0] if parts else "data:image/" b64_len = len(parts[1]) if len(parts) > 1 else 0 sanitized_payload["image"] = f"{header}," self._log(f"Request: {json.dumps(sanitized_payload, indent=2)}") ``` ## Flexible Artifact Processing ### Duck Typing for Artifacts Handle multiple artifact formats gracefully: ``` def _extract_image_value(self, image_input: Any) -> str | None: """Extract string value from various image input types.""" if isinstance(image_input, str): return image_input try: # ImageUrlArtifact: .value holds URL string if hasattr(image_input, "value"): value = getattr(image_input, "value", None) if isinstance(value, str): return value # ImageArtifact: .base64 holds raw or data-URI if hasattr(image_input, "base64"): b64 = getattr(image_input, "base64", None) if isinstance(b64, str) and b64: return b64 except Exception as e: self._log(f"Failed to extract image value: {e}") return None ``` ### Image Format Conversion for External APIs **Problem**: External APIs often have strict format requirements (e.g., JPEG, PNG, WebP only), but cameras may save images in unsupported formats like MPO (Multi Picture Object) for 3D/burst photos. **Solution**: Automatically detect and convert unsupported formats: ``` # Import at top of file from PIL import Image from io import BytesIO def _get_image_data(self, image_artifact: ImageArtifact | ImageUrlArtifact) -> str: """Convert image to API-compatible format.""" # ... extract image_bytes ... try: img = Image.open(BytesIO(image_bytes)) # Convert unsupported formats (MPO, TIFF, BMP, etc.) to JPEG if img.format not in ['JPEG', 'PNG', 'WEBP']: self._log(f"Converting {img.format} to JPEG for API compatibility") # Convert to RGB if needed (for formats like MPO) if img.mode not in ['RGB', 'L']: img = img.convert('RGB') # Save as JPEG to bytes output = BytesIO() img.save(output, format='JPEG', quality=95) image_bytes = output.getvalue() mime_type = "image/jpeg" else: format_to_mime = { 'JPEG': 'image/jpeg', 'PNG': 'image/png', 'WEBP': 'image/webp' } mime_type = format_to_mime.get(img.format, 'image/jpeg') except Exception as e: self._log(f"Could not detect image format: {e}") mime_type = "image/jpeg" # Encode as base64 data URI base64_data = base64.b64encode(image_bytes).decode('utf-8') return f"data:{mime_type};base64,{base64_data}" ``` **Key Points**: - Apply conversion at all image input points (ImageArtifact, ImageUrlArtifact, localhost URLs) - Use high quality (95%) to preserve image fidelity - Handle color mode conversion (MPO often uses non-RGB modes) - Log conversions for debugging - Gracefully fall back to JPEG if detection fails ### Utility Function Patterns Create reusable utility functions for common operations: ``` # Connection checking utilities def _outgoing_connection_exists(source_node: str, source_param: str) -> bool: """Check if a source node/parameter has any outgoing connections.""" from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes connections = GriptapeNodes.FlowManager().get_connections() source_connections = connections.outgoing_index.get(source_node) if source_connections is None: return False param_connections = source_connections.get(source_param) return bool(param_connections) if param_connections else False def _incoming_connection_exists(target_node: str, target_param: str) -> bool: """Check if a target node/parameter has any incoming connections.""" from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes connections = GriptapeNodes.FlowManager().get_connections() target_connections = connections.incoming_index.get(target_node) if target_connections is None: return False param_connections = target_connections.get(target_param) return bool(param_connections) if param_connections else False ``` **Best Practice**: Create utility functions for common operations like connection checking, validation, and data processing. ### Flexible Image Processing ``` def _image_to_bytes(self, image_artifact) -> bytes: """Convert various image artifact types to bytes.""" if not image_artifact: raise ValueError("No input image provided") try: # Handle dictionary format (serialized artifacts) if isinstance(image_artifact, dict): image_url_artifact = self._dict_to_image_url_artifact(image_artifact) image_bytes = image_url_artifact.to_bytes() # Handle artifact objects directly elif isinstance(image_artifact, (ImageArtifact, ImageUrlArtifact)): image_bytes = image_artifact.to_bytes() else: # Try generic to_bytes method image_bytes = image_artifact.to_bytes() # Verify we have valid image data if not image_bytes or len(image_bytes) < 100: raise ValueError("Image data is empty or too small") return image_bytes except Exception as e: raise ValueError(f"Failed to extract image data: {str(e)}") ``` ## Creating Node Libraries Bundle nodes into libraries for sharing. Create `griptape_nodes_library.json`: ``` { "name": "Library Name", "library_schema_version": "0.1.0", "settings": [ { "description": "API keys required by nodes in this library", "category": "app_events.on_app_initialization_complete", "contents": { "secrets_to_register": ["MY_SERVICE_API_KEY", "MY_OTHER_API_KEY"] } } ], "metadata": { "author": "Author Name", "description": "Library description", "library_version": "1.0.0", "engine_version": "0.55.0", "tags": ["AI", "Image Processing"], "dependencies": { "pip_dependencies": ["pillow", "requests"], "pip_install_flags": ["--upgrade"] } }, "widgets": [ { "name": "MyWidget", "path": "widgets/MyWidget.js", "description": "Custom UI component for the node" } ], "categories": [ { "image": { "title": "Image Processing", "description": "Image manipulation nodes", "color": "border-purple-500", "icon": "Image" } } ], "nodes": [ { "class_name": "MyImageNode", "file_path": "image/my_image_node.py", "metadata": { "category": "image", "description": "Process images with AI", "display_name": "AI Image Processor", "icon": "image", "group": "processing" } } ], "workflows": ["workflows/example_workflow.py"], "is_default_library": false } ``` ### Library Structure - **settings**: Register secrets/API keys used by library nodes - Use `secrets_to_register` array to declare required secrets - Category should be `app_events.on_app_initialization_complete` - Secrets are accessed via `GriptapeNodes.SecretsManager().get_secret()` - **metadata.dependencies**: PIP packages installed on library load - **widgets**: Register custom JS widget components (see [Custom Widget Components](#custom-widget-components)) - **categories**: Group nodes in UI with colors and icons - **nodes**: List node classes, file paths, and metadata - **workflows**: Template workflow files **Important:** The `secrets_to_register` array tells the system which secrets your library needs. Users will be prompted to configure these secrets through the UI or environment variables. Use flat directory structures. The engine automatically registers and loads libraries. ## Custom Widget Components Nodes can use custom JavaScript widget components to provide rich, interactive UI beyond the standard parameter controls. Widgets are standalone `.js` files that render into a container element and communicate value changes back to the framework via a callback. ### Widget Architecture A custom widget involves three pieces: 1. **Widget JS file** (`widgets/MyWidget.js`) — the UI component 1. **Node Python file** — references the widget via the `Widget` trait on a parameter 1. **Library JSON** (`griptape_nodes_library.json`) — registers the widget so the framework can find it ``` library_name/ ├── griptape_nodes_library.json ├── my_node.py └── widgets/ └── MyWidget.js ``` ### Registering a Widget Add a `"widgets"` array to `griptape_nodes_library.json`: ``` { "name": "My Library", "widgets": [ { "name": "MyWidget", "path": "widgets/MyWidget.js", "description": "Description of the widget" } ], "nodes": [ ... ] } ``` The `name` must match the name used in the `Widget` trait on the Python side, and the `library` argument must match the `"name"` field at the top level of the JSON. ### Attaching a Widget to a Parameter Use the `Widget` trait to bind a parameter to your custom widget. The parameter's value is passed to the widget as `props.value`, and changes flow back through `props.onChange`: ``` from griptape_nodes.exe_types.core_types import Parameter, ParameterMode from griptape_nodes.traits.widget import Widget self.add_parameter( Parameter( name="my_data", input_types=["list"], type="list", output_type="list", default_value=[], tooltip="Data managed by custom widget", allowed_modes={ParameterMode.PROPERTY, ParameterMode.OUTPUT}, traits={Widget(name="MyWidget", library="My Library")}, ) ) ``` ### Widget JS Function Signature Widgets are ES module default exports. The function receives a container DOM element and a props object, and must return a cleanup function: ``` export default function MyWidget(container, props) { const { value, onChange, disabled, height } = props; // Build your UI inside `container` // Call `onChange(newValue)` when the user changes data // Respect `disabled` to prevent interaction when appropriate // Return a cleanup function return () => { // Remove event listeners, dispose resources }; } ``` **Props:** | Prop | Type | Description | | ---------- | ---------- | ------------------------------------------------ | | `value` | `any` | Current parameter value (matches Python default) | | `onChange` | `function` | Callback to send updated value to the framework | | `disabled` | `boolean` | Whether the widget should be read-only | | `height` | `number` | Suggested height in pixels (may be 0 or absent) | ### Critical Patterns and Pitfalls #### Emit Changes Sparingly — Not on Every Keystroke Calling `onChange` triggers framework state updates that steal focus from the active element. For text inputs, this means the textarea loses focus after every keystroke, making typing impossible. This is not specific to custom widgets — the built-in `TextComponent` in the Griptape Nodes editor uses the same pattern: - **Local state** (your internal data array, counters, border colors) updates immediately on every `input` event. - **`onChange`** is called only on `blur` — when the user clicks away or tabs out of the field. - **Discrete controls** (buttons, steppers, drag-end) call `onChange` immediately since they don't hold focus. ``` // Local state updates on every keystroke — UI stays responsive textarea.addEventListener("input", (e) => { localData[index].text = e.target.value; // Update character counters, border colors, etc. here }); // Emit to framework only when the user leaves the field textarea.addEventListener("blur", () => { localData[index].text = textarea.value; onChange(structuredClone(localData)); }); // Discrete controls (buttons, steppers) can emit immediately button.addEventListener("pointerdown", (e) => { e.stopPropagation(); localData[index].value++; onChange(structuredClone(localData)); render(); }); ``` > **Why not `requestAnimationFrame` to restore focus?** Attempting to call `onChange` on every keystroke and then restore focus via `requestAnimationFrame` does not reliably work — the framework's React rendering cycle can complete asynchronously, and the focus restoration races with it. #### Prevent Node Drag Interference Node canvases handle drag events for panning and node movement. Interactive elements inside your widget must stop event propagation and use the `nodrag` / `nowheel` CSS classes: ``` // On the outermost wrapper const wrapper = document.createElement("div"); wrapper.className = "my-widget nodrag nowheel"; // On interactive child elements (textareas, sliders, etc.) textarea.addEventListener("pointerdown", (e) => e.stopPropagation()); textarea.addEventListener("mousedown", (e) => e.stopPropagation()); ``` #### Prevent Keyboard Shortcut Interference The node editor binds keyboard shortcuts at the canvas level — for example, pressing `Delete` deletes the selected node. When a text input inside your widget has focus, these shortcuts still fire because keyboard events bubble up. Stop propagation on `keydown` to isolate your text inputs: ``` textarea.addEventListener("keydown", (e) => e.stopPropagation()); ``` This prevents the Delete key from deleting the node while the user is editing text, and stops other canvas-level shortcuts (copy, paste, undo at the node level) from interfering with normal text editing. #### Override `user-select: none` for Text Inputs Widget wrappers typically set `user-select: none` to prevent accidental text selection during drag operations. This cascades into child elements and blocks textarea editing. Override it explicitly: ``` textarea { user-select: text; -webkit-user-select: text; } ``` #### Clone Values Before Emitting Always pass a fresh copy to `onChange` — not a reference to your internal state. Otherwise the framework and your widget share the same object, leading to subtle bugs: ``` onChange(localData.map((item) => ({ ...item }))); ``` #### Clean Up Document-Level Listeners If you attach listeners to `document` (e.g., for drag-and-drop or click-outside-to-close), remove them in the cleanup function: ``` document.addEventListener("pointerdown", onDocumentClick, true); return () => { document.removeEventListener("pointerdown", onDocumentClick, true); }; ``` #### Assign Stable IDs to List Items When building widgets that manage reorderable lists (e.g., drag-and-drop shot editors), give each item a unique ID that is decoupled from array index. Without stable IDs, item attributes such as text field contents can be lost during drag-and-drop reordering because the widget re-renders from scratch and identity was tied to position. ``` let nextItemId = 1; function assignId(item) { if (!item.id) { item.id = `item-${nextItemId++}`; } else { const num = parseInt(item.id.replace("item-", ""), 10); if (!isNaN(num) && num >= nextItemId) { nextItemId = num + 1; } } return item; } // On initialization — preserve existing IDs from saved data let items = value.map((v) => assignId({ ...v })); // When adding new items items.push(assignId({ name: "New Item", text: "" })); ``` The ID persists through reorders, re-renders, and round-trips via `onChange`. The display name (e.g., "Shot1", "Shot2") can be renumbered based on visual position while the `id` remains stable. #### Handle the `disabled` Attribute Correctly in DOM Helpers If you write a DOM helper function that creates elements from an attributes object, be careful with the `disabled` attribute. Using `setAttribute("disabled", false)` does **not** remove the disabled state — the presence of the attribute in any form disables the element. Use the property instead: ``` if (key === "disabled") { element.disabled = !!val; } ``` #### Show Drop Indicators at End of List When implementing drag-and-drop reordering, the drop target indicator (e.g., a blue border) must also appear when dragging past the last item in the list. A common approach: when the drag position is below all items, show a `border-bottom` on the last item instead of a `border-top` on a nonexistent next item: ``` if (dragOverIndex === items.length && item.index === lastIndex) { item.el.style.borderBottom = "2px solid #4a9eff"; } else if (item.index === dragOverIndex) { item.el.style.borderTop = "2px solid #4a9eff"; } ``` #### Enforce Aggregate Constraints (Min/Max Totals) When list items have numeric values that must sum to within a range (e.g., total duration 3–15 seconds), enforce the constraint in both directions: - **Ceiling:** Disable the increase stepper and "add" button when the total would exceed the maximum. - **Floor:** Disable the decrease stepper when reducing any item would bring the total below the minimum. - **Auto-compensate on delete:** When removing an item would drop the total below the minimum, increase the last remaining item's value to make up the difference. ``` const MIN_TOTAL = 3; const MAX_TOTAL = 15; // Disable decrease if total would go below minimum const wouldGoBelow = totalValue() - 1 < MIN_TOTAL; const canDecrease = !disabled && item.value > MIN_VALUE && !wouldGoBelow; // On delete — auto-compensate to maintain minimum total trash.addEventListener("pointerdown", (e) => { e.stopPropagation(); if (items.length <= 1) return; items.splice(index, 1); const total = totalValue(); if (total < MIN_TOTAL) { const lastItem = items[items.length - 1]; lastItem.value += MIN_TOTAL - total; } emitChange(); render(); }); ``` Show both bounds in the status bar so users understand the valid range: `"8s (3–15s)"`. Highlight in red when outside bounds. ### Example: List-Based Editor Widget A common pattern is a widget that manages a list of structured items with add, delete, reorder, and inline editing. Key implementation details: - **Stable IDs:** Assign a unique `id` to each item that survives reordering and round-trips through `onChange`. - **Drag-and-drop reordering:** Attach `pointerdown` on drag handles, create a floating clone for visual feedback, track the insertion point via `pointermove`, and finalize the reorder on `pointerup`. Call `onChange` only after the drop. Show drop indicators at both middle and end-of-list positions. - **Stepper controls:** For constrained numeric values (e.g., duration 1–15s), use ▲/▼ stepper buttons instead of dropdown menus. Disable buttons when they would violate constraints (min/max per item, min/max total across all items). - **Aggregate constraints:** Enforce both minimum and maximum totals across all items. Auto-compensate on delete to maintain the floor (see [Enforce Aggregate Constraints](#enforce-aggregate-constraints-minmax-totals)). - **Validation constraints:** Enforce limits (max items, max total values, max text length) by disabling the add button and stepper arrows rather than silently ignoring input. - **Status feedback:** Show a small status bar with current counts vs. limits (e.g., `"3 / 6 shots"`, `"8s (3–15s)"`) so users understand the valid range and why controls may be disabled. - **Text input isolation:** Stop propagation on `pointerdown`, `mousedown`, and `keydown` to prevent node drag and keyboard shortcut interference. Emit `onChange` only on `blur`. ``` # Python side — list parameter with widget self.add_parameter( Parameter( name="items", input_types=["list"], type="list", output_type="list", default_value=[{"name": "Item1", "duration": 2, "description": ""}], allowed_modes={ParameterMode.PROPERTY, ParameterMode.OUTPUT}, traits={Widget(name="MyListEditor", library="My Library")}, ) ) ``` ``` // JS side — skeleton for a list editor widget export default function MyListEditor(container, props) { const { value, onChange, disabled } = props; // Stable ID assignment let nextId = 1; function assignId(item) { if (!item.id) item.id = `item-${nextId++}`; return item; } let items = Array.isArray(value) ? value.map((v) => assignId({ ...v })) : [assignId({ name: "Item1", duration: 2, description: "" })]; function emitChange() { if (!disabled && onChange) { onChange(items.map((item) => ({ ...item }))); } } function render() { container.innerHTML = ""; const wrapper = document.createElement("div"); wrapper.className = "nodrag nowheel"; items.forEach((item, index) => { // ... build item row with drag handle, stepper, textarea, trash ... // Text input — local update on input, emit on blur textarea.addEventListener("input", (e) => { items[index].description = e.target.value; }); textarea.addEventListener("blur", () => { items[index].description = textarea.value; emitChange(); }); // Isolate text input from node-level events textarea.addEventListener("pointerdown", (e) => e.stopPropagation()); textarea.addEventListener("mousedown", (e) => e.stopPropagation()); textarea.addEventListener("keydown", (e) => e.stopPropagation()); }); container.appendChild(wrapper); } render(); return () => { /* cleanup document-level listeners */ }; } ``` ## Contributing to the Standard Library When adding nodes to the core `griptape_nodes_library` (as opposed to creating a standalone library), follow this process: ### 1. Create a Feature Branch ``` cd griptape-nodes git checkout -b feature/add-color-match-node ``` ### 2. Add the Node File Place your node in the appropriate category subdirectory: ``` libraries/griptape_nodes_library/griptape_nodes_library/ ├── image/ │ ├── color_match.py # New node file │ ├── load_image.py │ └── save_image.py ├── text/ ├── audio/ └── ... ``` ### 3. Update griptape_nodes_library.json Make three updates to `libraries/griptape_nodes_library/griptape_nodes_library.json`: #### a. Increment the library version ``` { "metadata": { "library_version": "0.59.0" // Was 0.58.0 } } ``` #### b. Add any new pip dependencies ``` { "metadata": { "dependencies": { "pip_dependencies": [ "existing-dep", "color-matcher" // New dependency ] } } } ``` #### c. Add the node entry ``` { "nodes": [ { "class_name": "ColorMatch", "file_path": "griptape_nodes_library/image/color_match.py", "metadata": { "category": "image", "description": "Transfer color characteristics from a reference image to a target image", "display_name": "Color Match", "icon": "palette", "group": "edit" } } ] } ``` ### 4. Add Documentation Create a documentation page at `docs/nodes//.md`: ``` # Color Match Transfer color characteristics from a reference image to a target image. ## What It Does Applies the color palette from a reference image to a target image... ## Parameters ### Inputs | Parameter | Type | Description | | --------------- | ---------------- | --------------------------- | | reference_image | ImageUrlArtifact | Source of the color palette | | target_image | ImageUrlArtifact | Image to apply colors to | ### Outputs | Parameter | Type | Description | | ------------ | ---------------- | -------------------- | | output_image | ImageUrlArtifact | Color-matched result | ## Example Usage 1. Connect a reference image with desired colors 2. Connect the target image to transform 3. Run the node ## Technical Details Uses the color-matcher library with histogram matching... ``` ### 5. Update mkdocs.yml Navigation Add your doc page to the navigation in `mkdocs.yml`: ``` nav: - Nodes Reference: - Image: - Load Image: nodes/image/load_image.md - Save Image: nodes/image/save_image.md - Color Match: nodes/image/color_match.md # New entry ``` ### 6. Run Quality Checks Before committing, run formatting and checks: ``` make format # Auto-format code make check/lint # Check for linting issues make check/types # Check for type errors ``` Fix any issues that arise before proceeding. ### 7. Commit and Create PR ``` git add . git commit -m "feat(image): add ColorMatch node for color transfer" git push -u origin HEAD gh pr create --title "Add ColorMatch node" --body "## Summary - Adds ColorMatch node for transferring colors between images - Uses color-matcher library - Includes documentation ## Test plan - [ ] Load two images - [ ] Run color match - [ ] Verify output has reference colors" ``` ### Standard Library vs External Library | Aspect | Standard Library | External Library | | ------------ | ------------------------------------------- | --------------------------------- | | Location | `griptape-nodes` repo | Separate repo | | Installation | Included by default | User installs | | Review | Requires PR approval | Self-published | | Dependencies | Added to core `griptape_nodes_library.json` | Own `griptape_nodes_library.json` | | Versioning | Follows core library version | Independent versioning | | Docs | Added to main docs site | README in library | **When to contribute to standard library:** - Node has broad utility for many users - No proprietary/paid API dependencies - Stable, well-tested implementation - Follows all code quality standards **When to create external library:** - Niche use case - Requires paid API keys - Experimental/rapidly changing - Want independent release cycle ## Appendix ### Imports ``` # Core imports from griptape_nodes.exe_types.core_types import ( Parameter, ParameterList, ParameterMode, ParameterTypeBuiltin, ParameterGroup, ParameterMessage, ControlParameterInput, ControlParameterOutput ) from griptape_nodes.exe_types.node_types import ( DataNode, ControlNode, BaseNode, SuccessFailureNode, StartNode, EndNode ) from griptape_nodes.exe_types.base_iterative_nodes import BaseIterativeStartNode, BaseIterativeEndNode from griptape_nodes.traits.options import Options from griptape_nodes.traits.slider import Slider from griptape_nodes.traits.color_picker import ColorPicker from griptape_nodes.traits.file_system_picker import FileSystemPicker # Artifacts from griptape.artifacts import ImageArtifact, ImageUrlArtifact, TextArtifact # Utilities from griptape_nodes_library.utils.artifact_path_tethering import ( ArtifactPathTethering, ArtifactTetheringConfig ) from griptape_nodes_library.utils.image_utils import ( dict_to_image_url_artifact, load_pil_from_url, save_pil_image_with_named_filename, ) from griptape_nodes_library.utils.file_utils import generate_filename ``` ### Utility Function Reference #### Image Utilities (`griptape_nodes_library.utils.image_utils`) | Function | Purpose | Returns | | --------------------------------------------------- | ---------------------------------------------------- | ------------------ | | `dict_to_image_url_artifact(d)` | Convert dict representation to ImageUrlArtifact | `ImageUrlArtifact` | | `load_pil_from_url(url)` | Load PIL Image from URL (handles localhost) | `PIL.Image.Image` | | `save_pil_image_with_named_filename(img, filename)` | Save PIL Image using project system (see note below) | `ImageUrlArtifact` | **Example usage:** ``` from griptape_nodes_library.utils.image_utils import ( dict_to_image_url_artifact, load_pil_from_url, save_pil_image_with_named_filename, ) # Convert parameter value to artifact value = self.get_parameter_value("image") if isinstance(value, dict): artifact = dict_to_image_url_artifact(value) else: artifact = value # Load as PIL for processing pil_image = load_pil_from_url(artifact.value) # Process image... processed = pil_image.filter(...) # Save and get output artifact output_artifact = save_pil_image_with_named_filename(processed, "result.png") self.parameter_output_values["output"] = output_artifact ``` #### File Utilities (`griptape_nodes_library.utils.file_utils`) | Function | Purpose | Returns | | ------------------------------------------- | -------------------------- | ------- | | `generate_filename(node_name, suffix, ext)` | Create consistent filename | `str` | **Example usage:** ``` from griptape_nodes_library.utils.file_utils import generate_filename # Generate filename like "ColorMatch_processed_abc123.png" filename = generate_filename(self.name, suffix="processed", ext="png") ``` #### Project System (`griptape_nodes.files.project_file`) | Class/Function | Purpose | Returns | | ----------------------------------------- | ------------------------------------------------ | ------------------------ | | `ProjectFileDestination.from_situation()` | Create file destination with named situation | `ProjectFileDestination` | | `ProjectFileParameter` | Parameter component for configurable file output | - | **Example usage:** ``` from griptape_nodes.files.project_file import ProjectFileDestination # Save file using project system dest = ProjectFileDestination.from_situation( filename="output.mp4", situation="save_node_output" ) saved = dest.write_bytes(file_bytes) artifact = VideoUrlArtifact(saved.location) ``` **Note:** The utility functions `save_pil_image_with_named_filename()` and similar helpers use the project system internally. For new code, prefer using `ProjectFileParameter` or `ProjectFileDestination` directly to have full control over file handling. See [Working with the Project System](#working-with-the-project-system) for comprehensive documentation. ### Advanced Parameter Types - **ControlParameterInput/Output**: For execution flow control - **ParameterGroup**: For organizing related parameters with collapsible UI - **ParameterMessage**: For status updates and external links - **ParameterList**: For accepting multiple inputs of the same type ### Enumerations - **NodeResolutionState**: UNRESOLVED, RESOLVING, RESOLVED - **ParameterMode**: INPUT, OUTPUT, PROPERTY - **ParameterTypeBuiltin**: STR("str"), BOOL("bool"), INT("int"), FLOAT("float"), ANY("any"), NONE("none"), CONTROL_TYPE("parametercontroltype"), ALL("all") ### Advanced Node Types - **BaseNode**: Most basic node type for custom implementations - **DataNode**: For data processing without execution flow - **ControlNode**: For nodes that manage execution flow - **SuccessFailureNode**: For operations that can succeed or fail - **BaseIterativeStartNode/BaseIterativeEndNode**: For iterative operations - **AsyncResult**: For asynchronous processing operations ### Custom Artifacts Inherit from BaseArtifact and override methods as needed: ``` from griptape.artifacts import BaseArtifact class CustomArtifact(BaseArtifact): def __init__(self, value: Any, **kwargs): super().__init__(value, **kwargs) def to_text(self) -> str: return str(self.value) ``` ### Advanced Lifecycle Methods #### Spotlight Control For conditional dependency resolution: ``` def initialize_spotlight(self) -> None: """Custom spotlight initialization - only include evaluate parameter initially.""" evaluate_param = self.get_parameter_by_name("evaluate") if evaluate_param and ParameterMode.INPUT in evaluate_param.get_mode(): self.current_spotlight_parameter = evaluate_param def advance_parameter(self) -> bool: """Custom parameter advancement with conditional dependency resolution.""" if self.current_spotlight_parameter is None: return False # Special handling for conditional parameters if self.current_spotlight_parameter is self.evaluate: try: evaluation_result = self.check_evaluation() next_param = self.output_if_true if evaluation_result else self.output_if_false if ParameterMode.INPUT in next_param.get_mode(): self.current_spotlight_parameter.next = next_param next_param.prev = self.current_spotlight_parameter self.current_spotlight_parameter = next_param return True except Exception: self.current_spotlight_parameter = None return False return super().advance_parameter() ``` #### Control Flow Management ``` def get_next_control_output(self) -> Parameter | None: """Return the appropriate control output based on evaluation.""" if "evaluate" not in self.parameter_output_values: self.stop_flow = True return None if self.parameter_output_values["evaluate"]: return self.get_parameter_by_name("Then") return self.get_parameter_by_name("Else") ``` ### Widget Testbed The **widget-testbed** is a standalone React + Vite application for testing and developing custom widget components outside the full Griptape Nodes environment. It provides a lightweight, hot-reloading development environment where you can iterate quickly on widget UI and behavior. #### Purpose Custom widgets for Griptape Nodes are imperative JavaScript functions that manage their own DOM and state. The widget-testbed allows you to: - **Rapid prototyping**: Test widget behavior with instant hot-reload during development - **Isolated testing**: Work on widget UI/UX without launching the full Griptape Nodes application - **State management verification**: Test complex state transitions and user interactions - **Cross-widget development**: Easily switch between testing different widgets - **Debug UI issues**: Inspect the widget's rendered output and state in a clean environment #### When to Use Use the widget-testbed when: - Creating a new custom widget component from scratch - Debugging widget behavior issues (focus loss, drag-and-drop, event handling) - Testing widget state management and `onChange` callback patterns - Verifying widget appearance and layout without node editor interference - Developing widgets that manage complex internal state (lists, editors, multi-step forms) #### File Structure ``` widget-testbed/ ├── index.html # Entry HTML with minimal styling ├── package.json # Dependencies (React 19, Vite 6) ├── vite.config.js # Vite configuration with React plugin ├── src/ │ ├── main.jsx # React app entry point │ ├── App.jsx # Main test harness with controls │ └── WidgetHost.jsx # React wrapper for imperative widgets └── node_modules/ # Dependencies ``` #### Key Components ##### WidgetHost.jsx The `WidgetHost` component is a React wrapper that hosts imperative widget functions using the same `(container, props)` signature as Griptape Nodes widgets. It handles the lifecycle of mounting, updating, and unmounting widgets while preventing unnecessary re-renders. **Key features:** - **Imperative widget support**: Calls your widget function with a container element and props - **Smart re-mounting**: Only re-mounts the widget when value changes externally (e.g., Reset button) - **onChange differentiation**: Tracks whether changes originated from the widget or parent - **Cleanup management**: Properly calls widget cleanup functions on unmount or re-mount **Props:** | Prop | Type | Description | | ---------- | ---------- | ----------------------------------------------------------- | | `widgetFn` | `function` | The widget function to render (container, props) => cleanup | | `value` | `any` | Current widget value | | `onChange` | `function` | Callback when widget emits changes | | `disabled` | `boolean` | Whether widget should be read-only (default: false) | | `height` | `number` | Suggested height in pixels (default: 0) | **Implementation pattern:** ``` import WidgetHost from "./WidgetHost"; import MyWidget from "../../path/to/widgets/MyWidget.js"; export default function App() { const [value, setValue] = useState(initialValue); const [disabled, setDisabled] = useState(false); return ( ); } ``` ##### App.jsx The main test harness that provides: - **Widget mounting**: Imports and renders the widget via `WidgetHost` - **State controls**: Checkbox to toggle disabled state - **Debug panel**: JSON view of current widget state (toggle with checkbox) - **Reset functionality**: Button to reset widget to initial state - **Visual layout**: Clean, dark-themed UI matching Griptape Nodes aesthetic #### Testing a Widget **1. Install dependencies:** ``` cd widget-testbed npm install ``` **2. Update App.jsx to import your widget:** ``` import MyWidget from "../../my-library/widgets/MyWidget.js"; const INITIAL_VALUE = { /* your initial state */ }; export default function App() { const [value, setValue] = useState(INITIAL_VALUE); const [disabled, setDisabled] = useState(false); const [showDebug, setShowDebug] = useState(true); return (

MyWidget Testbed

{showDebug && (
          {JSON.stringify(value, null, 2)}
        
)}
); } ``` **3. Start the development server:** ``` npm run dev ``` **4. Open in browser:** Navigate to `http://localhost:5173` (or the port shown in terminal). #### Development Workflow **Typical development cycle:** 1. **Write widget code**: Create or modify your widget `.js` file 1. **Update testbed**: Import the widget in `App.jsx` 1. **Run dev server**: `npm run dev` for hot-reload 1. **Test interactions**: Click, type, drag, and interact with the widget 1. **Verify state**: Check the JSON debug panel to see state changes 1. **Test edge cases**: Use Reset button and Disabled toggle to test edge cases 1. **Iterate**: Make changes to widget code and see updates instantly **Common testing scenarios:** - **Focus management**: Type in text fields, ensure focus isn't lost on `onChange` - **Drag-and-drop**: Test reordering, ensure item identity is preserved - **State transitions**: Add/remove items, verify correct state updates - **Disabled mode**: Toggle disabled, ensure widget becomes read-only - **External state changes**: Use Reset button to verify widget handles external updates - **Event propagation**: Ensure clicks/drags don't interfere with parent (use `nodrag` class) - **Keyboard shortcuts**: Test that Delete, Ctrl+C, etc. don't trigger node-level actions #### WidgetHost Pattern Details The `WidgetHost` component solves a critical problem: **preventing unnecessary widget re-mounts when the widget itself triggers `onChange`**. Without this, the widget would be destroyed and recreated on every keystroke, losing focus and internal state. **How it works:** 1. **Flag-based change tracking**: `isWidgetChangeRef` tracks whether the current change originated from the widget 1. **Conditional re-mount**: Widget is only re-mounted when `value` changes externally (not from `onChange`) 1. **Stable onChange callback**: Uses `useCallback` to prevent unnecessary effect triggers 1. **Cleanup on unmount**: Calls widget's cleanup function when widget is destroyed or value changes externally **Key implementation:** ``` const isWidgetChangeRef = useRef(false); const stableOnChange = useCallback( (newValue) => { isWidgetChangeRef.current = true; // Mark as widget-originated change onChange?.(newValue); }, [onChange], ); useEffect(() => { if (isWidgetChangeRef.current) { isWidgetChangeRef.current = false; // Clear flag and skip re-mount return; } // External value change: re-mount widget const cleanup = widgetFn(container, { value, onChange: stableOnChange, disabled, height }); return cleanup; }, [widgetFn, value, disabled, height, stableOnChange]); ``` This pattern ensures the widget maintains its internal DOM and state across `onChange` calls, preventing focus loss and other re-mount issues. #### Best Practices **When using the widget-testbed:** - **Match production props**: Use the same prop names (`value`, `onChange`, `disabled`, `height`) as Griptape Nodes - **Test disabled state**: Always verify your widget respects the `disabled` prop - **Verify cleanup**: Check that your widget's cleanup function properly removes event listeners - **Test edge cases**: Use the Reset button to test how your widget handles external value changes - **Inspect state**: Keep the JSON debug panel visible to understand state flow - **Test keyboard events**: Ensure `stopPropagation` on `keydown` prevents node-level shortcuts - **Test mouse events**: Ensure `stopPropagation` on `pointerdown`/`mousedown` prevents node dragging - **Verify cloning**: Check that `onChange` receives cloned data, not references to internal state **Don't:** - Don't commit `widget-testbed/` to your library repository (it's a development tool) - Don't test production-specific features (node connections, workflow execution) - Don't assume testbed behavior matches production exactly (always final-test in Griptape Nodes) #### Example: MultiShotEditor Testbed The current testbed configuration tests the `MultiShotEditor` widget from the Kling library: ``` import MultiShotEditor from "../../kling/widgets/MultiShotEditor.js"; const INITIAL_SHOTS = [{ name: "Shot1", duration: 2, description: "" }]; export default function App() { const [shots, setShots] = useState(INITIAL_SHOTS); // ... controls and debug UI ... return ( ); } ``` This demonstrates the testbed pattern for a complex widget managing an array of shot objects with drag-and-drop reordering, add/remove functionality, and multiple text inputs. ## Asynchronous API Integration ### Process Method with Yield Syntax For nodes that perform long-running asynchronous operations, use the `yield` syntax to properly handle async processing: ``` from griptape_nodes.exe_types.node_types import DataNode, AsyncResult class MyAsyncNode(DataNode): def process(self) -> AsyncResult | None: """Process the request asynchronously.""" yield lambda: self._process() def _process(self) -> None: """Main processing method.""" try: # Set safe defaults self._set_safe_defaults() # Validate API key api_key = self._validate_api_key() # Submit task task_id = self._submit_task(api_key) # Poll for completion result = self._poll_for_completion(task_id, api_key) # Process result self.parameter_output_values["output"] = result except Exception as e: self._set_safe_defaults() self._log(f"Processing failed: {e}") raise RuntimeError(f"{self.name}: {str(e)}") from e ``` **Key Points:** - `process()` returns `AsyncResult | None` and yields a lambda - Actual work is done in `_process()` method - Pattern matches Minimax and other async nodes - Enables proper async handling in the workflow engine ### Polling Pattern for Long-Running Tasks When integrating with APIs that use asynchronous task processing (video generation, model training, etc.), implement a three-step pattern: #### Step 1: Task Submission ``` def _submit_task(self, params: dict[str, Any], headers: dict[str, str]) -> dict[str, Any]: """Submit task and return response with task_id.""" payload = self._build_payload(params) response = requests.post( self.API_BASE_URL, json=payload, headers=headers, timeout=DEFAULT_TIMEOUT ) response.raise_for_status() response_data = response.json() task_id = response_data.get("task_id") return response_data ``` #### Step 2: Status Polling ``` POLLING_INTERVAL = 10 # seconds (use API-recommended value) MAX_POLLING_ATTEMPTS = 60 # 10 minutes max def _poll_for_completion(self, task_id: str, headers: dict[str, str]) -> str | None: """Poll API for task completion and return result identifier.""" query_url = "https://api.example.com/v1/query/task" for attempt in range(MAX_POLLING_ATTEMPTS): time.sleep(POLLING_INTERVAL) # Wait before each poll response = requests.get( query_url, headers=headers, params={"task_id": task_id}, # Use query params, not path timeout=DEFAULT_TIMEOUT ) response.raise_for_status() status_data = response.json() status = status_data.get("status") self._log(f"Polling attempt {attempt + 1}: Status = {status}") if status == "Success": file_id = status_data.get("file_id") return file_id elif status == "Fail": error_msg = status_data.get("error_message", "Unknown error") raise RuntimeError(f"Task failed: {error_msg}") # Continue polling for "Processing", "Pending", etc. raise RuntimeError(f"Task did not complete within {MAX_POLLING_ATTEMPTS * POLLING_INTERVAL} seconds") ``` #### Step 3: Result Retrieval ``` def _retrieve_result(self, file_id: str, headers: dict[str, str]) -> str: """Retrieve download URL from result identifier.""" retrieve_url = "https://api.example.com/v1/files/retrieve" response = requests.get( retrieve_url, headers=headers, params={"file_id": file_id}, timeout=DEFAULT_TIMEOUT ) response.raise_for_status() response_data = response.json() download_url = response_data.get("file", {}).get("download_url") return download_url ``` **Key Considerations:** - Always use API-recommended polling intervals (typically 5-10 seconds) - Set reasonable maximum attempts to prevent infinite loops - Use query parameters, not path parameters, for task_id (verify with API docs) - Handle all status states: Success, Fail, Processing, Pending - Log polling attempts for debugging - Set safe defaults on failure ### Dynamic Endpoint Selection Based on Inputs When a node can operate in multiple modes depending on which inputs are connected (e.g., image-to-video when images are provided, text-to-video when they are not), select the API endpoint dynamically in the process method rather than hardcoding a single URL: ``` IMAGE2VIDEO_URL = "https://api.example.com/v1/videos/image2video" TEXT2VIDEO_URL = "https://api.example.com/v1/videos/text2video" def _process(self): image_data = self._get_image_data("start_frame") has_images = image_data is not None if has_images: api_url = IMAGE2VIDEO_URL else: api_url = TEXT2VIDEO_URL payload = self._build_payload() if image_data: payload["image"] = image_data response = requests.post(api_url, headers=headers, json=payload, timeout=30) # ... polling uses the same api_url for status checks poll_url = f"{api_url}/{task_id}" ``` This avoids requiring image inputs when the user wants text-only generation, and ensures the correct API endpoint is called for each mode. The polling URL should use the same base endpoint. ### Image Artifact Conversion to Base64 **CRITICAL: Localhost URL Handling** When sending images to external APIs, ImageUrlArtifact URLs from static storage are localhost and inaccessible to external services. Always detect and convert localhost URLs to base64: ``` import base64 def _get_image_data(self, image_artifact: ImageArtifact | ImageUrlArtifact) -> str: """Convert image artifact to URL or base64 data URI.""" # ImageUrlArtifact - check if localhost or public URL if isinstance(image_artifact, ImageUrlArtifact): url = image_artifact.value # Localhost URLs must be converted to base64 for external APIs if url.startswith(('http://localhost', 'http://127.0.0.1', 'https://localhost', 'https://127.0.0.1')): self._log(f"Converting localhost URL to base64: {url[:100]}...") response = requests.get(url, timeout=30) response.raise_for_status() image_bytes = response.content # Detect MIME type from headers mime_type = response.headers.get('content-type', 'image/jpeg') if not mime_type.startswith('image/'): mime_type = 'image/jpeg' base64_data = base64.b64encode(image_bytes).decode('utf-8') return f"data:{mime_type};base64,{base64_data}" # Public URLs can be passed through self._log(f"Using public URL: {url[:100]}...") return url # ImageArtifact - use .base64 property (preferred method) if isinstance(image_artifact, ImageArtifact): # PREFERRED: Use built-in properties if hasattr(image_artifact, 'base64') and hasattr(image_artifact, 'mime_type'): base64_data = image_artifact.base64 # Raw base64 (no prefix) mime_type = image_artifact.mime_type # e.g., 'image/jpeg' # Check if already has data URI prefix if base64_data.startswith('data:'): self._log("Using ImageArtifact.base64 (already has data URI)") return base64_data # Add data URI prefix self._log(f"Using ImageArtifact.base64 with mime_type: {mime_type}") return f"data:{mime_type};base64,{base64_data}" # FALLBACK: Manual byte extraction self._log("Falling back to manual base64 encoding") if hasattr(image_artifact, 'value') and hasattr(image_artifact.value, 'read'): image_artifact.value.seek(0) image_bytes = image_artifact.value.read() elif hasattr(image_artifact, 'data'): if isinstance(image_artifact.data, bytes): image_bytes = image_artifact.data elif hasattr(image_artifact.data, 'read'): image_artifact.data.seek(0) image_bytes = image_artifact.data.read() else: raise ValueError("Unsupported ImageArtifact format") else: raise ValueError("Unsupported ImageArtifact format") # Detect MIME type with PIL mime_type = "image/jpeg" try: from PIL import Image from io import BytesIO img = Image.open(BytesIO(image_bytes)) format_to_mime = { 'JPEG': 'image/jpeg', 'PNG': 'image/png', 'WEBP': 'image/webp' } mime_type = format_to_mime.get(img.format, 'image/jpeg') except Exception: pass base64_data = base64.b64encode(image_bytes).decode('utf-8') return f"data:{mime_type};base64,{base64_data}" raise ValueError("Unsupported artifact type") ``` **Key Points:** 1. **Always detect localhost URLs** - External APIs cannot access them 1. **Use ImageArtifact.base64 property** - The proper Griptape way (returns raw base64) 1. **Use ImageArtifact.mime_type property** - Automatic MIME type detection 1. **Log which path is used** - Essential for debugging 1. **Download localhost files** - Convert to base64 before sending to API **Parameter Definition:** ``` Parameter( name="image_input", input_types=["ImageArtifact", "ImageUrlArtifact"], # Accept both type="ImageArtifact", tooltip="Image input (file or URL)", ui_options={"clickable_file_browser": True}, # Enable file browser ) ``` ### Multi-Image Input Validation When nodes accept multiple image parameters, use a reusable validation method with clear parameter identification: ``` def _validate_image(self, image_artifact: ImageArtifact | ImageUrlArtifact, param_name: str) -> list[Exception]: """Validate image with parameter name in error messages.""" exceptions = [] if isinstance(image_artifact, ImageArtifact): # Get image bytes if hasattr(image_artifact, 'value') and hasattr(image_artifact.value, 'read'): image_artifact.value.seek(0) image_bytes = image_artifact.value.read() image_artifact.value.seek(0) else: return exceptions # Validate size size_mb = len(image_bytes) / (1024 * 1024) if size_mb >= 20: exceptions.append(ValueError( f"{self.name}: {param_name} size must be < 20MB (current: {size_mb:.1f}MB)" )) # Validate format and dimensions try: from PIL import Image from io import BytesIO img = Image.open(BytesIO(image_bytes)) if img.format not in ['JPEG', 'PNG', 'WEBP']: exceptions.append(ValueError( f"{self.name}: {param_name} format must be JPG, PNG, or WebP (current: {img.format})" )) width, height = img.size short_edge = min(width, height) if short_edge <= 300: exceptions.append(ValueError( f"{self.name}: {param_name} short edge must be > 300px (current: {short_edge}px)" )) except ImportError: self._log("PIL not available for validation") except Exception as e: self._log(f"Error validating {param_name}: {e}") return exceptions def validate_before_node_run(self) -> list[Exception] | None: """Validate all image parameters.""" exceptions = [] # Validate each image parameter independently first_frame = self.get_parameter_value("first_frame_image") if first_frame: exceptions.extend(self._validate_image(first_frame, "first_frame_image")) last_frame = self.get_parameter_value("last_frame_image") if last_frame: exceptions.extend(self._validate_image(last_frame, "last_frame_image")) return exceptions if exceptions else None ``` **Benefits:** - Clear error messages identifying which image parameter has issues - Reusable validation logic across multiple image inputs - Independent validation for each parameter - Actionable feedback for users ### Model-Dependent Parameter Management When different models support different parameter combinations: ``` def after_value_set(self, parameter: Parameter, value: Any) -> None: """Handle model-dependent parameter visibility and options.""" if parameter.name == "model": if value == "AdvancedModel": # Show model-specific parameters self.show_parameter_by_name("advanced_option") # Update dropdown choices dynamically resolution_param = self.get_parameter_by_name("resolution") if resolution_param: for child in resolution_param.children: if hasattr(child, 'choices'): child.choices = ADVANCED_MODEL_RESOLUTIONS break else: # Hide and reset for other models self.hide_parameter_by_name("advanced_option") # Update to standard choices resolution_param = self.get_parameter_by_name("resolution") if resolution_param: for child in resolution_param.children: if hasattr(child, 'choices'): child.choices = STANDARD_RESOLUTIONS break self.set_parameter_value("resolution", "720P") return super().after_value_set(parameter, value) ``` **Model-Specific Validation:** ``` def validate_before_node_run(self) -> list[Exception] | None: """Validate model-specific parameter combinations.""" exceptions = [] model = self.get_parameter_value("model") duration = self.get_parameter_value("duration") resolution = self.get_parameter_value("resolution") # Example: 10s only for specific model/resolution if duration == 10: if model != "AdvancedModel": exceptions.append(ValueError(f"{self.name}: 10s duration only supported by AdvancedModel")) elif resolution == "4K": exceptions.append(ValueError(f"{self.name}: 10s duration not supported with 4K resolution")) # Model-specific parameter requirements if model in ["ModelB", "ModelC"]: required_param = self.get_parameter_value("required_for_model_b_c") if not required_param: exceptions.append(ValueError(f"{self.name}: Parameter required for {model}")) return exceptions if exceptions else None ``` ### Deprecated Model Migration and User Notification When a model provider deprecates endpoints (e.g., preview models replaced by GA equivalents), nodes should automatically migrate saved workflows while informing the user. This pattern uses three components working together: 1. A `DEPRECATED_MODELS` dictionary mapping old model names to their replacements 1. A hidden `ParameterMessage` element that acts as a dismissable info banner 1. The `before_value_set` lifecycle hook to intercept and replace deprecated values before they are applied **Step 1: Define the deprecation map and current models** ``` from griptape_nodes.exe_types.core_types import Parameter, ParameterMessage from griptape_nodes.traits.button import Button MODELS = [ "veo-3.1-generate-001", "veo-3.1-fast-generate-001", ] # Mapping of deprecated model names to their replacements. # When a saved workflow references one of these, the node auto-migrates. DEPRECATED_MODELS: dict[str, str] = { "veo-3.1-generate-preview": "veo-3.1-generate-001", "veo-3.1-fast-generate-preview": "veo-3.1-fast-generate-001", "veo-3.0-generate-001": "veo-3.1-generate-001", "veo-2.0-generate-001": "veo-3.1-generate-001", } ``` **Step 2: Add a hidden ParameterMessage in `__init__`** Place this after the model parameter so it appears near the model selector in the UI. The `hide=True` keeps it invisible until needed. The `Button` trait with `on_click` gives the user a "Dismiss" button. ``` def __init__(self, **kwargs): super().__init__(**kwargs) # ... model parameter added above ... # Hidden deprecation notice — shown when a deprecated model is detected self.add_node_element( ParameterMessage( name="model_deprecation_notice", title="Model Deprecation Notice", variant="info", value="", traits={ Button( full_width=True, on_click=lambda _, __: self.hide_message_by_name("model_deprecation_notice"), ) }, button_text="Dismiss", hide=True, ) ) ``` **Step 3: Implement `before_value_set` to intercept deprecated models** `before_value_set` fires before the parameter's value is applied. This is the right place to swap a deprecated model for its replacement, because `after_value_set` (and any logic that depends on the model value) will see the replacement. ``` def before_value_set(self, parameter: Parameter, value: Any) -> Any: """Auto-migrate deprecated models and show a deprecation notice.""" if parameter.name == "model" and value in DEPRECATED_MODELS: replacement = DEPRECATED_MODELS[value] message = self.get_message_by_name_or_element_id("model_deprecation_notice") if message is not None: message.value = ( f"The '{value}' model has been deprecated. " f"The model has been updated to '{replacement}'. " "Please save your workflow to apply this change." ) self.show_message_by_name("model_deprecation_notice") value = replacement return super().before_value_set(parameter, value) ``` **Step 4: Hide the notice when the user selects a valid model** In `after_value_set`, dismiss the banner when the current model is not deprecated. This handles the case where the user manually selects a different model after the migration. ``` def after_value_set(self, parameter: Parameter, value: Any) -> None: if parameter.name == "model": # ... model-specific logic (update duration choices, etc.) ... if value not in DEPRECATED_MODELS: self.hide_message_by_name("model_deprecation_notice") return super().after_value_set(parameter, value) ``` **How it works end-to-end:** 1. A user opens a workflow saved with `"veo-3.1-generate-preview"`. 1. The framework calls `before_value_set` with the saved value. 1. The hook detects it in `DEPRECATED_MODELS`, swaps it to `"veo-3.1-generate-001"`, and shows the info banner. 1. `after_value_set` fires with the replacement value — model-dependent UI updates (duration choices, parameter visibility, etc.) work correctly because they see the valid GA model. 1. The user sees the banner: *"The 'veo-3.1-generate-preview' model has been deprecated. The model has been updated to 'veo-3.1-generate-001'. Please save your workflow to apply this change."* 1. The user can dismiss the banner or it hides automatically on the next valid model selection. **Key API methods used:** | Method | Purpose | | ---------------------------------------------- | ------------------------------------------------- | | `self.add_node_element(ParameterMessage(...))` | Adds the message element to the node | | `self.get_message_by_name_or_element_id(name)` | Retrieves the message element to update its value | | `self.show_message_by_name(name)` | Makes the hidden message visible | | `self.hide_message_by_name(name)` | Hides the message again | **Reference implementations:** - `GriptapeCloudPrompt` in `griptape_nodes_library/config/prompt/griptape_cloud_prompt.py` (standard library) - `VeoVideoGenerator`, `VeoImageToVideoGenerator`, `VeoTextToVideoWithRef` in the `griptape-nodes-library-googleai` external library ### Enhanced Debug Logging for API Integration For nodes that integrate with external APIs, implement comprehensive debug logging to quickly diagnose issues: ``` # Task Submission - Log full response def _submit_task(self, params: dict, headers: dict) -> dict: response = requests.post(API_URL, json=payload, headers=headers) response.raise_for_status() response_data = response.json() self._log(f"Task submission response: {json.dumps(response_data, indent=2)}") return response_data # Payload Sizes - Log data sizes before sending def _log_request(self, payload: dict) -> None: if "first_frame_image" in payload: img_len = len(payload.get("first_frame_image", "")) self._log(f"first_frame_image data length: {img_len} chars (~{img_len/1024:.1f}KB)") if "last_frame_image" in payload: img_len = len(payload.get("last_frame_image", "")) self._log(f"last_frame_image data length: {img_len} chars (~{img_len/1024:.1f}KB)") # Error Responses - Log full API error details def _poll_for_completion(self, task_id: str, headers: dict) -> str: status_data = response.json() status = status_data.get("status") if status == "Fail": # Log complete error response for debugging self._log(f"Full API error response: {json.dumps(status_data, indent=2)}") error_msg = status_data.get("error_message", "Unknown error") raise RuntimeError(f"Task failed: {error_msg}") # Processing Paths - Log which code path is executed def _get_image_data(self, image_artifact) -> str: if isinstance(image_artifact, ImageUrlArtifact): if url.startswith('http://localhost'): self._log(f"Converting localhost URL to base64: {url[:100]}...") else: self._log(f"Using public URL: {url[:100]}...") elif isinstance(image_artifact, ImageArtifact): if hasattr(image_artifact, 'base64'): self._log(f"Using ImageArtifact.base64 with mime_type: {mime_type}") else: self._log("Falling back to manual base64 encoding") ``` **What to Log:** - **Full API responses** (submission, polling, retrieval) - **Payload sizes** (especially for base64 data) - **Processing paths** (which code branches execute) - **Model/parameter combinations** being used - **Error details** (full error response from API) **Benefits:** - Quickly identify where failures occur - Understand what data is being sent - Track which code paths execute - Get exact API error messages and codes - Debug without reproducing issues ### API Documentation Verification **Critical Best Practice:** Always verify API specifications directly from documentation. **Common Pitfalls to Avoid:** 1. **Model Names**: Check exact capitalization (`MiniMax-Hailuo-02` not `video-01`) 1. **Endpoints**: Verify exact URLs (`/v1/query/video_generation` not `/v1/video_generation/{id}`) 1. **Parameters**: Check query params vs path params 1. **Response Structure**: Verify exact field names (`file_id` vs `file_list`) 1. **Polling Intervals**: Use API-recommended values **Example: Correct vs Incorrect Polling:** ``` # ✅ CORRECT: Query parameter response = requests.get( "https://api.example.com/v1/query/task", params={"task_id": task_id} ) # ❌ INCORRECT: Path parameter (unless API specifies this) response = requests.get( f"https://api.example.com/v1/query/task/{task_id}" ) ``` **When Documentation is Inaccessible:** - Explicitly state inability to access web pages (e.g., JavaScript-heavy docs) - Request user to provide relevant documentation sections - Never assume or infer API patterns without verification - Update implementation when code samples are provided ### Library Structure with uv Dependency Management **Modern Approach**: Use `uv` for fast, reproducible dependency management following the Minimax library pattern. #### Directory Structure ``` library-name/ ├── pyproject.toml # uv configuration ├── uv.lock # Lock file (generated) ├── LICENSE # License file ├── README.md # Documentation ├── CHANGELOG.md # Version history ├── .gitignore # Ignore rules └── library_name/ ├── griptape_nodes_library.json # Library metadata └── node_file.py ``` #### pyproject.toml Configuration ``` [project] name = "library-name" version = "1.0.0" description = "Description of your library" authors = [ {name = "Your Name", email = "email@example.com"} ] readme = "README.md" requires-python = ">=3.12" dependencies = [ "griptape-nodes-engine", "requests", # Add other dependencies ] [tool.uv.sources] griptape-nodes-engine = { git = "https://github.com/griptape-ai/griptape-nodes", rev="latest"} [tool.hatch.build.targets.wheel] packages = ["library_name"] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" ``` #### Library Configuration (inside subdirectory) Place `griptape_nodes_library.json` inside the library subdirectory: ``` { "name": "Library Name", "library_schema_version": "0.1.0", "settings": [ { "description": "API keys required by nodes", "category": "app_events.on_app_initialization_complete", "contents": { "secrets_to_register": ["API_KEY_NAME"] } } ], "nodes": [ { "class_name": "NodeClassName", "file_path": "node_file.py", // Relative to library subdirectory "metadata": { "category": "category_name", "description": "Node description", "display_name": "Node Display Name" } } ] } ``` #### Installation Instructions in README Provide both uv (recommended) and pip (fallback) installation methods: ```` ## Installation ### Option 1: Using uv (Recommended) 1. Clone or download this library 2. Install dependencies: ```bash cd library-name uv sync ```` ``` 1. Place in Griptape Nodes libraries directory ### Option 2: Automatic Installation 1. Place folder in libraries directory 2. Dependencies install automatically via pip ``` #### Generate Lock File ```bash cd library-name uv sync ``` **Benefits:** - Fast installation (Rust-based) - Reproducible builds via lock file - Direct GitHub integration for griptape-nodes - Backward compatible with pip installation ### Two-Mode UI Pattern (Simple + Custom) **Use Case**: Create beginner-friendly nodes while offering advanced control for power users. **Example**: Music/video generation APIs often have "simple description" mode and "detailed control" mode. #### Implementation Pattern ``` class GenerativeNode(DataNode): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) # Mode selector mode_param = Parameter( name="custom_mode", input_types=["bool"], type="bool", default_value=False, tooltip="Custom Mode: Full control. Simple Mode: Auto-generate from prompt.", allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY}, ui_options={"display_name": "Custom Mode"}, ) self.add_parameter(mode_param) # Prompt (meaning changes by mode) prompt_param = Parameter( name="prompt", input_types=["str"], type="str", default_value="", tooltip=[ {"type": "text", "text": "Custom Mode: Exact lyrics/script"}, {"type": "text", "text": "Simple Mode: General description"}, ], allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY}, ui_options={"multiline": True, "display_name": "Prompt"}, ) self.add_parameter(prompt_param) # Advanced parameters (custom mode only) style_param = Parameter( name="style", input_types=["str"], type="str", default_value="", tooltip="Style/genre (Custom Mode only)", allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY}, ui_options={"hide": True}, # Hidden by default ) self.add_parameter(style_param) title_param = Parameter( name="title", input_types=["str"], type="str", default_value="", tooltip="Title (Custom Mode only)", allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY}, ui_options={"hide": True}, # Hidden by default ) self.add_parameter(title_param) # Initialize visibility self._initialize_parameter_visibility() def _initialize_parameter_visibility(self) -> None: """Initialize parameter visibility based on default mode.""" custom_mode = self.get_parameter_value("custom_mode") or False if custom_mode: self.show_parameter_by_name("style") self.show_parameter_by_name("title") else: self.hide_parameter_by_name("style") self.hide_parameter_by_name("title") def after_value_set(self, parameter: Parameter, value: Any) -> None: """Update UI based on mode selection.""" if parameter.name == "custom_mode": if value: self.show_parameter_by_name("style") self.show_parameter_by_name("title") else: self.hide_parameter_by_name("style") self.hide_parameter_by_name("title") return super().after_value_set(parameter, value) def validate_before_node_run(self) -> list[Exception] | None: """Validate based on selected mode.""" exceptions = [] custom_mode = self.get_parameter_value("custom_mode") if custom_mode: # Custom mode requires style and title style = self.get_parameter_value("style") or "" title = self.get_parameter_value("title") or "" if not style.strip(): exceptions.append(ValueError(f"{self.name}: Style required in Custom Mode")) if not title.strip(): exceptions.append(ValueError(f"{self.name}: Title required in Custom Mode")) else: # Simple mode just needs prompt prompt = self.get_parameter_value("prompt") or "" if not prompt.strip(): exceptions.append(ValueError(f"{self.name}: Prompt required in Simple Mode")) return exceptions if exceptions else None ``` **Best Practice for First-Time Users**: Default to Simple Mode with recommendation in documentation: ``` ### Getting Started #### Simple Mode (Recommended for First-Time Users) 1. Leave "Custom Mode" unchecked 2. Enter a description: "A calm piano melody" 3. Run! #### Custom Mode (Advanced) 1. Check "Custom Mode" 2. Fill in style, title, and detailed prompt 3. Fine-tune advanced parameters ``` ### Music/Audio Generation API Patterns #### Character Limits by Model Many generation APIs have model-specific character limits. Store limits as class constants: ``` class MusicGenerationNode(DataNode): # Prompt length limits by model PROMPT_LIMITS_CUSTOM = { "V3_5": 3000, "V4": 3000, "V4_5": 5000, "V5": 5000, } PROMPT_LIMIT_SIMPLE = 500 # Style length limits by model STYLE_LIMITS = { "V3_5": 200, "V4": 200, "V4_5": 1000, "V5": 1000, } TITLE_LIMIT = 80 def validate_before_node_run(self) -> list[Exception] | None: """Validate with model-specific limits.""" exceptions = [] model = self.get_parameter_value("model") custom_mode = self.get_parameter_value("custom_mode") if custom_mode: prompt = self.get_parameter_value("prompt") or "" prompt_limit = self.PROMPT_LIMITS_CUSTOM.get(model, 3000) if len(prompt) > prompt_limit: exceptions.append(ValueError( f"{self.name}: Prompt exceeds {prompt_limit} character limit for {model} " f"(current: {len(prompt)} characters)" )) style = self.get_parameter_value("style") or "" style_limit = self.STYLE_LIMITS.get(model, 200) if len(style) > style_limit: exceptions.append(ValueError( f"{self.name}: Style exceeds {style_limit} character limit for {model} " f"(current: {len(style)} characters)" )) return exceptions if exceptions else None ``` #### Model Selection with Detailed Tooltips Use list of dict tooltip format for model comparison: ``` model_param = Parameter( name="model", input_types=["str"], type="str", default_value="V5", tooltip=[ {"type": "text", "text": "Model version for generation:"}, {"type": "text", "text": "• V5: Superior quality, fastest (4 min max)"}, {"type": "text", "text": "• V4_5PLUS: Richest sound, up to 8 min"}, {"type": "text", "text": "• V4_5: Superior blending, up to 8 min"}, {"type": "text", "text": "• V4: Best quality, refined structure (4 min)"}, {"type": "text", "text": "• V3_5: Creative diversity (4 min)"}, ], allowed_modes={ParameterMode.INPUT, ParameterMode.PROPERTY}, ) model_param.add_trait(Options(choices=["V5", "V4_5PLUS", "V4_5", "V4", "V3_5"])) ``` #### Dual Track Output Pattern APIs that generate multiple variations: ``` # Output parameter for multiple tracks music_urls_param = Parameter( name="music_urls", output_type="list[str]", type="list[str]", tooltip="Download URLs for generated tracks (2 variations)", allowed_modes={ParameterMode.OUTPUT}, settable=False, ui_options={"is_full_width": True, "display_name": "Music URLs"}, ) self.add_parameter(music_urls_param) def process(self) -> None: # ... generation logic ... urls = self._extract_music_urls(response_data) self.parameter_output_values["music_urls"] = urls # Build detailed result result_lines = [ f"✓ Generated {len(urls)} track variation(s)", "", "Music URLs:", ] for i, url in enumerate(urls, 1): result_lines.append(f"{i}. {url}") self.parameter_output_values["result_details"] = "\n".join(result_lines) ``` #### Status Updates During Long Operations Update status parameter in real-time during polling: ``` def _poll_for_completion(self, task_id: str, api_key: str) -> dict[str, Any]: """Poll API with real-time status updates.""" for attempt in range(self.MAX_POLLING_ATTEMPTS): time.sleep(self.POLLING_INTERVAL) # Update status parameter with progress status_msg = f"Generating... ({attempt + 1}/{self.MAX_POLLING_ATTEMPTS})" self.set_parameter_value("status", status_msg) response = requests.get(query_url, headers=headers, params={"ids": task_id}) # ... check completion ... ``` **Best Practice**: Always provide progress feedback for operations longer than 10 seconds. ### Documentation Patterns for Node Libraries #### Comprehensive README Structure ``` # Library Name Brief description and key features. ## Features - Bullet list of main capabilities - Include model options - Highlight unique features ## Installation ### Option 1: Using uv (Recommended) Steps for uv installation ### Option 2: Automatic Installation Steps for pip installation ## Getting Started ### Simple Mode (Recommended for First-Time Users) Minimal example with explanations ### Custom Mode (Advanced) Advanced example showing all features ## Parameters ### Basic Parameters Table with Name, Type, Description ### Advanced Parameters (Hidden by Default) Table with Name, Type, Default, Description ### Output Parameters Table with outputs ## Model Comparison Table comparing models: | Model | Max Duration | Quality | Speed | Character Limits | ## Character Limits Clear tables showing limits by model/mode ## API Rate Limits Document: - Concurrency limits - Generation time expectations - File retention policies ## Example Workflows 3-5 complete examples covering common use cases ## Error Handling Common errors and solutions ## Troubleshooting ### Common Errors and Solutions #### Error: "Missing required variables: file_extension, file_name_base" **Full Error:** ``` ERROR: Attempted to resolve macro path. Failed because missing required variables: file_extension, file_name_base ERROR: Attempted to create download URL. Failed with file_path='{outputs}/{node_name?:\_}{file_name_base}{\_index?:03}.{file_extension}' ```` **Cause:** Not capturing the return value from `write_bytes()`. Using `dest.location` instead of `saved.location`. **Incorrect Code:** ```python dest = self._output_file.build_file() dest.write_bytes(video_bytes) # ❌ Return value not captured artifact = VideoUrlArtifact(dest.location) # Using dest, not saved file ```` **Solution:** ``` dest = self._output_file.build_file() saved = dest.write_bytes(video_bytes) # ✅ Capture the saved file artifact = VideoUrlArtifact(saved.location) # Use saved file's resolved location ``` **Explanation:** Macro variables are populated when `write_bytes()` actually saves the file. The `saved` object returned by `write_bytes()` contains the fully resolved path. ______________________________________________________________________ #### Error: Type Conversion Issues with Image Parameters **Symptom:** Image parameters don't handle different input types consistently, or errors occur when passing URLs, file paths, or artifacts between nodes. **Problem:** Using generic `Parameter` with manual type configuration doesn't standardize type conversion logic: ``` # ❌ Inconsistent type handling Parameter( name="image", input_types=["ImageArtifact", "ImageUrlArtifact", "str"], type="ImageArtifact", ) ``` **Solution:** Use `ParameterImage` for standardized type conversion: ``` # ✅ Standardized type handling ParameterImage( name="image", tooltip="Input image", allow_output=False, ) ``` **Benefits:** - Handles ImageArtifact, ImageUrlArtifact, and strings consistently - Built-in support for URLs, file paths, and data URIs - Graceful error handling for various input formats - Reduces type conversion errors in complex workflows ______________________________________________________________________ FAQ-style troubleshooting guide ## API Reference Link to official API docs ## Best Practices Tips for optimal usage ## Support Where to get help ## Version History Link to CHANGELOG ```` #### Model Comparison Table Always include a comparison table for services with multiple models: ```markdown | Model | Max Duration | Quality | Speed | Character Limits | | ----- | ------------ | -------- | ------- | ------------------------- | | V5 | 4 min | Superior | Fastest | Prompt: 5000, Style: 1000 | | V4_5 | 8 min | High | Fast | Prompt: 5000, Style: 1000 | | V4 | 4 min | Best | Medium | Prompt: 3000, Style: 200 | ```` ______________________________________________________________________ This guide represents the current best practices for Griptape node development, incorporating both foundational concepts and modern patterns demonstrated in production nodes. Use these patterns to create robust, user-friendly, and maintainable nodes that integrate seamlessly with the Griptape ecosystem. # Making Custom Nodes ## Getting Started with Custom Node Development Creating your own custom nodes allows you to extend functionality and tailor it to your specific needs. ## Using the Template Repository The easiest way to get started is by using our official template repository: [Griptape Nodes Library Template](https://github.com/griptape-ai/griptape-nodes-library-template/) This template provides a structured foundation with all the necessary boilerplate code, testing frameworks, and documentation patterns to help you create production-ready node libraries. [Go straight to the readme](https://github.com/griptape-ai/griptape-nodes-library-template/blob/main/README.md) ## Custom Node Development Workflow 1. **Use the template repository** - Create your own repository from the GitHub template 1. **Set up your environment** - Clone the repo to your Griptape Nodes workspace directory 1. **Configure your library** - Rename directories and update package information in `pyproject.toml` 1. **Create your nodes** - Define node classes (either ControlNode or DataNode) with appropriate parameters 1. **Implement your logic** - Code the required `process()` method and any additional functionality 1. **Configure library metadata** - Set up your library.json file with nodes and category information 1. **Register with the engine** - Add your library to Griptape Nodes through the settings interface 1. **Test and use** - Create flows using your custom nodes in the Griptape Nodes interface ## Best Practices for Custom Node Development - Keep nodes focused on single responsibilities - Follow Griptape's input/output patterns for consistency - Add comprehensive error handling - Include type hints and docstrings - Write tests for both normal operation and edge cases - Consider backward compatibility when updating nodes ## Example Use Cases - Integration with company-specific APIs or services - Custom data processing pipelines - Domain-specific tools (financial calculations, scientific algorithms, etc.) - Workflow automation specific to your organization - Enhanced visualization or reporting capabilities ## Standard Library Reference To better understand how to design your custom nodes, explore the patterns used in our standard library: [Explore the standard node reference](https://docs.griptapenodes.com/nodes/overview/index.md) ## Getting Help If you encounter any issues while developing custom nodes, check out our [FAQ section](https://docs.griptapenodes.com/faq/index.md) or reach out to our community for assistance through these channels: - [Discord Community](https://discord.gg/gnWRz88eym) - [Griptape Nodes GitHub Repository](https://github.com/griptape-ai/griptape-nodes) # Making Custom Scripts ## Getting Started with Custom Script Development Creating your own custom scripts allows you to automate complex workflows, manipulate nodes programmatically, and extend Griptape Nodes functionality using Python. ## Understanding Retained Mode Retained mode provides a comprehensive interface for interacting with Griptape Nodes programmatically: - Create and manage flows and nodes - Set and retrieve parameter values - Establish connections between nodes - Execute and control flow operations - Access library information ## Custom Script Development Workflow There are two primary ways to develop and use scripts in Griptape Nodes: 1. **Using the Script Editor** - Create scripts directly in the Griptape Nodes script editor for immediate execution - Access the script editor through the Griptape Nodes interface - Write your script using retained mode commands - Execute directly from the editor to modify or control your flows 1. **Importing External Scripts** - Import pre-written scripts into the script editor - Create reusable script modules in external files - Import them using Python's import system in the script editor - Execute the imported functionality from within the editor The script editor currently serves as the primary entry point for all scripting operations in Griptape Nodes. ## Common Script Operations The retained mode interface offers a comprehensive set of functions for manipulating and controlling Griptape Nodes: - **Flow Management** - Create, delete, and query flows - **Node Operations** - Create, delete, and manage nodes - **Parameter Management** - List, get, and set parameter values - **Connections** - Create and manage connections between nodes - **Flow Execution** - Run, reset, and control flow execution ## Utility Script Types You can create various types of utility scripts to enhance your workflow: ### Node Duplication Create scripts that duplicate nodes with their properties and connections. ### Value Export and Import Develop scripts to export node values and import them into other flows. ### Flow Creation Build scripts that programmatically create entire flows with predefined nodes and connections. ## Advanced Scripting Techniques ### Working with Complex Parameters Retained mode supports indexed access for working with complex data structures like lists and dictionaries. ### Bulk Operations Scripts can automate repetitive tasks by performing operations on multiple nodes or connections in loops. ### Integration with External Libraries Combine retained mode with other Python libraries like pandas, numpy, or requests to extend functionality. ## Best Practices for Custom Scripts - Comment your code thoroughly for maintainability - Use functions to organize complex operations - Include error handling for robustness - Use descriptive variable names - Add logging for troubleshooting - Consider creating a reusable script library - Test scripts thoroughly before using in production ## Getting Help If you encounter any issues while developing custom scripts, check out our [FAQ section](https://docs.griptapenodes.com/faq/index.md) or reach out to our community for assistance through these channels: - [Website](https://www.griptape.ai) - [Discord Community](https://discord.gg/gnWRz88eym) - [GitHub Repository](https://github.com/griptape-ai/griptape-nodes) # Beginner tutorial (FTUE) # Tutorial Overview Welcome to your journey with Griptape Nodes! This learning path will guide you through everything from basic setup to creating complex, coordinated workflows with multiple agents. Each tutorial builds upon the knowledge gained in previous lessons, gradually introducing more complex concepts and techniques. - # [Lesson 1. Getting Started](https://docs.griptapenodes.com/ftue/00_tour/FTUE_00_tour/index.md) Now that you've [installed](https://docs.griptapenodes.com/installation/index.md) the Griptape Nodes Editor, it's time to launch and work in Griptape Nodes. Explore the interface, and understand how to add nodes to your workspace. This simple but foundational tutorial will prepare you for all future workflows. - # [Lesson 2: Prompt an Image](https://docs.griptapenodes.com/ftue/01_prompt_an_image/FTUE_01_prompt_an_image/index.md) Learn how to use the powerful [GenerateImage](https://docs.griptapenodes.com/nodes/image/create_image/index.md) node to transform text descriptions into vivid visual creations. This tutorial introduces you to one of the most popular and versatile nodes in the Griptape ecosystem. - # [Lesson 3: Coordinating Agents](https://docs.griptapenodes.com/ftue/02_coordinating_agents/FTUE_02_coordinating_agents/index.md) Discover how to coordinate multiple agents to perform sequential tasks. Examine a workflow that translates content between languages, teaching you the fundamentals of agent connections and execution chains. This tutorial introduces the concept of multi-step workflows. - # [Lesson 4: Compare Prompts](https://docs.griptapenodes.com/ftue/03_compare_prompts/FTUE_03_compare_prompts/index.md) Take your image generation to the next level by exploring a few different prompting methods. Compare basic prompts, GenerateImage "enhanced prompts", and custom agent-enhanced prompts to see how each affects your results. Learn how to craft detailed instructions for more precise creative control. - # [Lesson 5: Build a Photography Team](https://docs.griptapenodes.com/ftue/04_photography_team/FTUE_04_photography_team/index.md) Build an advanced system with specialized agents working together to create spectacular image prompts. Learn about rule sets and tools, and discover how to convert agents into reusable tools for enhanced functionality. This advanced tutorial brings together everything you've learned so far. ## What You'll Learn By completing this learning path, you'll master: - Setting up and navigating the Griptape Nodes interface - Creating and connecting different types of nodes - Working with AI image generation - Implementing rule sets and tools for specialized agents - Converting agents into reusable tools - Coordinating multiple agents in a single system Ready to start your journey with Griptape Nodes? Let's learn about the interface in [Lesson 1: Getting Started](https://docs.griptapenodes.com/ftue/00_tour/FTUE_00_tour/index.md) # Lesson 1: Getting Started Welcome to Griptape Nodes! This tutorial will guide you through the basics of this powerful visual workflow tool. You'll learn how to launch the application, navigate the interface, add and connect nodes, and run your first AI agent. By the end of this guide, you'll have the foundational knowledge needed to continue with subsequent tutorials. ## What We'll Cover In this tutorial, topics include: - Launching Griptape Nodes - Navigating through the landing page to a workflow - Getting familiar with the Griptape Nodes Editor - Adding nodes to the workspace - Learning what can connect to what - Run an agent ## Launch Griptape Nodes To launch Griptape Nodes, open your terminal and run one of the following commands: ``` griptape-nodes ``` Or use the shorter version: ``` gtn ``` After executing the command, your browser should automatically open to . If it doesn't, simply ctrl-click (or cmd-click on Mac) the link displayed in your terminal and select "Open in browser" if prompted. Of course, you can always simply bookmark it in your browser, and come back to it that way. Tip For the best experience, keep two browser windows open side-by-side: this tutorial in one, and your Griptape Nodes session in the other. ## The Landing Page When your browser opens, you'll be greeted by the Griptape Nodes landing page. This page displays several template workflows that showcase different things we want to introduce you to. Once you start saving your own workflows, they will appear here in order of newest-to-oldest. These sample workflows are excellent resources for learning about Griptape Nodes' capabilities, but for now, let's start "from scratch". ## Create a new workflow from scratch On the landing page, locate and click on the **"Create from scratch"** tile. This action opens a blank workspace where you can build workflows. ## Get familiar with the Griptape Nodes interface Once you're in the Workflow Editor, take a moment to familiarize yourself with the interface: ### Libraries The most important area to focus on initially is the left panel, the node library. At the top, you'll find the **Create Nodes** section. This panel houses all the standard nodes that come pre-packaged with Griptape Nodes. Each node serves a specific function. As you become familiar with Griptape Nodes, you'll learn how these nodes work and how to combine them to create powerful automations. ## Adding Nodes to the Workspace There are three interactive methods to creating nodes (and even more in [Retained Mode](https://docs.griptapenodes.com/retained_mode/index.md)) **Drag and Drop**: Click and hold on a node from the left panel, then drag it onto your workspace. #### Drag and Drop **Double-Click**: Simply double-click any node in the left panel to automatically place it in the center of your workspace. #### Double Click **Shift+A or Double-Click**: Pressing Shift+A or double-clicking on the flow editor brings up a search field. You can type to find the node you want, and enter to create it. #### Shift+A or Double-Click Search After adding a node, you can: - Click and drag to reposition it on the workspace - Edit its values and behaviors - Connect it to other nodes ## Connecting Nodes Let's create some nodes using the first technique mentioned above - dragging and dropping a node from the library to the workspace to create three nodes. 1. An **Agent** ( agents > Agent ) - This is an agent that interacts with LLMs (Like OpenAI ChatGPT or Anthropic Claude) 1. Open the agents category in the sidebar 1. Drag the Agent node to the workspace and release to create it Info For brevity, we'll describe this as ( category > Node ), so for an Agent, we'd shorthand the above with ( agents > Agent ). Try the same process with the next two nodes. 1. A **FloatInput** ( number > FloatInput ) - A node to input decimal numbers (floats) 1. A **TextInput** ( text > TextInput ) - A node to input text Experiment with connections by dragging from ports on both input nodes to various ports on the Agent. Try multiple combinations and observe that not all connections succeed. This happens because parameters can only connect directly when their data types are compatible: - The **TextInput** node outputs **text**, so it can connect to any Agent parameter that accepts text. - The **FloatInput** node outputs decimal numbers (**floats**), which can't connect to any parameter on the Agent. Don't worry that you can't connect the FloatInput to anything - that's exactly the point. This node was included *here* solely to demonstrate how not all parameters can connect to each other. The FloatInput node is indeed very useful, it's just not one we can use with the other nodes currently in *this* workflow. Pro Tip Use the port colors as a visual guide for compatibility. Ports with matching colors can connect to each other. ## Use an Agent For now, lets try another method to wipe the slate clean, and get a real AI interaction under our belt: 1. Go to the File menu and choose **New**. 1. In your new workflow, make an Agent any way you prefer 1. Type a question into the agent's prompt field. You can use "Who trained you?" to verify the AI service, or simply enter any question you'd normally ask a chatbot. 1. Click the play button icon in the top right corner of the agent to run the node 1. When text appears, read the output. You just interacted with a Large Language Model (LLM). If you kept the default settings, you specifically used OpenAI ChatGPT (GPT-4.1). While this experience might seem similar to using OpenAI ChatGPT or Anthropic Claude on the web, the real power comes from using LLMs alongside other components in Griptape Nodes. Take another look at the library panel on the left to see all the other nodes available. We're just getting started—there's so much more to explore! ## Summary In this tutorial, we covered how to: - Launch Griptape Nodes - Navigate through the landing page to a workflow - Get familiar with the Griptape Nodes Editor - Add your first nodes to the workspace - Learn about what can connect to what - Run an agent ## Next Up In the next section: [Lesson 2: Prompt an Image](https://docs.griptapenodes.com/ftue/01_prompt_an_image/FTUE_01_prompt_an_image/index.md), we'll start in on the good stuff: making images! # Lesson 2: Prompt an Image Welcome to the second tutorial in our Griptape Nodes series! This guide focuses on getting familiar with the GenerateImage node. While the workflow itself is exceedingly simple (consisting of just this single node), the GenerateImage node will be your primary workhorse for image generation in your early projects. This powerful node truly is the cornerstone for creating a wide variety of visual content. ## What we'll cover In this tutorial, you will: - Learn how to open saved workflows - Learn about the GenerateImage node - Generate images using text prompts ## Navigate to the Landing Page To begin this tutorial, you'll need to return to the main landing page. Click on the navigation element at the top left of the editor to go back to where all the template workflows and your own saved files are displayed. ## Open the Image Prompt Example On the landing page, locate and click on the **"Prompt an Image"** tile to open this example workflow. ## Understand the GenerateImage Node When the example loads, you'll notice it consists of just a single node. Don't be fooled by its simplicity – this node is one of the most powerful tools in Griptape Nodes and will likely feature prominently in your future flows. This node has been configured to handle many tasks that would typically require a more complex flow, making it perfect for getting started with AI image generation. ## Generate Images Using Text Prompts The primary point of interaction for this node is the text prompt field where you describe what image you want the AI to create. To generate your first image: 1. Locate the text prompt field in the node 1. Type a description for the image you want to create Now, run your node. There are three UI buttons but those perform only two distinct operations: 1. Run the *whole* workflow: 1. Click the **Run Workflow** button at the top of the editor. This executes the entire workflow from start to finish. All nodes will be processed in sequence according to their connections. 1. Run a single node in the workflow (two methods that do the same thing): 1. Click the **Run Node** button in the top right corner of a specific node. 1. Or, select a node and click **Run Selected** from the toolbar. Running a single node can be very useful for testing or debugging specific parts of your workflow, or simply getting things to run faster if you don't care about updating other parts of the workflow just yet. The difference is in scope - the first option runs everything, while the second options run just the selected node and everything that precedes it that it may need. Generation times For those new to image generation: generation operations can take time. Please notice the yellow outlines and spinning **Running** icon on nodes, where the **run node** icon was before execution began. These visual hints tell you which nodes are currently resolving, letting you know that things are functioning as expected. ## Experiment with Different Descriptions Let's try generating some images with different prompts: 1. **First Example**: The workflow loads with "A potato making an oil painting" in the prompt field. Run the flow 1. **Second Example**: Change the prompt to "A potato doing aerobics in 70s workout attire" and run the flow again Notice how dramatically different the results are just by changing a few words in your prompt. This demonstrates the flexibility and power of the GenerateImage node. Anything you can describe, you can generate. ## Summary In this tutorial, we covered: - How to open saved workflows - The GenerateImage node - How to generate images using text prompts The GenerateImage node is a fundamental building block for creative flows in Griptape Nodes. As you progress, you'll discover how to combine it with other nodes to develop even more powerful applications. ## Next Up In the next section: [Lesson 3: Coordinating Agents](https://docs.griptapenodes.com/ftue/02_coordinating_agents/FTUE_02_coordinating_agents/index.md), we'll learn how to get AIs to team up and form a bucket-brigade through a workflow. # Lesson 3: Coordinating Agents Welcome to the third tutorial in our Griptape Nodes series! In this guide, you'll learn how to coordinate multiple agents within a workflow to perform sequential tasks—specifically, translating stories between languages and summarizing them. ## What we'll cover In this tutorial, we will: - Study then recreate a translation workflow between agents working sequentially - Discover how to "merge texts" to take outputs and modify them into new prompts - Learn about the "exec chain" for controlling workflow execution order - Build more into the template workflow to add a summarization task By the end of this journey, you'll understand how to create workflows where agents build upon each other's work, passing information seamlessly between specialized tasks. This foundation will prepare you for creating sophisticated AI systems that can handle multi-step processes requiring different types of intelligence at each stage. Let's begin by examining a simple translation workflow that demonstrates these principles in action. ## Navigate to the Landing Page To begin this tutorial, go to the landing page via the nav bar with the Griptape Nodes logo in the top left. Locate and open the example workflow called "coordinating_agents" at the top of the page. ## Explore the Template Workflow When the template loads, you'll see a workflow with the following components: 1. **Agent Node (spanish_story)**: Generates a four-line story in Spanish 1. **Merge Text Node**: Combines the Spanish story with "Rewrite this in English" 1. **Second Agent Node (to_english)**: Translates the merged prompt into English 1. **Display Text Node**: Shows the final English translation This workflow demonstrates how multiple agents can each perform their own distinct "jobs." By connecting one agent's output to another through a **MergeTexts** node, you create *new* prompts that direct the next agent's behavior. ## How we're using the MergeText node here All a **MergeTexts** node does is combine incoming texts using the "merge string" as a separator. The default merge string is two newlines: `\n\n`. In this example, I've typed "Rewrite this in English:" into **input_1** of the MergeTexts node and connected the output of my **spanish_story** node to **input_2**. When run, the **MergeTexts** node will output: > Rewrite this in English: > > Bajo la luna, el río cantó, Un secreto antiguo en su agua dejó. La niña lo escuchó y empezó a soñar, Que el mundo era suyo, listo para amar. You can see how this method of creating new prompts out of the results of other nodes can allow for a sophisticated multi-agent workflow where the first agent writes a Spanish story, and the second agent translates it to English. Your final output will be the English translation of whatever unique Spanish story was generated. Info You should expect variability in these from run-to-run. That's okay! Remember, talking with an agent can in a way be like talking to a person. You may get slightly different answers if you ask them the same question many times. ## Build a sibling workflow This is what we're aiming to get to: Now it's time to build your own workflow. Create another nearly identical flow just below this one to practice creating and connecting nodes. Add the following to your workflow: 1. Two **Agents** ( agents > Agent ) - These are agents that interact with LLMs (Like ChatGPT, or Claude) 1. A **MergeTexts** node ( text > MergeTexts ) - A node to accept multiple texts and output them "merged" 1. A **DisplayText** ( text > DisplayInput ) - A node to simply display text output for easier viewing ## Configure the First Agent Set up your first agent to generate content in your chosen language: 1. In the first agent node, enter: `Write me a four line story in [your chosen language]` (e.g., Mandarin, French, etc.) 1. This agent will generate the initial story that we'll translate ## Connect to the MergeTexts Node Next, prepare the translation prompt: 1. Type directly into the MergeTexts node's **input_1** field and enter: `Rewrite this in English` 1. Connect the output from the first Agent to **input_2** of the MergeTexts node ## Configure the Second Agent Set up the translator agent: Connect the output of the MergeTexts node to the second Agent node's **prompt**. This agent will now receive both the original story, and the instruction to translate it ## Display the Result To see the final translation: 1. Connect the output of the second Agent to the DisplayText node 1. Run your workflow. 1. After the workflow runs, this node will show the translated English text: ## Understanding Execution Order (Exec Chain) A key concept in Griptape Nodes is the execution chain. As workflows become more complex, controlling the order of execution becomes important. Let's explore this concept. 1. Notice the "exec in" and "exec out" pins (half-circle connectors) on nodes 1. These define the order in which nodes run 1. For complex workflows, connect the exec ports in the order you want execution to occur 1. This ensures nodes run in the intended sequence, even with complex data flows Info Griptape Nodes will automatically determine the execution order of nodes by analyzing their dependencies. However, when you need more precise control over the execution sequence, you can use the exec chain feature. This provides a way to explicitly define the order you want when the automatic dependency detection might not align with your intended behavior. There is no cost or penalty to using the exec chain anytime you want, except for the possibility of forcing things to execute in a faulty order. For most simple flows, it is unnecessary. ## Expand the Workflow: Summarize Multiple Stories Let's enhance our workflow to handle summarization of *all* the stories: 1. Add another new **MergeTexts** node that combines both English translations 1. In this merge text node, enter: `Summarize both these stories` in **input_1** 1. Connect both the translation nodes' **outputs** into **input_1** and **input_2** on the MergeTexts node 1. Add another **Agent** node 1. Connect the MergeTexts **output** into the **prompt** for your new agent 1. Connect the agent **output** to a new **DisplayText** node 1. Optionally, use exec chain connections to ensure this summary step runs last (you can even connect *everything* up to run in the order you want) ## Run the Complete Workflow Execute your expanded workflow and observe the process: 1. The first agents generate stories in different languages 1. The merge text nodes create prompts to translate them 1. The second agents translate the stories into English 1. The summary agent combines and summarizes both translations 1. The display nodes show all the results Info Again, remember! Look for this *construction* in the response you get, not that it matches what you see here - it is likely to be wildly different! ## Summary In this tutorial, we covered: - How a workflow can hand things off between agents to perform tasks like translation - Discovered how "merge texts" allows you to take outputs and modify them into new prompts - Learned about the "exec chain" for controlling workflow execution order - Built more into the template workflow to add a summarization task ## Next Up In the next section: [Lesson 4: Compare Prompts](https://docs.griptapenodes.com/ftue/03_compare_prompts/FTUE_03_compare_prompts/index.md), we'll learn how to get AIs to bucket-brigade, where agents pass work sequentially, through flows! # Lesson 4: Compare Prompts Welcome to our fourth Griptape Nodes tutorial! Building on what you already know about multi-agent systems and image generation, we're diving into the exciting world of prompt engineering. You'll discover how to use ready-made solutions and create your own prompt-building workflows to get consistently better results. Plus, we'll peek behind the curtain to see how the "Enhance Prompt" feature actually works its magic! ## What we'll cover In this tutorial, we will: - Compare three different prompting approaches for image generation - Understand the "Enhance Prompt" feature - Learn about custom prompt enhancement flows ## Navigate to the Landing Page To begin this tutorial, return to the main landing page by clicking on the Griptape Nodes logo at the top left of the interface. ## Open the Compare Prompts Example On the landing page, locate and click on the **"Compare Prompts"** tile to open this example workflow. ## Understand the Workflow Structure When the example loads, you'll see a workflow with multiple nodes: This workflow contains: - Two TextInput nodes - **basic_prompt** - **detail_prompt** - A MergeTexts node, **assemble_prompt** - Three GenerateImage nodes: - **basic_image** - **enhanced_prompt_image** - **bespoke_prompt_image** - An agent node, **bespoke_prompt** We'll run each part of the workflow individually to compare the results of different prompting techniques. ## Comparing Different Prompting Methods ### Method 1: Basic Prompt Let's start with the most straightforward approach: 1. Locate the TextInput node with our simple prompt: "A capybara eating with utensils" 1. Follow the connection to the first GenerateImage node 1. Notice that **Enhance Prompt** is set to **False** on this node 1. Run just this node by clicking the **run node** button at the top right of the node Observe the resulting image. This shows how the AI interprets your direct, simple prompt. ### Method 2: Using Enhance Prompt Feature For the second method, we'll use the same simple prompt but using the GenerateImage's built-in pronpt enhancement: 1. Find the second GenerateImage node that receives the same simple prompt 1. Notice that **Enhance Prompt** is set to **True** on this node 1. Run this node using the it's **Run Node** button Compare this result with the first image. You should see a much more complex and artistic interpretation. The difference is striking—same simple prompt, but the enhanced version produces a significantly more detailed and visually appealing image. ### Method 3: Bespoke Agent-Enhanced Prompt The third method demonstrates how we can create our own custom prompt enhancement: 1. Take a look at how we're using the MergeTexts node and an agent to create a prompt: - This detailed prompt is connected into the first input of the MergeTexts node - The same simple prompt from the other examples is connected to the second input of the MergeTexts node - the "Merge Texts" node then combines these - An agent node then processes this combined prompt to generate another prompt for image generation 1. Run the agent node 1. Examine the output of the agent (if you want a closer look, try adding a DisplayText node and hook it up!) You'll see that the agent has created a much more elaborate prompt that addresses all the specifications: - Unique details about the capybara - Specific time of day (late afternoon sunlight) - Depth of field information - Color palette guidance - Professional photography elements - Finally, run the third GenerateImage node (with **"Enhance Prompt** set to **False**). It uses this agent-enhanced prompt we just covered. Notice how this image contains specific details and artistic elements compared to the first, but is about the same level of sophistication as the second. ## Understanding What's Happening Behind the Scenes Here's the key insight: When you toggle the **Enhance Prompt**"\*\* feature to **True**, Griptape is automatically doing what we just demonstrated manually with the bespoke route. It is: 1. Taking your basic prompt and a detailed prompt 1. Running it through an agent with enhancement instructions (verbatim what we wrote) 1. Using the enhanced output for image generation By creating our own explicit enhancement flow, we gain *full* control over exactly how we want the prompt to be improved or changed. ## Applications and Best Practices Based on what we've learned, consider these approaches for your own projects: - Use basic prompts (with Enhance Prompt off) for quick, straightforward image generation - Enable "Enhance Prompt" when you want general improvements with minimal effort - Create custom agent-based enhancement flows when you need precise control over specific artistic elements or want to emphasize particular aspects. ## Summary In this tutorial, we covered: - Three different prompting approaches for image generation - Learned about the "Enhance Prompt" feature - Learned about custom prompt enhancement flows These techniques demonstrate the power of prompt engineering—the art of crafting and refining prompts to achieve specific, high-quality outputs from AI systems. ## Next Up In the next section: [Lesson 5: Build a Photography Team](https://docs.griptapenodes.com/ftue/04_photography_team/FTUE_04_photography_team/index.md), we'll learn about Rulesets, Tools, and converting Agents into tools to achieve even more sophisticated coordination! # Lesson 5: Build a Photography Team Welcome to the fifth and final tutorial in our Griptape Nodes New-Users series! In this guide, we'll build on our prompt engineering concepts and introduce additional nodes that provide greater precision and control. We'll examine a sophisticated system of coordinated agents that collaborate like a team to generate spectacular image prompts, while also learning several important new nodes along the way. ## What we'll cover In this tutorial, we will: - Learn about what rule sets are and what they do - Learn about tools - Learn about "list" parameters and interacting with them - See how we can convert agents *into* tools - Coordinate multiple agents-as-tools through a single orchestrator - Generate high-quality image prompts through team collaboration ## Navigate to the Landing Page To begin this tutorial, return to the main landing page by clicking on the Griptape Nodes logo at the top left of the interface. ## Open the Photography Team Example On the landing page, locate and click on the **"Build a Photography Team"** tile to open this example workflow. ## Overview of the Workflow When the example loads, you'll notice this is the most complex workflow we've seen so far: The workflow consists of several key components: - Multiple specialized Agents - Cinematographer - Color Theorist - Detail Enthusiast - Image Generation Specialist - Rulesets for each RuleSetList - RuleSetLists for each Agent - AgentToTool converters - A ToolList - An orchestrator Agent - A GenerateImage node You'll notice that the pattern upstream of the TooList repeats the same structure four times. Each upstream segment follows identical logic and structure, differing only in the specific data being processed. Let's examine just one instance in detail, as understanding this single example will give you the conceptual framework for all four occurrences. **RuleSet->RuleSetList->Agent->AgentToTool** ## RuleSets RuleSets are nodes that define how agents should approach their tasks. Within each RuleSet, you can have multiple rules. Rules are understood to be separated by appearing on their own line using intentional line breaks. Don't worry about word wrapping - if text simply flows to the next line, it's still considered part of the same rule. Every RuleSet requires a unique name, which agents use to identify and distinguish between different RuleSets. When working with multiple RuleSets, choosing clear, distinctive names becomes especially important for proper organization and reference. Tip This workflow is constructed so that each agent only has one RuleSet, so the chance for a RuleSet name collision is zero, but it's still good practice to name these well. The Cinematographer RuleSet you see above contains detailed instructions covering framing, composition, and visual storytelling techniques. These rules influence how the agent tends to respond—establishing preferred patterns for its outputs. For more predictable performance, you guide the agent through these RuleSets. It's helpful to ensure your rules maintain reasonable consistency with one another. Contradictory or conflicting instructions may lead to mixed results, as the agent attempts to balance competing priorities in its responses. ## RuleSetLists RuleSetLists function as collectors or containers, similar to another node we'll talk about momentarily, ToolLists. RuleSetLists serve a specific organizational purpose: allowing you to gather multiple Rulesets into a single collection that can connect to "list" parameters—parameters designed to accept multiple items of the same type. I've included RuleSetLists here primarily to introduce you to them rather than for any immediate functional value. Optional Playtime If you'd like to independently explore changing the graph harmlessly to work around the RuleSetList: 1. On the Agent, uncollapse the **Advanced Options** 1. Disconnect the RuleSetList from the Agent's **ruelsets** parameter 1. The agent's display will change slightly, and you should see **add item to rulesets** underneath the **rulesets** parameter. Click that, and you should see a new port will appear. This new port takes in a *single* RuleSet. 1. Connect directly from the RuleSet's **ruleset** parameter to the new port. ## Understanding Tools in Griptape Nodes **Tools** extend an agent's capabilities by connecting it to external functions and services. When faced with tasks beyond its internal knowledge—like retrieving current data or performing calculations—tools provide the bridge. The agent decides when to use a tool, formats the appropriate request, and interprets the returned results. This transforms agents from isolated text generators into assistants that can access databases, run code, or manipulate files while maintaining their reasoning abilities throughout a conversation. Tools consist of two key components: 1. **Underlying code**: This flexible, open-ended component can be anything from simple calculations to complex integrations. 1. **Description**: This precise element communicates the tool's purpose and appropriate use cases to the agent, so it knows when and how to use the tool. As of initial release, we have just a few important tool nodes like a Calculator and DateTime, but we've also included what can easily become the most adaptive tool of them all: Agents *as* Tools. ## Converting Agents to Tools Griptape Nodes allows you to turn entire agents into tools. This allows a primary agent to delegate specific tasks to specialized agents, creating a hierarchical system where expertise is distributed. When agents can "stay in their lane" and focus on specific domains, they perform more reliably—just like people who can concentrate on one task rather than juggling many simultaneously. This crazy-powerful concept is part of Griptape Nodes via the **AgentToTool** converter node. While fully-formed tools typically handle both components (underlying code, description) under-the-hood, agents-as-tools require you to provide the description part yourself. This is where you establish the boundaries and expectations for how that agent should function as a tool. The description you write helps the orchestrator agent understand when and how to use your specialized agent-as-tool. ## The ToolList node The ToolList functions similarly to the RuleSetList discussed earlier, but for tools instead of RuleSets. It serves as a collector that gathers multiple tools into a single collection, which can then connect to parameters accepting tool lists. Just as the RuleSetList organizes RuleSets, the ToolList provides a streamlined way to pass multiple tools to an agent. This becomes especially valuable when your agent needs access to several specialized tools. This organizational pattern is consistent throughout Griptape Nodes - "list" nodes act as waypoints, collecting multiple items of the same type before sending them to components that work with collections. Optional Playtime Just as you might have bypassed the RuleSetList earlier for practice, you can do the same with the ToolList. Simply disconnect it from the orchestrator agent, click "add item to tools" to create a new port, and connect your tool directly. This change won't affect how your workflow functions. ## The Orchestrator The central component of this workflow is the orchestrator agent: 1. Locate the orchestrator agent 1. Notice it has its own rule set: ``` You are creating a prompt for an image generation engine. You have access to topic experts in their respective fields. Work with the experts to get the results you need. You facilitate communication between them. If they ask for feedback, you can provide it. Ask the image generation specialist for the final prompt. Output only the image generation prompt. Do not wrap it in markdown context. ``` 1. The orchestrator's prompt is actually very simple: "Use all the tools at your disposal to create a spectacular image generation prompt about a skateboarding lion." ## How the Workflow Functions The entire system operates through this process: 1. The orchestrator receives a relatively simple prompt 1. It has access to all specialized tools (converted agents) 1. The orchestrator can call upon: - The Cinematographer for framing and composition guidance - The Color Theorist for color palette recommendations - The Detail Enthusiast for intricate details to include - The Image Generation Specialist for formatting the final prompt 1. The final output connects to the GenerateImage node ## Running the Workflow Let's execute the workflow to see the photography team in action: 1. Run the workflow 1. Observe as the orchestrator calls upon different specialized tools (it is a quick flash, but you can see them all "tagged in") 1. The final output is collected into a sophisticated image prompt 1. This prompt is then used to generate the image ## Summary In this tutorial, we covered: - Learning about what rule sets are and what they do - Learning about tools - Seeing how we can convert agents *into* tools - Seeing what a "team" of specialized AI experts looks like - Coordinating multiple agents through an orchestrator - Generating high-quality image prompts through team collaboration These advanced techniques showcase the full power of Griptape Nodes for creating complex, collaborative AI systems. Thank you for completing this series of tutorials. We're excited to see what you'll build with these powerful tools! If you're more in the mood to keep going to something more advanced, please continue on to our "I'm A Pro" series (\*Coming Soon!) # External services # How to get and use an OpenAI API Key While Griptape Nodes provides easy access to OpenAI models by default via your Griptape Cloud account and it's key (this is all handled automatically during install), you might want to connect directly to your own OpenAI account. This gives you full control over your usage, settings, and permissions. If that's what you'd like to do, getting your OpenAI API key is the simple first step. ## Account and API Key Creation Before you can get API keys for your OpenAI account, you'll need an OpenAI account. To begin, head to Info If you already have an account, go ahead and skip ahead to [Step 2](#2-create-an-api-key) ### 1. Create an OpenAI account 1. You can either go to the Log In (choose any option, they all create the same account), and then choose "sign up", or go directly to 1. Complete the registration process ### 2. Create an API Key 1. If you're not there already, go back to and from the **Log In** button in the top right, select the **API Platform** option 1. Go to the "Dashboard" area - look for it near the top of the page after logging in. 1. Navigate to the **API Keys** section - look for it on the left after opening the dashboard. Account Verification Required If you encounter issues during key creation, ensure you've **verified** your account which should have been part of the account creation the website guided you through. Complete the verification process before continuing. That should present you a screen, that towards the top right looks like this: 1. Click **Create New Secret Key**, which should take you to a model like this: 1. Set the key permissions to "Read-Only" (feel free to research other options, this is recommended merely for expedience here) 1. Click **Create Secret Key**. That will bring up a window with your new API key. Read and understand the messages there; this really is the only time you'll be able to see or copy this key. 1. Copy and securely store your API key Security Notice It is recommended to save this token in a password locker or secure notes app, so you can find it, but also keep it secure. Your access token is a personal identifier for requests made to OpenAI services. Never share it with anyone, and take precautions to avoid displaying it in screenshots, videos, or during screen-sharing sessions. Treat it like you would a credit card number. 1. Click **Done** to close the key window ## Add Your Secret Key to Griptape Nodes Settings Overview Now that you've set up your OpenAI account, you need to configure Griptape Nodes to use your secret key. This process is straightforward. ### 1. Open the Griptape Nodes Settings Menu 1. Launch Griptape Nodes 1. Look for the **Settings** menu located in the top menu bar (just to the right of File and Edit) 1. Click on **Settings** to open the configuration options ### 2. Add your OpenAI Secret Key in API Keys & Secrets 1. In the Configuration Editor, click on **API Keys and Secrets** on the left 1. Scroll down to the **OPENAI_API_KEY** field 1. Paste the secret key you just generated into this field 1. Close the Configuration Editor to automatically save your settings Setup Complete After completing these steps, you should have the ability to use models via your own account credentials. # How to get and use an xAI Grok API key Grok is a family of Large Language Models (LLMs) developed by xAI. You can access these models via the GrokPrompt config node. To make use of these, however, you'll need to have an XAI account, and generate an API key. It's worth noting as well, that xAI is a paid service, and to make use of it, you'll need to set up billing details on their website. ## Account and API Key Creation Before you can get API keys for your xAI account, you'll *need* a xAI account. To begin, head to Info If you already have an account, go ahead and skip ahead to [Step 2](#2-set-up-billing) ### 1. Create a xAI account 1. Click on the console login option, or navigate to 1. Complete the signup process, it is extremely straightforward. ### 2. Set Up Billing Billing Required Before using xAI models with Griptape Nodes, be aware that xAI *requires* billing information to be set up. Without this step, the models won't be available for use in your Nodes. If you attempt to run Nodes using xAI without completing this billing setup, your workflows will fail as the service will reject the account credentials. ### 3. Generate an API Key 1. Navigate to the API keys section; there are multiple links to take you there. 1. Click on "Create API Key" 1. Name your API key (suggested name: GriptapeNodes!) 1. Click **save** 1. Copy and securely store your API key Security Notice It is recommended to save this token in a password locker or secure notes app, so you can find it, but also keep it secure. Your access token is a personal identifier for requests made to OpenAI services. Never share it with anyone, and take precautions to avoid displaying it in screenshots, videos, or during screen-sharing sessions. Treat it like you would a credit card number. ## Add Your API Key to Griptape Nodes Settings Overview Now that you've set up your xAI account, you need to configure Griptape Nodes to use your API key. This process is straightforward. ### 1. Open the Griptape Nodes Settings Menu 1. Launch Griptape Nodes 1. Look for the **Settings** menu located in the top menu bar (just to the right of File and Edit) 1. Click on **Settings** to open the configuration options ### 2. Add your xAI API Key in API Keys & Secrets 1. In the Configuration Editor, click on **API Keys and Secrets** on the left 1. Scroll down to the **GROK_API_KEY** field 1. Paste the API key you just generated into this field 1. Close the Configuration Editor to automatically save your settings Setup Complete After completing these steps, you should have the ability to use models via your own Grok.ai account credentials. # Setup for Nodes that use Hugging Face ## Account and Token Creation This guide will walk you through setting up a Hugging Face account, creating an access token, and installing the required models for use with Griptape Nodes. Info If you already have an account skip ahead to [Step 2](#2-create-an-access-token) ### 1. Create a new account on Hugging Face 1. Go to 1. Click **Sign Up** in the top-right corner 1. Complete the verification step to prove you're not a robot ### 2. Create an Access Token 1. Access Your Account Settings 1. Log in to your Hugging Face account 1. Click on your profile icon in the top right corner 1. Select **Settings** from the dropdown menu (or go directly to [Settings](https://huggingface.co/settings/profile/)) Email Verification Required If you encounter issues during token creation, ensure you've verified your email address. Complete the verification process before continuing. 1. Navigate to **Access Tokens** in the settings menu 1. Click **Create new token** in the top right area 1. Select **Read** as the token type for the access you'll need 1. Give your token a descriptive name (GriptapeNodes, for example) 1. Click **Create Token**. That will bring up a window with you new token in it. Read and understand the messages there; this really is the only time you'll be able to see or copy this key. 1. Copy and securely store your token 1. Click **Done** to close the token window. Security Notice It is recommended to save this token in a password locker or secure notes app, so you can find it, but also keep it secure. Your access token is a personal identifier for requests made to Hugging Face services. Never share it with anyone, and take precautions to avoid displaying it in screenshots, videos, or during screen-sharing sessions. Treat it like you would a credit card number. ## Install Required Files Now that you have a token associated with your account, you can install the Hugging Face CLI (Command Line Interface) to interact with Hugging Face from the command line. ### 1. Install the Hugging Face CLI Open a terminal and run: ``` pip install -U "huggingface_hub[cli]" ``` For more information, visit the [official CLI documentation](https://huggingface.co/docs/huggingface_hub/main/en/guides/cli). ### 2. Login with Your Token In your terminal, authenticate with the access token you made earlier: ``` huggingface-cli login ``` You'll be prompted to enter your token. ### 3. Install Models as Required Each Hugging Face node is designed to work with specific models. While some nodes work with only one model, others are compatible with multiple options. The table below outlines which models can be used with each node: | Node | Compatible Model(s) | | ----------------- | -------------------------- | | SPAN Upscale | 4x-ClearRealityV1.pth | | Flux | FLUX.1-dev, FLUX.1-schnell | | Flux Post Upscale | FLUX.1-dev, FLUX.1-schnell | ## Model Installation Considerations You have two options for model installation. Your choice depends on your specific workflow requirements, available disk space, and internet connection speed. 1. **Selective Installation**: Install only the specific models needed for the nodes you plan to use. - **Advantages**: Reduced download time and disk space usage. - **Disadvantages**: Limited functionality until additional models are installed. 1. **Complete Installation**: Download all available models. - **Advantages**: Full access to all node capabilities without additional downloads. - **Disadvantages**: Longer initial download time and greater disk space requirements. Download Time These model downloads are quite large an may collectively take anywhere from 30 minutes to several hours to complete, depending on your internet connection speed. You can continue to the last step while downloads are ongoing. Download Location Models will be downloaded into your Hugging Face Hub Cache Directory. To see where this is, you can use the huggingface-cli, and look for HF_HUB_CACHE. Running this command in the terminal will produce a list with several entries ``` huggingface-cli env ``` Look for an entry that starts with `- HF_HUB_CACHE:` ``` - HF_HUB_CACHE: /Users/jason/.cache/huggingface/hub ``` #### For SPAN Upscale ``` huggingface-cli download skbhadra/ClearRealityV1 4x-ClearRealityV1.pth ``` #### For Flux and Flux Post Upscale ``` huggingface-cli download black-forest-labs/FLUX.1-schnell ``` #### For FLUX.1-dev ``` huggingface-cli download black-forest-labs/FLUX.1-dev ``` Download errors It is possible to encounter errors during download that start like this: ``` Cannot access gated repo for url https://huggingface.co... ``` The end of those errors will contain a link, though, with instructions to request access. Follow those instructions: Visit to ask for access. When that is successful you should see a message about being granted access, and you can try to download again. ## Add Your Token to Griptape Nodes settings Overview Now that you've set up your Hugging Face account and installed the required models, you need to configure Griptape Nodes to use your token. This process is straightforward. ### 1. Open the Griptape Nodes Settings Menu 1. Launch Griptape Nodes 1. Look for the **Settings** menu located in the top menu bar (just to the right of File and Edit) 1. Click on **Settings** to open the configuration options ### 2. Add your Hugging Face Token in API Keys & Secrets 1. In the Configuration Editor, locate **API Keys and Secrets** in the bottom left 1. Click to expand this section 1. Scroll down to the **HUGGINGFACE_HUB_ACCESS_TOKEN** field 1. Paste your previously created token into this field 1. Close the Configuration Editor to automatically save your settings Setup Complete After completing these steps, the Hugging Face Nodes should be ready to use in Griptape Nodes! # MCP integration # Connect Your Agent to the World Think of MCP (Model Context Protocol) as a universal translator that lets your Agents talk to various apps and services. Instead of your Agent being stuck in its own little world, MCP lets it reach out and use real tools, access real data, and work with real applications. ## What Can Your Agent Do with MCP? Using MCP, Agents can now: - **Work with your files** - Read, write, and organize documents on your computer - **Use professional software** - Control 3D modeling tools like Blender and Maya - **Search the web** - Find information using search engines like Brave - **Send messages** - Post updates to Slack channels and communicate with your team - **Manage databases** - Store and retrieve information from databases - **Remember things** - Keep track of important information between conversations ## Why This Matters Before MCP, your Agent was like a smart assistant locked in a room with no windows. Now it's like giving that assistant keys to the entire building - it can go anywhere, use any tool, and help you with real work. **Real examples:** - Ask your Agent to "Rename all the models in my Blender scene to .." and it will actually connect to Blender and rename them - Tell it to "Find the latest news about AI" and it will search the web and bring back real results - Say "Send a project update to the team" and it will post to your Slack channel - Request "Organize my documents" and it will actually move and sort your files ## Connection Types There are two official ways your Agent can connect to other applications: 1. **[Local Apps](https://docs.griptapenodes.com/how_to/mcp/connection_types/stdio/index.md)** - Connect to software on your computer **Examples**: [Fetch](https://docs.griptapenodes.com/how_to/mcp/servers/fetch/index.md) (web content), [Filesystem](https://docs.griptapenodes.com/how_to/mcp/servers/filesystem/index.md) (your files), [Time](https://docs.griptapenodes.com/how_to/mcp/servers/time/index.md) (dates and schedules) **Best for**: Software you have installed on your computer 1. **[Web Services](https://docs.griptapenodes.com/how_to/mcp/connection_types/streamable_http/index.md)** - Connect to web-based applications and services **Examples**: [Exa](https://docs.griptapenodes.com/how_to/mcp/servers/exa/index.md) (web search), live data feeds, real-time updates **Best for**: Web-based applications, remote services, and real-time communication ### Additional Options - **[Server-Sent Events (SSE)](https://docs.griptapenodes.com/how_to/mcp/connection_types/sse/index.md)** - ⚠️ Deprecated (now part of Streamable HTTP) - **[WebSocket](https://docs.griptapenodes.com/how_to/mcp/connection_types/websocket/index.md)** - Custom transport (may be implemented by clients/servers) ## Ready to Get Started? 1. **[Getting Started Tutorial](https://docs.griptapenodes.com/how_to/mcp/getting_started/index.md)** - Step-by-step tutorial using the Fetch MCP server 1. **[Using MCPTask with Agents](https://docs.griptapenodes.com/how_to/mcp/mcp_task_agents/index.md)** - How to make your AI agents use these tools 1. **[MCP Server Rules](https://docs.griptapenodes.com/how_to/mcp/rules/index.md)** - Guide agents with custom rules for each MCP server 1. **[Local Models with Agents](https://docs.griptapenodes.com/how_to/mcp/advanced_local_models/index.md)** - Use local AI models for sensitive data processing 1. **[Example MCP Servers](https://docs.griptapenodes.com/how_to/mcp/servers/index.md)** - Setup guides for some example servers ## Need More Information? - [Official MCP Documentation](https://modelcontextprotocol.io/docs/getting-started/intro) - Learn more about the technology behind MCP - [MCP Servers Repository](https://github.com/modelcontextprotocol/servers) - Find more apps you can connect - [MCP Specification](https://modelcontextprotocol.io/specification/2025-06-18) - Technical details for developers ## Ready to Connect Your First App? Start with our [Getting Started Guide](https://docs.griptapenodes.com/how_to/mcp/getting_started/index.md) - it will walk you through connecting your first app in just a few minutes! # Getting Started with MCP: Fetch Server Tutorial This tutorial will walk you through setting up your first MCP server using the **[Fetch MCP Server](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch)** - a simple web content fetching tool that's perfect for beginners. ## What We're Going to Build We'll set up the [Fetch MCP Server](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch), which provides web content fetching capabilities. This server enables your AI agents to retrieve and process content from web pages, converting HTML to markdown for easier consumption. **Why Fetch?** - ✅ **Easy to set up** - Uses `uvx` for automatic installation - ✅ **No complex dependencies** - Just needs internet access - ✅ **Immediate results** - You can test it right away - ✅ **Real-world useful** - Perfect for research and content analysis ## How MCP Works in Griptape Nodes ``` graph LR A[Agent] --> B[MCPTask Node] B --> C[MCP Server] C --> D[External System/Tool] D --> E[Results/Data] E --> C C --> B B --> A ``` The MCPTask node acts as a bridge between your Griptape Nodes workflow and external MCP servers, enabling your AI agents to interact with external systems seamlessly. ## Step-by-Step Tutorial ### Step 1: Open MCP Server Settings 1. Open Griptape Nodes 1. Go to **Settings** → **MCP Servers** 1. If this is your first time opening the MCP Server Settings, you won't have any MCP servers configured yet. ### Step 2: Creating an MCP Server To create a new MCP Server you'll need to configure the appropriate MCP Server settings. 1. Click **+ New MCP Server** 1. Set the **Server Name/ID** to `fetch` 1. Set the **Connection Type** to `Local Process (stdio)` if it's not already set. 1. Look at the **Configuration (JSON)**. It will look something like: ``` { "transport": "stdio", "command": "", "args": [], "env": {}, "encoding": "utf-8", "encoding_error_handler": "strict" } ``` This is the information you'll need to fill out that tells the *Client* (Griptape Nodes) how to connect to the *Server*. Most MCP Server documentation will provide instructions on how to set something up for Claude or VS Code. The Fetch server is no different, we'll adapt it's instructions for our use. 1. Navigate to the [Fetch](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch) MCP Server GitHub page 1. Scroll down until you see the [Configuration](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch#configuration) section 1. In the **Configure for Claude.app** section, you'll see "Using `uvx`". Open this and take a look at the instructions: ``` { "mcpServers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] } } } ``` The parts we are interested in are the `command` and the `args`. This is what we'll use in our server. 1. Modify the **Configuration (JSON)** to look like: ``` { "transport": "stdio", "command": "uvx", "args": ["mcp-server-fetch"], "env": {}, "encoding": "utf-8", "encoding_error_handler": "strict" } ``` Notice we just modified the **command** and **args** settings to match what was in the docs. **Optional: Add Custom Rules** You can optionally add custom rules in the **Rules** text area to provide instructions for the AI agent when using this MCP server. These rules are automatically applied when the agent uses tools from this server. For example, you might enter: ``` Always validate URLs before fetching. Return errors in JSON format if a fetch fails. ``` Rules help guide the agent's behavior when interacting with this specific MCP server, ensuring consistent and appropriate usage. 1. Click **Create Server** ### Step 3: Manage Your Server Once created, you can see your server in the list. You can: - **Enable/Disable** servers using the toggle button - **Edit** a server by clicking the Edit button (you can add or modify rules here) - **Delete** servers using the delete button Close the settings when you're done. ### Step 4: Create an MCP Task 1. In your workflow editor, drag an **MCP Task** node from the **Agent** section 1. In the MCP Task node, select `fetch` from the **mcp_server_name** dropdown 1. If the server isn't showing up, click the **reload button** to refresh the list ### Step 5: Test Your Setup In the prompt field, ask a question about something from the internet. For example: ``` Explain MCP: https://www.anthropic.com/news/model-context-protocol ``` Then hit the **Run** button on the node. ### Step 6: See the Results The node will execute and return results like: ## Next Steps: Chaining Tasks Now that you have a working MCP server, you can: ### Chain Multiple Tasks - Connect another **MCP Task** node to your first one - The second task can ask for more information and will use the MCP server if needed - You can use different MCP servers in each task if required Notice in the second task, we're asking the agent about the official MCP site, but we don't *specifically* mention the actual url. That's because in the previous response the MCP server responded with it. The Agent remembers the conversation, and is able to iterate on it. ## What's Next? Now that you've successfully set up your first MCP server, explore: - **[Using MCPTask with Agents](https://docs.griptapenodes.com/how_to/mcp/mcp_task_agents/index.md)** - Advanced integration patterns - **[Example MCP Servers](https://docs.griptapenodes.com/how_to/mcp/servers/index.md)** - Setup guides for other useful servers - **[Connection Types](https://docs.griptapenodes.com/how_to/mcp/#connection-types)** - Learn about different ways to connect to apps # Using MCPTask with Agents The **MCPTask** node allows you to give your AI agents temporary access to external tools and data sources. This powerful feature lets you control exactly when and how your agents can interact with external systems. ## Key Concept: Temporary Access with Memory When you connect an agent to an MCPTask node: - ✅ **The agent gains access** to the MCP server's tools - ✅ **The agent can perform tasks** it couldn't do before - ✅ **The agent retains memory** of what it learned - ❌ **The agent loses access** to MCP tools after the task This gives you precise control over when agents can use external capabilities while preserving their learning. ## How MCPTask Works with Agents ``` graph LR A[Agent] --> B[MCPTask Node] subgraph MCP ["MCP System"] B --> C[MCP Server] C --> D[External System/Tool] D --> E[Results/Data] E --> C C --> B end B --> F[Agent with Memory] F --> G[Next Agent Task] ``` The MCPTask node acts as a bridge that temporarily extends your agent's capabilities, then returns an agent with the knowledge gained but without the external access. ## Step-by-Step Demonstration: Temporary Access with Memory Let's build a workflow that demonstrates how agents temporarily gain MCP capabilities and retain memory. We'll use the Fetch MCP server to show how an agent can access the internet, then continue working with that knowledge. ### Prerequisites - You have the **Fetch MCP server** set up (from the [Getting Started tutorial](https://docs.griptapenodes.com/how_to/mcp/getting_started/index.md)) - You understand basic Griptape Nodes workflow concepts ### Step 1: Create Your First Agent 1. **Drag an Agent node** to your workflow editor 1. **Configure the agent**: - Set **prompt model** to `gpt-4o-mini` (or your preferred model) - Set **prompt** to: `"Look up information about https://griptapenodes.com"` - Leave **additional_context** empty for now 1. **Run the agent** - Notice it will respond that it cannot access the internet: > I'm unable to browse the internet or access real-time data. However, I can help you understand what kind of information you might find on a website like griptapenodes.com... This demonstrates the agent's limitations without MCP access. ### Step 2: Add MCPTask Node 1. **Drag an MCPTask node** to your workflow 1. **Connect the Agent output** to the MCPTask node's **agent** input 1. **Configure the MCPTask**: - Set **mcp_server_name** to `fetch` - Set **prompt** to: `"Can you look up the information now?"` 1. **Run the MCPTask** - The agent now has access to the Fetch server and can retrieve information: > [Using tool mcpFetch] Griptape Nodes is a platform designed to help creative professionals work with AI in a predictable, controllable way that fits their thinking process. It offers an intuitive drag-and-drop interface for creating advanced creative pipelines using graphs, nodes, and flowcharts... ### Step 3: Connect to Another Agent 1. **Drag another Agent node** to your workflow 1. **Connect the MCPTask's agent output** to the new Agent's **agent** input 1. **Configure the second agent**: - Set **prompt** to: `"Do you still have access?"` 1. **Run the second agent** - Notice it responds: > I don't have the ability to access or retrieve real-time information from the internet... The information I provided earlier is based on my training data... **Key observation**: The agent no longer has MCP access, but it remembers the information it learned during the MCPTask! ### Step 4: Test Memory Retention 1. **Add another Agent node** connected to the previous one 1. **Configure it** with: `"What did you learn about Griptape Nodes?"` 1. **Run it** - The agent will recall the information it learned: > Based on the information I learned earlier, Griptape Nodes is a platform designed to help creative professionals work with AI in a predictable, controllable way... This demonstrates that the agent retains knowledge even after losing MCP access. ## Advanced Agent Configuration ### Customizing Agent Behavior You can enhance your agents with additional capabilities: #### Rulesets Add rulesets to control how your agent behaves: - **Research Agent**: "Always cite sources when providing information" - **Creative Agent**: "Think outside the box and suggest innovative solutions" - **Analytical Agent**: "Provide detailed analysis with pros and cons" #### Tools Add additional tools to complement MCP capabilities: - **Calculator tools** for mathematical operations - **Date Time** for understanding dates #### Prompt Models Choose different models for different tasks: - **gpt-4o-mini**: Fast and cost-effective for simple tasks - **gpt-4o**: More capable for complex reasoning - **Claude Sonnet**: Excellent for analysis and writing ## Next Steps Now that you understand how agents work with MCPTask nodes, explore: - **[Local Models with Agents](https://docs.griptapenodes.com/how_to/mcp/advanced_local_models/index.md)** - Use local AI models for sensitive data processing - **[Example MCP Servers](https://docs.griptapenodes.com/how_to/mcp/servers/index.md)** - Set up additional servers for different capabilities - **[Connection Types](https://docs.griptapenodes.com/how_to/mcp/#connection-types)** - Learn about different ways to connect to external systems # Local Models with Agents This advanced tutorial demonstrates how to use **local AI models** with MCP servers to handle sensitive data without sending it to external services. This is perfect for internal documents, private files, or any data you want to keep on your local machine. ## Why Use Local Models with MCP? ### Security Benefits - ✅ **No data leaves your machine** - Everything stays local - ✅ **No API costs** - Run models on your own hardware - ✅ **Complete privacy** - Perfect for sensitive documents - ✅ **Offline capability** - Works without internet connection ### Perfect Use Cases - **Internal documents** - Company files, contracts, sensitive reports - **Personal data** - Private notes, passwords, personal information - **Proprietary code** - Source code you don't want to share - **Local file management** - Organizing files on your computer ## What We're Building We'll create a workflow that: 1. **Sets up a [Filesystem MCP](https://github.com/modelcontextprotocol/servers/blob/main/src/filesystem/README.md) server** to access your local files 1. **Uses Ollama with a local model** (`qwen3:1.7b`) for AI processing 1. **Demonstrates secure file access** by reading a "secret" file 1. **Shows how sensitive data stays local** throughout the entire process ## Prerequisites - You understand basic MCP concepts (from [Getting Started](https://docs.griptapenodes.com/how_to/mcp/getting_started/index.md)) - You have a Mac, Windows, or Linux computer - You're comfortable with basic file operations ## Step 1: Install Ollama ### Download and Install Ollama 1. **Go to [ollama.org](https://ollama.org)** 1. **Download Ollama** for your operating system 1. **Install Ollama** following the installation instructions 1. **Verify installation** by opening a terminal and running: ``` ollama --version ``` ### Download the Qwen3 Model 1. **Open a terminal** and run: ``` ollama pull qwen3:1.7b ``` 1. **Wait for download** - This may take a few minutes depending on your internet speed 1. **Verify the model** is available: ``` ollama list ``` > **Why Qwen3:1.7b?** This model is small enough to run on most computers while still supporting tool use, which is required for MCP integration. ## Step 2: Set Up Filesystem MCP Server The [Filesystem MCP Server](https://github.com/modelcontextprotocol/servers/blob/main/src/filesystem/README.md) provides secure access to your local file system, allowing AI agents to read, write, and manage files and directories. This server is perfect for local data processing because it only accesses directories you explicitly allow, ensuring your sensitive files remain protected. The server supports common file operations like listing directories, reading files, creating new files, and organizing your local storage - all while keeping everything on your machine. For detailed information about the filesystem server's capabilities and configuration options, see our [Filesystem Server documentation](https://docs.griptapenodes.com/how_to/mcp/servers/filesystem/index.md). ### Create the MCP Server Configuration 1. **Open Griptape Nodes** and go to **Settings** → **MCP Servers** 1. **Click + New MCP Server** 1. **Configure the server**: - **Server Name/ID**: `filesystem` - **Connection Type**: `Local Process (stdio)` - **Configuration JSON**: ``` { "transport": "stdio", "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "/Users/jason/Desktop", "/Users/jason/Downloads" ], "env": {}, "cwd": null, "encoding": "utf-8", "encoding_error_handler": "strict" } ``` > **Important**: Replace `/Users/jason/Desktop` and `/Users/jason/Downloads` with your actual desktop and downloads folder paths. The filesystem server can only access directories you explicitly allow. 1. **Click Create Server** ### Test the Filesystem Access 1. **Create a test file** on your desktop called `secret.txt` 1. **Add some content** to the file: ``` The password is: CAPYBARA ``` 1. **Save the file** ## Step 3: Build the Local AI Workflow ### Create the Ollama Prompt Configuration 1. **Drag an Ollama Prompt node** to your workflow 1. **Configure the model**: - **Model**: `qwen3:1.7b` - **Temperature**: `0.1` (for consistent results) - **Max Tokens**: `-1` (unlimited) - **Stream**: `True` - **Use Native Tools**: `True` (required for MCP) ### Create the Agent 1. **Drag an Agent node** to your workflow 1. **Connect the Ollama Prompt's `prompt_model_config` output** to the Agent's `prompt_model_config` input 1. **Configure the agent**: - **Prompt**: Leave empty for now - **Additional Context**: Leave empty for now ### Create the MCP Task 1. **Drag an MCPTask node** to your workflow 1. **Connect the Agent's `agent` output** to the MCPTask's `agent` input 1. **Configure the MCPTask**: - **MCP Server Name**: `filesystem` - **Prompt**: `"what's the password in secret.txt? it's in the desktop folder"` ## Step 4: Run the Secure Workflow ### Execute the Workflow 1. **Run the Ollama Prompt node** first to initialize the model 1. **Run the Agent node** to create the agent with local model access 1. **Run the MCPTask node** to execute the file access ### Expected Results The MCPTask should return something like: ``` Okay, the user asked for the password in secret.txt. After checking the allowed directories and listing the contents, the secret.txt file was found. Reading the file revealed the password "CAPYBARA". Since the user requested the password, the assistant needs to provide it. The previous steps were successful, so the final answer is the password. The password in secret.txt is **CAPYBARA**. ``` ## Why This is Secure ### Data Flow Analysis ``` graph LR A[Local Files] --> B[Filesystem MCP Server] B --> C[Local Ollama Model] C --> D[Your Computer Only] subgraph Security ["🔒 Everything Stays Local"] A B C D end ``` ### Security Benefits Demonstrated 1. **No External API Calls** - The AI model runs on your machine 1. **No Data Transmission** - Files are read locally, processed locally 1. **No Cloud Storage** - Nothing is sent to external servers 1. **Complete Control** - You control exactly what the AI can access ## Advanced Configuration Options ### Model Selection Choose models based on your needs: | Model | Size | Speed | Capabilities | Best For | | ------------ | ------ | ------ | ------------------ | ----------------- | | `qwen3:1.7b` | Small | Fast | Basic tool use | Simple tasks | | `qwen3:4b` | Medium | Medium | Better reasoning | Complex analysis | | `llama4` | Large | Slower | Advanced reasoning | Complex workflows | ### Filesystem Security Configure the filesystem server to only access specific directories: ``` { "args": [ "-y", "@modelcontextprotocol/server-filesystem", "/Users/jason/Documents/Work", "/Users/jason/Documents/Projects" ] } ``` ### Performance Optimization - **Allocate sufficient RAM** (8GB+ recommended for larger models) - **Close other applications** when running large models - **Use smaller models** for faster responses ## Troubleshooting ### Common Issues #### Model Not Found ``` # Check available models ollama list # Pull the model if missing ollama pull qwen3:1.7b ``` #### Filesystem Access Denied - Verify the directory paths in your MCP server configuration - Check that the directories exist and are accessible - Ensure you have read permissions for the directories #### Model Performance Issues - Try a smaller model like `qwen3:1.7b` - Close other applications to free up memory - Check your system resources (CPU, RAM usage) #### Tool Use Not Working - Ensure `use_native_tools` is set to `True` in Ollama Prompt - Verify the model supports tool use (Qwen3 models do) - Check that the MCP server is properly configured ## Next Steps Now that you've mastered local models with MCP servers, explore: - **[Example MCP Servers](https://docs.griptapenodes.com/how_to/mcp/servers/index.md)** - Set up additional servers for different capabilities - **[Connection Types](https://docs.griptapenodes.com/how_to/mcp/#connection-types)** - Explore different ways to connect to external systems # Local Process (stdio) Connection The **stdio** connection type allows Griptape Nodes to communicate with MCP servers running as local processes using standard input/output streams. ## When to Use stdio - **Local Applications**: Running MCP servers on the same machine - **Command-Line Tools**: Interacting with CLI-based MCP servers - **Development**: Testing and development scenarios - **Simple Setup**: When you want minimal configuration overhead ## Example stdio MCP Servers Here are a couple of examples. There are of course many more MCP Servers you can set up, but we wanted to show a few that can help you get started. - **[Fetch](https://docs.griptapenodes.com/how_to/mcp/servers/fetch/index.md)** - Web content fetching and processing - **[Filesystem](https://docs.griptapenodes.com/how_to/mcp/servers/filesystem/index.md)** - File and directory operations ## Configuration ### Required Fields | Field | Type | Description | Example | | --------- | ------ | ------------------------------- | ----------------------------------------------- | | `command` | string | Command to start the MCP server | `"npx"`, `"python"`, `"uvx"` | | `args` | array | Arguments passed to the command | `["-y", "@modelcontextprotocol/server-memory"]` | ### Optional Fields | Field | Type | Description | Default | | ------------------------ | ------ | ----------------------- | ----------------- | | `env` | object | Environment variables | `{}` | | `cwd` | string | Working directory | Current directory | | `encoding` | string | Text encoding | `"utf-8"` | | `encoding_error_handler` | string | Error handling strategy | `"strict"` | ## Example Configurations ### Memory Server (Node.js) ``` { "name": "memory", "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-memory"], "description": "Persistent memory storage for conversations" } ``` ### Filesystem Server ``` { "name": "filesystem", "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"], "env": { "NODE_ENV": "production" }, "cwd": "/home/user/projects" } ``` ## Setup Steps ### 1. Install MCP Server ``` # For Node.js servers npm install -g @modelcontextprotocol/server-memory # For Python servers pip install mcp-server-git # or uvx mcp-server-git ``` ### 2. Configure in Griptape Nodes 1. Open Griptape Nodes settings 1. Navigate to MCP Server configuration 1. Add new server with stdio transport 1. Fill in command and arguments 1. Save the connection ### 3. Use in Workflow 1. Add MCPTask node to your flow 1. Select your configured stdio server 1. Enter your prompt 1. Execute the workflow ## Advantages - **Low Latency**: Direct process communication - **Simple Setup**: Minimal configuration required - **Local Control**: Full control over the server process - **Resource Efficient**: No network overhead ## Limitations - **Local Only**: Cannot connect to remote servers - **Process Management**: Must handle server lifecycle - **Platform Dependent**: Command syntax varies by OS - **Single Connection**: One connection per server instance ## Troubleshooting ### Server Won't Start - Verify the command exists in PATH - Check file permissions - Ensure all dependencies are installed - Test the command manually in terminal ### Connection Timeout - Check if server is responding to stdio - Verify encoding settings - Look for server error messages - Check working directory permissions ### Permission Errors - Ensure proper file system permissions - Check if user has access to required directories - Verify environment variable access ## Best Practices 1. **Use Absolute Paths**: For commands and working directories 1. **Set Environment Variables**: For configuration and secrets 1. **Handle Errors Gracefully**: Implement proper error handling 1. **Monitor Resources**: Watch for memory leaks or high CPU usage 1. **Test Commands**: Verify server commands work before configuration ## Next Steps - [SSE Connection](https://docs.griptapenodes.com/how_to/mcp/connection_types/sse/index.md) - HTTP-based streaming - [Streamable HTTP](https://docs.griptapenodes.com/how_to/mcp/connection_types/streamable_http/index.md) - HTTP with streaming support - [WebSocket Connection](https://docs.griptapenodes.com/how_to/mcp/connection_types/websocket/index.md) - Full-duplex communication # Server-Sent Events (SSE) Connection ⚠️ **Deprecated** - SSE as a standalone transport has been deprecated in favor of **Streamable HTTP**. ## What Happened to SSE? According to the [official MCP specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports), SSE as a standalone transport was deprecated in protocol version 2024-11-05 and replaced by **Streamable HTTP**. ## Current MCP Transport Options The MCP specification now defines only **two standard transport mechanisms**: 1. **[stdio](https://docs.griptapenodes.com/how_to/mcp/connection_types/stdio/index.md)** - Communication over standard in and standard out 1. **[Streamable HTTP](https://docs.griptapenodes.com/how_to/mcp/connection_types/streamable_http/index.md)** - HTTP-based communication with optional SSE support ## What This Means - **SSE functionality is still available** - but as part of Streamable HTTP, not as a standalone transport - **Streamable HTTP can use SSE** - for server-to-client streaming when needed - **No standalone SSE servers** - SSE is now integrated into the HTTP transport ## Migration Path If you were planning to use SSE, consider: - **[Streamable HTTP](https://docs.griptapenodes.com/how_to/mcp/connection_types/streamable_http/index.md)** - The modern replacement that includes SSE capabilities - **[Local Process (stdio)](https://docs.griptapenodes.com/how_to/mcp/connection_types/stdio/index.md)** - For local server connections - **[WebSocket](https://docs.griptapenodes.com/how_to/mcp/connection_types/websocket/index.md)** - For full-duplex real-time communication (custom transport) ## Next Steps - **[Streamable HTTP](https://docs.griptapenodes.com/how_to/mcp/connection_types/streamable_http/index.md)** - The modern HTTP transport with SSE capabilities - **[Local Process (stdio)](https://docs.griptapenodes.com/how_to/mcp/connection_types/stdio/index.md)** - Local server connections with many examples - **[Example MCP Servers](https://docs.griptapenodes.com/how_to/mcp/servers/index.md)** - Available server examples # Streamable HTTP Connection The **streamable_http** connection type enables Griptape Nodes to communicate with MCP servers over HTTP with support for **bidirectional streaming** (client ↔ server) and real-time communication. ## When to Use Streamable HTTP - **Bidirectional Communication**: When both client and server need to send data - **Interactive Applications**: Real-time chat, collaborative editing, live collaboration - **HTTP Infrastructure**: Leveraging existing HTTP-based systems - **Custom Streaming**: When you need more control than SSE provides - **Session Management**: Applications requiring persistent session state ## Available Streamable HTTP MCP Servers - **[Exa](https://docs.griptapenodes.com/how_to/mcp/servers/exa/index.md)** - Advanced web search and research capabilities ## Example Streamable HTTP MCP Server Configuration ### Chat Application Server ``` { "name": "chat_app", "transport": "streamable_http", "url": "https://api.chat-service.com/mcp/stream", "headers": { "Authorization": "Bearer chat-token" }, "timeout": 60, "sse_read_timeout": 120, "terminate_on_close": false, "description": "Real-time messaging and communication" } ``` ## Popular Streamable HTTP Use Cases - **Chat Applications** - Real-time messaging and communication - **Collaborative Editing** - Shared document editing (like Google Docs) - **Live Collaboration** - Team workspaces and shared whiteboards - **Interactive Dashboards** - Real-time data visualization and interaction - **Customer Support** - Live chat and support systems - **Online Gaming** - Turn-based and real-time multiplayer games ## Configuration ### Required Fields | Field | Type | Description | Example | | ----- | ------ | -------------------------------- | ------------------------------- | | `url` | string | HTTP endpoint for the MCP server | `"https://api.example.com/mcp"` | ### Optional Fields | Field | Type | Description | Default | | -------------------- | ------- | ------------------------------- | ------- | | `headers` | object | HTTP headers for authentication | `{}` | | `timeout` | number | Request timeout in seconds | `30` | | `sse_read_timeout` | number | SSE read timeout in seconds | `60` | | `terminate_on_close` | boolean | Terminate session on close | `true` | ## Example Configurations ### Basic Streamable HTTP ``` { "name": "streamable_api", "transport": "streamable_http", "url": "https://api.example.com/mcp/stream", "description": "HTTP API with bidirectional streaming (client ↔ server)" } ``` ### Authenticated Streamable HTTP ``` { "name": "auth_streamable", "transport": "streamable_http", "url": "https://api.example.com/mcp/stream", "headers": { "Authorization": "Bearer your-token-here", "Content-Type": "application/json", "Accept": "application/json" }, "timeout": 60, "sse_read_timeout": 120, "terminate_on_close": true } ``` ### Custom Configuration ``` { "name": "custom_streamable", "transport": "streamable_http", "url": "https://mcp.example.com/stream", "headers": { "X-API-Key": "your-api-key", "X-Client-Version": "1.0.0", "User-Agent": "GriptapeNodes/1.0" }, "timeout": 90, "sse_read_timeout": 300, "terminate_on_close": false } ``` ## Setup Steps ### 1. Deploy MCP Server Ensure your MCP server supports streamable HTTP: ``` # Example streamable HTTP endpoint @app.post("/mcp/stream") async def mcp_stream(request: Request): return StreamingResponse( process_mcp_stream(request), media_type="application/json", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive" } ) ``` ### 2. Configure in Griptape Nodes 1. Open Griptape Nodes settings 1. Navigate to MCP Server configuration 1. Add new server with streamable_http transport 1. Enter the server URL 1. Configure authentication and timeouts 1. Test the connection ### 3. Use in Workflow 1. Add MCPTask node to your flow 1. Select your configured streamable HTTP server 1. Enter your prompt 1. Execute the workflow ## Advantages - **Bidirectional Streaming**: Full duplex communication (client ↔ server) - **HTTP Compatible**: Works with standard web infrastructure - **Real-Time Updates**: Live data streaming in both directions - **Session Management**: Built-in session handling - **Custom Implementation**: More control over streaming behavior - **Interactive Applications**: Perfect for real-time collaboration ## Limitations - **HTTP Overhead**: More overhead than direct connections - **Network Dependent**: Requires stable network connection - **Complexity**: More complex than simple HTTP requests - **Resource Usage**: Higher resource consumption - **Custom Implementation**: Requires more development work than SSE ## Streamable HTTP vs SSE | Feature | Streamable HTTP | SSE | | ------------------ | --------------------------------------- | ------------------------------------- | | **Direction** | Bidirectional (client ↔ server) | Unidirectional (server → client) | | **Protocol** | Custom HTTP streaming | Standardized (`text/event-stream`) | | **Use Case** | Interactive apps, real-time chat | Notifications, live feeds, monitoring | | **Implementation** | Custom client/server logic | Built-in browser support | | **Reconnection** | Manual implementation | Automatic reconnection | | **Example** | Chat application, collaborative editing | Stock ticker, news feed | ## Authentication ### Bearer Token ``` { "headers": { "Authorization": "Bearer your-jwt-token" } } ``` ### API Key ``` { "headers": { "X-API-Key": "your-api-key", "X-Client-ID": "griptape-nodes" } } ``` ### Custom Authentication ``` { "headers": { "X-Custom-Auth": "your-custom-token", "X-User-ID": "user123", "X-Session-ID": "session456" } } ``` ## Session Management ### Terminate on Close ``` { "terminate_on_close": true } ``` - Automatically terminates the session when connection closes - Useful for stateless operations - Default behavior ### Persistent Sessions ``` { "terminate_on_close": false } ``` - Maintains session state across connections - Useful for stateful operations - Requires server-side session management ## Troubleshooting ### Connection Issues - Verify server URL is accessible - Check network connectivity - Test with curl or Postman - Monitor server logs ### Timeout Problems - Increase timeout values - Check server response times - Monitor network latency - Optimize server performance ### Authentication Failures - Verify credentials are correct - Check token expiration - Ensure proper header format - Test authentication separately ### Streaming Issues - Verify server supports streaming - Check for proper content types - Monitor connection stability - Test with smaller payloads ## Best Practices 1. **Use HTTPS**: Always use secure connections 1. **Handle Reconnection**: Implement automatic reconnection 1. **Monitor Sessions**: Track session state and cleanup 1. **Optimize Timeouts**: Set appropriate timeout values 1. **Secure Credentials**: Store sensitive data securely ## Example Use Cases ### Interactive Chat ``` { "name": "chat_interactive", "transport": "streamable_http", "url": "https://chat.example.com/stream", "headers": { "Authorization": "Bearer chat-token" }, "terminate_on_close": false } ``` ### Real-Time Collaboration ``` { "name": "collaboration", "transport": "streamable_http", "url": "https://collab.example.com/stream", "headers": { "X-User-ID": "user123", "X-Workspace-ID": "workspace456" } } ``` ### Live Data Processing ``` { "name": "data_processor", "transport": "streamable_http", "url": "https://processor.example.com/stream", "timeout": 120, "sse_read_timeout": 600 } ``` ## Performance Considerations ### Timeout Configuration - **Short Timeouts**: For quick operations (30-60 seconds) - **Medium Timeouts**: For standard operations (60-120 seconds) - **Long Timeouts**: For complex operations (120+ seconds) ### Connection Pooling - Reuse connections when possible - Monitor connection limits - Implement proper cleanup - Handle connection failures gracefully ## Next Steps - [WebSocket Connection](https://docs.griptapenodes.com/how_to/mcp/connection_types/websocket/index.md) - Full-duplex communication - [SSE Connection](https://docs.griptapenodes.com/how_to/mcp/connection_types/sse/index.md) - Unidirectional streaming - [stdio Connection](https://docs.griptapenodes.com/how_to/mcp/connection_types/stdio/index.md) - Local process communication # WebSocket Connection The **websocket** connection type enables Griptape Nodes to communicate with MCP servers using WebSocket protocol for full-duplex, real-time communication. > **Note**: WebSocket is not an official MCP transport type, but it can be implemented as a custom transport according to the [MCP specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports). ## What WebSocket Is WebSocket is a communication protocol that provides full-duplex communication channels over a single TCP connection. Unlike HTTP, WebSocket allows both the client and server to send data at any time. ## Coming Soon WebSocket support for MCP servers is still emerging. While the connection type can be implemented as a custom transport, there are currently no widely available WebSocket-based MCP servers to demonstrate with. **For now, consider:** - **[Streamable HTTP](https://docs.griptapenodes.com/how_to/mcp/connection_types/streamable_http/index.md)** for HTTP-based communication with real examples - **[Local Process (stdio)](https://docs.griptapenodes.com/how_to/mcp/connection_types/stdio/index.md)** for local server connections with many examples ## Configuration When WebSocket MCP servers become available, they will use a configuration similar to: ``` { "transport": "websocket", "url": "ws://your-websocket-server.com/mcp", "headers": { "Authorization": "Bearer your-api-key" } } ``` ## Next Steps - **[Streamable HTTP](https://docs.griptapenodes.com/how_to/mcp/connection_types/streamable_http/index.md)** - HTTP-based communication with real examples - **[Local Process (stdio)](https://docs.griptapenodes.com/how_to/mcp/connection_types/stdio/index.md)** - Local server connections with many examples - **[Example MCP Servers](https://docs.griptapenodes.com/how_to/mcp/servers/index.md)** - Available server examples # MCP Server Rules MCP server rules allow you to provide custom instructions to AI agents when they use tools from a specific MCP server. These rules are automatically applied as a ruleset whenever an agent uses that server's tools, helping ensure consistent and appropriate behavior. ## What Are Rules? Rules are text instructions that guide how the AI agent should interact with a particular MCP server. They're added to the MCP server configuration and automatically applied when: - Using the **MCPTask** node with that server - Using the **Agent** node with that server configured ## Why Use Rules? Rules help you: - **Guide Agent Behavior**: Provide specific instructions on how to use the server's tools - **Ensure Consistency**: Make sure the agent follows your preferred patterns - **Handle Edge Cases**: Instruct the agent on how to handle errors or special situations - **Optimize Usage**: Guide the agent to use the server's capabilities most effectively ## Adding Rules to an MCP Server ### When Creating a Server When creating a new MCP server, you can add rules in the **Rules** text area: 1. Go to **Settings** → **MCP Servers** 1. Click **+ New MCP Server** 1. Fill in the server configuration (name, connection type, etc.) 1. In the **Rules** text area, enter your custom rules 1. Click **Create Server** ### When Editing a Server 1. Go to **Settings** → **MCP Servers** 1. Click the **Edit** button on the server you want to modify 1. Modify the text in the **Rules** text area 1. Save your changes ## Example Rules ### For a Web Fetching Server ``` Always validate URLs before fetching. Check that URLs use HTTPS when possible. If a fetch fails, return a clear error message explaining what went wrong. ``` ### For a File System Server ``` Always check if a file exists before attempting to read it. Use absolute paths when possible. Never delete files without explicit user confirmation. ``` ### For a Search Server ``` Always verify search results are relevant before returning them. If no relevant results are found, suggest alternative search terms. Format results in a clear, readable structure. ``` ### For a Database Server ``` Always validate SQL queries before executing them. Never execute DROP or DELETE operations without explicit confirmation. Return query results in a structured format. ``` ## How Rules Work 1. **Storage**: Rules are stored as part of the MCP server configuration 1. **Application**: When an agent uses tools from that server, the rules are automatically added as a ruleset 1. **Scope**: Rules apply only when using that specific MCP server 1. **Format**: Rules are a single string - you can include multiple instructions separated by periods or newlines ## Best Practices ### Be Specific ✅ **Good**: "Always validate URLs before fetching. Check for HTTPS and return clear error messages." ❌ **Vague**: "Be careful with URLs." ### Focus on Behavior ✅ **Good**: "Return errors in JSON format with 'error' and 'message' fields." ❌ **Too Generic**: "Handle errors well." ### Keep It Concise ✅ **Good**: "Validate inputs before processing. Return structured JSON responses." ❌ **Too Long**: A paragraph explaining every possible scenario in detail. ### Test Your Rules After adding rules, test them with your MCP server to ensure they work as expected: 1. Create an MCPTask node using the server 1. Run a test prompt 1. Verify the agent follows the rules you specified ## Rules in Different Contexts ### MCPTask Node When using the MCPTask node, rules from the selected MCP server are automatically applied to the agent that processes the task. ### Agent Node When using the Agent node with MCP servers configured, rules from all enabled MCP servers are collected and applied to the agent. ## Troubleshooting ### Rules Not Being Applied - Verify the rules are entered in the Rules text area (not empty) - Check that the server is enabled - Ensure you're using the correct server name in your workflow ### Agent Not Following Rules - Make rules more specific and actionable - Test with simpler rules first - Check that the rules are appropriate for the server's capabilities ## Next Steps - **[Getting Started Tutorial](https://docs.griptapenodes.com/how_to/mcp/getting_started/index.md)** - Learn how to set up your first MCP server - **[Connection Types](https://docs.griptapenodes.com/how_to/mcp/#connection-types)** - Learn about different connection methods - **[Example Servers](https://docs.griptapenodes.com/how_to/mcp/servers/index.md)** - See examples of configured MCP servers # Example MCP Servers This section provides setup guides and documentation for example MCP servers that work well with Griptape Nodes. These are just a few of the many MCP servers available in the community. ## Where to Find More MCP Servers The [official MCP servers repository](https://github.com/modelcontextprotocol/servers) contains a comprehensive collection of reference implementations and community-built servers. This repository showcases the versatility of MCP and demonstrates how it can give Large Language Models secure, controlled access to tools and data sources. ### Official Repository Features: - **Reference implementations** for common use cases - **Community contributions** from developers worldwide - **Multiple language support** (TypeScript, Python, JavaScript, etc.) - **Easy installation** with `npx`, `uvx`, and `pip` - **Comprehensive documentation** for each server ## Available Server Documentation ### Local Applications (stdio) These servers run locally on your machine and provide direct access to your software and files: - **[Blender](https://docs.griptapenodes.com/how_to/mcp/servers/blender/index.md)** - 3D modeling, scene creation, and manipulation (3rd party) - **[Maya](https://docs.griptapenodes.com/how_to/mcp/servers/maya/index.md)** - 3D modeling, animation, and scene creation with Autodesk Maya (3rd party) - **[Fetch](https://docs.griptapenodes.com/how_to/mcp/servers/fetch/index.md)** - Web content fetching and processing - **[Filesystem](https://docs.griptapenodes.com/how_to/mcp/servers/filesystem/index.md)** - File and directory operations - **[Time](https://docs.griptapenodes.com/how_to/mcp/servers/time/index.md)** - Date and time operations ### Web Services (streamable_http) These servers connect to web-based applications and services: - **[Exa](https://docs.griptapenodes.com/how_to/mcp/servers/exa/index.md)** - Advanced web search and research capabilities ## Need More Options? - **Browse the [official repository](https://github.com/modelcontextprotocol/servers)** for hundreds of additional servers - **Explore [MCP Servers](https://mcpservers.org/)** - A curated collection of MCP servers with featured and official options - **Check community directories** like MCPHub, MCP Servers Hub, and other curated lists - **Build your own** using the MCP SDKs available in multiple programming languages The MCP ecosystem is constantly growing, with new servers being added regularly by the community! # Blender MCP Server The **[Blender MCP Server](https://github.com/ahujasid/blender-mcp)** enables AI agents to directly interact with and control Blender. This integration allows for prompt-assisted 3D modeling, scene creation, and manipulation. This is a third-party server created by [Siddharth Ahuja](https://github.com/ahujasid), and is not made by Blender or Griptape. Prerequisites Before using the Blender MCP server, you must: 1. Have [Blender](http://blender.org) 3.0 or newer installed 1. Install the `uv` package manager ([installation instructions](https://docs.astral.sh/uv/getting-started/installation/)) 1. Download and install the Blender addon from the [repository](https://github.com/ahujasid/blender-mcp) ## Installing the Blender Addon 1. Download the `addon.py` file from the [Blender MCP repository](https://github.com/ahujasid/blender-mcp) 1. Open Blender 1. Go to **Edit** → **Preferences** → **Add-ons** 1. Click **Install...** and select the `addon.py` file 1. Enable the addon by checking the box next to "Interface: Blender MCP" ## Starting the Blender Connection 1. In Blender, open the 3D View sidebar (press **N** if not visible) 1. Find the **BlenderMCP** tab 1. (Optional) Turn on the **Poly Haven** checkbox if you want to use assets from their API 1. Click **Connect to Claude** ## Installation 1. **Open Griptape Nodes** and go to **Settings** → **MCP Servers** 1. **Click + New MCP Server** 1. **Configure the server**: - **Server Name/ID**: `blender` - **Connection Type**: `Local Process (stdio)` - **Configuration JSON**: ``` { "transport": "stdio", "command": "uvx", "args": [ "blender-mcp" ], "env": { "BLENDER_PATH": "/usr/bin/blender" }, "cwd": null, "encoding": "utf-8", "encoding_error_handler": "strict" } ``` 1. **Click Create Server** Blender Path Varies by System The `BLENDER_PATH` environment variable should point to your Blender executable. Common locations include: - **macOS**: `/Applications/Blender.app/Contents/MacOS/Blender` - **Linux**: `/usr/bin/blender` or `/usr/local/bin/blender` - **Windows**: `C:\Program Files\Blender Foundation\Blender\blender.exe` Your installation path may vary depending on how you installed Blender. ## Available Tools - **`get_scene_info`** - Get detailed information about the current Blender scene - **`get_object_info`** - Get information about a specific object in the scene - **`get_viewport_screenshot`** - Capture a screenshot of the current 3D viewport - **`execute_blender_code`** - Execute arbitrary Python code in Blender - **`get_polyhaven_categories`** - Get available asset categories from Poly Haven (requires Poly Haven enabled) - **`search_polyhaven_assets`** - Search for assets on Poly Haven (requires Poly Haven enabled) - **`download_polyhaven_asset`** - Download and import Poly Haven assets (requires Poly Haven enabled) - **`set_texture`** - Apply a downloaded Poly Haven texture to an object (requires Poly Haven enabled) - **`get_polyhaven_status`** - Check if Poly Haven integration is enabled - **`get_hyper3d_status`** - Check if Hyper3D Rodin integration is enabled - **`search_sketchfab_models`** - Search for models on Sketchfab - **`download_sketchfab_model`** - Download and import Sketchfab models - **`generate_hyper3d_model_via_text`** - Generate 3D models from text descriptions using Hyper3D Rodin - **`generate_hyper3d_model_via_images`** - Generate 3D models from images using Hyper3D Rodin - **`poll_rodin_job_status`** - Check the status of a Hyper3D Rodin generation task - **`import_generated_asset`** - Import a generated Hyper3D Rodin asset into Blender ## Configuration Options You can configure additional environment variables to customize the Blender connection: ``` { "transport": "stdio", "command": "uvx", "args": [ "blender-mcp" ], "env": { "BLENDER_PATH": "/Applications/Blender.app/Contents/MacOS/Blender", "BLENDER_HOST": "localhost", "BLENDER_PORT": "9876" }, "cwd": null, "encoding": "utf-8", "encoding_error_handler": "strict" } ``` Available environment variables: - **`BLENDER_PATH`** - Path to your Blender executable - **`BLENDER_HOST`** - Host address for Blender socket server (default: `localhost`) - **`BLENDER_PORT`** - Port number for Blender socket server (default: `9876`) ## Advanced Features ### Poly Haven Integration The Blender MCP server can download and import assets (HDRIs, textures, models) from Poly Haven's free library: 1. Enable Poly Haven in the Blender addon by checking the **Poly Haven** checkbox 1. Use tools like `search_polyhaven_assets` and `download_polyhaven_asset` to find and import assets Example commands: - "Download a beach HDRI from Poly Haven and use it as the environment" - "Find a wood texture on Poly Haven and apply it to this cube" ### Hyper3D Rodin Integration Generate 3D models using AI through Hyper3D Rodin: 1. Check the Hyper3D status with `get_hyper3d_status` 1. Generate models from text descriptions or images 1. Poll for completion and import the generated asset Hyper3D Free Trial Hyper3D's free trial key allows a limited number of model generations per day. For unlimited access, obtain your own key from [hyper3d.ai](https://hyper3d.ai) and [fal.ai](https://fal.ai). ### Sketchfab Integration Search and download models from Sketchfab's library: 1. Use `search_sketchfab_models` to find models 1. Download models with `download_sketchfab_model` ## Example Use Cases Here are some examples of what you can create with the Blender MCP server: - "Create a low poly scene in a dungeon, with a dragon guarding a pot of gold" - "Create a beach vibe using HDRIs, textures, and models like rocks and vegetation from Poly Haven" - "Generate a 3D model of a garden gnome through Hyper3D" - "Make this car red and metallic" - "Create a sphere and place it above the cube" - "Make the lighting like a studio" - "Point the camera at the scene, and make it isometric" - "Get information about the current scene and export it as JSON" ## Troubleshooting ### Common Issues - **Connection Issues**: Ensure the Blender addon server is running (check that you clicked "Connect to Claude" in Blender), verify the MCP server is configured correctly in Griptape Nodes, check that the `BLENDER_PATH` environment variable points to the correct Blender executable - **First Command Fails**: The first command after connecting sometimes doesn't go through, but subsequent commands typically work. Try running the command again. - **Timeout Errors**: Try simplifying your requests or breaking them into smaller steps, complex operations may need to be executed in multiple steps - **Poly Haven Not Working**: Ensure the Poly Haven checkbox is enabled in the Blender addon, verify you have an internet connection, note that Claude can be erratic with Poly Haven integration - **Code Execution Warnings**: The `execute_blender_code` tool runs arbitrary Python code in Blender, which can be powerful but potentially dangerous. **Always save your work before using it.** ### Debug Tips 1. Check that the Blender addon is enabled and running (look for the BlenderMCP tab in the sidebar) 1. Verify the socket server is active in Blender (status shown in the addon panel) 1. Test with simple commands first (e.g., "get scene information") 1. If Poly Haven or Hyper3D features aren't working, check their status with the respective status tools 1. Restart both Blender and the MCP connection if issues persist ## Resources - [Blender MCP Server Repository](https://github.com/ahujasid/blender-mcp) - Official repository and documentation - [Blender Python API](https://docs.blender.org/api/current/) - Reference for Blender scripting - [Poly Haven](https://polyhaven.com/) - Free 3D assets library - [Hyper3D Rodin](https://hyper3d.ai) - AI-powered 3D model generation - [Sketchfab](https://sketchfab.com/) - 3D model marketplace ## Security Considerations Arbitrary Code Execution The `execute_blender_code` tool allows running arbitrary Python code in Blender. This can be powerful but potentially dangerous: - **Always save your Blender work** before using code execution - Review generated code when possible - Use with caution in production environments - Be aware that malicious code could potentially harm your system or data Asset Downloads Poly Haven and Sketchfab integrations require downloading external assets: - Downloads may be large (textures, HDRIs, models) - Ensure you have adequate disk space - Assets are stored in Blender's cache directory - If you don't want to use these features, keep Poly Haven disabled in the addon # Maya MCP Server The **[Maya MCP Server](https://github.com/PatrickPalmer/MayaMCP)** enables AI agents to directly interact with and control Autodesk Maya through natural language using the Model Context Protocol (MCP). This integration allows for prompt-assisted 3D modeling, scene creation, and manipulation. This is a third-party server created by [Patrick Palmer](https://github.com/PatrickPalmer), and is not made by Autodesk Maya or Griptape. Third-Party Server This MCP server is not officially supported by Griptape or Autodesk Maya. Use at your own discretion and ensure you have proper backups of your work. Prerequisites Before using the Maya MCP server, you must: 1. Have [Autodesk Maya](https://www.autodesk.com/products/maya) 2023 or newer installed 1. Install Python 3.10 or greater 1. Download and set up the Maya MCP server from the [repository](https://github.com/PatrickPalmer/MayaMCP). See the [installation instructions](#1-download-and-setup-maya-mcp-server) for specific info. ## Installation ### 1. Download and Setup Maya MCP Server 1. Open up a terminal and navigate to the location on your machine where you'd like to download the repository. I would recommend placing the server in an easily discoverable location. For example, on my Mac I like to place it where I keep my GitHub repos: `$HOME/Documents/GitHub`. ``` cd $HOME/Documents/GitHub ``` 1. **Clone the repository**: ``` git clone https://github.com/PatrickPalmer/MayaMCP.git cd MayaMCP ``` 1. **Create a virtual environment**: ``` python -m venv .venv ``` 1. **Activate the virtual environment**: - **Windows**: `.venv\Scripts\activate.bat` - **Mac/Linux**: `source .venv\bin\activate` 1. **Install dependencies**: ``` pip install -r requirements.txt ``` ### 2. Open Maya and Enable Command Port 1. **Open Autodesk Maya** 1. **Enable the Command Port** - This is required for the MCP server to communicate with Maya. In Maya's Script Editor, run this Python code: ``` import maya.cmds as cmds def setup_maya_command_port(port=50007): """Setup Maya command port with error handling""" try: # First, try to close any existing command port on this port try: cmds.commandPort(name=f"localhost:{port}", close=True) print(f"Closed existing command port on localhost:{port}") except: # No existing port to close, that's fine pass # Enable the command port cmds.commandPort(name=f"localhost:{port}") print(f"Command Port successfully enabled on localhost:{port}") return True except Exception as e: print(f"Error setting up command port: {e}") return False # Run the setup if setup_maya_command_port(50007): print("Maya MCP server should now be able to connect!") else: print("Failed to setup command port. Check Maya's Command Port settings in Preferences.") ``` Command Port Required Every Session You must enable the Command Port every time you start Maya. The Command Port setting is not persistent between Maya sessions. Make It Easier: Save as Maya Script To make this process easier, you can save the command port setup as a Maya script: 1. **Save the following script as `enable_mcp_command_port.py`**: ``` import maya.cmds as cmds def enable_mcp_command_port(port=50007): """Setup Maya command port with error handling""" try: # First, try to close any existing command port on this port try: cmds.commandPort(name=f"localhost:{port}", close=True) print(f"Closed existing command port on localhost:{port}") except: # No existing port to close, that's fine pass # Enable the command port cmds.commandPort(name=f"localhost:{port}") print(f"Command Port successfully enabled on localhost:{port}") return True except Exception as e: print(f"Error setting up command port: {e}") return False ``` 1. **Test the script** in Maya's Script Editor: ``` import enable_mcp_command_port enable_mcp_command_port.enable_mcp_command_port() ``` 1. **Choose one of these options**: **Option A: Create a Shelf Button** - Drag the test code from step 2 to the shelf to create a button - Click the button whenever you need to enable the command port **Option B: Auto-Start with userSetup.py** - Find Maya's userScripts directory: - **Windows**: `%USERPROFILE%\Documents\maya\2025\scripts\` - **macOS**: `~/Library/Preferences/Autodesk/maya/2025/scripts/` - **Linux**: `~/maya/2025/scripts/` - Add this line to your existing `userSetup.py` file (or create one if it doesn't exist): ``` import enable_mcp_command_port enable_mcp_command_port.enable_mcp_command_port() ``` - Restart Maya - the command port will be enabled automatically Tip The Maya MCP server communicates with Maya through the Command Port. When the MCP server first attempts to communicate with Maya, you may get a popup within Maya asking for permission. Click **"Allow All"** to enable ongoing communication. ### 3. Configure Griptape Nodes 1. **Open Griptape Nodes** and go to **Settings** → **MCP Servers** 1. **Click + New MCP Server** 1. **Configure the server**: - **Server Name/ID**: `maya` - **Connection Type**: `Local Process (stdio)` - **Configuration JSON** (choose the appropriate example for your platform): **Windows**: ``` { "transport": "stdio", "command": "C:\\path\\to\\MayaMCP\\.venv\\Scripts\\python.exe", "args": [ "C:\\path\\to\\MayaMCP\\src\\maya_mcp_server.py" ], "cwd": null, "encoding": "utf-8", "encoding_error_handler": "strict" } ``` **macOS/Linux**: ``` { "transport": "stdio", "command": "/path/to/MayaMCP/.venv/bin/python", "args": [ "/path/to/MayaMCP/src/maya_mcp_server.py" ], "cwd": null, "encoding": "utf-8", "encoding_error_handler": "strict" } ``` Path Configuration Replace the example paths with the actual absolute path to your MayaMCP project directory. Use forward slashes (`/`) on macOS/Linux and backslashes (`\\`) on Windows. 1. **Click Create Server** ## Available Tools ### Basic Tools - **`list_objects_by_type`** - Get a list of objects in the scene, with optional filtering for cameras, lights, materials, or shapes - **`create_object`** - Create basic objects (cube, cone, sphere, cylinder, camera, lights) - **`get_object_attributes`** - Get a list of attributes on a Maya object - **`set_object_attribute`** - Set an object's attribute with a specific value - **`scene_new`** - Create a new scene in Maya - **`scene_open`** - Load a scene into Maya - **`scene_save`** - Save the current scene - **`select_object`** - Select an object in the scene - **`clear_selection_list`** - Clear the user selection list - **`viewport_focus`** - Center and fit the viewport to focus on an object ### Advanced Modeling Tools - **`create_advanced_model`** - Create complex 3D models (cars, trees, buildings, cups, chairs) with detailed parameters - **`mesh_operations`** - Perform modeling operations (extrude, bevel, subdivide, boolean, combine, bridge, split) - **`create_material`** - Create and assign materials (lambert, phong, wood, marble, chrome, glass, etc.) - **`create_curve`** - Generate NURBS curves (line, circle, spiral, helix, star, gear, etc.) - **`curve_modeling`** - Create geometry using curve-based modeling (extrude, loft, revolve, sweep, etc.) - **`organize_objects`** - Organize objects through grouping, parenting, layout, alignment, and distribution ## Configuration Options You can customize the Maya connection by modifying the configuration: ``` { "transport": "stdio", "command": "/path/to/MayaMCP/.venv/bin/python", "args": [ "/path/to/MayaMCP/src/maya_mcp_server.py" ], "cwd": "/path/to/MayaMCP", "encoding": "utf-8", "encoding_error_handler": "strict" } ``` ### Platform-Specific Paths **Windows**: ``` { "command": "C:\\path\\to\\MayaMCP\\.venv\\Scripts\\python.exe", "args": ["C:\\path\\to\\MayaMCP\\src\\maya_mcp_server.py"] } ``` **macOS/Linux**: ``` { "command": "/path/to/MayaMCP/.venv/bin/python", "args": ["/path/to/MayaMCP/src/maya_mcp_server.py"] } ``` ## Example Use Cases Here are some examples of what you can create with the Maya MCP server: - "Create a simple car model with 4 wheels and a sporty design" - "Build a tree with 3 branches and dense foliage" - "Create a building with windows and apply a brick material" - "Make a cup and apply a chrome material to it" - "Create a chair and position it in the scene" - "Generate a spiral curve and extrude it to create a spring" - "Create a gear-shaped curve for mechanical modeling" - "Group all the furniture objects together" - "Align all objects to the world origin" - "Create a new scene and save it as 'my_project.ma'" ## Advanced Features ### Custom Model Creation The `create_advanced_model` tool supports various model types with specific parameters: **Car Model**: ``` { "model_type": "car", "parameters": { "wheels": 4, "sporty": true, "convertible": false } } ``` **Tree Model**: ``` { "model_type": "tree", "parameters": { "branches": 3, "leaf_density": 0.8, "type": "pine" } } ``` ### Material Creation Create various material types with custom properties: **Chrome Material**: ``` { "material_type": "chrome", "color": [0.8, 0.8, 0.8], "parameters": { "reflectivity": 0.9 } } ``` **Wood Material**: ``` { "material_type": "wood", "color": [0.6, 0.4, 0.2], "parameters": { "veinSpread": 0.5, "veinColor": [0.3, 0.2, 0.1] } } ``` ## Troubleshooting ### Common Issues - **Connection Issues**: Ensure Maya is running and the Command Port is enabled, verify the MCP server is configured correctly in Griptape Nodes, check that the Python path points to the correct virtual environment - **Permission Denied**: When Maya first connects, click "Allow All" in the Maya popup to enable communication - **Path Issues**: Use absolute paths in the configuration, ensure the MayaMCP project path is correct - **Python Version**: Ensure you're using Python 3.10 or greater ### Debug Tips 1. Test with simple commands first (e.g., "list all objects in the scene") 1. Verify Maya is running and accessible 1. Check that the virtual environment is properly activated 1. Ensure all file paths in the configuration are absolute and correct 1. Restart both Maya and the MCP connection if issues persist ### Maya Command Port The Maya MCP server uses Maya's default Command Port for communication. This means: - No additional Maya plugins or addons are required - Communication happens through MEL scripting - Python code is executed within Maya's Python interpreter - Results are returned through the command port ## Security Considerations Arbitrary Code Execution The Maya MCP server executes Python code within Maya's environment. This can be powerful but potentially dangerous: - **Always save your Maya work** before using the MCP server - Review generated operations when possible - Use with caution in production environments - Be aware that complex operations could potentially affect your Maya scene Maya Command Port The server uses Maya's Command Port for communication: - This is a standard Maya feature for external communication - No additional security measures are implemented - Ensure your Maya installation is secure and up-to-date - Consider the implications of allowing external access to Maya ## Resources - [Maya MCP Server Repository](https://github.com/PatrickPalmer/MayaMCP) - Official repository and documentation - [Autodesk Maya](https://www.autodesk.com/products/maya) - Official Maya documentation - [Maya Python API](https://help.autodesk.com/view/MAYAUL/2023/ENU/?guid=Maya_SDK_Python_ref_index_html) - Reference for Maya scripting - [Model Context Protocol](https://modelcontextprotocol.io/) - MCP specification and documentation ## Developer Notes The Maya MCP server is designed to be easily extensible. New tools can be added by creating Python files in the `mayatools/thirdparty` directory. The server automatically discovers and registers new tools at runtime. Key design principles: - Tools run directly in Maya's Python environment - No MCP decorators needed in tool files - Functions are scoped to prevent namespace pollution - Results are returned through Maya's command port - Error handling is built into the communication layer # Exa MCP Server The **[Exa MCP Server](https://github.com/exa-labs/exa-mcp-server)** provides powerful web search and research capabilities through Exa AI's advanced search engine. It offers both local and remote deployment options, with specialized tools for code search, web research, and content extraction. ## Installation The easiest way to use Exa is through their hosted MCP server: 1. **Open Griptape Nodes** and go to **Settings** → **MCP Servers** 1. **Click + New MCP Server** 1. **Configure the server**: - **Server Name/ID**: `exa` - **Connection Type**: `Streamable HTTP` - **Configuration JSON**: ``` { "transport": "streamable_http", "url": "https://mcp.exa.ai/mcp", "headers": {}, "timeout": 30, "sse_read_timeout": 300, "terminate_on_close": true } ``` 1. **Click Create Server** ## Available Tools - **`get_code_context_exa`** - Search billions of GitHub repos, docs, and Stack Overflow for relevant code examples - **`web_search_exa`** - Real-time web searches with optimized results - **`crawling`** - Extract content from specific URLs - **`company_research`** - Comprehensive company information gathering - **`linkedin_search`** - Search LinkedIn for companies and people - **`deep_researcher_start`** - Start AI-powered research on complex topics - **`deep_researcher_check`** - Get comprehensive research reports ## Configuration Options You can enable specific tools by adding them to your configuration: ``` { "transport": "streamable_http", "url": "https://mcp.exa.ai/mcp", "enabled_tools": ["get_code_context_exa", "web_search_exa"] } ``` Available tool combinations: - **For Developers**: `["get_code_context_exa", "web_search_exa"]` - **For Researchers**: `["web_search_exa", "deep_researcher_start", "deep_researcher_check"]` - **For Business Intelligence**: `["company_research", "linkedin_search", "web_search_exa"]` - **All Tools**: `["get_code_context_exa", "web_search_exa", "company_research", "crawling", "linkedin_search", "deep_researcher_start", "deep_researcher_check"]` ## Troubleshooting ### Common Issues - **Connection Issues**: Test the remote server URL `https://mcp.exa.ai/mcp`, check your internet connection, verify firewall settings allow HTTPS connections - **Tool Not Available**: Ensure the tool is enabled in your configuration, check the tool name spelling, verify the tool is available in the current Exa service ### Debug Tips 1. Test with simple queries first 1. Check the Exa dashboard for usage and errors 1. Use the remote server for easier troubleshooting 1. Start with basic tools before using advanced features ## Resources - [Exa MCP Server](https://mcp.exa.ai/mcp) - Official server endpoint - [Exa AI](https://exa.ai) - Exa AI platform and documentation # Fetch MCP Server The **[Fetch MCP Server](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch)** provides web content fetching capabilities, allowing AI agents to retrieve and process content from web pages. It converts HTML to markdown for easier consumption and is perfect for research, content analysis, and web scraping tasks. ## Installation 1. **Open Griptape Nodes** and go to **Settings** → **MCP Servers** 1. **Click + New MCP Server** 1. **Configure the server**: - **Server Name/ID**: `fetch` - **Connection Type**: `Local Process (stdio)` - **Configuration JSON**: ``` { "transport": "stdio", "command": "uvx", "args": ["mcp-server-fetch"], "env": {}, "encoding": "utf-8", "encoding_error_handler": "strict" } ``` 1. **Click Create Server** ## Available Tools - **`fetch`** - Fetch content from a web URL and convert to markdown ## Configuration Options You can customize the fetch server behavior with environment variables: ``` { "transport": "stdio", "command": "uvx", "args": ["mcp-server-fetch"], "env": { "FETCH_TIMEOUT": "30000", "FETCH_USER_AGENT": "MyApp/1.0" }, "encoding": "utf-8", "encoding_error_handler": "strict" } ``` Available environment variables: - **`FETCH_TIMEOUT`** - Request timeout in milliseconds (default: `30000`) - **`FETCH_USER_AGENT`** - User agent string for requests (default: `mcp-server-fetch`) - **`FETCH_MAX_SIZE`** - Maximum response size in bytes (default: `10485760` (10MB)) ## Troubleshooting ### Common Issues - **Server Not Responding**: Check your internet connection, verify the URL is accessible in a browser, try a different URL to test the server - **Content Not Loading**: Some websites block automated requests, try adding a custom user agent, check if the site requires authentication - **Timeout Errors**: Increase the `FETCH_TIMEOUT` value, try smaller, simpler pages first, check your network connection speed - **Invalid URLs**: Ensure URLs include the protocol (http:// or https://), check for typos in the URL, verify the website is accessible ## Resources - [Fetch MCP Server](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch) - Official repository and documentation # Filesystem MCP Server The **[Filesystem MCP Server](https://github.com/modelcontextprotocol/servers/blob/main/src/filesystem/README.md)** enables AI agents to perform file and directory operations on your local machine. It provides secure, controlled access to specific directories for file management, content organization, and data processing tasks. ## Installation 1. **Open Griptape Nodes** and go to **Settings** → **MCP Servers** 1. **Click + New MCP Server** 1. **Configure the server**: - **Server Name/ID**: `filesystem` - **Connection Type**: `Local Process (stdio)` - **Configuration JSON**: ``` { "transport": "stdio", "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/directory"], "env": {}, "encoding": "utf-8", "encoding_error_handler": "strict" } ``` 1. **Click Create Server** ## Available Tools - **`read_file`** - Read file contents - **`write_file`** - Write content to files - **`list_directory`** - List directory contents - **`create_directory`** - Create new directories - **`search_files`** - Find files by name or pattern - **`move_file`** - Move or rename files - **`delete_file`** - Remove files ## Configuration Options To allow access to multiple directories, add them as separate arguments: ``` { "transport": "stdio", "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "/Users/username/Desktop", "/Users/username/Downloads", "/Users/username/Documents" ], "env": {}, "encoding": "utf-8", "encoding_error_handler": "strict" } ``` ## Resources - [Filesystem MCP Server](https://github.com/modelcontextprotocol/servers/blob/main/src/filesystem/README.md) - Official repository and documentation - [Node.js File System API](https://nodejs.org/api/fs.html) - Reference for file operations # Time MCP Server The [**Time MCP Server**](https://github.com/modelcontextprotocol/servers/tree/main/src/time) provides date and time operations for AI agents, enabling them to work with temporal data, scheduling, and time-based calculations. It's perfect for any workflow that needs to handle dates, times, timezones, or scheduling. ## Installation 1. **Open Griptape Nodes** and go to **Settings** → **MCP Servers** 1. **Click + New MCP Server** 1. **Configure the server**: - **Server Name/ID**: `time` - **Connection Type**: `Local Process (stdio)` - **Configuration JSON**: ``` { "transport": "stdio", "command": "uvx", "args": ["mcp-server-time"], "env": {}, "cwd": null, "encoding": "utf-8", "encoding_error_handler": "strict" } ``` 1. **Click Create Server** ## Available Tools - **`get_current_time`** - Get the current date and time - **`parse_date`** - Parse date strings into structured format - **`format_date`** - Format dates into different string formats - **`add_time`** - Add time periods to dates - **`subtract_time`** - Subtract time periods from dates - **`compare_dates`** - Compare two dates - **`get_timezone_info`** - Get information about timezones - **`convert_timezone`** - Convert times between timezones ## Resources - [Time MCP Server](https://github.com/modelcontextprotocol/servers/tree/main/src/time) - Official repository and documentation - [JavaScript Date Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) - Reference for date operations - [Timezone Database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) - Complete list of timezones # Agent skills # Griptape Nodes Workflow Construction Guide This skill covers the full cold-start cycle (build → wire → run → read) against the engine's MCP server, plus the idioms and gotchas discovered from running real workflows. ## Mental Model - **Workflow**: Top-level namespace. Only ONE can be active at a time. Reset with `ClearAllObjectStateRequest`. - **Flow**: The canvas inside a workflow. A workflow has exactly one top-level "canvas" flow. Sub-flows are possible but rarely needed for scratch work. - **Node**: A unit of work with parameters (inputs, outputs, properties). - **Connection**: An edge between two parameters. Two kinds: - **Data flow**: A typed parameter on one node → a typed parameter on another. The engine derives execution order from data dependencies, so data connections alone are usually sufficient. - **Control flow**: `exec_out` → `exec_in`. Only needed when two nodes must run in a specific order but don't share data (e.g. side-effecting steps, branching). Skip these by default. - **Current Context**: A stack (workflow → flow → node). Most requests default to "current" when a name is omitted. ## What the MCP server actually exposes Every MCP tool corresponds 1:1 to a `RequestPayload` class registered in `SUPPORTED_REQUEST_EVENTS` (`src/griptape_nodes/servers/mcp.py`). The server name is prefixed onto each tool, so the request `CreateNodeRequest` is reachable as `griptape_nodes_CreateNodeRequest`. A few consequences worth remembering up front: - **There are no plural variants of individual requests.** `CreateNodesRequest`, `CreateConnectionsRequest`, etc. do not exist. To create N nodes, send N `CreateNodeRequest` calls — or wrap them in a single `EventRequestBatch` call (see below). - **`CreateNodeRequest` does not take parameter values.** Setting a parameter is always a separate `SetParameterValueRequest` after the node exists. There is no `parameter_values` / `inputs` shortcut on create. - **`EventRequestBatch` is the only fan-out primitive.** It is a synthetic tool (no matching `RequestPayload` class) that ships an ordered list of inner requests in one transport frame. Reach for it whenever you already know the shape of a build phase. See "EventRequestBatch: collapse a build phase into one round trip" below. ## Survey the Workspace First Before building anything, find out what is actually loaded in this engine. The set of registered libraries and the node types they expose is the catalog every later step draws from, so spend the round trips up front instead of guessing names that may not exist (or that exist in a different library than you expect). ### Find the workspace directory on disk The MCP surface does not expose `GetConfigValueRequest`, so resolve the workspace path by reading the user config file directly. On macOS / Linux it lives at: ``` ~/.config/griptape_nodes/griptape_nodes_config.json ``` The keys you care about are: - `workspace_directory`: absolute (or `~`-prefixed) workspace root. The sandbox library lives inside this directory. - `app_events.on_app_initialization_complete.libraries_to_register`: list of locally registered `griptape_nodes_library.json` paths. Entries can be either a string or an object `{"path": "...", "enabled": true}`. Reading these tells you where each library's source lives if you need to look at an existing node's Python module before writing a similar sandbox node. The sandbox subdirectory key (`sandbox_library_directory`) is optional and defaults to `sandbox_library`. The absolute sandbox path is therefore `/` (e.g. `~/Projects/.../GriptapeNodes/sandbox_library`). Do this once per session and remember the paths; they don't change mid-run. ### Survey the registered libraries via MCP ``` A. griptape_nodes_ListRegisteredLibrariesRequest() → list of library names currently loaded (e.g. "Griptape Nodes Library", "Sandbox Library"). If a library you expect is missing, no `DescribeNodeType` call against its node types will resolve. B. griptape_nodes_ListNodeTypesInLibraryRequest(library="") → call once per library you might pull from. The returned node-type names are the exact strings to pass to `CreateNodeRequest.node_type` and `DescribeNodeTypeRequest.node_type`. C. (optional) griptape_nodes_ListCategoriesInLibraryRequest(library="") → useful when you only care about a slice of a large library (e.g. only image nodes) and want to scope the next step. ``` Do this once per session, then reuse the catalog. Skip the survey only when you already know (from a prior call in the same session) which library provides each node type you intend to use. ## EventRequestBatch: collapse a build phase into one round trip `EventRequestBatch` (MCP tool name: `griptape_nodes_EventRequestBatch`) wraps an ordered list of inner requests in a single transport frame. Use it whenever the shape of the build phase is already decided — typical pattern is N `CreateNodeRequest` - M `SetParameterValueRequest` + K `CreateConnectionRequest` + one `AutoLayoutFlowRequest`, all in one round trip. Shape: ``` { "requests": [ {"request_type": "CreateNodeRequest", "request": {"node_type": "TextInput", "node_name": "TextInput_1"}}, {"request_type": "SetParameterValueRequest", "request": {"node_name": "TextInput_1", "parameter_name": "text", "value": "..."}} ], "timeout_ms": 60000 } ``` Behavior: - **Sequential dispatch.** The engine awaits inner requests in submission order (`for inner in batch.requests: await _dispatch_event_request(inner)`), so a `CreateNodeRequest` followed by a `SetParameterValueRequest` on that same node is safe inside one batch. - **Pre-flight validation.** Every inner request is constructed against its `RequestPayload` class before anything goes on the wire. An unknown `request_type`, a `request` that is not a JSON object, or unknown kwargs in the inner payload all reject the entire batch up front with a `TypeError` / `ValueError`. - **Per-slot failure isolation.** Once the batch is dispatched, a failure in one slot does not abort siblings. Failed slots come back as `{"ok": false, "details": "..."}` in the result array; successful slots come back as the same flattened object a single tool call would return. Walk every slot before assuming the batch succeeded. - **No nesting.** `EventRequestBatch` is intentionally absent from `SUPPORTED_REQUEST_EVENTS` and the inner `request_type` enum, so a batch cannot contain another batch. - **Default timeout scales with size.** `timeout_ms` defaults to `30000 × len(requests)` clamped at `300000` ms (5 min). Pass an explicit override when the last slot is `StartFlowRequest(wait_for_completion=True)` or any other long-running call; otherwise the synchronous run can eat the budget meant for the rest of the batch. `bool` is rejected explicitly so `True` cannot silently become 1ms. Return shape: a JSON array of trimmed slot responses in submission order. Each slot looks identical to the response that single-tool dispatch would have returned for that `request_type`. ``` [ {"ok": true, "node_name": "TextInput_1", "...": "..."}, {"ok": true, "finalized_value": "...", "...": "..."} ] ``` ### Critical idiom: pre-name nodes you reference later in the same batch Inside one batch you cannot read `CreateNodeResultSuccess.node_name` from an earlier slot before composing a later one — every entry is fixed when the batch is submitted. So either: - Pass an explicit `node_name` on every `CreateNodeRequest` and reuse those names verbatim in later `SetParameterValueRequest` / `CreateConnectionRequest` entries, or - Split the build across two batches: one that creates nodes (read the assigned names back from the result array), then a second that sets parameters and wires them up. The one-batch + explicit names path is almost always shorter and is what the batched recipe below uses. ## Canonical Cold-Start Recipe For a typical 3-node linear pipeline (`TextInput → Agent → DisplayText`), once the workspace survey above has confirmed the relevant library exposes those node types: ``` 1. griptape_nodes_EnsureWorkflowAndFlowRequest() → returns workflow_name, flow_name, created_workflow, created_flow. Idempotent: if both pieces are already in context, reuses them. 2. griptape_nodes_DescribeNodeTypeRequest(node_type="TextInput") 3. griptape_nodes_DescribeNodeTypeRequest(node_type="Agent") 4. griptape_nodes_DescribeNodeTypeRequest(node_type="DisplayText") → get exact parameter names/types/modes. Look for: - data input(s) (mode_allowed_input == true) - data output(s) (mode_allowed_output == true) - control params (type == "parametercontroltype"): exec_in / exec_out are usually skippable (see Mental Model) 5. griptape_nodes_CreateNodeRequest(node_type="TextInput") 6. griptape_nodes_CreateNodeRequest(node_type="Agent") 7. griptape_nodes_CreateNodeRequest(node_type="DisplayText") → each returns a flat `node_name` (e.g. "TextInput_1", "Agent_1"). Read it from the response, do not assume a naming convention. The default name comes from `metadata.display_name` and may include spaces (e.g. "Text Input_1"). Pass an explicit `node_name` if you want a stable handle. 8. griptape_nodes_SetParameterValueRequest( node_name="TextInput_1", parameter_name="text", value="...") → there is no `parameter_values` shortcut on CreateNode; set every non-default parameter with its own SetParameterValue call. 9. griptape_nodes_CreateConnectionRequest( source_node_name="TextInput_1", source_parameter_name="text", target_node_name="Agent_1", target_parameter_name="prompt") 10. griptape_nodes_CreateConnectionRequest( source_node_name="Agent_1", source_parameter_name="output", target_node_name="DisplayText_1", target_parameter_name="text") → data connections only; the engine orders execution from data dependencies. 11. griptape_nodes_AutoLayoutFlowRequest() → required after any multi-node build. Without it, every node lands at (0, 0) and the canvas shows them stacked on top of each other. Topologically sorts the graph and assigns column-and-row positions. Omit `flow_name` to lay out the current-context flow. 12. griptape_nodes_StartFlowRequest(wait_for_completion=True, completion_timeout_ms=60000) → omit flow_name; the handler uses the current-context flow. wait_for_completion blocks until the flow resolves or times out. 13. griptape_nodes_GetParameterValueRequest(node_name="DisplayText_1", parameter_name="text") → the terminal node's output. ``` Thirteen MCP calls: 1 ensure + 3 describe + 3 create + 1 set + 2 connect + 1 layout + 1 run + 1 read. Wider graphs scale linearly: every node adds one CreateNode plus its SetParameterValue calls; every edge adds one CreateConnection. ### Batched variant (4 round trips) The build phase (steps 5-11 above) has fixed shape, so it collapses into one `EventRequestBatch` call. The describe phase still benefits from being a separate batch because its results inform the build payload, and `StartFlowRequest` is usually kept out of the build batch so its long timeout does not gate the rest: ``` 1. EnsureWorkflowAndFlowRequest (1 call) 2. EventRequestBatch([ (1 call, runs sequentially) DescribeNodeTypeRequest("TextInput"), DescribeNodeTypeRequest("Agent"), DescribeNodeTypeRequest("DisplayText"), ]) 3. EventRequestBatch([ (1 call, runs sequentially) CreateNodeRequest(node_type="TextInput", node_name="TextInput_1"), CreateNodeRequest(node_type="Agent", node_name="Agent_1"), CreateNodeRequest(node_type="DisplayText", node_name="DisplayText_1"), SetParameterValueRequest(node_name="TextInput_1", parameter_name="text", value="..."), CreateConnectionRequest(source_node_name="TextInput_1", source_parameter_name="text", target_node_name="Agent_1", target_parameter_name="prompt"), CreateConnectionRequest(source_node_name="Agent_1", source_parameter_name="output", target_node_name="DisplayText_1", target_parameter_name="text"), AutoLayoutFlowRequest(), ]) 4. StartFlowRequest(wait_for_completion=True, completion_timeout_ms=60000) + GetParameterValueRequest("DisplayText_1", "text") (2 calls) ``` Four round trips instead of thirteen. Walk the result arrays after each batch and verify every slot returned `ok: true` before moving on; per-slot failures don't abort the rest of the batch, so a typo in slot 4 still lets slots 5 and 6 run against stale state. ## Key Idioms - **Survey the workspace before picking node types.** Read the JSON config at `~/.config/griptape_nodes/griptape_nodes_config.json` to learn where the sandbox directory and registered library JSONs live, then run `ListRegisteredLibrariesRequest` and `ListNodeTypesInLibraryRequest` for the libraries you care about before reaching for `DescribeNodeType`. The catalog tells you which node types actually exist in this engine and which library owns each one, which is the input both `DescribeNodeTypeRequest.library` and `CreateNodeRequest.specific_library_name` expect when the same name lives in more than one library. - **Discover before wiring.** Always call `DescribeNodeType` for each node type you intend to use before guessing parameter names. The cost is 3-5 calls up front but saves many round trips fighting typos and assumed-wrong names. - **Batch with `EventRequestBatch` once the shape is known.** Anything past the discovery phase usually has fixed shape (N creates + M sets + K connects + a layout). Wrap them in one `EventRequestBatch` call and pre-name every node you reference later in the same batch. Inspect every slot of the result array; per-slot failures do not abort siblings. Keep `StartFlowRequest` out of the build batch unless you raise `timeout_ms` to cover the synchronous run. - **Wire data, not control.** The engine derives execution order from data dependencies, so `exec_out` → `exec_in` connections are usually noise. Only add them when two nodes must run in a specific order but don't exchange data. - **Always run AutoLayout after a multi-node build.** Without it nodes land at (0, 0) and stack on top of each other. `AutoLayoutFlowRequest` is one round trip and idempotent; treat it as the closing step of any build phase. - **Use `wait_for_completion=True` on `StartFlowRequest`.** For workflows that touch LLMs, image generators, or long I/O, set `completion_timeout_ms` generously (60000+ ms). Otherwise the call returns the instant the flow is kicked off and you have to poll `GetNodeResolutionStateRequest` yourself. - **Omit `flow_name` on `StartFlowRequest`** when you just finished building a single flow. The handler defaults to the current-context flow. - **Read the response, don't assume names.** `CreateNodeResultSuccess.node_name` is the authoritative handle for every later call. Pass it verbatim into `SetParameterValueRequest`, `CreateConnectionRequest`, and `GetParameterValueRequest`. ## Response Shape Every MCP tool returns a trimmed object (the engine envelope is unwrapped server-side in `_trim_response`): ``` { "ok": true, "details": "", "altered_workflow_state": true|false, "...": "...payload fields..." } ``` `ok` reflects whether the engine produced a `*ResultSuccess` payload. The remaining fields are flattened from the inner result class, so for example `CreateNodeResultSuccess.node_name` is reachable as `response["node_name"]`, `EnsureWorkflowAndFlowResultSuccess` exposes `workflow_name`, `flow_name`, `created_workflow`, `created_flow` at the top level, and `AutoLayoutFlowResultSuccess` exposes `flow_name` and `positioned_nodes`. Failures surface as MCP tool errors with the engine's `result_details` message attached. `EventRequestBatch` returns a JSON **array** instead of a single object. Each slot mirrors the single-call shape above, so a batched build phase looks like `[{"ok": true, "node_name": "TextInput_1", ...}, {"ok": true, ...}, ...]`. Failed slots come back as `{"ok": false, "details": "..."}` in place; the rest of the array still executes. ## Gotchas ### DescribeNodeType may be partial `DescribeNodeType` probes by instantiating the node class. For node types whose `__init__` performs I/O (network, auth, disk), instantiation may fail. When that happens the response still succeeds but returns: - Full library-level `metadata` (category, description, display_name, etc.) - `parameters: []` (empty — instantiation failed before parameters were declared) - A WARNING-level entry in `details` naming the cause You still know what the node does, but you cannot see its parameter schema from MCP alone. Consider falling back to a different node type, or describe on a system where credentials are present. ### CreateNode can silently produce an ErrorProxyNode When a node fails to instantiate and `create_error_proxy_on_failure=True` (the default), the engine substitutes an `ErrorProxyNode` and reports success. Inspect the response's `node_type` (it will be the proxy class) or the `details` string if a later step fails mysteriously. If you need strict failure semantics, set `create_error_proxy_on_failure=False` on the request. ### Default node names contain spaces The engine names nodes after `metadata.display_name`, which is often human-readable with spaces (e.g. `"Text Input_1"`, `"Display Text_1"`). That works for every API, but it's easy to typo. Either pass an explicit `node_name` per request, or always read the returned `node_name` from `CreateNodeResultSuccess` and reuse it verbatim. ### Connection request field names are long `CreateConnectionRequest` uses the unabbreviated names `source_node_name`, `source_parameter_name`, `target_node_name`, `target_parameter_name`. There are no short aliases (`source`, `source_param`, etc.). A typo here surfaces as an opaque validation error from pydantic, not a friendly "unknown field" message. ### Only one workflow in context at a time `SetWorkflowContextRequest` refuses if a workflow is already in context. To swap, `ClearAllObjectStateRequest(i_know_what_im_doing=True)` first — this wipes EVERYTHING (nodes, flows, connections, workflow). There is no softer reset today. ### Agents cannot be interrupted mid-run There is no pause/cancel for a running flow today. Use `completion_timeout_ms` to bound the wait; if the timeout fires, `StartFlowRequest` returns a failure but the flow keeps running in the engine until it finishes or errors. A subsequent `StartFlowRequest` will fail with "Flow is already running" until it does. ## Tool Cheat Sheet | Goal | Tool | | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | Bootstrap a workflow + flow from cold | `EnsureWorkflowAndFlowRequest` | | Fan N requests out in one round trip | `EventRequestBatch` (synthetic; pre-name nodes that later slots reference) | | Discover libraries / node types | `ListRegisteredLibrariesRequest`, `ListNodeTypesInLibraryRequest`, `ListCategoriesInLibraryRequest` | | Inspect a node type's parameters | `DescribeNodeTypeRequest` | | Create a node | `CreateNodeRequest` | | Wire a single edge | `CreateConnectionRequest` | | Lay out the canvas after a multi-node build | `AutoLayoutFlowRequest` | | Move a single node to an explicit position | `SetNodeMetadataRequest` (set `metadata.position`) | | Set a parameter value | `SetParameterValueRequest` | | Read a parameter value | `GetParameterValueRequest` | | Inspect a parameter's schema/details on a live node | `GetParameterDetailsRequest`, `ListParametersOnNodeRequest` | | Run synchronously | `StartFlowRequest(wait_for_completion=True, completion_timeout_ms=...)` | | Run from a specific node | `StartFlowFromNodeRequest` | | Resolve a single node without firing the control flow | `ResolveNodeRequest` | | Execute a single node directly | `ExecuteNodeRequest` | | Rename a node or flow | `RenameObjectRequest(allow_next_closest_name_available=True)` | | Lock or unlock a node | `SetLockNodeStateRequest` | | Reset a node's parameters to defaults | `ResetNodeToDefaultsRequest` | | Inspect state | `ListNodesInFlowRequest`, `ListConnectionsForNodeRequest`, `GetNodeResolutionStateRequest`, `GetNodeMetadataRequest`, `GetConnectionsForParameterRequest` | | Register a sandbox node type from Python source already on disk | `RegisterSandboxNodeFromSourceRequest` (see Custom nodes below) | | Reset everything | `ClearAllObjectStateRequest(i_know_what_im_doing=True)` | ## Custom nodes If the task involves writing a new node type via `RegisterSandboxNodeFromSourceRequest`, read [`docs/developing_nodes/comprehensive_guide.md`](https://docs.griptapenodes.com/developing_nodes/comprehensive_guide/index.md) **before** drafting source. The guide documents the engine-side conventions a sandbox class must follow: - `BaseNode` subclassing and the `process` / `aprocess` contract - `Parameter` declaration, modes (`mode_allowed_input` / `..._property` / `..._output`), traits - `ParameterString` / `ParameterImage` / etc. helpers (preferred over hand-rolled `Parameter`) - `ParameterGroup` / `ParameterList` containers - Connection rules and node states `RegisterSandboxNodeFromSourceRequest` only **registers** Python source already on disk inside the sandbox library directory; it never writes the file itself. The agent is responsible for placing the `.py` file under `/` (via its own filesystem tool, e.g. pi's `write`) before issuing the request. The imported source then runs inside the engine process with no isolation, so matching the conventions up front is faster than iterating on registration failures. For pure workflow-driving tasks (build → wire → run → read) the guide is overkill — stick to this skill. ## Example: One-Shot Haiku Pipeline Goal: run an `Agent` on a one-line prompt and read the output. 1. `EnsureWorkflowAndFlowRequest()` 1. `DescribeNodeTypeRequest(node_type="TextInput")` → text output parameter is `text` 1. `DescribeNodeTypeRequest(node_type="Agent")` → input `prompt`; output `output` 1. `DescribeNodeTypeRequest(node_type="DisplayText")` → input `text` 1. `CreateNodeRequest(node_type="TextInput")` → read assigned `node_name` 1. `CreateNodeRequest(node_type="Agent")` → read assigned `node_name` 1. `CreateNodeRequest(node_type="DisplayText")` → read assigned `node_name` 1. `SetParameterValueRequest(node_name="TextInput_1", parameter_name="text", value="Write a haiku about clouds.")` 1. `CreateConnectionRequest(TextInput_1.text → Agent_1.prompt)` 1. `CreateConnectionRequest(Agent_1.output → DisplayText_1.text)` 1. `AutoLayoutFlowRequest()` → arrange the 3 nodes across columns 1. `StartFlowRequest(wait_for_completion=True, completion_timeout_ms=60000)` 1. `GetParameterValueRequest(node_name="DisplayText_1", parameter_name="text")` Total: 13 MCP calls from empty engine to rendered output. ### Same pipeline, batched (4 round trips) 1. `EnsureWorkflowAndFlowRequest()` 1. `EventRequestBatch([DescribeNodeTypeRequest × 3])` 1. `EventRequestBatch([CreateNodeRequest × 3 (with explicit node_name), SetParameterValueRequest, CreateConnectionRequest × 2, AutoLayoutFlowRequest])` 1. `StartFlowRequest(wait_for_completion=True, completion_timeout_ms=60000)` then `GetParameterValueRequest("DisplayText_1", "text")` The build batch in step 3 only works because every `CreateNodeRequest` carries an explicit `node_name`; the later `SetParameterValueRequest` and `CreateConnectionRequest` slots reference those names directly instead of waiting on the per-create response. # Node reference # Griptape Nodes... **Nodes** This documentation provides a comprehensive overview of the various nodes available within Griptape Nodes. It focuses on individual nodes, their specific capabilities, and how they can interact with each other, explaining: - Core functionality and purpose - Configuration options and parameters - Input/output behaviors # Diffusion Pipelines You need to perform setup steps to use Hugging Face Diffusion Pipeline nodes [This guide](https://docs.griptapenodes.com/how_to/installs/hugging_face/index.md) will walk you through setting up a Hugging Face account, creating an access token, and installing the required models to make this node fully functional. ## What are they? The Diffusion Pipeline system consists of two complementary nodes that work together to provide efficient image generation: - **Diffusion Pipeline Builder**: Builds and caches 🤗 Diffusers Pipelines for reuse across multiple execution nodes - **Generate Image (Diffusion Pipeline)**: Generates images using the cached pipelines This modular approach allows you to configure a pipeline once and reuse it multiple times, improving performance and resource efficiency. The system supports a wide range of providers and models through dynamic parameters. ## Supported Providers The Diffusion Pipeline Builder supports multiple AI model providers: - **Flux** - High-quality text-to-image generation - **Qwen** - Multimodal capabilities - **Stable Diffusion** - Popular open-source diffusion models - **Allegro** - Video generation capabilities - **Amused** - Efficient masked image modeling - **AudioLDM** - Audio generation from text - **WAN** - Specialized image generation - **Wuerstchen** - Efficient diffusion architecture - **Custom** - Support for custom pipeline configurations and self-provided models ## When would I use it? Use these nodes when you need to: - Generate images from textual descriptions with various model architectures - Leverage advanced image generation models for creative projects - Experiment with different providers and model configurations - Optimize performance by reusing cached pipelines across multiple generations - Work with specialized models for audio, video, or multimodal generation ## How to use it ### Basic Setup The Diffusion Pipeline system uses a two-node workflow: 1. **Configure the Builder**: - Add a "Diffusion Pipeline Builder" node to your workflow - Select your desired provider (Flux, Stable Diffusion, etc.) - Configure provider-specific parameters (model, LoRAs, optimizations) - Run the builder to cache the pipeline 1. **Generate Images**: - Add a "Generate Image (Diffusion Pipeline)" node - Connect the pipeline output from the builder to the runtime node - Configure generation parameters (prompt, dimensions, steps, etc.) - Run the runtime node to generate images ### Pipeline Builder Parameters The builder node has dynamic parameters that change based on the selected provider: - **provider**: Select from supported providers (Flux, Stable Diffusion, etc.) - **Provider-specific parameters**: Model selection, LoRA configurations, optimization settings - **pipeline**: Output connection containing the cached pipeline configuration ### Runtime Parameters The runtime node parameters are dynamically generated based on the connected pipeline: - **pipeline**: Input connection from the builder node - **Dynamic generation parameters**: Prompts, dimensions, inference steps, guidance scales - **output_image**: The generated image as an ImageArtifact - **seed**: An integer seed for random number generation - **logs**: Detailed logs of the generation process Dynamic Parameters Both nodes use dynamic parameters that automatically adjust based on your selections. The available parameters will change when you select different providers or connect different pipelines. ### Advanced Features - **Pipeline Caching**: Built pipelines are cached using configuration hashes for efficient reuse - **LoRA Support**: Load and configure LoRA adapters for model customization - **Optimization Options**: Enable various optimizations for better performance - **Real-time Previews**: Optional intermediate image previews during generation (may slow inference) - **Connection Preservation**: Runtime node preserves parameter connections when pipeline changes ## Manual Memory Settings The Diffusion Pipeline Builder ships with `memory_optimization_strategy` set to **Manual** by default. Manual mode is the default because the Automatic strategy is conservative — it enables every optimization needed to fit the model, which bottlenecks powerful GPUs and slows generation on capable hardware. The tradeoff is that manual mode exposes a row of toggles that assume some familiarity with [🤗 Diffusers memory optimization concepts](https://huggingface.co/docs/diffusers/main/en/optimization/memory). This section explains each manual-mode parameter: what it does, the tradeoff it makes, and a heuristic for when to enable it. ### `attention_slicing` - **What it does**: Computes the attention operation in sequential slices instead of all at once, lowering peak VRAM during attention. - **Tradeoff**: Lower memory at the cost of speed (often 5–20% slower). - **When to enable**: When you hit out-of-memory errors during generation, particularly on GPUs with less than 8 GB VRAM, on Apple Silicon (MPS) with less than 64 GB unified memory, or on CPU. Leave off when you have headroom. ### `vae_slicing` - **What it does**: Decodes the VAE latent in batched slices rather than as a single tensor. - **Tradeoff**: Lower peak memory during the VAE decode step, with negligible speed cost. - **When to enable**: When generating multiple images in a single batch, or when decoding at higher resolutions exhausts VRAM in the final step. Cheap to leave on; effectively free for batch sizes of 1. ### `transformer_layerwise_casting` - **What it does**: Stores the transformer (or UNet) weights in fp8 (`float8_e4m3fn`) and upcasts each layer to bfloat16 only while it is computing. - **Tradeoff**: Roughly halves transformer weight memory vs. bfloat16, with a small speed cost from per-layer casting and a small quality hit on some models. - **When to enable**: When the model fits in VRAM only after weight compression, but you don't want a full quantization pass. Skip if the pipeline is pre-quantized or doesn't support layerwise casting — the node logs a notice and ignores the toggle in those cases. ### `cpu_offload_strategy` - **What it does**: Moves pipeline components between CPU RAM and GPU VRAM to reduce GPU memory residency. - **Choices**: - **None** — All components stay on the GPU. Fastest, requires the most VRAM. - **Model** — One full submodel (e.g. text encoder, transformer, VAE) is on the GPU at a time; others live in CPU RAM and swap in as needed. Moderate VRAM savings, modest speed penalty. - **Sequential** — Even finer-grained: individual `nn.Module` layers are streamed to GPU on demand. Largest VRAM savings, largest speed penalty (often several times slower). - **When to use which**: - **None** if the pipeline already fits with room to spare. - **Model** if you're a few GB short of fitting everything resident. - **Sequential** as a last resort to run a model that wouldn't otherwise load. ### `quantization_mode` - **What it does**: Quantizes pipeline weights via `optimum-quanto` to `fp8`, `int8`, or `int4` before inference. - **Tradeoff**: Significant memory savings (`int4` ≈ 1/4 the size of bfloat16) at increasing risk of quality loss as the bit width drops. The first run also pays a one-time quantization cost. - **When to enable**: When even with offloading the model won't fit, or when you want to free VRAM for other work (larger batch, longer context, additional LoRAs). `fp8` is usually a safe starting point; drop to `int8`/`int4` only if needed. ### If you run out of memory Escalate one knob at a time in this order: enable `vae_slicing` → enable `attention_slicing` → switch `cpu_offload_strategy` to `Model` → enable `transformer_layerwise_casting` → step `quantization_mode` down (`fp8` → `int8` → `int4`) → switch `cpu_offload_strategy` to `Sequential`. ### When in doubt: switch to Automatic Automatic mode runs the pipeline through a memory-aware decision tree (see `_automatic_optimize_diffusion_pipeline` in `pipeline_utils.py`) that enables only the optimizations needed to fit the model on the detected device. It's slower than a hand-tuned manual configuration on capable hardware, but it's a safe fallback when you don't want to hand-pick settings. For a deeper treatment of the underlying concepts, see the upstream [🤗 Diffusers memory optimization guide](https://huggingface.co/docs/diffusers/main/en/optimization/memory). ## Performance Optimization - **Reuse Pipelines**: Build once, generate many times by connecting multiple runtime nodes to one builder - **Cache Management**: Pipelines are automatically cached and reused across workflow runs - **Memory Management**: Configure optimization settings in the builder for your hardware - **Preview Settings**: Disable intermediate previews for faster generation ## Common Issues - **Missing API Key**: Ensure the Hugging Face API token is set as `HUGGINGFACE_HUB_ACCESS_TOKEN`; instructions for that are in [this guide](https://docs.griptapenodes.com/how_to/installs/hugging_face/index.md) - **Pipeline Not Found**: If you see cache errors, ensure the builder node has been executed successfully - **Memory Constraints**: Large models or high-resolution generation may require significant GPU memory - **Provider Compatibility**: Ensure your selected model is compatible with the chosen pipeline type # YOLOv8 Face Detection You need to perform setup steps to use Hugging Face models [This guide](https://docs.griptapenodes.com/how_to/installs/hugging_face/index.md) will walk you through setting up a Hugging Face account, creating an access token, and installing the required models to make this node fully functional. ## What is it? YOLOv8 Face Detection is a computer vision node that detects human faces in images using the YOLOv8 (You Only Look Once version 8) object detection model from 🤗 Hugging Face. The node processes images and returns bounding box coordinates for each detected face along with confidence scores. The implementation uses the `arnabdhar/YOLOv8-Face-Detection` model, which is specifically trained for face detection tasks and provides fast, accurate results suitable for real-time applications. ## When would I use it? Use this node when you need to: - Detect faces in images for face recognition pipelines - Crop or extract face regions from photos - Count the number of people in an image - Filter images based on face presence or absence - Create face-focused compositions or effects - Process images for privacy protection (face blurring/masking) - Build automated photo organization systems - Implement face-based access control systems ## How to use it ### Basic Setup 1. **Add the Node**: - Add a "YOLOv8 Face Detection" node to your workflow - The node will automatically download the model on first use (requires Hugging Face access) 1. **Connect Input**: - Connect an `ImageArtifact` or `ImageUrlArtifact` to the `input_image` parameter - This can come from file loaders, image generation nodes, or other image processing nodes 1. **Configure Parameters**: - Set the `confidence_threshold` (0.0-1.0) to filter detections - Optionally set `dilation` (0-100%) to expand the detected bounding boxes 1. **Run Detection**: - Execute the node to detect faces in the input image - The `detected_faces` output will contain a list of face detections ### Parameters #### Input Parameters - **input_image** (required) - Type: `ImageArtifact` or `ImageUrlArtifact` - The image to analyze for face detection - **confidence_threshold** - Type: `float` (0.0-1.0) - Default: `0.5` - Minimum confidence score for a detection to be included in the results - Higher values = fewer but more confident detections - Lower values = more detections but may include false positives - **dilation** - Type: `float` (0.0-100.0) - Default: `0.0` - Percentage to expand bounding boxes while keeping them centered - Useful for including more context around detected faces - Example: `10.0` expands the box by 10% in all directions #### Output Parameters - **detected_faces** - Type: `list` - A list of detected faces, each containing: - `x`: Top-left x-coordinate of the bounding box - `y`: Top-left y-coordinate of the bounding box - `width`: Width of the bounding box - `height`: Height of the bounding box - `confidence`: Detection confidence score (0.0-1.0) - **logs** - Type: `string` - Detailed logs of the detection process including: - Model loading status - Detection parameters - Number of faces detected ### Output Format Each detected face is represented as a dictionary: ``` { "x": 150, "y": 200, "width": 300, "height": 350, "confidence": 0.95 } ``` The bounding box coordinates are in pixels relative to the input image dimensions, with the origin (0,0) at the top-left corner. ### Example Workflows #### Basic Face Detection 1. Load an image using "File to Bytes" or similar node 1. Connect to YOLOv8 Face Detection's `input_image` 1. Set `confidence_threshold` to `0.5` for balanced detection 1. The `detected_faces` output contains all face locations and confidence scores #### Face Cropping Pipeline 1. Detect faces using YOLOv8 Face Detection 1. Set `dilation` to `10.0` to include some background around faces 1. Connect `detected_faces` to a "Crop Image" node 1. Extract individual face images for further processing #### High-Confidence Detection Only 1. Set `confidence_threshold` to `0.8` or higher 1. This filters out uncertain detections 1. Ideal for applications requiring high precision ### Advanced Features - **Automatic Model Caching**: Downloaded models are cached locally for faster subsequent runs - **Boundary Clamping**: Dilated bounding boxes are automatically clamped to image boundaries - **Centered Dilation**: Box expansion maintains the center point of the original detection - **Batch-Compatible**: Process multiple images by connecting to loop structures ## Performance Considerations - **First Run**: The initial execution downloads the model (~6MB), which may take a few moments - **Subsequent Runs**: Cached models load almost instantly - **Image Size**: Larger images take longer to process but may detect more distant faces - **Detection Speed**: YOLOv8 is optimized for real-time performance, typically processing images in milliseconds - **Memory Usage**: Model requires ~50MB of RAM when loaded ## Common Issues - **Missing API Key**: Ensure the Hugging Face API token is set as `HF_TOKEN`; instructions for that are in [this guide](https://docs.griptapenodes.com/how_to/installs/hugging_face/index.md) - **Model Not Found**: If you see "model not available" warnings, click the provided link to open the Model Manager and download the model - **No Faces Detected**: Try lowering the `confidence_threshold` if you expect faces but none are detected - **Too Many False Positives**: Increase the `confidence_threshold` to filter out low-confidence detections - **Bounding Box Issues**: If boxes seem too tight, increase the `dilation` parameter to add padding ## Technical Details - **Model**: YOLOv8 Face Detection from Hugging Face (`arnabdhar/YOLOv8-Face-Detection`) - **Architecture**: YOLOv8 object detection framework specialized for face detection - **Dependencies**: `ultralytics>=8.0.0`, `supervision>=0.20.0` - **Output Format**: Standard bounding box format (x, y, width, height) with confidence scores - **Processing**: Single-pass detection, optimized for speed and accuracy # Agent ## What is it? The Agent node lets you configure an AI Agent with customizable capabilities like tools and rulesets. This node can create an Agent for immediate use given it's own prompt, or can be passed to other nodes' "agent" inputs. ## When would I use it? Use this node when you want to: - Create a configurable AI Agent from scratch - Set up an Agent with specific tools and rulesets - Prepare an Agent that can be reused across your workflow - Get immediate responses from your agent using a custom prompt ## How to use it ### Basic Setup 1. Add the Agent to your workflow 1. Configure the agent's capabilities (tools and rulesets) ### Parameters - **agent**: An existing Agent configuration (optional). If specified, it will use the existing Agent when prompting. - **prompt**: The instructions or question you want to ask the Agent - **additional_context**: String or key-value pairs providing additional context to the Agent - **model**: The large language model to choose for your Agent. If you use the `prompt_model_config`, this will be ignored. - **prompt_model_config**: The an external model configuration for how the Agent communicates with AI models. - **tools**: Capabilities you want to give your Agent - **rulesets**: Rules that tell your Agent what it can and cannot do - **output_schema**: A JSON Schema template that defines the exact format you want the Agent's response to follow (optional) ### Outputs - **output**: The text response from your agent (if a prompt was provided) - **agent**: The configured agent object, which can be connected to other nodes ## Example Imagine you want to create an Agent that can write haikus based on prompt_context: 1. Add a KeyValuePair 1. Set the "key" to "topic" and "value" to "swimming" 1. Add an Agent 1. Set the Agent "prompt" to "Write me a haiku about {{topic}}" 1. Connect the KeyValuePair dictionary output to the Agent "prompt_context" input 1. Run the workflow 1. The Agent "output" will contain a haiku about swimming! ## Using Output Schemas ### What is an Output Schema? Think of an output schema as a form you're asking the AI to fill out. Instead of getting a free-form text response, you can specify exactly what pieces of information you want and how they should be organized. For example, instead of asking "Tell me about this product" and getting a paragraph of text, you can ask for: - A product name (text) - A price (number) - Whether it's in stock (yes/no) - A list of features (multiple text items) The AI will then respond with structured data that matches exactly what you asked for. ### Why Use an Output Schema? Use output schemas when you need: - **Consistent formats**: Every response follows the same structure, making it easier to process - **Specific data types**: Guarantee you get numbers where you need numbers, lists where you need lists, etc. - **Easier automation**: Structured data is much easier to connect to other nodes in your workflow - **Validation**: The AI must provide all required fields in the correct format ### How to Create an Output Schema 1. Add a **JSON Input** node to your workflow 1. Add a JSON Schema that defines the output that you want (you can use online tools to help create this) ### Output Schema Example Let's say you want to extract information about a restaurant from a review: 1. Create Schema Fields: - Field "restaurant_name" (type: string) - Field "rating" (type: integer) - Field "price_range" (type: string) - Field "cuisine_type" (type: string) - Field "recommended_dishes" (type: list, list_type: string) 1. Connect all fields to a Create Schema node 1. Connect the schema to your Agent's output_schema input 1. Set your Agent's prompt: "Extract restaurant information from this review: [review text]" 1. The Agent will now respond with structured data instead of plain text, containing exactly those fields 1. The generated schema will look like this: ``` { "type": "object", "properties": { "restaurant_name": { "type": "string" }, "rating": { "type": "integer" }, "price_range": { "type": "string" }, "cuisine_type": { "type": "string" }, "recommended_dishes": { "type": "array", "items": { "type": "string" } } }, "required": ["restaurant_name", "rating", "price_range", "cuisine_type", "recommended_dishes"] } ``` ### What Changes When Using a Schema? - **Output type**: The Agent's output changes from plain text to structured data (JSON format) - **Validation**: If the AI cannot provide data in the requested format, it will try again or return an error ## Important Notes - If you don't provide a prompt, the node will create the agent without running it and the output will contain exactly "Agent Created" - The node supports both streaming and non-streaming prompt drivers - Tools and rulesets can be provided as individual items or as lists - The additional_context parameter allows you to provide additional_context to the agent as a string or dictionary of key/value pairs - By default, you need a valid Griptape API key set up in your environment as `GT_CLOUD_API_KEY` for the node to work. Depending on the models you want to use, the keys you need will be different. - When you pass an Agent from one node to another using the agent input/output pins, the conversation memory is maintained, which means: - The Agent "remembers" previous interactions in the same flow - Context from previous prompts influences how the Agent interprets new prompts - You can build multi-turn conversations across multiple nodes - The Agent can reference information provided in earlier steps of your workflow - Don't know how to create a JSON Schema for the output_schema parameter? Use an online tool like [JSON Schema Builder](https://transform.tools/json-to-json-schema) to create one based on your desired output structure. Or just give an Agent an example of the output you want and have it generate the schema for you! ## Common Issues - **Missing Prompt Driver**: If not specified, the node will use the default prompt driver (It will use the GT_CLOUD_API_KEY and gpt-4o) - **Streaming Issues**: If using a streaming prompt driver, ensure your flow supports handling streamed outputs # LoadAudio ## What is it? The LoadAudio node is a simple building block that lets you bring an audio file into your workflow. Think of it as picking up an audio recording so you can use it in your project. ## When would I use it? Use this node when you want to: - Use an audio file that was created by another node - Pass an audio file to other nodes in your workflow - Process an audio file as part of your project ## How to use it ### Basic Setup 1. Add the LoadAudio node to your workflow 1. Connect it to a source of audio (like the Transcribe Audio node) ### Parameters - **audio**: The audio to load (this can be connected to an output from another node) ### Outputs - **audio**: The loaded audio that can be used by other nodes in your flow ## Example Imagine you've recorded some audio and have it saved on your computer, and you'd like to use it elsewhere: 1. Create a LoadAudio node 1. Click on the load audio button, and choose your audio file from disk 1. The LoadAudio will make the audio available to use in the rest of your workflow ## Important Notes - The LoadAudio simply passes the audio through - it doesn't change the audio itself - You can click the file browser icon to select an audio file from your computer - The audio can be played by clicking the play button # Microphone ## What is it? The Microphone node allows you to capture audio directly from your computer's microphone. It's a simple way to record audio input that can be used in your workflow. ## When would I use it? Use this node when you want to: - Record audio directly from your microphone - Capture voice input for transcription - Create audio recordings as part of your workflow - Provide real-time audio input to other nodes ## How to use it ### Basic Setup 1. Add the Microphone node to your workflow 1. Click the microphone icon to start recording 1. Speak or provide audio input 1. Stop recording when finished ### Parameters - **audio**: The captured audio output (AudioArtifact) ### Outputs - **audio**: The recorded audio that can be used by other nodes in your flow ## Example A simple workflow to record and transcribe audio: 1. Add a Microphone node to your workflow 1. Record your audio input 1. Connect the "audio" output to a TranscribeAudio node 1. The transcription will be available in the TranscribeAudio node's output ## Important Notes - The node requires microphone permissions in your browser - Audio is captured in real-time - The quality of the recording depends on your microphone and system settings - The node supports various audio formats through the AudioArtifact interface ## Common Issues - **No Microphone Access**: Ensure your browser has permission to access your microphone - **Poor Audio Quality**: Check your microphone settings and system audio configuration - **Recording Not Starting**: Make sure no other application is using the microphone # TranscribeAudio ## What is it? The TranscribeAudio node uses OpenAI's models to convert audio into text. It supports multiple transcription models and can work with both direct model configuration and agent-based transcription. ## When would I use it? Use this node when you want to: - Convert speech to text - Transcribe audio recordings - Extract text from audio files - Process voice input into written form - Create transcripts of audio content ## How to use it ### Basic Setup 1. Add the TranscribeAudio node to your workflow 1. Connect an audio source to the "audio" input 1. Choose a transcription model or connect an agent 1. Run the node to generate the transcription ### Parameters - **agent**: An optional existing agent configuration to use for transcription - **model**: The transcription model to use (defaults to "gpt-4o-mini-transcribe") - **audio**: The audio file to transcribe (required) - **output**: The transcribed text output ### Outputs - **output**: The transcribed text from the audio - **agent**: The agent object used for transcription, which can be connected to other nodes ## Example A complete workflow for recording and transcribing audio: 1. Add a Microphone node to capture audio 1. Connect the Microphone's "audio" output to the TranscribeAudio node's "audio" input 1. Select your preferred transcription model 1. Run the workflow 1. The transcribed text will be available in the "output" parameter ## Important Notes - The node requires a valid OpenAI API key set up in your environment as `OPENAI_API_KEY` - Available models include: - gpt-4o-mini-transcribe - gpt-4o-transcribe - whisper-1 - You can provide your own agent configuration for more customized behavior - The quality of transcription depends on the audio quality and the selected model ## Common Issues - **Missing API Key**: Ensure your OpenAI API key is properly set up as the environment variable - **No Audio Provided**: Make sure you've connected a valid audio source to the "audio" input - **Poor Transcription Quality**: Try using a different model or improving the audio quality - **Processing Errors**: Very long audio files or poor audio quality might result in less accurate transcriptions # AnthropicPrompt ## What is it? The AnthropicPrompt node sets up a connection to Anthropic's AI models (like Claude). ## When would I use it? Use this node when you want to: - Use Anthropic's chat models in your workflow - Customize how your agents interact with Anthropic models - Control specific settings for Anthropic model responses ## How to use it ### Basic Setup 1. Add an AnthropicPrompt node to your workflow 1. Connect its output to nodes that can use Anthopic prompt models like Claude (lfor instance, an Agent!) ### Parameters - **model**: The model to use (default is "claude-3-7-sonnet-latest") - **stream**: Whether to receive responses as they're generated (true) or all at once (false) - **temperature**: Controls randomness in responses (higher values = more creative, lower = more focused) - **max_attempts_on_fail**: How many times to retry if there's an error - **use_native_tools**: Whether to use Anthropic's built-in tools - **max_tokens**: Maximum length of responses - **top_p**: Controls diversity of outputs (similar to temperature) - **top_k**: Controls focus on most likely tokens ### Outputs - **prompt_model_config**: The configured Anthropic driver that other nodes can use ## Example To create an agent that uses Anthropic models with specific settings: 1. Add an AnthropicPrompt to your workflow 1. Connect the "driver" output to an Agent's "prompt_driver" input 1. Now that agent will use Claude with your custom settings Things to try: 1. Try changing the "model" from "claude-3-5-sonnet-latest" 1. Set "temperature" to 0.7 (for more creative responses) 1. Set "max_tokens" to 2000 (for longer responses) ## Important Notes - You need a valid Anthropic API key set up in your environment as `ANTHROPIC_API_KEY` - The default model is "claude-3-5-sonnet-latest" - The node checks if your API key is valid during setup ## Common Issues - **Missing API Key**: Make sure your Anthropic API key is properly set up in app settings - **Connection Errors**: Check your internet connection and API key validity # CoherePrompt ## What is it? The CoherePrompt node sets up a connection to Cohere's AI models. ## When would I use it? Use this node when you want to: - Use Cohere's AI models in your workflow - Take advantage of Cohere's specific capabilities ## How to use it ### Basic Setup 1. Add the CoherePrompt node to your workflow 1. Connect its output to nodes that can use prompt drivers (like Agent) ### Parameters - **model**: The Cohere model to use (default is "command-r-plus") - **max_attempts_on_fail**: How many times to retry if there's an error - **use_native_tools**: Whether to use Cohere's built-in tools - **max_tokens**: Maximum length of responses - **p**: Controls diversity of outputs (similar to temperature) - **k**: Controls focus on most likely tokens - **temperature**: Controls randomness in responses (higher values = more creative, lower = more focused) - **stream**: Whether to receive responses as they're generated (true) or all at once (false) ### Outputs - **prompt_model_config**: The configured Cohere driver that other nodes can use ## Example If you want to create an agent that uses Cohere: 1. Add a CoherePrompt node to your workflow 1. Connect the "driver" output to an Agent's "prompt_driver" input Try: 1. Set "model" to something besides the default 1. Set "max_tokens" to 1000 (for moderate length responses) ## Important Notes - You need a valid Cohere API key set up in your environment as `COHERE_API_KEY` - The default model is "command-r-plus" - The node checks if your API key is valid during setup ## Common Issues - **Missing API Key**: Make sure your Cohere API key is properly set up - **Connection Errors**: Check your internet connection and API key validity # GriptapeCloudImage ## What is it? The GriptapeCloudImage node sets up a connection to Griptape Cloud's image generation service. ## When would I use it? Use this node when you want to: - Generate images using the Griptape Cloud service (you won't have to go register with OpenAI and get an api key) - Create images with DALL-E 3 through Griptape's platform ## How to use it ### Basic Setup 1. Add a GriptapeCloudImage node to your workflow 1. Connect its driver output to nodes that need to generate images (like GenerateImage) ### Parameters - **model**: The model to use (default is "gpt-image-1-mini") - **image_size**: The size of images to generate (default is "1024x1024") - **style**: natural or vivid. Natural creates photorealistic images with natural lighting and textures, while vivid creates images with enhanced colors, contrast, and more dramatic compositions - **quality**: Select the quality for image generation. Standard or HD. ### Outputs - **image_model_config**: The configured Griptape Cloud image model configuration that other nodes can use ## Example Imagine you want to create images using Griptape Cloud: 1. Add a GriptapeCloudImage node to your workflow 1. Set "size" to "1024x1792" for vertical images 1. Connect the "image_model_config" output to a GenerateImage's "image_model_config" input 1. Now that node will generate images using Griptape Cloud with your settings ## Important Notes - You need a valid Griptape API key set up in your environment as `GT_CLOUD_API_KEY` (this should be automagic with your being in Griptape Nodes at all!) - The node will automatically adjust image sizes based on model choice - It should be noted, this is the default image_model_config for the GenerateImage node, and in fact should have no effect if plugged into that node. # GriptapeCloudPrompt ## What is it? The GriptapeCloudPrompt node sets up a connection to Griptape Cloud's AI services. ## When would I use it? Use this node when you want to: - Use various AI models through the Griptape Cloud service (you won't have to go register with OpenAI and get an api key) - Customize which AI model for anything that takes a prompt driver ## How to use it ### Basic Setup 1. Add a GriptapeCloudPrompt node to your workflow 1. Connect its driver output to nodes that need to use AI models (like an Agent) ### Parameters - **model**: The AI model to use (default is "gpt-4o") - **stream**: Whether to receive responses as they're generated (true) or all at once (false) - **temperature**: Controls randomness in responses (higher values = more creative, lower = more focused) - **max_attempts_on_fail**: How many times to retry if there's an error - **use_native_tools**: Whether to use the model's built-in tools - **max_tokens**: Maximum length of responses - **top_p**: Controls diversity of outputs (converted to top_p internally) ### Outputs - **prompt_model_config**: The configured Griptape Cloud driver that other nodes can use ## Example Imagine you want to create an agent that uses GPT-4o through Griptape Cloud: 1. Add a GriptapeCloudPrompt to your workflow 1. Set "model" to "gpt-4o" 1. Connect the "driver" output to an Agent's "prompt_driver" input 1. Now that agent will use Griptape Cloud with your custom settings Try these things: 1. Set "temperature" to 0.7 (for more creative responses) 1. Set "stream" to true or false (to see responses as they're generated, or just at once when done) ## Important Notes - You need a valid Griptape API key set up in your environment as `GT_CLOUD_API_KEY` - The default model is "gpt-4o" - The min_p parameter is converted to top_p internally (top_p = 1 - min_p) ## Common Issues - **Missing API Key**: Make sure your Griptape API key is properly set up - **Connection Errors**: Check your internet connection and API key validity # GrokImage ## What is it? The GrokImage node sets up a connection to Grok's image generation service (DALL-E). ## When would I use it? Use this node when you want to: - Generate images using Grok's DALL-E models - Create visual content from text descriptions - Connect image generation capabilities to your workflow ## How to use it ### Basic Setup 1. Add an GrokImage node to your workflow 1. Connect its driver output to nodes that can generate images (like GenerateImage) ### Parameters - **model**: The model to use (default is "grok-2-image-1212") ### Outputs - **image_model_config**: The configured Grok image model configuration that other nodes can use ## Example Imagine you want to create images using Grok: 1. Add an GrokImage node to your workflow 1. Configure any available settings 1. Connect the "image_model_config" output to a GenerateImage's "image_model_config" input ## Important Notes - You need a valid Grok API key set up in your environment as `GROK_API_KEY`, available from: https://console.x.ai - This node is a simple wrapper around Grok's image generation capabilities ## Common Issues - **Missing API Key**: Make sure your Grok API key is properly set up - **Connection Errors**: Check your internet connection and API key validity - **Generation Limits**: Be aware of Grok's rate limits and usage quotas # GrokPrompt Billing Required for xAI API Usage The GrokPrompt node requires an xAI account and billing information to be set up before xAI API keys will work. Without completing the billing setup, any nodes using xAI will fail, even with a valid API key. See [this guide](https://docs.griptapenodes.com/how_to/keys/grok/index.md) for instructions on setting up a xAI account with billing. ## What is it? The GrokPrompt node sets up a connection to xAI's Grok models. ## When would I use it? Use this node when you want to: - Use Grok's chat models in your workflow - Customize how your agents interact with Grok models - Control specific settings for Grok model responses ## How to use it ### Basic Setup 1. Add a GrokPrompt node to your workflow 1. Connect its output to nodes that can use Grok prompt models (for instance, an Agent!) ### Parameters - **model**: The model to use. Default is "grok-3-beta", choices are "grok-3-beta", "grok-3-fast-beta", "grok-3-mini-beta", "grok-3-mini-fast-beta", "grok-2-vision-1212" - **top_p**: Controls diversity of outputs (default: 0.9) - **stream**: Whether to receive responses as they're generated (true) or all at once (false) - **temperature**: Controls randomness in responses (higher values = more creative, lower = more focused) - **max_attempts_on_fail**: How many times to retry if there's an error - **use_native_tools**: Whether to use Anthropic's built-in tools - **max_tokens**: Maximum length of responses ### Outputs - **prompt_model_config**: The configured Grok driver that other nodes can use ## Example To create an agent that uses Grok models with specific settings: 1. Add a GrokPrompt to your workflow 1. Connect the **prompt_model_config** output to an Agent's **prompt_model_config** input 1. Now that agent will use Grok with your custom settings Things to try: 1. Try changing the "model" from "grok-3-beta" to "grok-3-mini-beta" 1. Set "top_p" to 0.7 (for more focused responses) ## Important Notes - You need a valid Grok API key set up in your environment as `GROK_API_KEY` - Unlike some other drivers, Grok doesn't support parameters like 'seed' and 'top_k' - Grok is a paid service, and you must have billing set up on the Grok.ai website before your API key will work # Groq Prompt Driver The Groq Prompt Driver node allows you to configure and utilize Groq's language models within the Griptape Nodes framework. This node provides access to Groq's high-performance inference engine and various language models. ## Configuration ### API Key To use the Groq Prompt Driver, you need to provide a Groq API key. You can obtain an API key from the [Groq Console](https://console.groq.com/keys). ### Available Models The Groq Prompt Driver supports the following models: #### Production Models - [gemma2-9b-it](https://huggingface.co/google/gemma-2-9b-it) - Google's Gemma 2 9B model - [meta-llama/llama-guard-4-12b](https://console.groq.com/docs/model/llama-guard-4-12b) - Meta's Llama Guard model for content moderation - [llama-3.3-70b-versatile](https://console.groq.com/docs/model/llama-3.3-70b-versatile) - Meta's versatile 70B parameter model - [llama-3.1-8b-instant](https://console.groq.com/docs/model/llama-3.1-8b-instant) - Meta's fast 8B parameter model - [llama3-70b-8192](https://console.groq.com/docs/model/llama3-70b-8192) - Meta's 70B parameter model with 8K context - [llama3-8b-8192](https://console.groq.com/docs/model/llama3-8b-8192) - Meta's 8B parameter model with 8K context - [meta-llama/llama-4-scout-17b-16e-instruct](https://console.groq.com/docs/model/llama-4-scout-17b-16e-instruct) - Meta's vision model with 16K context, compatible with the Image Description node - [meta-llama/llama-4-maverick-17b-128e-instruct](https://console.groq.com/docs/model/llama-4-maverick-17b-128e-instruct) - Meta's vision model with 128K context, compatible with the Image Description node #### Preview Models - [allam-2-7b](https://ai.azure.com/explore/models/ALLaM-2-7b-instruct/version/2/registry/azureml) - Saudi Data and AI Authority's 7B parameter model - [deepseek-r1-distill-llama-70b](https://console.groq.com/docs/model/deepseek-r1-distill-llama-70b) - DeepSeek's distilled 70B parameter model ### Parameters The Groq Prompt Driver supports the following configuration parameters: | Parameter | Type | Default | Description | | ------------ | ------- | ------------ | ---------------------------------------------------- | | model | string | gemma2-9b-it | The Groq model to use for text generation | | temperature | float | 0.7 | Controls randomness in the output (0.0 to 1.0) | | top_p | float | 0.9 | Controls diversity via nucleus sampling (0.0 to 1.0) | | max_tokens | integer | 2048 | Maximum number of tokens to generate | | stream | boolean | false | Whether to stream the response | | max_attempts | integer | 3 | Maximum number of retry attempts for failed requests | ## Usage 1. Add the Groq Prompt Driver node to your workflow 1. Make sure to get an API key here: [Groq API Keys](https://console.groq.com/keys) 1. Configure your `GROQ_API_KEY` in the Griptape Configuration settings 1. Select your desired model from the available options 1. Adjust the generation parameters as needed 1. Connect the node to other nodes in your workflow ## Notes - The Groq Prompt Driver uses the OpenAI-compatible API endpoint at `https://api.groq.com/openai/v1` - Preview models are intended for evaluation purposes and may be discontinued at short notice - Production models are recommended for production environments - The node automatically handles API key validation before workflow execution # OpenAiImage ## What is it? The OpenAiImage node sets up a connection to OpenAI's image generation service (DALL-E). ## When would I use it? Use this node when you want to: - Generate images using OpenAI's DALL-E models - Create visual content from text descriptions - Connect image generation capabilities to your workflow ## How to use it ### Basic Setup 1. Add an OpenAiImage node to your workflow 1. Connect its driver output to nodes that can generate images (like GenerateImage) ### Parameters - **model**: The model to use (default is "dall-e-3") - **image_size**: The size of images to generate (default is "1024x1024") - **style**: natural or vivid. Natural creates photorealistic images with natural lighting and textures, while vivid creates images with enhanced colors, contrast, and more dramatic compositions - **quality**: Select the quality for image generation. Standard or HD. - **background**: Select the background for image generation - **moderation**: Standard or strict, regarding filtering for harmful or offensice content - **output_format**: png, jpeg ### Outputs - **image_model_config**: The configured OpenAI image model configuration that other nodes can use ## Example Imagine you want to create images using OpenAI's DALL-E: 1. Add an OpenAiImage node to your workflow 1. Configure any available settings 1. Connect the "image_model_config" output to a GenerateImage's "image_model_config" input ## Important Notes - You need a valid OpenAI API key set up in your environment as `OPENAI_API_KEY` - This node is a simple wrapper around OpenAI's image generation capabilities - The specific DALL-E model used will depend on what's configured in the underlying driver ## Common Issues - **Missing API Key**: Make sure your OpenAI API key is properly set up - **Connection Errors**: Check your internet connection and API key validity - **Generation Limits**: Be aware of OpenAI's rate limits and usage quotas # OpenAiPrompt ## What is it? The OpenAiPrompt node sets up a direct connection to OpenAI's chat models like GPT-4o. ## When would I use it? Use this node when you want to: - Use OpenAI's models in your workflow - Customize how your agents interact with models like GPT-4o - Control specific settings for OpenAI's responses ## How to use it ### Basic Setup 1. Add the OpenAiPrompt to your workflow 1. Connect its driver output to nodes that need to use OpenAI (like Agent) ### Parameters - **model**: The OpenAI model to use (default is "gpt-4o") - **stream**: Whether to receive responses as they're generated (true) or all at once (false) - **temperature**: Controls randomness in responses (higher values = more creative, lower = more focused) - **use_native_tools**: Whether to use OpenAI's built-in tools - **max_tokens**: Maximum length of responses - **max_attempts_on_fail**: How many times to retry if there's an error - **top_p**: Controls diversity of outputs (converted to top_p internally) ### Outputs - **prompt_model_config**: The configured OpenAI driver that other nodes can use ## Example Imagine you want to create an agent that uses GPT-4o with specific settings: 1. Add an OpenAiPrompt node to your workflow 1. Connect the "driver" output to an Agent's "prompt_driver" input Things to try: 1. Set "model" to anything other than "gpt-4o" 1. Set "temperature" to 0.2 (for more focused, deterministic responses) 1. Set "max_tokens" to 2000 (for longer responses) ## Important Notes - You need a valid OpenAI API key set up in your environment as `OPENAI_API_KEY` - The default model is "gpt-4o" - The min_p parameter is converted to top_p internally (top_p = 1 - min_p) - Unlike some other drivers, OpenAI doesn't support the top_k parameter # To JSON Converts incoming value to JSON data using json-repair. ## Description The To JSON node converts various data types to JSON format using `json-repair` for robust handling of malformed JSON strings. It provides intelligent conversion for different input types and includes automatic repair capabilities. ## Parameters ### Input Parameters | Parameter | Type | Description | Default | | --------- | ---- | --------------------------- | ------- | | `from` | any | The data to convert to JSON | `{}` | ### Output Parameters | Parameter | Type | Description | | --------- | ---- | -------------------------- | | `output` | json | The converted data as JSON | ## Features - **JSON Repair Integration**: Uses `json_repair.repair_json()` to handle malformed JSON strings - **Multiple Input Type Support**: Handles different input types intelligently - **Error Handling**: Graceful fallbacks when repair or parsing fails - **Robust Conversion**: Can handle various input formats and convert them to proper JSON data ## Examples ### Dictionary to JSON ``` # Input: {"name": "John", "age": 30, "active": True} # Output: {"name": "John", "age": 30, "active": true} ``` ### Malformed JSON String Repair ``` # Input: '{"name": "John", age: 30, "city": "New York"}' # Missing quotes around age # Output: {"name": "John", "age": 30, "city": "New York"} # Repaired JSON ``` ### Regular JSON String ``` # Input: '{"user": {"name": "Alice", "active": true}}' # Output: {"user": {"name": "Alice", "active": true}} ``` ### List to JSON ``` # Input: [1, 2, 3, "four", {"nested": "value"}] # Output: [1, 2, 3, "four", {"nested": "value"}] ``` ### Other Data Types ``` # Input: "simple string" # Output: "simple string" # Input: 42 # Output: 42 # Input: True # Output: true ``` ## Input Type Handling ### Dictionary Input - **Behavior**: Uses as-is if already a dict - **Example**: `{"key": "value"}` → `{"key": "value"}` ### String Input - **Behavior**: Attempts to repair malformed JSON, falls back to regular JSON parsing - **Example**: `'{"name": "John", age: 30}'` → `{"name": "John", "age": 30}` ### Other Types - **Behavior**: Converts to string first, then attempts repair, with fallback to empty dict - **Example**: `[1, 2, 3]` → `[1, 2, 3]` ## Use Cases - **Data Standardization**: Convert various data formats to JSON - **API Integration**: Prepare data for JSON-based APIs - **Data Cleaning**: Repair and standardize malformed JSON data - **Workflow Integration**: Convert different data types to JSON format for processing - **Configuration Processing**: Handle configuration data in various formats ## Related Nodes - [JSON Input](https://docs.griptapenodes.com/nodes/json/json_input/index.md) - Create JSON data from inputs - [JSON Extract Value](https://docs.griptapenodes.com/nodes/json/json_extract_value/index.md) - Extract values from JSON - [JSON Replace](https://docs.griptapenodes.com/nodes/json/json_replace/index.md) - Replace values in JSON - [Display JSON](https://docs.griptapenodes.com/nodes/json/display_json/index.md) - Display and format JSON data # Dictionary ## What is it? The Dictionary node lets you create a dictionary (a collection of key-value pairs) by providing separate lists of keys and values. It's a simple way to organize related data with named values that can be used throughout your workflow. ## When would I use it? Use this node when you want to: - Organize related data with named values - Create structured data to pass to other nodes - Build configuration settings for other components - Prepare data in a format that's easy to access by key - Combine multiple values into a single organized structure ## How to use it ### Basic Setup 1. Add the Dictionary node to your workflow 1. Set up your lists of keys and values 1. Connect the dictionary output to other nodes that need structured data ### Parameters - **keys**: A list of strings that will be used as dictionary keys - **values**: A list of values (strings, numbers, booleans, etc.) that correspond to each key ### Outputs - **dict**: The constructed dictionary containing all key-value pairs ## Example Imagine you want to create a configuration dictionary for a user profile: 1. Add a Dictionary node to your workflow 1. Set the "keys" parameter to: ``` ["name", "age", "premium_member", "interests"] ``` 1. Set the "values" parameter to: ``` ["Jane Smith", 32, true, ["hiking", "photography", "coding"]] ``` 1. The output dictionary will be: ``` {"name": "Jane Smith", "age": 32, "premium_member": true, "interests": ["hiking", "photography", "coding"]} ``` 1. Connect this dictionary to other nodes that need user profile information ## Important Notes - Keys are automatically converted to strings - If you provide more keys than values, the extra keys will be assigned `None` values - Empty or `None` keys are skipped unless it's the only key and has a value - You can provide single values instead of lists, and they'll be converted to single-item lists - The node works with various value types including strings, numbers, booleans, and nested lists - The dictionary format is compatible with nodes that accept dictionary inputs ## Common Issues - **Mismatched Lists**: If your keys and values lists have different lengths, some keys may have `None` values or some values may be ignored - **Key Conversion**: All keys are converted to strings, which may cause unexpected behavior if you're using complex objects as keys - **Empty Keys**: Empty strings or `None` values used as keys may be skipped depending on their values # DisplayDictionary Node ## What is it? The `DisplayDictionary` node is a utility node that displays the contents of a dictionary in your workflow. It provides a visual representation of dictionary data, making it easier to inspect and debug dictionary values during workflow execution. ## When would I use it? Use the DisplayDictionary node when: - You need to inspect the contents of a dictionary in your workflow - You want to visualize complex data structures for debugging purposes - You need to monitor how dictionary values change throughout your workflow - You're working with JSON-like data and need to see its structure clearly ## How to use it ### Basic Setup 1. Add the DisplayDictionary node to your workflow 1. Connect any node that outputs a dictionary to the "dictionary" input of this node 1. The dictionary content will be displayed in the node's interface ### Parameters - **dictionary**: The dictionary to be displayed (input/output parameter) - **dictionary_display**: A string representation of the dictionary content (property parameter, displayed in the UI) ### Outputs - **dictionary**: The same dictionary that was provided as input (passed through) ## Example Imagine you're working with a workflow that processes user data: 1. Add a DisplayDictionary node to your workflow 1. Connect the output of a node that produces user data (like a database query or API response) to the "dictionary" input 1. The DisplayDictionary node will show the user data structure in a readable format 1. You can continue your workflow by connecting the "dictionary" output to other nodes that need this data ## Important Notes - The DisplayDictionary node doesn't modify the dictionary data, it simply displays it - The multiline text area in the node's UI makes it easier to read complex nested dictionaries - This node is particularly useful for debugging and understanding data flow in your workflows ## Common Issues - None # KeyValuePair Node ## What is it? The `KeyValuePair` node creates a simple dictionary containing a single key-value pair. It takes a key and a value as inputs and outputs a dictionary with that single mapping. ## When would I use it? Use the KeyValuePair node when: - You need to create a simple dictionary with a single key-value association - You want to dynamically generate configuration parameters - You're building data structures piece by piece - You need to transform two separate values into a dictionary format - You're preparing data for nodes that require dictionary inputs ## How to use it ### Basic Setup 1. Add the KeyValuePair node to your workflow 1. Set the "key" parameter to your desired dictionary key 1. Set the "value" parameter to the value you want associated with that key 1. Connect the "dictionary" output to other nodes that require dictionary input ### Parameters - **key**: The string that will be used as the dictionary key (string) - **value**: The string that will be associated with the key (string, supports multiline text) ### Outputs - **dictionary**: A dictionary containing the single key-value pair ## Example Imagine you're creating a workflow that needs to set configuration options: 1. Add a KeyValuePair node to your workflow 1. Set the "key" parameter to "max_tokens" 1. Set the "value" parameter to "1024" 1. Connect the "dictionary" output to a node that requires configuration parameters The output will be a dictionary: `{"max_tokens": "1024"}` This dictionary can then be used by other nodes in your workflow that need this configuration parameter. ## Important Notes - Both the key and value are treated as strings in this node - The value parameter supports multiline text for longer content - If you need to create a dictionary with multiple key-value pairs, you can use multiple KeyValuePair nodes and combine their outputs with a merge node # MergeKeyValuePair Node ## What is it? The `MergeKeyValuePair` node merges multiple dictionaries into a single dictionary. It takes up to four input dictionaries and combines them into one unified output dictionary. ## When would I use it? Use the MergeKeyValuePair node when: - You need to combine multiple dictionaries into a single dictionary - You're working with separate configuration settings that need to be unified - You want to aggregate data from different sources into one structure - You're building complex parameter sets from individual components - You need to consolidate related key-value pairs from different parts of your workflow ## How to use it ### Basic Setup 1. Add the MergeKeyValuePair node to your workflow 1. Connect up to four dictionary outputs from other nodes to the inputs of this node 1. The node will combine all input dictionaries into a single output dictionary ### Parameters - **key_value_pair_1**: First dictionary to merge (dictionary input) - **key_value_pair_2**: Second dictionary to merge (dictionary input) - **key_value_pair_3**: Third dictionary to merge (dictionary input) - **key_value_pair_4**: Fourth dictionary to merge (dictionary input) ### Outputs - **output**: A single dictionary containing all key-value pairs from the input dictionaries ## Example Imagine you're building a workflow that needs to combine configuration settings from different sources: 1. Add a MergeKeyValuePair node to your workflow 1. Connect a dictionary with database settings to "key_value_pair_1" (e.g., {"host": "localhost", "port": 5432}) 1. Connect a dictionary with authentication settings to "key_value_pair_2" (e.g., {"username": "admin", "password": "secure123"}) 1. Connect a dictionary with application settings to "key_value_pair_3" (e.g., {"app_name": "MyApp", "debug": true}) 1. The output will be a single dictionary containing all these settings: {"host": "localhost", "port": 5432, "username": "admin", "password": "secure123", "app_name": "MyApp", "debug": true} ## Important Notes - If the same key appears in multiple input dictionaries, the value from the later dictionary will overwrite earlier values - The node ignores any inputs that are not dictionaries or are None - You don't need to connect all four inputs - the node works with any number of inputs from one to four - The order of inputs matters when there are key conflicts # EndFlowNode ## What is it? The EndFlowNode is a simple building block that marks the end of your workflow. Think of it as a stop sign that tells the system "the flow ends here." ## When would I use it? Use this node when you want to: - Clearly mark where your workflow ends - Create a visual endpoint for your flow - Ensure your workflow has a proper ending point ## How to use it ### Basic Setup 1. Add the EndFlowNode to your workflow 1. Connect it to the end of your flow ### Parameters - **control**: This is an input connection point for the flow (you connect other nodes to this) ### Outputs - **None** - this node doesn't output anything, it just marks the end of the flow ## Example Imagine you have a workflow that generates and saves text: 1. Create a flow with several nodes (like an agent that generates text and a node that saves it) 1. Add an EndFlowNode at the end of your sequence 1. Connect the last active node in your flow to the EndFlowNode ## Important Notes - The EndFlowNode doesn't actually do anything - it's just a marker for readability - You can have multiple EndFlowNodes in complex workflows with different branches - Using EndFlowNodes makes your workflows easier to understand ## Common Issues - **Flow Continues Past EndFlowNode**: Make sure you're not connecting anything after the EndFlowNode - **Flow Doesn't Reach EndFlowNode**: Check your connections to ensure the flow can reach the end # Reroute ## What is it? The Reroute node is a utility node that acts as a connection point for rerouting data between different parts of your workflow. It dynamically adjusts the allowed types of its parameters based on the connections established, making it easier to organize complex workflows. In short, it's just a way to shape your connections between nodes. ## When would I use it? Use the Reroute node when: - You need to organize complex workflows with many crossing connections - You want to improve the visual clarity of your workflow - You're working with connections that need to span across distant parts of your workflow - You need to bundle multiple connections together - You want to create a cleaner, more maintainable workflow layout ## How to use it ### Basic Setup 1. Add a Reroute node to your workflow 1. Connect the source node output to the Reroute node's input 1. Connect the Reroute node's output to the target node's input 1. The node will automatically adapt to pass through the correct data types ### Parameters - **passThru**: A parameter that can function as both input and output, dynamically adapting its allowed types based on connections ### Outputs - **passThru**: The data passed through the node without modification ## Example Add a Reroute node between two distant nodes in your workflow to improve organization. For instance, connect an image output from a node at the top of your workflow to a Reroute node, then connect that Reroute node's output to an image processing node at the bottom of your workflow. ## Important Notes - The Reroute node does not modify the data passing through it - It automatically adjusts its parameter types based on the connections made - It can help improve workflow organization and readability - Multiple Reroute nodes can be chained together for complex routing needs # StartFlow ## What is it? The StartFlow is a special building block that marks the beginning of your workflow. Think of it as the "Go" sign that tells the system where to start running your flow. ## When would I use it? Use this node when you want to: - Create a clear starting point for your workflow - Begin a sequence of connected nodes - Define the entry point for your flow ## How to use it ### Basic Setup 1. Add the StartFlow to your workflow 1. Connect it to the first action node in your flow ### Parameters ### Outputs - **exec_out** - use this pin to pass control to the next node in the flow ## Example Imagine you're creating a workflow that generates and saves text: 1. Create a StartFlow node 1. Connect the top, white "exec chain" pins to an Agent that will generate text 1. Connect that to a SaveText to save the generated text 1. Connect that to an EndFlowNode to complete the flow The StartFlow tells the system "start here and follow the exec chain in order." ## Important Notes - Every workflow needs exactly one StartFlow - The StartFlow doesn't take any inputs - it's just a starting point - You can only have one StartFlow per contiguous workflow ## Common Issues - **Flow Doesn't Run**: Make sure your StartFlow is properly connected to the next node - **Multiple Start Points**: Ensure you only have one StartFlow in your workflow # AddBoundingBoxes ## What is it? The AddBoundingBoxes node draws bounding boxes on images from coordinate dictionaries. It's designed to visualize object detection results by overlaying colored rectangles with optional labels on your images. Perfect for displaying YOLO, face detection, or any other detection system outputs. ## When would I use it? Use this node when you want to: - Visualize object detection results from YOLO, SSD, or other detection models - Display face detection bounding boxes with confidence scores - Annotate images with coordinate-based regions of interest - Create visual feedback for computer vision workflows - Debug and verify detection model outputs - Combine multiple detection results on a single image ## How to use it ### Basic Setup 1. Add the AddBoundingBoxes node to your workflow 1. Connect an image source to the "input_image" input 1. Connect bounding box data (dict or list of dicts) to the "bounding_boxes" input 1. Optionally customize colors, labels, and thickness 1. The annotated image will be available at the "output" parameter ### Parameters #### Required Inputs - **input_image**: The image to draw bounding boxes on (ImageUrlArtifact or ImageArtifact) - **bounding_boxes**: Single dict or list of dicts containing box coordinates - Each dict must have: `x`, `y`, `width`, `height` (integers or convertible strings) - Can include additional keys for label templates (e.g., `confidence`, `class`, etc.) - Example: `{"x": 100, "y": 50, "width": 200, "height": 150, "confidence": 0.95}` #### Styling Parameters - **box_color** (hex color, default: "#FF0000"): Color of the bounding box outlines - Uses ColorPicker for easy color selection - Supports hex format (e.g., "#FF0000" for red, "#00FF00" for green) - **line_thickness** (1-10, default: 2): Thickness of the bounding box lines in pixels - Lower values for subtle annotations - Higher values for bold, prominent boxes #### Label Parameters - **show_labels** (boolean, default: True): Toggle label visibility - Set to True to show labels above bounding boxes - Set to False to hide labels and show only boxes - **label_key** (string template): Template for bounding box labels - Default: `"{x}, {y}, width: {width}, height: {height}"` - Use `{key}` syntax to insert values from bounding box dicts - Example: `"Class: {class}, Conf: {confidence}"` - Example: `"{x}, {y} - Size: {width}x{height}"` - Keys not in the dict will remain as `{key}` in the output ### Outputs - **output**: The image with bounding boxes and labels drawn ## Example A typical object detection visualization workflow: 1. Load an image using LoadImage 1. Run object detection (e.g., using YOLO or custom detection) 1. Format detection results as dictionaries: ``` [ {"x": 100, "y": 50, "width": 200, "height": 150, "class": "person", "confidence": 0.95}, {"x": 350, "y": 120, "width": 180, "height": 200, "class": "car", "confidence": 0.87} ] ``` 1. Connect the image to AddBoundingBoxes "input_image" 1. Connect the detection results to "bounding_boxes" 1. Set label template: `"Class: {class}, Conf: {confidence}"` 1. Choose box color: "#00FF00" (green) 1. Set line thickness: 3 for visibility 1. Connect the "output" to DisplayImage to view the annotated result ## Important Notes - **Coordinate System**: Bounding boxes use (x, y) as the top-left corner, with width and height extending right and down - **String Conversion**: The node automatically converts string coordinate values to integers if possible - Example: `{"x": "100", "y": "50"}` will work correctly - **Label Positioning**: Labels are positioned with smart spacing: - Default: Above the box with a gap equal to half the label height - If near the top of the image: Inside the box at the top - **Label Size**: Font size automatically scales to 4% of the image height for proportional labeling - **Validation**: The node validates all inputs before processing: - Coordinates must be non-negative (x ≥ 0, y ≥ 0) - Dimensions must be positive (width > 0, height > 0) - Clear error messages guide you to correct any issues - **RGBA Support**: The node preserves transparency in RGBA images ## Common Issues - **Missing Required Keys**: Ensure each bounding box dict has `x`, `y`, `width`, `height` - Error message will specify which keys are missing - **String Coordinates**: If your data has string coordinates, the node will convert them automatically - If conversion fails, you'll get a clear error message - **Negative Coordinates**: Bounding boxes must have non-negative coordinates - Check that x ≥ 0 and y ≥ 0 - **Zero or Negative Dimensions**: Width and height must be greater than zero - Verify width > 0 and height > 0 - **Labels Not Showing**: Check that: - `show_labels` is set to True - `label_key` has a valid template - The keys in your template exist in your bounding box dicts - **Label Text Wrong**: Make sure the `{key}` names in your template match the keys in your bounding box dictionaries ## Technical Details The node performs the following operations: 1. **Input Validation**: - Validates bounding box format (dict or list) - Checks for required keys (x, y, width, height) - Converts string values to integers if needed - Validates coordinate ranges 1. **Color Parsing**: Converts hex color codes to RGB tuples for drawing 1. **Font Loading**: Dynamically loads a font sized at 4% of image height (configurable via `LABEL_HEIGHT_PERCENT` constant) 1. **Box Drawing**: For each bounding box: - Calculates rectangle corners from x, y, width, height - Draws rectangle outline with specified color and thickness 1. **Label Rendering** (if enabled): - Processes template string by replacing `{key}` patterns with values - Calculates text dimensions for proper positioning - Draws black background rectangle for text visibility - Draws white text on the background 1. **Smart Positioning**: Labels are positioned above boxes with proportional spacing, or inside boxes when near the top of the image The node uses PIL (Pillow) ImageDraw for all rendering operations, ensuring high-quality output compatible with standard image formats. # AddTextToExistingImage ## What is it? The AddTextToExistingImage node overlays text onto an existing image. It supports text/background colors (with alpha), alignment controls, and simple template placeholders like `{key}` that are expanded from a separate dictionary input. ## When would I use it? Use this node when you want to: - Add titles, captions, or labels onto an image - Stamp metadata (like filenames, timestamps, or IDs) onto images - Create annotated image outputs for downstream steps - Render dynamic text using `{key}` placeholders sourced from a dictionary ## How to use it ### Basic Setup 1. Add the AddTextToExistingImage node to your workflow 1. Connect an image source to **input_image** 1. Enter the text template in **text** 1. (Optional) Connect a dictionary to **template_values** to expand `{key}` placeholders 1. Adjust alignment, colors, border, and font size 1. Run the node to produce **output** ### Parameters #### Required Inputs - **input_image**: The image to render text onto (ImageUrlArtifact / ImageArtifact / dict) #### Text Inputs - **text** (string): The text template to render - Supports placeholders like `{key_name}` - Placeholder expansion is applied only to what’s rendered on the image; the `text` output parameter remains the original template - **template_values** (dict, optional): Values used to expand placeholders in `text` - If a placeholder key is not present, it will be left as-is in the rendered text - Missing keys are also reported in `result_details` when the node runs #### Styling Parameters - **text_color** (hexa, default: `#ffffffff`): Text color including alpha - **text_background** (hexa, default: `#000000ff`): Background rectangle color behind the text including alpha - **text_vertical_alignment** (top | center | bottom, default: top): Vertical alignment of the text block - **text_horizontal_alignment** (left | center | right, default: left): Horizontal alignment of the text block - **margin** (int, default: 10): Margin inset from image edges for text placement - **font_size** (int, default: 36): Font size used for rendering ### Outputs - **output**: The updated image (ImageUrlArtifact) - **text**: The original text template string (not the expanded render string) - **was_successful**: Indicates whether the node succeeded - **result_details**: Success/failure details, including missing placeholder key messages ## Example A typical “stamp metadata” workflow: 1. Load an image using LoadImage 1. Add AddTextToExistingImage 1. Set **text** to: `"Photo: {name} | #{index}"` 1. Provide **template_values**: ``` {"name": "Portrait", "index": 7} ``` 1. Set `text_background` to a semi-transparent black like `#00000080` 1. Set `text_color` to white `#ffffffff` 1. Run the node and connect **output** to DisplayImage ## Important Notes - **Missing keys**: If `{key}` is not present in `template_values`, the placeholder stays in the rendered text and a message like `key: key not found in dictionary input` is appended to `result_details`. - **Alpha support**: Both text and background colors support transparency via hexa (`#RRGGBBAA`). - **Execution**: The node uploads and outputs the final image on `process()`. # AdjustImageEQ ## What is it? The AdjustImageEQ node allows you to fine-tune your images with precise controls for brightness, contrast, saturation, and gamma correction. It's like having a professional photo editing tool built into your workflow, giving you complete control over the visual appearance of your images. ## When would I use it? Use this node when you want to: - Enhance image brightness and contrast for better visibility - Adjust color saturation to make colors more vibrant or muted - Apply gamma correction to fix exposure issues - Fine-tune images before passing them to other processing nodes - Create consistent lighting across multiple images - Prepare images for specific output requirements ## How to use it ### Basic Setup 1. Add the AdjustImageEQ node to your workflow 1. Connect an image source to the "input_image" input 1. Adjust the sliders to fine-tune your image 1. The processed image will be available at the "output" parameter ### Parameters #### Adjustment Settings - **brightness** (0.0-3.0, default: 1.0): Controls overall image brightness - Values < 1.0 make the image darker - Values > 1.0 make the image brighter - 1.0 = no change - **contrast** (0.0-3.0, default: 1.0): Controls the difference between light and dark areas - Values < 1.0 reduce contrast (more gray) - Values > 1.0 increase contrast (more dramatic) - 1.0 = no change - **saturation** (0.0-3.0, default: 1.0): Controls color intensity - Values < 1.0 reduce saturation (more grayscale) - Values > 1.0 increase saturation (more vibrant colors) - 1.0 = no change - **gamma** (0.1-10.0, default: 1.0): Controls midtone brightness - Values < 1.0 brighten midtones - Values > 1.0 darken midtones - 1.0 = no change ### Outputs - **output**: The adjusted image with your EQ settings applied ## Example A typical image enhancement workflow: 1. Load or generate an image using LoadImage or GenerateImage 1. Connect that image to the AdjustImageEQ node's "input_image" parameter 1. Adjust the sliders to achieve your desired look: - Increase brightness to 1.2 for a brighter image - Increase contrast to 1.1 for more dramatic shadows - Increase saturation to 1.3 for more vibrant colors - Adjust gamma to 0.9 to brighten midtones 1. Connect the "output" to DisplayImage to view the result ## Important Notes - **Live Preview**: Changes are applied in real-time as you move the sliders - **RGBA Support**: The node preserves transparency in RGBA images - **High Quality**: Uses PIL's ImageEnhance for professional-quality adjustments - **Non-destructive**: Original image data is preserved in the workflow ## Common Issues - **No Changes Visible**: Make sure you're moving the sliders away from their default values (1.0) - **Over-processed Look**: Be careful with extreme values - subtle adjustments often work best - **Color Shifts**: Gamma adjustments can affect color balance, so monitor the overall result ## Technical Details The node uses PIL's ImageEnhance module for high-quality image adjustments: - **Brightness**: Multiplies pixel values by the brightness factor - **Contrast**: Applies contrast enhancement using PIL's contrast algorithm - **Saturation**: Uses PIL's color enhancement for saturation control - **Gamma**: Applies gamma correction using lookup tables for precise control # AdjustImageLevels ## What is it? The AdjustImageLevels node provides professional-level image adjustment capabilities similar to Photoshop's levels tool. It allows you to control input levels (shadows, midtones, highlights) and output levels, giving you precise control over the tonal range and contrast of your images. ## When would I use it? Use this node when you want to: - Fine-tune the tonal range of your images - Adjust shadows and highlights independently - Control midtone brightness with gamma correction - Create high-contrast or low-contrast effects - Fix exposure issues in images - Match the tonal characteristics of multiple images - Prepare images for specific output requirements ## How to use it ### Basic Setup 1. Add the AdjustImageLevels node to your workflow 1. Connect an image source to the "input_image" input 1. Adjust the input and output level sliders 1. The processed image will be available at the "output" parameter ### Parameters #### Input Levels - **shadows** (0-255, default: 0): Input shadows level - Pixels below this value will be mapped to output shadows - Higher values clip more shadow detail - **midtones** (0.1-10.0, default: 1.0): Midtones adjustment (gamma) - Values < 1.0 brighten midtones - Values > 1.0 darken midtones - 1.0 = no change - **highlights** (0-255, default: 255): Input highlights level - Pixels above this value will be mapped to output highlights - Lower values clip more highlight detail #### Output Levels - **output_shadows** (0-255, default: 0): Output shadows level - Input shadows will be mapped to this value - Higher values brighten the overall image - **output_highlights** (0-255, default: 255): Output highlights level - Input highlights will be mapped to this value - Lower values darken the overall image ### Outputs - **output**: The adjusted image with your levels settings applied ## Example A typical levels adjustment workflow: 1. Load an image using LoadImage 1. Connect that image to the AdjustImageLevels node's "input_image" parameter 1. Adjust the levels to improve the image: - Set shadows to 20 to preserve more shadow detail - Set midtones to 0.8 to brighten midtones - Set highlights to 240 to preserve more highlight detail - Set output_shadows to 10 to slightly brighten shadows - Set output_highlights to 245 to slightly darken highlights 1. Connect the "output" to DisplayImage to view the result ## Important Notes - **Live Preview**: Changes are applied in real-time as you move the sliders - **RGBA Support**: The node preserves transparency in RGBA images - **Professional Quality**: Uses lookup tables for precise level adjustments - **Validation**: The node ensures shadows < highlights for proper operation ## Common Issues - **No Changes Visible**: Make sure you're adjusting the sliders away from their default values - **Over-processed Look**: Be careful with extreme values - subtle adjustments often work best - **Clipping**: Setting shadows too high or highlights too low can cause detail loss ## Technical Details The node applies a three-step process: 1. **Input Levels**: Clips and maps the input range based on shadows and highlights 1. **Gamma Correction**: Applies midtone adjustment using power-law transformation 1. **Output Levels**: Maps the processed values to the output range This provides the same functionality as professional photo editing software's levels adjustment tool. # ApplyMask ## What is it? The ApplyMask node applies a mask to an input image to create transparency effects. It takes an image and a mask, then uses a specified channel from the mask as the alpha channel for the final image. You can choose which channel to use (red, green, blue, or alpha) depending on where your mask data is stored. This allows you to selectively make parts of an image transparent based on the mask. ## When would I use it? Use this node when you want to: - Apply transparency effects to images using masks - Create selective transparency for image compositing - Use masks created by other nodes (DisplayMask, PaintMask, InvertMask) to modify image transparency - Create cutouts or remove backgrounds from images - Apply complex transparency effects in image processing workflows - Control which channel of a mask image to use for transparency ## How to use it ### Basic Setup 1. Add the ApplyMask node to your workflow 1. Connect an image source to the "input_image" input 1. Connect a mask source to the "input_mask" input 1. Select the appropriate channel in the "channel" parameter 1. The masked image will be available at the "output" parameter ### Parameters - **input_image**: The image to apply the mask to (can be connected from other image nodes) - **input_mask**: The mask to apply (can be connected from mask nodes like DisplayMask, PaintMask, or InvertMask) - **channel**: Which channel to use from the mask image: - **red**: Use the red channel (default, compatible with PaintMask output) - **green**: Use the green channel - **blue**: Use the blue channel - **alpha**: Use the alpha channel (when available) ### Outputs - **output**: The final image with the mask applied (transparency based on the mask) ## Example A common workflow pattern: 1. Generate or load an image using nodes like GenerateImage or LoadImage 1. Create a mask using nodes like DisplayMask or PaintMask 1. Connect the image to the ApplyMask node's "input_image" parameter 1. Connect the mask to the ApplyMask node's "input_mask" parameter 1. Select the appropriate channel (usually "red" for PaintMask output, "alpha" for RGBA masks) 1. The ApplyMask node will apply the mask to create transparency effects 1. Connect the "output" to DisplayImage to view the final result ## Important Notes - **Channel Selection**: Choose the channel that contains your mask data: - **PaintMask output**: Usually use "red" channel - **RGBA masks with alpha data**: Use "alpha" channel - **Custom masks**: Try different channels to see which works best - **Mask Processing**: The mask is automatically resized to match the input image dimensions - **RGBA Output**: The final image is converted to RGBA format to support transparency - **Real-time Processing**: The node processes immediately when both inputs are available - **Output Format**: The masked image is saved as a PNG file to preserve transparency ## Common Issues - **No Output**: Check that both "input_image" and "input_mask" are properly connected - **Unexpected Transparency**: Remember that white areas in the mask become opaque, black areas become transparent - **Wrong Channel**: If the mask isn't applying correctly, try a different channel (red, green, blue, or alpha) - **Mask Size Mismatch**: The mask is automatically resized to match the input image, but this may affect quality ## Technical Details The node processes the mask and image through several steps: - **Image Loading**: Loads both the input image and mask from their URLs - **Channel Extraction**: Extracts the specified channel from the mask image: - RGB images: Can extract red, green, or blue channels - RGBA images: Can extract red, green, blue, or alpha channels - L (grayscale) images: Uses the grayscale channel directly - LA images: Can extract alpha or grayscale channels - **Resizing**: Resizes the extracted channel to match the input image dimensions - **Alpha Application**: Applies the channel as the alpha channel to the input image using `putalpha()` - **Output Generation**: Saves the result as a new RGBA image artifact The node handles different mask formats: - **RGB**: Can extract red, green, or blue channels (alpha falls back to red) - **RGBA**: Can extract red, green, blue, or alpha channels - **L**: Uses grayscale channel directly (channel parameter ignored) - **LA**: Can extract alpha or grayscale channels - **Other formats**: Raises an error for unsupported modes # BloomEffect ## What is it? The BloomEffect node applies a beautiful bloom or glow effect to your images, creating dreamy, ethereal results. It simulates the way bright light sources create a soft, luminous halo around them, adding a magical quality to your images. ## When would I use it? Use this node when you want to: - Create dreamy, ethereal image effects - Add a soft glow around bright areas - Enhance the magical quality of fantasy or sci-fi images - Create romantic or atmospheric lighting effects - Add visual interest to portraits or landscapes - Simulate lens flare or light bloom effects - Enhance the mood and atmosphere of your images ## How to use it ### Basic Setup 1. Add the BloomEffect node to your workflow 1. Connect an image source to the "input_image" input 1. Adjust the bloom amount and radius sliders 1. The processed image will be available at the "output" parameter ### Parameters #### Bloom Settings - **bloom_amount** (0.0-2.0, default: 0.5): Controls the intensity of the bloom effect - 0.0 = no bloom effect - Higher values create more intense glow - 2.0 = maximum bloom intensity - **bloom_radius** (1-20, default: 5): Controls the size of the bloom effect - Lower values create tighter, more focused glow - Higher values create larger, more diffuse glow - Larger radius values take longer to process ### Outputs - **output**: The image with the bloom effect applied ## Example A typical bloom effect workflow: 1. Load or generate an image using LoadImage or GenerateImage 1. Connect that image to the BloomEffect node's "input_image" parameter 1. Adjust the bloom settings: - Set bloom_amount to 0.8 for a moderate glow effect - Set bloom_radius to 8 for a medium-sized bloom 1. Connect the "output" to DisplayImage to view the result ## Important Notes - **Live Preview**: Changes are applied in real-time as you move the sliders - **Processing Time**: Larger radius values take longer to process - **Best Results**: Works best on images with bright areas or light sources - **RGBA Support**: The node preserves transparency in RGBA images ## Common Issues - **No Visible Effect**: Make sure bloom_amount is greater than 0 - **Too Subtle**: Increase bloom_amount for more visible effect - **Too Strong**: Decrease bloom_amount for more subtle effect - **Slow Processing**: Reduce bloom_radius for faster processing ## Technical Details The bloom effect is created by: 1. **Brightness Detection**: Identifying bright areas in the image 1. **Gaussian Blur**: Applying blur to create the glow effect 1. **Blending**: Combining the original image with the blurred version 1. **Intensity Control**: Scaling the effect based on bloom_amount This creates a realistic bloom effect similar to what you'd see in professional photography or film. # ColorMatch ## What is it? The ColorMatch node transfers color characteristics from a reference image to a target image using various color matching algorithms. This is useful for automatic color grading, matching the look and feel between images, correcting lighting inconsistencies, and creating consistent visual styles across multiple images. ## When would I use it? Use this node when you want to: - Match colors between two images for consistency - Apply color grading from one image to another - Correct lighting inconsistencies between shots - Create a unified color palette across multiple images - Transfer the mood or atmosphere of a reference image - Color-match stop-motion or video frames - Apply a color style from a reference photo ## How to use it ### Basic Setup 1. Add the ColorMatch node to your workflow 1. Connect the reference image to "reference_image" (the image with the color palette you want to copy) 1. Connect the target image to "target_image" (the image you want to modify) 1. Select a color transfer method 1. Adjust the strength parameter as needed 1. The color-matched image will be available at the "output" parameter ### Parameters #### Image Inputs - **reference_image**: The reference image providing the color palette to transfer - **target_image**: The target image to apply color transfer to (will be modified) #### Color Match Settings - **method**: The color transfer algorithm to use - **mkl**: Monge-Kantorovich Linearization (fast, good quality, default) - **hm**: Histogram Matching (fast, moderate quality) - **reinhard**: Reinhard et al. color transfer (fast, good quality) - **mvgd**: Multi-Variate Gaussian Distribution (medium speed, good quality) - **hm-mvgd-hm**: Compound method (slower, best quality) - **hm-mkl-hm**: Alternative compound method (slower, best quality) - **strength** (0.0-10.0, default: 1.0): Controls the intensity of the color transfer - 0.0 = no change (original image) - 1.0 = full color transfer - Values > 1.0 exaggerate the effect ### Outputs - **output**: The color-matched image with transferred color characteristics ## Example A typical color matching workflow: 1. Load two images using LoadImage nodes 1. Connect the image you want to modify to "input_image" 1. Connect the reference image (with the desired colors) to "reference_image" 1. Select the "mkl" method for a good balance of speed and quality 1. Set strength to 1.0 for full color transfer 1. Connect the "output" to DisplayImage to view the result 1. Adjust strength if the effect is too strong or too subtle ### Use Cases #### Photo Color Grading Match the color palette of a professionally graded photo to your own images: 1. Load your photo as the target image 1. Load the reference photo with the desired color grading 1. Use "reinhard" or "mkl" method 1. Adjust strength to taste (0.7-1.0 for subtle, 1.0-1.5 for dramatic) #### Stop-Motion Correction Correct lighting inconsistencies between stop-motion frames: 1. Load a frame with correct lighting as the reference 1. Load frames that need correction as target images 1. Use "mkl" method for fast processing 1. Set strength to 1.0 for accurate matching #### Batch Style Transfer Apply a consistent color style across multiple images: 1. Load your style reference image 1. Process each target image through the ColorMatch node 1. Use "hm-mvgd-hm" for highest quality results ## Important Notes - **Live Preview**: Changes are applied in real-time as you adjust parameters - **RGB Processing**: Images are converted to RGB for processing - **Preserves Dimensions**: Output image maintains the same dimensions as the target image - **Algorithm Differences**: Different methods produce subtly different results - experiment to find the best one for your use case ## Common Issues - **No Change Visible**: Make sure strength is greater than 0 - **Effect Too Strong**: Reduce the strength value below 1.0 - **Unexpected Colors**: Try a different method - "reinhard" often produces more natural results - **Slow Processing**: Use "mkl" or "hm" for faster results; avoid compound methods for large images ## Technical Details The node uses the color-matcher library to perform color transfer. The available algorithms are: - **MKL (Monge-Kantorovich Linearization)**: Uses optimal transport theory to match color distributions - **HM (Histogram Matching)**: Matches the histogram of each color channel independently - **Reinhard**: Classic color transfer method based on decorrelated color spaces - **MVGD (Multi-Variate Gaussian Distribution)**: Matches the mean and covariance of color distributions - **Compound Methods**: Combine multiple algorithms for improved results The strength parameter uses linear interpolation: `result = target + strength * (matched - target)` # CombineMasks ## What is it? The CombineMasks node merges a list of mask images into a single consolidated mask by taking the **maximum value per pixel** (a union operation). This is useful when a segmentation node produces multiple masks and you want one mask you can apply downstream. ## When would I use it? Use this node when you want to: - Combine multiple segmentation masks into one mask - Create a single “subject mask” from many detected objects - Simplify workflows that expect one mask input ## How to use it ### Basic Setup 1. Add the CombineMasks node to your workflow 1. Provide one or more masks to the `masks` input (as a list) 1. Use the `output_mask` output as the consolidated mask ### Parameters - **masks**: A list of mask images to combine ### Outputs - **output_mask**: The consolidated mask (PNG) ## Important Notes - **Union logic**: For each pixel, the output mask uses the maximum value across all input masks. - **Size requirement**: All input masks must be the **same dimensions**. If sizes differ, the node will fail validation with a clear error. - **Alpha handling**: If an input mask is RGBA/LA, the node uses the **alpha channel** as the mask; otherwise it converts the image to grayscale. # CompareImages ## What is it? The CompareImages node allows you to compare two images side by side, making it easy to see differences, evaluate changes, or compare different versions of the same image. It's perfect for A/B testing, quality control, or analyzing the effects of different image processing operations. ## When would I use it? Use this node when you want to: - Compare before and after versions of processed images - A/B test different image generation prompts - Evaluate the quality of different image processing settings - Compare different versions of the same image - Analyze the effects of image adjustments - Quality control for image processing workflows - Present multiple image options for review ## How to use it ### Basic Setup 1. Add the CompareImages node to your workflow 1. Connect two image sources to the "image1" and "image2" inputs 1. The comparison will be displayed showing both images side by side ### Parameters - **image1**: The first image to compare (left side) - **image2**: The second image to compare (right side) ### Outputs - **comparison**: A side-by-side comparison of both images ## Example A typical comparison workflow: 1. Load an original image using LoadImage 1. Process the image with AdjustImageEQ or another processing node 1. Connect the original image to CompareImages "image1" input 1. Connect the processed image to CompareImages "image2" input 1. The node will display both images side by side for easy comparison ## Important Notes - **Side-by-Side Display**: Images are displayed next to each other for easy comparison - **Automatic Scaling**: Images are automatically scaled to fit the comparison view - **Real-time Updates**: The comparison updates when either input image changes - **High Quality**: Maintains image quality for accurate comparison ## Common Issues - **No Images Showing**: Make sure both image1 and image2 inputs are connected - **Images Too Small**: The node automatically scales images to fit the display - **Layout Issues**: The side-by-side layout is automatic and optimized for comparison ## Technical Details The node creates a composite image that displays both input images side by side, making it easy to: - Compare image quality - See processing effects - Evaluate different versions - Make informed decisions about image processing This is essential for any workflow that involves image processing or generation where you need to evaluate results. # CreateColorBars ## What is it? The CreateColorBars node generates standard color bar test patterns used for video calibration, display testing, and color accuracy verification. It supports over 40 different test patterns including SMPTE, EBU, ARIB standards, as well as various calibration patterns like PLUGE, zone plates, and chroma ramps. ## When would I use it? Use this node when you want to: - Calibrate video displays and monitors - Test color accuracy and brightness levels - Generate test patterns for video production workflows - Verify display settings and color space handling - Create reference images for color grading - Test video encoding and decoding pipelines - Generate patterns for display quality assessment ## How to use it ### Basic Setup 1. Add the CreateColorBars node to your workflow 1. Select the type of color bars you want to generate from the "bar_type" dropdown 1. Set the width and height for your test pattern (default: 1920x1080) 1. The generated color bars image will be available at the "image" output ### Parameters #### Main Settings - **bar_type**: The type of color bars to generate (string, default: "SMPTE 219-100 Bars") Available options include: - **SMPTE Patterns**: SMPTE 219-100 Bars, SMPTE 75% Bars, SMPTE Bars, SMPTE 219+i Bars - **Full Field Patterns**: 100% Full Field Bars, 75% Full Field Bars, 75% Bars Over Red - **International Standards**: EBU Bars (European), ARIB 28-100, ARIB 28-75, ARIB 28+i (Japanese) - **HD Patterns**: HD Color Bars - **Solid Colors**: Full Field White, Blue, Cyan, Green, Magenta, Red, Yellow - **Test Patterns**: Zone Plate, Tartan Bars, Multi Burst, Bowtie - **Grayscale Ramps**: Stair 5 Step, Stair 5 Step Vert, Stair 10 Step, Stair 10 Step Vert - **Gradient Patterns**: Y Ramp Up, Y Ramp Down, Vertical Ramp - **Chroma Patterns**: Legal Chroma Ramp, Full Chroma Ramp, Chroma Ramp - **Calibration Patterns**: Pluge (with advanced options), Pathological EG, Pathological PLL - **Special Patterns**: AV Delay Pattern 1, AV Delay Pattern 2, Bouncing Box - **width**: Width of the color bars image in pixels (integer, default: 1920) - **height**: Height of the color bars image in pixels (integer, default: 1080) #### PLUGE-Specific Parameters These parameters are only visible when "Pluge" is selected as the bar_type: - **pluge_ire_setup**: IRE setup type for PLUGE pattern calibration (string, default: "NTSC 7.5 IRE") - **NTSC 7.5 IRE**: Standard NTSC setup level (default) - **PAL 0 IRE**: PAL standard with 0 IRE black level - **RGB Full Range**: RGB full range (0-255) calibration - **pluge_bar_count**: Number of PLUGE bars to display (integer, default: 3, range: 2-5) - 2 bars: Black level and just above black - 3 bars: Super black, black level, and just above black (standard) - 4-5 bars: Includes intermediate values between black levels - **pluge_orientation**: Orientation of PLUGE bars (string, default: "vertical") - **vertical**: Bars arranged vertically (default) - **horizontal**: Bars arranged horizontally ### Outputs - **image**: The generated color bars image as an ImageUrlArtifact ## Example ### Basic Color Bars Generation 1. Add a CreateColorBars node to your workflow 1. Select "SMPTE 75% Bars" from the bar_type dropdown 1. Set width to 1920 and height to 1080 1. Connect the "image" output to a DisplayImage node to view the result 1. Optionally connect to SaveImage to save the test pattern ### PLUGE Calibration Pattern 1. Add a CreateColorBars node 1. Select "Pluge" from the bar_type dropdown 1. The PLUGE-specific parameters will automatically appear 1. Configure the calibration: - Set pluge_ire_setup to match your video standard (e.g., "NTSC 7.5 IRE" for NTSC, "PAL 0 IRE" for PAL) - Set pluge_bar_count to 3 for standard calibration - Choose vertical or horizontal orientation 1. Connect the output to DisplayImage to view the calibration pattern ### Custom Resolution Test Pattern 1. Add a CreateColorBars node 1. Select your desired pattern type (e.g., "HD Color Bars") 1. Set custom dimensions: - width: 3840 (4K width) - height: 2160 (4K height) 1. The pattern will be generated at your specified resolution ## Important Notes - **Live Preview**: The image is automatically regenerated when you change bar_type, width, height, or PLUGE parameters - **SMPTE Standards**: SMPTE 75% Bars and SMPTE Bars use 75% intensity levels per SMPTE ECR 1-1978 standard for SDTV - **IRE Values**: PLUGE patterns use IRE (Institute of Radio Engineers) units for precise black level calibration - **Real-time Updates**: Changes to parameters trigger immediate regeneration of the color bars - **PNG Format**: All generated images are saved as PNG files for lossless quality ## Common Use Cases ### Video Production - Generate SMPTE color bars for broadcast standards compliance - Create reference patterns for color grading workflows - Test video encoding pipelines with standard test patterns ### Display Calibration - Use PLUGE patterns to calibrate black levels and brightness - Generate zone plates to test display sharpness and focus - Create grayscale ramps to verify gamma curves ### Quality Testing - Use pathological patterns to test video processing equipment - Generate chroma ramps to verify color space handling - Create multi-burst patterns to test frequency response ## Technical Details - **Image Format**: PNG (lossless) - **Color Space**: RGB - **Default Resolution**: 1920x1080 (Full HD) - **Standards Supported**: SMPTE, EBU, ARIB - **IRE Conversion**: IRE values are converted to RGB using the formula: RGB = (IRE / 100) * 255 The node generates industry-standard test patterns suitable for professional video production, broadcast, and display calibration workflows. # GenerateImage ## What is it? The GenerateImage node generates images based on text prompts using Griptape Cloud services. It integrates with Griptape Cloud Image Generation Driver and Prompt Driver to transform textual descriptions into high-quality images. ## When would I use it? Use the GenerateImage node when: - You need to generate images dynamically in your workflow - You want to create visual content based on text descriptions - You need to visualize concepts described in natural language ## How to use it ### Basic Setup 1. Add a GenerateImage node to your workflow 1. Provide a text prompt describing the image you want to generate 1. Run your workflow! ### Parameters - **agent**: The agent responsible for handling prompt and image generation tasks (Agent or dict) - **image_generation_driver**: The driver used for image generation (defaults to None) - **prompt**: Text description of the image to generate (string) - **enhance_prompt**: Whether to enhance the prompt for better image quality (boolean, defaults to True) ### Outputs - **output**: The generated image as an ImageArtifact ## Example A simple workflow to generate and save an image: 1. Add a GenerateImage node to your workflow 1. Connect an Agent node into the agent parameter 1. Set the prompt to "A serene mountain landscape at sunset with a lake reflecting the orange sky" 1. Connect the output to a SaveImage node to store the generated image ## Important Notes - You must set the `GT_CLOUD_API_KEY` environment variable for authentication with Griptape Cloud - The node uses 'dall-e-3' as the default model and 'hd' as the default quality - Enhanced prompts can improve image quality but may interpret your prompt differently than expected # CropImage ## What is it? The CropImage node provides precise control over image cropping, zooming, and rotation. It allows you to select specific areas of an image, apply zoom effects, and rotate images to achieve the perfect composition for your workflow. ## When would I use it? Use this node when you want to: - Crop images to specific dimensions or aspect ratios - Focus on particular areas of an image - Apply zoom effects to magnify details - Rotate images to correct orientation - Create consistent image sizes across your workflow - Remove unwanted areas from images - Prepare images for specific output requirements ## How to use it ### Basic Setup 1. Add the CropImage node to your workflow 1. Connect an image source to the "input_image" input 1. Set the crop coordinates and dimensions 1. Optionally adjust zoom and rotation 1. The processed image will be available at the "output" parameter ### Parameters #### Crop Settings - **left** (0+, default: 0): Left edge of the crop area in pixels - **top** (0+, default: 0): Top edge of the crop area in pixels - **width** (1+, default: 100): Width of the crop area in pixels - **height** (1+, default: 100): Height of the crop area in pixels #### Transform Settings - **zoom** (1.0+, default: 1.0): Zoom factor for the crop area - 1.0 = no zoom - Values > 1.0 zoom in (magnify) - Values < 1.0 zoom out (shrink) - **rotate** (degrees, default: 0): Rotation angle in degrees - Positive values rotate clockwise - Negative values rotate counter-clockwise ### Outputs - **output**: The cropped and transformed image ## Example A typical cropping workflow: 1. Load an image using LoadImage 1. Connect that image to the CropImage node's "input_image" parameter 1. Set the crop area: - Set left to 100 to start cropping 100 pixels from the left - Set top to 50 to start cropping 50 pixels from the top - Set width to 400 to crop 400 pixels wide - Set height to 300 to crop 300 pixels tall 1. Optionally add effects: - Set zoom to 1.2 to magnify the cropped area by 20% - Set rotate to 15 to rotate the image 15 degrees clockwise 1. Connect the "output" to DisplayImage to view the result ## Important Notes - **Live Preview**: Changes are applied in real-time as you adjust parameters - **Boundary Checking**: The node ensures crop areas stay within image bounds - **High Quality**: Uses high-quality resampling for zoom and rotation - **RGBA Support**: The node preserves transparency in RGBA images ## Common Issues - **Empty Crop Area**: Make sure width and height are greater than 0 - **Crop Outside Image**: Ensure left + width and top + height don't exceed image dimensions - **Unexpected Results**: Check that your crop coordinates are within the image bounds ## Technical Details The node performs the following operations: 1. **Crop**: Extracts the specified rectangular area from the image 1. **Zoom**: Scales the cropped area using high-quality resampling 1. **Rotate**: Rotates the image around its center point 1. **Boundary Management**: Ensures all operations stay within valid image bounds This provides professional-quality image cropping and transformation capabilities. # DescribeImage ## What is it? The DescribeImage node uses AI to generate textual descriptions of an image you provide it. It leverages vision-capable models to analyze image content and produce detailed descriptions based on your specific prompting instructions. ## When would I use it? Use this node when you want to: - Generate textual descriptions of images - Extract information from visual content - Create alt text for accessibility purposes - Analyze the content of photographs, diagrams, or other visual media - Convert visual information into text format for further processing ## How to use it ### Basic Setup 1. Add the DescribeImage node to your workflow 1. Connect an image source to the "image" input 1. Optionally provide specific instructions in the "prompt" field 1. Run the node to generate a description of the image Be Specific About What You Want When creating your prompt, clearly state the elements you are interested in. For example: - For color information, try prompts like "Describe the color palette, the color of light and shadow, the saturation and value" - For character details, try "Describe the character's gender, age, clothing, posture, demeanor, and actions" The more precise you are with your prompts, the more likely it is you'll get the kind of output you desire. ### Parameters - **agent**: An optional existing agent configuration to use for image description - **model**: Select a vision-capable prompt model (or connect a Prompt Model Config). Ignored when **agent** is connected. - **image**: The image you would like to describe (required) - **prompt**: Instructions for how you want the image described (defaults to "Describe the image") - **description_only**: If enabled, returns only the description (no conversation) - **output_schema**: Optional JSON Schema for structured output. Accepts either a JSON schema object or a JSON string. When connected, **output** switches to JSON. ### Outputs - **output**: The image description. Returns **text** by default, or **JSON** when **output_schema** is provided. - **agent**: The agent object used for the description, which can be connected to other nodes ## Example Imagine you want to get a detailed description of a landscape photograph: 1. Add a DescribeImage node to your workflow 1. Connect an image output from another node to the "image" input 1. In the "prompt" field, type: "Describe this landscape in detail, including colors, features, and mood" 1. Run the node 1. The "output" will contain a detailed description of the landscape image ### Structured Output Example (JSON) If you want the output in a specific JSON shape: 1. Add a `CreateAgentSchema` node and provide an example JSON object (the shape you want back) 1. Connect `CreateAgentSchema.schema` to `DescribeImage.output_schema` 1. Run `DescribeImage` 1. The `output` will be JSON matching your schema (or the run will fail validation) ## Important Notes - The node requires a valid Griptape API key set up in your environment as `GT_CLOUD_API_KEY` - By default, the node uses the `gpt-5.2` model through the Griptape Cloud API - If no prompt is provided, the default "Describe the image" will be used - If no image is provided, the output will be "No image provided" - You can provide your own agent configuration for more customized behavior - The quality of descriptions depends on the clarity and content of the provided image ## Common Issues - **Missing API Key**: Ensure your Griptape API key is properly set up as the environment variable - **No Image Provided**: Make sure you've connected a valid image to the "image" input - **Poor Description Quality**: Try refining your prompt to be more specific about what aspects of the image you want described - **Processing Errors**: Very complex images or unusual content might result in less accurate descriptions # DisplayChannel ## What is it? The DisplayChannel node creates a mask from an input image by extracting a specific color channel (red, green, blue, or alpha). This node is similar to DisplayMask but defaults to extracting the red channel instead of the alpha channel. A mask is a grayscale image where white areas represent high values and black areas represent low values for the selected channel. ## When would I use it? Use this node when you want to: - Extract specific color channels (red, green, blue) as an image - Analyze the intensity of individual color channels in an image - Create masks for image editing workflows based on color information - Visualize how different color channels contribute to an image - Generate color-based masks for use in other image processing nodes - Debug color channel issues in your image pipeline - Focus on red channel analysis (since it's the default) ## How to use it ### Basic Setup 1. Add the DisplayChannel node to your workflow 1. Connect an image source to the "input_image" input 1. Select the desired channel from the "channel" dropdown (defaults to "red") 1. The generated channel image will be available at the "output" parameter ### Parameters - **input_image**: The image to create a mask from (can be connected from other image nodes) - **channel**: The channel to extract as a mask (red, green, blue, or alpha) - defaults to "red" ### Outputs - **output**: A grayscale mask image created from the selected channel of the input image ## Example A common workflow pattern: 1. Generate or load an image using nodes like GenerateImage or LoadImage 1. Connect that image to the DisplayChannel node's "input_image" parameter 1. The node will default to extracting the red channel, or you can select a different channel 1. The DisplayChannel node will extract the selected channel and create a visible mask 1. Connect the "output" to DisplayImage to view the mask, or to other mask processing nodes ## Channel Options - **red**: Extracts the red color channel (default) - useful for isolating red elements in an image - **green**: Extracts the green color channel - useful for isolating green elements in an image - **blue**: Extracts the blue color channel - useful for isolating blue elements in an image - **alpha**: Extracts the transparency channel - useful for seeing which parts of an image are transparent ## Important Notes - **Default Channel**: Unlike DisplayMask, this node defaults to the "red" channel instead of "alpha" - **Channel Extraction**: The node extracts the specified channel from the input image to create the mask - **Grayscale Output**: The resulting mask is a grayscale image where white represents high values and black represents low values for the selected channel - **Real-time Processing**: The node processes the image immediately when a connection is made or when the input value or channel selection changes - **Output Format**: The mask is saved as a PNG file to preserve quality - **Image Mode Support**: Supports RGB, RGBA, grayscale (L), and grayscale+alpha (LA) image modes ## Common Issues - **No Output**: Check that your image source is properly connected to the "input_image" parameter - **Solid Black Mask**: This may indicate that the selected channel has no variation or low values - **Solid White Mask**: This may indicate that the selected channel has high values throughout - **Unsupported Mode Error**: The image format may not support the selected channel (e.g., alpha channel in RGB images) ## Technical Details The node uses channel extraction logic to: - Load the input image from its URL - Extract the specified channel (red, green, blue, or alpha) - Convert it to a grayscale mask - Save the result as a new image artifact For RGB images, alpha channel selection will fallback to the red channel since RGB doesn't have an alpha channel. ## Difference from DisplayMask The main difference between DisplayChannel and DisplayMask is the default channel selection: - **DisplayChannel**: Defaults to "red" channel - **DisplayMask**: Defaults to "alpha" channel This makes DisplayChannel more suitable for color analysis workflows, while DisplayMask is better for transparency-based workflows. # DisplayImage ## What is it? The DisplayImage node displays an image and provides its dimensions. It's a simple viewer that shows an image in your workflow while also extracting useful information like width and height. ## When would I use it? Use this node when you want to: - Display an image in your workflow for visual feedback - Get the dimensions (width and height) of an image - Pass an image through your workflow while viewing it - Debug your workflow by seeing what image is being processed ## How to use it ### Basic Setup 1. Add the DisplayImage node to your workflow 1. Connect an image source to the "image" input 1. The image will be displayed and its dimensions will be available as outputs (note: They are hidden by default. You can display them in the Properties panel on the right.) ### Parameters - **image**: The image to display ### Outputs - **image**: The same image that was input, passed through for use by other nodes - **width**: The width of the image in pixels (integer) - **height**: The height of the image in pixels (integer) ## Example A common workflow pattern: 1. Connect an image source (like GenerateImage or LoadImage) to the DisplayImage node 1. View the image in the DisplayImage node to confirm it's what you expected 1. Use the width and height outputs if you need the image dimensions for other nodes 1. Connect the image output to other nodes that need the image ## Important Notes - The DisplayImage node passes the image through unchanged - it's purely for display and dimension extraction - For ImageUrlArtifact inputs, the node will fetch the image from the URL to determine dimensions - For ImageArtifact inputs, dimensions are read directly from the artifact's properties - The width and height outputs are hidden from the UI by default, but can be enabled in the Properties Panel. ## Common Issues - **Image not displaying**: Check that your image source is properly connected and producing valid image data - **Dimensions showing as 0**: This may indicate an issue with the image data or connection - **Slow performance with URLs**: Large images from URLs may take time to fetch and process for dimension calculation # DisplayImageGrid ## What is it? The DisplayImageGrid node allows you to display multiple images in a grid or masonry layout with customizable styling options, preset output sizes, and flexible alignment controls. It's perfect for creating image galleries, showcasing multiple results, or presenting image collections in an organized, visually appealing format with professional output quality. ## When would I use it? Use this node when you want to: - Create image galleries or portfolios - Display multiple image generation results - Showcase different versions of processed images - Create image collections or albums - Present multiple image options for review - Build image-based presentations - Organize images in a grid layout - Export grids at standard video resolutions (4K, 1440p, 1080p, 720p) ## How to use it ### Basic Setup 1. Add the DisplayImageGrid node to your workflow 1. Connect multiple image sources to the "images" input 1. Choose your layout style and customize the appearance 1. Select output size mode (custom or preset) 1. The grid will be displayed with all your images ### Parameters #### Image Input - **images**: List of images to display in the grid #### Layout Settings - **layout_style**: The arrangement style for the images - **grid**: Regular grid layout with equal-sized cells - **masonry**: Pinterest-style layout with varying heights - **grid_justification** (grid layout only): Horizontal alignment of images in incomplete rows - **left**: Align images to the left (default) - **center**: Center images horizontally - **right**: Align images to the right - **columns** (1-10, default: 4): Number of columns in the grid - **spacing** (0-100, default: 10): Space between images in pixels #### Styling Options - **crop_to_fit** (boolean, default: true): Crop images to fit perfectly within the grid cells for clean borders - **transparent_bg** (boolean, default: false): Use transparent background instead of solid color - **background_color** (hex color, default: #000000): Background color for the grid (visible by default) - **border_radius** (0-500, default: 8): Corner radius for rounded corners (0 for square) #### Output Settings - **output_image_size**: Choose between custom width or preset sizes - **custom**: Use a custom width value - **preset**: Use standard video resolutions - **output_image_width** (default: 1200): Maximum width in pixels (shown when custom is selected) - **output_preset**: Standard video resolutions (shown when preset is selected) - **4K (3840x2160)**: Ultra HD resolution - **1440p (2560x1440)**: Quad HD resolution - **1080p (1920x1080)**: Full HD resolution - **720p (1280x720)**: HD resolution - **output_format**: Output file format - **png**: Best quality, supports transparency - **jpeg**: Smaller file size, no transparency - **webp**: Modern format with good compression ### Outputs - **output**: The formatted image grid as an ImageUrlArtifact ## Example A typical image grid workflow: 1. Generate multiple images using GenerateImage nodes 1. Connect all images to the DisplayImageGrid node's "images" parameter 1. Set the layout settings: - Select "grid" layout_style for uniform tiles - Set grid_justification to "center" to center incomplete rows - Set columns to 4 for a 4-column grid - Set spacing to 10 for comfortable spacing between images 1. Customize the appearance: - Set background_color to "#000000" (black) for a dark background - Set border_radius to 8 for rounded corners - Enable crop_to_fit for clean borders 1. Choose output size: - Select "preset" for output_image_size - Choose "1080p (1920x1080)" for Full HD output 1. The grid will display all your images in an organized, attractive layout at exactly 1920×1080 pixels ## Important Notes - **Flexible Layout**: Choose between grid and masonry layouts - **Preset Sizes**: Export at standard video resolutions (4K, 1440p, 1080p, 720p) - **Smart Scaling**: When using presets, the grid scales proportionally to fit without distortion - **Justification Control**: Align incomplete rows left, center, or right (grid layout only) - **Background Control**: Choose between solid colors or transparent backgrounds - **Customizable Styling**: Full control over colors, spacing, and appearance - **Multiple Images**: Can handle any number of input images - **No Cropping**: When using presets, images are scaled to fit (not cropped), with padding as needed ## Common Issues - **No Images Showing**: Make sure you've connected images to the "images" parameter - **Layout Issues**: Adjust columns and spacing for better appearance - **Images Too Small**: Increase the overall grid size or reduce the number of columns - **Preset Output Different Than Expected**: Preset mode scales the grid to fit within the exact dimensions while maintaining aspect ratio ## Technical Details The node creates a responsive grid layout that: - Automatically arranges images in the specified layout style - Maintains aspect ratios while fitting images to the grid - Provides consistent spacing and styling - Supports both regular grid and masonry layouts - When using presets, creates a canvas at exact dimensions and scales the grid proportionally - Applies justification for horizontal positioning in grid layout - Centers the grid vertically with background padding when needed This is perfect for creating professional-looking image galleries and collections at standard output resolutions. # DisplayMask ## What is it? The DisplayMask node creates a mask from an input image by extracting a specific channel (red, green, blue, or alpha). A mask is a grayscale image where white areas represent fully opaque regions and black areas represent fully transparent regions. This node is useful for visualizing and working with different color channels in images. ## When would I use it? Use this node when you want to: - Extract transparency information from an image as a visible mask - Extract specific color channels (red, green, blue) as masks - Create masks for image editing workflows - Visualize different channels of an image - Generate masks for use in other image processing nodes - Debug transparency or color channel issues in your image pipeline ## How to use it ### Basic Setup 1. Add the DisplayMask node to your workflow 1. Connect an image source to the "input_image" input 1. Select the desired channel from the "channel" dropdown 1. The generated mask will be available at the "output_mask" parameter ### Parameters - **input_image**: The image to create a mask from (can be connected from other image nodes) - **channel**: The channel to extract as a mask (red, green, blue, or alpha) ### Outputs - **output_mask**: A grayscale mask image created from the selected channel of the input image ## Example A common workflow pattern: 1. Generate or load an image using nodes like GenerateImage or LoadImage 1. Connect that image to the DisplayMask node's "input_image" parameter 1. Select the desired channel (e.g., "alpha" for transparency, "red" for red channel) 1. The DisplayMask node will extract the selected channel and create a visible mask 1. Connect the "output_mask" to DisplayImage to view the mask, or to other mask processing nodes ## Channel Options - **alpha**: Extracts the transparency channel (default) - useful for seeing which parts of an image are transparent - **red**: Extracts the red color channel - useful for isolating red elements in an image - **green**: Extracts the green color channel - useful for isolating green elements in an image - **blue**: Extracts the blue color channel - useful for isolating blue elements in an image ## Important Notes - **Channel Extraction**: The node extracts the specified channel from the input image to create the mask - **Grayscale Output**: The resulting mask is a grayscale image where white represents high values and black represents low values for the selected channel - **Real-time Processing**: The node processes the image immediately when a connection is made or when the input value or channel selection changes - **Output Format**: The mask is saved as a PNG file to preserve quality - **Image Mode Support**: Supports RGB, RGBA, grayscale (L), and grayscale+alpha (LA) image modes ## Common Issues - **No Output**: Check that your image source is properly connected to the "input_image" parameter - **Solid Black Mask**: This may indicate that the selected channel has no variation or low values - **Solid White Mask**: This may indicate that the selected channel has high values throughout - **Unsupported Mode Error**: The image format may not support the selected channel (e.g., alpha channel in RGB images) ## Technical Details The node uses channel extraction logic to: - Load the input image from its URL - Extract the specified channel (red, green, blue, or alpha) - Convert it to a grayscale mask - Save the result as a new image artifact For RGB images, alpha channel selection will fallback to the red channel since RGB doesn't have an alpha channel. # ExtendCanvas ## What is it? The ExtendCanvas node allows you to extend the canvas around an image to fit target aspect ratios or custom dimensions. It's perfect for adding padding, creating specific aspect ratios, or preparing images for different output formats without cropping the original content. ## When would I use it? Use this node when you want to: - Add padding or margins around images - Create specific aspect ratios (16:9, 4:3, square, etc.) - Prepare images for different social media formats - Add space for text overlays or graphics - Create consistent image dimensions across a workflow - Prepare images for print layouts - Add borders or frames around images ## How to use it ### Basic Setup 1. Add the ExtendCanvas node to your workflow 1. Connect an image source to the "input_image" input 1. Set the target aspect ratio or custom dimensions 1. The extended image will be available at the "output" parameter ### Parameters #### Canvas Settings - **target_aspect_ratio**: The desired aspect ratio for the output - **16:9**: Widescreen format - **4:3**: Standard format - **1:1**: Square format - **3:2**: Photo format - **21:9**: Ultra-wide format - **Custom**: Set custom dimensions - **custom_width** (pixels): Custom width when using "Custom" aspect ratio - **custom_height** (pixels): Custom height when using "Custom" aspect ratio #### Extension Settings - **extension_method**: How to extend the canvas - **center**: Center the original image on the new canvas - **top_left**: Position the original image at the top-left - **top_right**: Position the original image at the top-right - **bottom_left**: Position the original image at the bottom-left - **bottom_right**: Position the original image at the bottom-right - **background_color** (hex color, default: #ffffff): Color for the extended areas - **background_opacity** (0.0-1.0, default: 1.0): Opacity of the background color ### Outputs - **output**: The image with extended canvas ## Example A typical canvas extension workflow: 1. Load an image using LoadImage 1. Connect that image to the ExtendCanvas node's "input_image" parameter 1. Set the canvas settings: - Select "16:9" target_aspect_ratio for widescreen format - Select "center" extension_method to center the image - Set background_color to "#000000" for a black background 1. Connect the "output" to DisplayImage to view the result ## Important Notes - **Non-destructive**: The original image content is never cropped or modified - **Flexible Positioning**: Choose where to position the original image on the new canvas - **Aspect Ratio Control**: Create any aspect ratio or use preset formats - **RGBA Support**: The node preserves transparency in RGBA images ## Common Issues - **Image Not Centered**: Check the extension_method setting - **Wrong Aspect Ratio**: Verify your target_aspect_ratio selection - **Background Not Visible**: Make sure background_opacity is greater than 0 ## Technical Details The node performs the following operations: 1. **Canvas Creation**: Creates a new canvas with the target dimensions 1. **Background Fill**: Fills the canvas with the specified background color 1. **Image Positioning**: Places the original image at the specified position 1. **Output Generation**: Saves the final extended image This provides a powerful way to prepare images for specific output requirements without losing any original content. # ExtractKeyColors ## What is it? The ExtractKeyColors node analyzes images and extracts the most prominent colors using either KMeans clustering or Median Cut (MMCQ) algorithms. It automatically orders colors by their frequency in the image and creates dynamic color picker parameters for each extracted color, making it perfect for color palette generation, brand analysis, and design workflows. ## When would I use it? Use this node when you want to: - Generate color palettes from images for design projects - Analyze dominant colors in logos and marketing materials - Extract brand colors from images - Create color schemes based on image content - Build color-based filters and effects - Generate color data for visualization projects - Analyze color distribution in photographs - Create design systems from visual references ## How to use it ### Basic Setup 1. Add the ExtractKeyColors node to your workflow 1. Connect an image source to the "input_image" input 1. Set the number of colors you want to extract 1. The extracted colors will be available as dynamic color picker parameters (color_1, color_2, etc.) ### Parameters #### Image Input - **input_image**: The image to analyze (can be connected from other image nodes) - Supports common formats: JPG, PNG, GIF, BMP, TIFF, ICO, WEBP - Accepts ImageArtifact or ImageUrlArtifact - File browser integration for easy image selection #### Color Settings - **num_colors** (2-12, default: 3): Number of colors to extract - Slider interface for easy selection - Colors are ordered by prominence (most frequent first) - Each extracted color becomes a separate output parameter - **algorithm** (default: kmeans): Color extraction algorithm to use - **kmeans**: Uses KMeans clustering for accurate color grouping - **median_cut**: Uses Modified Median Cut Quantization (MMCQ) for balanced color space division - Dropdown menu for easy selection ### Outputs Dynamic color picker parameters are created for each extracted color: - **color_1, color_2, etc.**: Hexadecimal color values (#RRGGBB) - Each parameter includes an interactive color picker UI - Colors are ordered by frequency (most dominant first) - Available in the Properties panel - Can be connected to other nodes for color-based workflows ## Example A typical color extraction workflow: 1. Load or generate an image using LoadImage or GenerateImage 1. Connect that image to the ExtractKeyColors node's "input_image" parameter 1. Set num_colors to 5 to extract the top 5 dominant colors 1. The node will analyze the image and create 5 color parameters: - color_1: The most prominent color (highest frequency) - color_2: The second most prominent color - color_3: The third most prominent color - And so on... 1. View the extracted colors in the Properties panel 1. Connect the color outputs to other nodes for color-based processing ## Important Notes - **Algorithm Choice**: Choose between KMeans (accurate clustering) and Median Cut (balanced color space) - **Automatic Sorting**: Colors are automatically ordered by their frequency in the image - **Color Diversity**: Both algorithms ensure extracted colors are distinct and representative - **Dynamic Parameters**: The node creates only the number of color parameters you request - **Real-time Processing**: Colors are extracted immediately when the image changes - **Hex Format**: All colors are provided as hexadecimal values (#RRGGBB) - **Compatibility**: Works with Pillow 11+ and NumPy 1.20+ without compatibility issues ## Common Issues - **Too Few Colors**: Increase the num_colors setting to extract more colors - **Similar Colors**: The KMeans algorithm ensures color diversity, but very similar images may produce similar colors - **Processing Time**: Larger images may take longer to process - **No Colors Extracted**: Check that your image source is properly connected and contains valid image data ## Technical Details The node uses a sophisticated color extraction process: 1. **Image Conversion**: Converts input image to PIL Image format and ensures RGB color space 1. **Algorithm Selection**: Choose between two proven color extraction algorithms: - **KMeans**: Uses scikit-learn's KMeans clustering to identify dominant color clusters - Groups similar pixels into clusters based on perceptual similarity - Identifies representative colors for each cluster - Orders colors by cluster size (pixel count) - **Median Cut (MMCQ)**: Modified Median Cut Quantization algorithm - Iteratively divides color space into buckets - Always splits the bucket with the largest color range - Stops at exactly the requested number of colors - Orders colors by bucket size (pixel count) 1. **Frequency Analysis**: Each extracted color includes its frequency/prominence in the image 1. **Automatic Ordering**: Colors are automatically sorted by frequency (most prominent first) 1. **Color Diversity**: Both algorithms ensure extracted colors are distinct and representative 1. **Dynamic UI Creation**: Generates color picker parameters for each extracted color ### Algorithm Benefits - **KMeans Algorithm**: - Accurate color clustering based on perceptual similarity - Excellent for images with distinct color regions - Handles gradients and transitions well - **Median Cut Algorithm**: - Balanced color space representation - Classic quantization approach - Good for images with uniform color distribution - Faster than KMeans for large images - **Shared Benefits**: - Native Python implementations using stable libraries (sklearn, numpy) - Compatible with Pillow 11+ and NumPy 1.20+ - Efficient processing for images of various sizes - Built-in frequency data for each color This provides a professional-grade solution for color extraction using modern, actively-maintained libraries, making it ideal for both creative and technical workflows. # FlipImage ## What is it? The FlipImage node flips images horizontally, vertically, or in both directions. It's perfect for creating mirror effects, correcting orientation issues, or creating symmetrical compositions from existing images. ## When would I use it? Use this node when you want to: - Create mirror effects or reflections - Correct images that are oriented incorrectly - Create symmetrical compositions - Generate variations of existing images - Flip images for artistic or design purposes - Create left/right or up/down variations of the same image ## How to use it ### Basic Setup 1. Add the FlipImage node to your workflow 1. Connect an image source to the "input_image" input 1. Choose your flip direction 1. The flipped image will be available at the "output" parameter ### Parameters #### Flip Settings - **direction**: The direction to flip the image - **horizontal**: Flip left to right (mirror effect) - **vertical**: Flip top to bottom (upside down) - **both**: Flip both horizontally and vertically (180° rotation) ### Outputs - **output**: The flipped image - **was_successful**: Indicates whether the flip operation succeeded - **result_details**: Detailed information about the flip operation ## Example A typical flipping workflow: 1. Load an image using LoadImage 1. Connect that image to the FlipImage node's "input_image" parameter 1. Set the flip direction: - Select "horizontal" to create a mirror image - Select "vertical" to flip upside down - Select "both" to rotate 180 degrees 1. Connect the "output" to DisplayImage to view the result ## Important Notes - **Live Preview**: Changes are applied in real-time as you adjust parameters - **Format Preservation**: The node preserves the original image format and quality - **RGBA Support**: The node preserves transparency in RGBA images - **Success/Failure Tracking**: The node provides detailed status information about the operation ## Common Issues - **No Output**: Check that your image source is properly connected to the "input_image" parameter - **Unexpected Results**: Remember that "horizontal" flips left-to-right, "vertical" flips top-to-bottom - **Both Direction**: "both" applies both horizontal and vertical flips, effectively rotating the image 180 degrees ## Technical Details The node uses PIL's image operations: - **Horizontal Flip**: Uses `ImageOps.mirror()` for left-to-right flipping - **Vertical Flip**: Uses `ImageOps.flip()` for top-to-bottom flipping - **Both Directions**: Applies both operations sequentially This provides reliable image flipping capabilities with proper error handling and status reporting. # Gaussian Edge Fade ## Overview The **Gaussian Edge Fade** node applies a Gaussian-blurred alpha channel fade to image edges for smooth compositing. This creates smooth transitions at the edges of images, making them ideal for overlaying on other images or backgrounds without harsh borders. ## Use Cases - **Image Compositing**: Seamlessly blend images together with smooth edge transitions - **Vignette Effects**: Create professional vignette-style edge fading - **Overlay Preparation**: Prepare images for overlay on varied backgrounds - **Logo Processing**: Add edge fading to logos with transparent backgrounds while preserving cutouts - **Photo Editing**: Create organic, natural-looking edge fades with rounded corners ## Parameters ### Input Parameters | Parameter | Type | Description | | ----------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **input_image** | ImageArtifact / ImageUrlArtifact | The input image to apply edge fade to | | **fade_mode** | String | How to measure fade distance: "percentage" (relative to image size) or "pixels" (absolute distance). Default: "percentage" | | **fade_distance** | Integer | Distance from edge to fade. In percentage mode: 5 = 5% of image dimension. In pixels mode: 5 = 5 pixels. Default: 5 | | **blur_radius** | Integer | Gaussian blur radius for smooth edge transition (0-100). Higher values create softer fades. Default: 10 | | **fade_curve** | Float | Controls transparency transition shape (0.5-4.0). 1.0 = linear, >1.0 = more transparent near edges (aggressive), \<1.0 = less transparent near edges (gentle). Default: 2.0 | | **edge_shape** | String | Shape of the fade: "square" (straight edges) or "rounded" (curved corners). Default: "square" | | **replace_mask** | Boolean | If False, combines edge fade with existing alpha channel. If True, replaces existing alpha entirely. Default: False | | **apply_top** | Boolean | Apply fade to top edge. Default: True | | **apply_bottom** | Boolean | Apply fade to bottom edge. Default: True | | **apply_left** | Boolean | Apply fade to left edge. Default: True | | **apply_right** | Boolean | Apply fade to right edge. Default: True | ### Output Parameters | Parameter | Type | Description | | ---------------- | ---------------- | ------------------------------------------------ | | **output_image** | ImageUrlArtifact | The processed image with alpha channel edge fade | ## Key Features ### Fade Modes **Percentage Mode**: Resolution-independent fading that scales with image size. A 5% fade looks consistent across images of any size. **Pixels Mode**: Fixed-size fading with precise pixel control. A 50-pixel fade is the same absolute distance regardless of image size. ### Fade Curve The fade curve parameter uses a power function to control how transparency transitions from edge to center: - **0.5-0.9**: Gentle curve - edges stay MORE opaque with quick transition to full opacity - **1.0**: Linear - even transition from transparent to opaque - **2.0 (Default)**: Aggressive - edges stay MORE transparent with gradual transition - **3.0-4.0**: Very aggressive - extreme transparency at edges, very gradual fade ### Edge Shapes **Square**: Each edge fades independently, creating straight transitions. Best for traditional vignettes and letterbox effects. **Rounded**: Fade follows curved contours using distance field calculations, creating smooth organic corner transitions. Best for modern UI designs and natural photo compositing. ### Alpha Channel Handling **Replace Mask = False (Default)**: Preserves existing transparency and combines it with the edge fade using multiplicative blending. Perfect for logos with transparent backgrounds. **Replace Mask = True**: Ignores existing alpha channel and applies only the new edge fade. Useful for images with unwanted transparency. ## Example Usage ### Basic Vignette Effect 1. Connect an image to **input_image** 1. Set **fade_mode** to "percentage" 1. Set **fade_distance** to 10 (10%) 1. Set **blur_radius** to 15 1. Set **fade_curve** to 2.0 1. Keep all **apply\_**\* edges set to True 1. Run the node Result: Image with soft, even fade on all edges. ### Letterbox Effect 1. Connect an image to **input_image** 1. Set **fade_mode** to "pixels" 1. Set **fade_distance** to 50 1. Set **blur_radius** to 20 1. Set **apply_top** and **apply_bottom** to True 1. Set **apply_left** and **apply_right** to False 1. Run the node Result: Horizontal fades only, suitable for widescreen overlays. ### Rounded Corner Composite 1. Connect an image to **input_image** 1. Set **edge_shape** to "rounded" 1. Set **fade_mode** to "percentage" 1. Set **fade_distance** to 8 1. Set **blur_radius** to 12 1. Set **fade_curve** to 2.5 (aggressive) 1. Run the node Result: Image with smooth rounded corners perfect for modern UI compositing. ### Preserve Logo Transparency 1. Connect a logo PNG with transparent background to **input_image** 1. Set **replace_mask** to False 1. Set **fade_distance** to 5 1. Set **blur_radius** to 10 1. Run the node Result: Logo keeps its transparent cutouts while adding smooth edge fade. ## Tips - **For natural compositing**: Use fade_curve values between 2.0-3.0 for more transparency at edges - **For resolution-independent effects**: Use percentage mode with consistent values across different image sizes - **For rounded corners**: Combine edge_shape "rounded" with all edges enabled for organic oval shapes - **For selective fading**: Disable specific edges to create directional fades - **For text overlays**: Use gentle fade_curve (0.7-0.9) to keep edges more visible ## Technical Notes - Output format is RGBA PNG with alpha channel preserved - Supports any input image format that PIL can process - Uses NumPy for efficient gradient calculations - Applies Gaussian blur using PIL's ImageFilter for smooth transitions - Multiplicative alpha blending when combining with existing alpha channels - Automatically clamps fade distance to prevent artifacts when exceeding image dimensions # ImageBash ## What is it? The ImageBash node allows you to create composite images by combining multiple images into a single canvas. It's perfect for creating collages, image grids, or combining multiple images into one cohesive composition with customizable canvas sizes and background colors. ## When would I use it? Use this node when you want to: - Create image collages or grids - Combine multiple images into a single composition - Create image mosaics or patterns - Build image galleries or portfolios - Combine related images for comparison - Create social media image layouts - Build image-based presentations ## How to use it ### Basic Setup 1. Add the ImageBash node to your workflow 1. Connect multiple image sources to the "input_images" input 1. Set the canvas size and background color 1. The composite image will be available at the "output_image" parameter ### Parameters #### Canvas Settings - **canvas_size**: The size of the output canvas - **Custom**: Set custom width and height - **HD**: 1280x720 - **Full HD**: 1920x1080 - **4K**: 3840x2160 - **Square**: 1024x1024 - **Instagram Post**: 1080x1080 - **Instagram Story**: 1080x1920 - **width** (pixels, default: 1920): Custom canvas width (when canvas_size is "Custom") - **height** (pixels, default: 1080): Custom canvas height (when canvas_size is "Custom") - **background_color** (hex color, default: #ffffff): Background color for empty areas #### Image Input - **input_images**: List of images to combine (can be connected from multiple image sources) ### Outputs - **output_image**: The composite image with all input images combined ## Example A typical image bashing workflow: 1. Load multiple images using LoadImage nodes 1. Connect all images to the ImageBash node's "input_images" parameter 1. Set the canvas settings: - Select "Full HD" for a 1920x1080 canvas - Set background_color to "#f0f0f0" for a light gray background 1. The node will automatically arrange the images on the canvas 1. Connect the "output_image" to DisplayImage to view the result ## Important Notes - **Automatic Layout**: The node automatically arranges images on the canvas - **Flexible Canvas**: Choose from preset sizes or set custom dimensions - **Background Control**: Set any background color for empty areas - **Multiple Images**: Can handle any number of input images ## Common Issues - **No Images Showing**: Make sure you've connected images to the "input_images" parameter - **Images Too Small**: Increase canvas size or check image dimensions - **Wrong Background**: Adjust the background_color parameter to your preference ## Technical Details The node performs the following operations: 1. **Canvas Creation**: Creates a new canvas with the specified size and background color 1. **Image Arrangement**: Automatically positions input images on the canvas 1. **Compositing**: Combines all images into a single composite image 1. **Output Generation**: Saves the final composite as a high-quality image This provides a powerful way to create complex image compositions from multiple sources. # ImageBlendCompositor ## What is it? The ImageBlendCompositor node allows you to blend two images together using various blend modes, similar to layers in photo editing software. It provides precise control over how images are combined, including positioning, opacity, and advanced compositing options. ## When would I use it? Use this node when you want to: - Combine two images using different blend modes - Create composite images with multiple layers - Apply creative blending effects - Overlay images with precise positioning - Create double exposure effects - Blend textures or patterns onto images - Create artistic compositions ## How to use it ### Basic Setup 1. Add the ImageBlendCompositor node to your workflow 1. Connect two image sources to the "input_image" and "blend_image" inputs 1. Select a blend mode and adjust opacity 1. The composited image will be available at the "output" parameter ### Parameters #### Image Inputs - **input_image**: The base image (background layer) - **blend_image**: The image to blend on top (foreground layer) #### Blend Settings - **blend_mode**: The blending method to use - **normal**: Standard alpha blending - **multiply**: Darkens the base image - **screen**: Lightens the base image - **overlay**: Combines multiply and screen - **soft_light**: Gentle light/dark effect - **hard_light**: Strong light/dark effect - **color_dodge**: Brightens base image - **color_burn**: Darkens base image - **darken**: Keeps darker pixels - **lighten**: Keeps lighter pixels - **difference**: Subtracts colors - **exclusion**: Similar to difference but softer - **opacity** (0.0-1.0, default: 1.0): Controls the strength of the blend - 0.0 = completely transparent (no blend) - 1.0 = completely opaque (full blend) #### Positioning - **x_offset** (pixels, default: 0): Horizontal position of the blend image - **y_offset** (pixels, default: 0): Vertical position of the blend image #### Advanced Options - **resize_blend_to_fit** (boolean, default: false): Resize blend image to fit base image - **preserve_alpha** (boolean, default: true): Preserve transparency in the result - **invert_blend** (boolean, default: false): Invert the blend image before blending ### Outputs - **output**: The composited image with both images blended together ## Example A typical blending workflow: 1. Load two images using LoadImage nodes 1. Connect the first image to "input_image" (background) 1. Connect the second image to "blend_image" (foreground) 1. Adjust the blend settings: - Select "multiply" blend mode for a darkening effect - Set opacity to 0.7 for a subtle blend - Set x_offset to 50 to shift the blend image 50 pixels right - Set y_offset to 30 to shift the blend image 30 pixels down 1. Connect the "output" to DisplayImage to view the result ## Important Notes - **Live Preview**: Changes are applied in real-time as you adjust parameters - **Image Sizes**: The blend image can be positioned outside the base image bounds - **RGBA Support**: The node preserves transparency in RGBA images - **High Quality**: Uses professional-grade blending algorithms ## Common Issues - **No Blend Visible**: Make sure opacity is greater than 0 and blend_mode is not "normal" with full opacity - **Blend Image Not Visible**: Check that the blend image is positioned within the base image bounds - **Unexpected Results**: Different blend modes produce very different effects - experiment to find the right one ## Technical Details The node supports multiple blend modes: - **Mathematical Blends**: multiply, screen, overlay, soft_light, hard_light - **Color Operations**: color_dodge, color_burn, difference, exclusion - **Selection Blends**: darken, lighten - **Standard Blending**: normal (alpha blending) Each blend mode uses different mathematical operations to combine the pixel values, creating unique visual effects. # Image Grid Splitter ## What is it? The Image Grid Splitter node takes a single image that contains a **regular grid** of images and splits it into individual images. It supports: - **Auto** grid detection (best-effort) - **Manual** grid definition (rows/columns) - A **preview overlay** showing dotted yellow grid lines - Outputs as both a **list** and **per-cell outputs** (`r1c1`, `r1c2`, etc.) ## When would I use it? Use this node when you have an image that contains multiple images (e.g., a 2×2 or 3×3 collage) and you want to: - Extract each tile as its own image for downstream processing - Feed individual grid cells into other nodes - Keep both a list output and named per-cell outputs for easy wiring ## How to use it ### Basic setup 1. Add **Image Grid Splitter** to your workflow 1. Connect your grid image to **Input Image** 1. Choose **Grid Detection**: - **auto**: the node estimates rows/columns - **manual**: you specify **Rows** and **Columns** 1. Review **Preview** to confirm the split boundaries 1. Run the node to generate split images Auto detection is best-effort Auto mode assumes a **regular grid** (equal-sized cells) and may be less accurate when adjacent cells are visually similar. If the preview lines don’t match your grid, switch to **manual**. ### Parameters - **Input Image**: The grid image to split. Accepts `ImageArtifact` or `ImageUrlArtifact`. - **Grid Detection**: - **auto**: detect a regular grid - **manual**: use explicit Rows/Columns - **Rows / Columns** (manual only): Sliders from 1–12. - **Preview** (output): The original image with dotted yellow grid lines. - **Detections (auto)** (output group): Detected Rows/Columns/Count (hidden when manual mode is selected). ### Outputs - **Images**: List of `ImageUrlArtifact` in row-major order (left-to-right, top-to-bottom). - **Grid Cells**: One `ImageUrlArtifact` output per cell, named: - `r1c1`, `r1c2`, ..., `r{row}c{col}` Per-cell outputs are created on run The `r{row}c{col}` outputs are created/updated when you run the node, based on the chosen (or detected) grid size. ## Example If your input is a 2×2 grid: - `r1c1` is top-left - `r1c2` is top-right - `r2c1` is bottom-left - `r2c2` is bottom-right And `Images` will contain `[r1c1, r1c2, r2c1, r2c2]`. ## Common issues - **Preview grid lines don’t match**: switch to **manual** and set the correct Rows/Columns. - **Unexpected auto detection**: some grids have weak visual boundaries; manual mode is the reliable fallback. # InvertImage ## What is it? The InvertImage node creates a negative version of an input image by inverting all the color values. It's like creating a photographic negative - dark areas become light, light areas become dark, and colors are inverted to their complementary colors. ## When would I use it? Use this node when you want to: - Create artistic effects by inverting image colors - Generate negative versions of images for creative purposes - Create high-contrast effects for image processing workflows - Experiment with different visual styles in your image generation pipelines - Create complementary color schemes from existing images ## How to use it ### Basic Setup 1. Add the InvertImage node to your workflow 1. Connect an image source to the "input_image" input 1. The inverted image will be available at the "output" parameter ### Parameters - **input_image**: The image to invert (can be connected from other image nodes) ### Outputs - **output**: The inverted (negative) version of the input image ## Example A common workflow pattern: 1. Generate or load an image using nodes like GenerateImage or LoadImage 1. Connect that image to the InvertImage node's "input_image" parameter 1. The InvertImage node will create a negative version of the image 1. Connect the "output" to DisplayImage to view the result, or to other processing nodes ## Important Notes - **RGBA Images**: For images with transparency (RGBA), the node inverts the RGB color channels while preserving the original alpha channel - **Other Formats**: Images in other formats are converted to RGB before inversion - **Real-time Processing**: The node processes the image immediately when a connection is made or when the input value changes - **Output Format**: The inverted image is saved as a PNG file to preserve quality ## Common Issues - **No Output**: Check that your image source is properly connected to the "input_image" parameter - **Unexpected Results**: Remember that inversion creates a negative - dark areas become light and vice versa - **Transparency Issues**: If you need to preserve specific transparency effects, the node will maintain the original alpha channel for RGBA images ## Technical Details The node handles different image modes intelligently: - **RGBA**: Inverts RGB channels, preserves alpha channel - **RGB**: Direct inversion of all color values - **Other modes**: Converts to RGB first, then inverts # InvertMask ## What is it? The InvertMask node creates an inverted version of an input mask. It flips the transparency values - areas that were opaque become transparent, and areas that were transparent become opaque. This is useful for creating complementary masks or switching between positive and negative selections. ## When would I use it? Use this node when you want to: - Create the opposite of an existing mask - Switch between positive and negative selections - Invert transparency effects - Create complementary masks for image compositing - Reverse the effect of a mask in your workflow ## How to use it ### Basic Setup 1. Add the InvertMask node to your workflow 1. Connect a mask source to the "input_mask" input 1. The inverted mask will be available at the "output_mask" parameter ### Parameters - **input_mask**: The mask to invert (can be connected from other mask nodes like DisplayMask or PaintMask) ### Outputs - **output_mask**: The inverted version of the input mask ## Example A common workflow pattern: 1. Create a mask using nodes like DisplayMask or PaintMask 1. Connect that mask to the InvertMask node's "input_mask" parameter 1. The InvertMask node will create an inverted version of the mask 1. Connect the "output_mask" to DisplayImage to view the inverted mask, or to other mask processing nodes ## Important Notes - **Inversion Logic**: White areas in the input mask become black in the output, and black areas become white - **Alpha Channel Handling**: For RGBA images, the node inverts the alpha channel specifically - **Grayscale Conversion**: Non-grayscale images are converted to grayscale before inversion - **Real-time Processing**: The node processes the mask immediately when a connection is made or when the input value changes - **Output Format**: The inverted mask is saved as a PNG file to preserve quality ## Common Issues - **No Output**: Check that your mask source is properly connected to the "input_mask" parameter - **Unexpected Results**: Remember that inversion flips the mask values - what was opaque becomes transparent - **Color Images**: If you input a color image, it will be converted to grayscale before inversion ## Technical Details The node handles different image modes intelligently: - **RGBA**: Inverts the alpha channel while preserving RGB channels - **Grayscale (L)**: Direct inversion of grayscale values - **Other modes**: Converts to grayscale first, then inverts The inversion is performed using the formula: `255 - original_value`, which flips the brightness values. # LoadImage ## What is it? The LoadImage is a simple building block that lets you bring an image into your workflow. Think of it as picking up a photo so you can use it in your project. ## When would I use it? Use this node when you want to: - Use an image that was created by another node (like CreateImage) - Pass an image to other nodes in your workflow - Display an image as part of your project ## How to use it ### Basic Setup 1. Add the LoadImage to your workflow 1. Connect it to a source of images (like a CreateImage) ### Parameters - **image**: The image to load (this can be connected to an output from another node) ### Outputs - **image**: The loaded image that can be used by other nodes in your flow ## Example Imagine you've created an image with the CreateImage and now want to use it elsewhere: 1. Connect the "output" from your CreateImage to the "image" input of the LoadImage 1. The LoadImage will make the image available to use in the rest of your workflow ## Important Notes - The LoadImage simply passes the image through - it doesn't change the image itself - You can click the file browser icon to select an image from your computer - The image preview can be expanded by clicking the expander icon ## Common Issues - **No Image Showing**: Make sure you've properly connected an image source to this node - **Wrong Image Type**: Make sure you're connecting an ImageArtifact or BlobArtifact to this node # MergeImages ## What is it? The MergeImages node allows you to combine multiple images into a single composition using different layout options. It's perfect for creating collages, image grids, or combining related images into one cohesive composition with flexible arrangement options. ## When would I use it? Use this node when you want to: - Create image collages or grids - Combine multiple related images - Create image mosaics or patterns - Build image galleries or portfolios - Combine images for comparison - Create social media image layouts - Build image-based presentations ## How to use it ### Basic Setup 1. Add the MergeImages node to your workflow 1. Connect multiple image sources to the "input_images" input 1. Choose your layout style and arrangement 1. The merged image will be available at the "output" parameter ### Parameters #### Image Input - **input_images**: List of images to merge (can be connected from multiple image sources) #### Layout Settings - **layout_style**: The arrangement style for the images - **grid**: Regular grid layout with equal-sized cells - **horizontal**: Arrange images in a horizontal row - **vertical**: Arrange images in a vertical column - **masonry**: Pinterest-style layout with varying heights - **columns** (1-10, default: 3): Number of columns in grid layout - **spacing** (0-50, default: 10): Space between images in pixels #### Styling Options - **background_color** (hex color, default: #ffffff): Background color for the merged image - **border_radius** (0-20, default: 0): Corner radius for image borders - **padding** (0-100, default: 20): Padding around the entire composition ### Outputs - **output**: The merged image with all input images combined ## Example A typical image merging workflow: 1. Load multiple images using LoadImage nodes 1. Connect all images to the MergeImages node's "input_images" parameter 1. Set the layout settings: - Select "grid" layout_style for a regular grid - Set columns to 2 for a 2-column layout - Set spacing to 15 for comfortable spacing between images 1. Customize the appearance: - Set background_color to "#f0f0f0" for a light gray background - Set border_radius to 5 for slightly rounded corners - Set padding to 30 for space around the entire composition 1. Connect the "output" to DisplayImage to view the result ## Important Notes - **Flexible Layout**: Choose from multiple layout styles - **Automatic Arrangement**: Images are automatically arranged based on your settings - **Customizable Styling**: Full control over colors, spacing, and appearance - **Multiple Images**: Can handle any number of input images ## Common Issues - **No Images Showing**: Make sure you've connected images to the "input_images" parameter - **Layout Issues**: Adjust columns and spacing for better appearance - **Images Too Small**: Increase the overall composition size or reduce the number of columns ## Technical Details The node creates a composite image that: - Automatically arranges input images in the specified layout - Maintains aspect ratios while fitting images to the layout - Provides consistent spacing and styling - Supports multiple layout styles for different use cases This provides a powerful way to create complex image compositions from multiple sources. # PaintMask ## What is it? The PaintMask node creates an editable mask from an input image and allows you to paint on it to modify the transparency. It automatically generates an initial mask from the image's alpha channel, then provides an interactive painting interface where you can edit the mask by painting on it. The node also applies the mask to the input image to show the final result. ## When would I use it? Use this node when you want to: - Create custom masks for image editing and compositing - Manually refine automatically generated masks - Paint transparency effects on images - Create selective transparency for image overlays - Fine-tune mask boundaries for precise image editing ## How to use it ### Basic Setup 1. Add the PaintMask node to your workflow 1. Connect an image source to the "input_image" input 1. The node will automatically generate an initial mask from the image's alpha channel 1. Use the painting interface in the "output_mask" parameter to edit the mask 1. The final masked image will be available at the "output_image" parameter ### Parameters - **input_image**: The image to create a mask from (can be connected from other image nodes) ### Outputs - **output_mask**: An editable mask image that you can paint on to modify transparency - **output_image**: The final image with the mask applied (transparency based on the mask) ## Example A common workflow pattern: 1. Generate or load an image using nodes like GenerateImage or LoadImage 1. Connect that image to the PaintMask node's "input_image" parameter 1. The node will create an initial mask from the image's alpha channel 1. Use the painting tools in the "output_mask" parameter to refine the mask 1. The "output_image" will show the result with your painted mask applied 1. Connect the "output_image" to DisplayImage to view the final result ## Important Notes - **Automatic Mask Generation**: The node automatically creates an initial mask from the input image's alpha channel - **Interactive Painting**: You can paint directly on the mask using the built-in painting interface - **Mask Persistence**: The mask is preserved when you change the input image, unless the source image URL changes - **Red Channel Usage**: The mask uses the red channel of the painted image as the alpha channel for the final result - **Real-time Updates**: Changes to the mask are immediately reflected in the output image ## Common Issues - **No Output**: Check that your image source is properly connected to the "input_image" parameter - **Mask Not Updating**: If you change the input image, the mask may need to be regenerated - **Painting Not Working**: Make sure you're using the painting interface in the "output_mask" parameter - **Unexpected Transparency**: Remember that white areas in the mask become opaque, black areas become transparent ## Technical Details The node handles mask generation and editing through several key processes: - **Initial Mask Creation**: Extracts the alpha channel from the input image and converts it to a grayscale mask - **Mask Editing**: Provides an interactive interface for painting on the mask - **Mask Application**: Uses the red channel of the painted mask as the alpha channel for the final image - **Persistence**: Tracks whether the mask has been manually edited to determine when to regenerate vs. preserve edits # RescaleImage ## What is it? The RescaleImage node allows you to resize images with precise control over dimensions, scaling methods, and quality. It supports both pixel-based and percentage-based scaling, making it perfect for preparing images for specific output requirements or creating different sizes from a single source. ## When would I use it? Use this node when you want to: - Resize images to specific dimensions - Scale images by percentage - Prepare images for different output formats - Create thumbnails or preview images - Optimize images for web or print - Batch resize multiple images - Maintain aspect ratios while scaling ## How to use it ### Basic Setup 1. Add the RescaleImage node to your workflow 1. Connect an image source to the "input_image" input 1. Choose your resize mode and set the target size 1. The resized image will be available at the "output" parameter ### Parameters #### Resize Settings - **resize_mode**: The method for determining the new size - **percentage**: Scale by percentage (e.g., 50% = half size) - **pixels**: Set exact pixel dimensions - **target_size** (pixels, default: 512): Target size when using pixel mode - For width-based scaling: sets the width, height scales proportionally - For height-based scaling: sets the height, width scales proportionally - **percentage_scale** (1-1000, default: 100): Scale percentage when using percentage mode - 50 = half size - 100 = original size - 200 = double size #### Quality Settings - **resample_filter**: The algorithm used for resampling - **lanczos**: High quality, slower (recommended for most cases) - **bicubic**: Good quality, medium speed - **bilinear**: Lower quality, faster - **nearest**: Pixelated, fastest ### Outputs - **output**: The resized image ## Example A typical rescaling workflow: 1. Load an image using LoadImage 1. Connect that image to the RescaleImage node's "input_image" parameter 1. Set the resize settings: - Select "percentage" resize_mode - Set percentage_scale to 50 to create a half-size version - Select "lanczos" resample_filter for high quality 1. Connect the "output" to DisplayImage to view the result ## Important Notes - **Live Preview**: Changes are applied in real-time as you adjust parameters - **Aspect Ratio**: The node maintains the original aspect ratio - **High Quality**: Lanczos resampling provides the best quality for most use cases - **RGBA Support**: The node preserves transparency in RGBA images ## Common Issues - **Image Too Small**: Increase the percentage_scale or target_size - **Poor Quality**: Use "lanczos" resample_filter for better quality - **Wrong Dimensions**: Check that your resize_mode and target settings are correct ## Technical Details The node uses PIL's resampling algorithms: - **Lanczos**: Uses Lanczos kernel for high-quality resampling - **Bicubic**: Uses bicubic interpolation for good quality - **Bilinear**: Uses bilinear interpolation for faster processing - **Nearest**: Uses nearest neighbor for pixelated results This provides professional-quality image resizing capabilities suitable for any workflow. # SaveImage ## What is it? The SaveImage node allows you to save images to your local file system. It's a simple but essential node for preserving the results of your image processing workflows, allowing you to export images in various formats and quality settings. ## When would I use it? Use this node when you want to: - Save processed images to your computer - Export images in specific formats (PNG, JPEG, etc.) - Control image quality and compression settings - Preserve the results of your image processing workflows - Create backups of important images - Export images for use in other applications ## How to use it ### Basic Setup 1. Add the SaveImage node to your workflow 1. Connect an image source to the "input_image" input 1. Set the output file path and format 1. The image will be saved to the specified location ### Parameters #### Image Input - **input_image**: The image to save (can be connected from any image processing node) #### Output Settings - **output_path**: The file path where the image will be saved - Can be a relative path (e.g., "output/image.png") - Can be an absolute path (e.g., "/Users/username/Desktop/image.png") - File extension determines the format - **image_format**: The format to save the image in - **PNG**: Lossless format, supports transparency - **JPEG**: Compressed format, smaller file sizes - **BMP**: Uncompressed bitmap format - **TIFF**: High-quality format for professional use #### Quality Settings (JPEG only) - **quality** (1-100, default: 95): JPEG compression quality - Higher values = better quality, larger files - Lower values = lower quality, smaller files ### Outputs - **saved_path**: The path where the image was saved - **file_size**: The size of the saved file in bytes ## Example A typical image saving workflow: 1. Process an image using AdjustImageEQ or another processing node 1. Connect the processed image to the SaveImage node's "input_image" parameter 1. Set the output settings: - Set output_path to "processed_image.png" - Select "PNG" image_format for lossless quality 1. The image will be saved to your specified location 1. The saved_path output will show where the file was saved ## Important Notes - **File Paths**: Make sure the output directory exists or the node will create it - **Format Support**: Different formats support different features (transparency, compression, etc.) - **Quality Control**: JPEG quality setting only affects JPEG format - **Overwrite Protection**: The node will overwrite existing files with the same name ## Common Issues - **File Not Saved**: Check that the output_path is valid and you have write permissions - **Wrong Format**: Make sure the file extension matches the image_format setting - **Poor Quality**: For JPEG, increase the quality setting ## Technical Details The node uses PIL's image saving capabilities: - **PNG**: Lossless compression with transparency support - **JPEG**: Lossy compression with quality control - **BMP**: Uncompressed bitmap format - **TIFF**: High-quality format with various compression options This provides reliable image saving functionality for any workflow that needs to preserve results. # Webcam ## What is it? The Webcam node allows you to capture images directly from your device's camera. It's perfect for real-time image capture, creating interactive workflows, or incorporating live camera input into your image processing pipelines. ## When would I use it? Use this node when you want to: - Capture images from your webcam or camera - Create real-time image processing workflows - Build interactive applications with live camera input - Capture photos for immediate processing - Create live image analysis or recognition systems - Build camera-based automation workflows ## How to use it ### Basic Setup 1. Add the Webcam node to your workflow 1. Click the "Capture" button to take a photo 1. The captured image will be available at the "output" parameter ### Parameters #### Camera Settings - **camera_index** (0-10, default: 0): Which camera to use (if multiple cameras are available) - **resolution**: The capture resolution - **640x480**: Standard resolution - **1280x720**: HD resolution - **1920x1080**: Full HD resolution - **Custom**: Set custom width and height - **custom_width** (pixels): Custom width when using "Custom" resolution - **custom_height** (pixels): Custom height when using "Custom" resolution #### Capture Settings - **auto_capture** (boolean, default: false): Automatically capture images at intervals - **capture_interval** (seconds, default: 1): Interval between automatic captures ### Outputs - **output**: The captured image from the camera ## Example A typical webcam workflow: 1. Add the Webcam node to your workflow 1. Set the camera settings: - Set camera_index to 0 for the default camera - Select "1280x720" resolution for HD quality 1. Click the "Capture" button to take a photo 1. Connect the "output" to DisplayImage to view the captured image 1. Optionally connect to other processing nodes for real-time image processing ## Important Notes - **Camera Access**: The node requires camera permissions to work - **Real-time Capture**: Images are captured in real-time when you click "Capture" - **Multiple Cameras**: Use camera_index to select different cameras if available - **Resolution Control**: Choose the appropriate resolution for your needs ## Common Issues - **No Camera Found**: Make sure your camera is connected and not being used by other applications - **Permission Denied**: Grant camera permissions to the application - **Poor Quality**: Increase the resolution setting for better image quality - **Camera Busy**: Close other applications that might be using the camera ## Technical Details The node uses your device's camera system to: - Access available cameras - Capture images at the specified resolution - Provide real-time image capture capabilities - Support multiple camera inputs This provides a powerful way to incorporate live camera input into your image processing workflows. # Display JSON Display JSON data with automatic repair and formatting. ## Description The Display JSON node takes JSON data and formats it for display. It uses `json-repair` to handle malformed JSON strings and provides a clean, readable output for viewing and debugging JSON data in your workflows. ## Parameters ### Input Parameters | Parameter | Type | Description | Default | | --------- | --------------- | ------------------------ | ------- | | `json` | json, str, dict | The JSON data to display | `{}` | ### Output Parameters | Parameter | Type | Description | | --------- | ---- | ----------------------- | | `json` | json | The formatted JSON data | ## Features - **Automatic JSON Repair**: Handles malformed JSON strings using `json-repair` - **Multiple Input Types**: Accepts dictionaries, strings, and other data types - **Error Handling**: Graceful fallbacks when parsing fails - **Display Optimization**: Formats JSON for easy reading and debugging - **Real-time Processing**: Updates automatically when input changes ## Examples ### Basic JSON Display ``` # Input: {"name": "John", "age": 30, "active": true} # Output: {"name": "John", "age": 30, "active": true} ``` ### Malformed JSON Repair and Display ``` # Input: '{"name": "John", age: 30, "city": "New York"}' # Missing quotes around age # Output: {"name": "John", "age": 30, "city": "New York"} # Repaired and formatted ``` ### Complex JSON Structure ``` # Input: {"user": {"name": "Alice", "profile": {"email": "alice@example.com", "preferences": {"theme": "dark"}}}} # Output: {"user": {"name": "Alice", "profile": {"email": "alice@example.com", "preferences": {"theme": "dark"}}}} ``` ### String to JSON Conversion and Display ``` # Input: '{"items": [{"title": "Book", "price": 25}, {"title": "Magazine", "price": 10}]}' # Output: {"items": [{"title": "Book", "price": 25}, {"title": "Magazine", "price": 10}]} ``` ## Use Cases - **Debugging**: View and inspect JSON data during workflow development - **Data Validation**: Verify JSON structure and content - **API Response Inspection**: Examine responses from external APIs - **Configuration Review**: Check JSON configuration files - **Data Flow Monitoring**: Monitor JSON data as it moves through your workflow ## Related Nodes - [JSON Input](https://docs.griptapenodes.com/nodes/json/json_input/index.md) - Create JSON data from inputs - [JSON Extract Value](https://docs.griptapenodes.com/nodes/json/json_extract_value/index.md) - Extract values from JSON - [JSON Replace](https://docs.griptapenodes.com/nodes/json/json_replace/index.md) - Replace values in JSON - [To JSON](https://docs.griptapenodes.com/nodes/convert/to_json/index.md) - Convert other data types to JSON # JSON Extract Value Extract values from JSON using [JMESPath](https://jmespath.org) expressions. ## Description The JSON Extract Value node allows you to extract specific values from JSON data using JMESPath expressions. It supports nested object access, array indexing, and powerful wildcard operations, making it easy to navigate and extract data from complex JSON structures. ## Parameters ### Input Parameters | Parameter | Type | Description | Default | | --------- | --------------- | ------------------------------------------------------------------------------------------ | ------- | | `json` | json, str, dict | The JSON data to extract from | `{}` | | `path` | str | JMESPath expression to extract data (e.g., 'user.name', 'items[0].title', '[\*].assignee') | `""` | ### Output Parameters | Parameter | Type | Description | | --------- | ---- | ------------------- | | `output` | json | The extracted value | ## Features - **JMESPath Expressions**: Use powerful JMESPath syntax for flexible data extraction - **Dot Notation Paths**: Use simple dot notation to navigate JSON structure - **Array Indexing**: Access array elements using `[index]` syntax - **Wildcard Operations**: Extract all values from arrays using `[*]` syntax - **Native Python Types**: Returns raw Python values (strings, dicts, lists, etc.) - no JSON serialization needed - **Real-time Updates**: Automatically updates when input changes - **Safe Extraction**: Returns empty dict `{}` if path doesn't exist ## JMESPath Syntax > **📚 Learn More**: For complete JMESPath syntax and advanced features, see the [JMESPath Documentation](https://jmespath.org/tutorial.html). ### Basic Object Access ``` # Access nested object properties path = "user.name" # Gets user's name path = "user.profile.email" # Gets nested email ``` ### Array Indexing ``` # Access array elements path = "items[0]" # Gets first item path = "items[0].title" # Gets title of first item path = "users[2].name" # Gets name of third user ``` ### Wildcard Operations ``` # Extract all values from arrays path = "items[*].title" # Gets all titles from items array path = "users[*].name" # Gets all names from users array path = "[*].assignee" # Gets all assignees from root array ``` ### Complex Paths ``` # Combine object and array access path = "orders[0].items[1].price" # Gets price of second item in first order path = "projects[*].tasks[*].assignee" # Gets all assignees from all project tasks ``` ## Examples ### Basic Object Extraction ``` # Input JSON: {"user": {"name": "John", "age": 30}} # Path: "user.name" # Output: "John" # Returns raw Python string value ``` ### Array Element Extraction ``` # Input JSON: {"items": [{"title": "Book", "price": 25}, {"title": "Magazine", "price": 10}]} # Path: "items[0].title" # Output: "Book" # Returns raw Python string value ``` ### Nested Array Access ``` # Input JSON: {"orders": [{"items": [{"name": "Product A"}, {"name": "Product B"}]}]} # Path: "orders[0].items[1].name" # Output: "Product B" # Returns raw Python string value ``` ### Non-existent Path ``` # Input JSON: {"user": {"name": "John"}} # Path: "user.email" # Output: {} # Empty dict (Python dict object) for non-existent paths ``` ### Different Return Types The node returns raw Python values directly from JMESPath, matching the extracted data type: ``` # String extraction # Input JSON: {"product": {"name": "Widget", "category": "Electronics"}} # Path: "product.name" # Output: "Widget" # Python string # Object extraction # Input JSON: {"user": {"name": "John", "age": 30}} # Path: "user" # Output: {"name": "John", "age": 30} # Python dict # Array extraction # Input JSON: {"items": [{"title": "Book"}, {"title": "Magazine"}]} # Path: "items" # Output: [{"title": "Book"}, {"title": "Magazine"}] # Python list ``` **Important Notes:** - The node returns native Python types (str, dict, list, int, bool, etc.) - no JSON serialization - This makes the output directly usable by other nodes without parsing - Consistent with other JSON nodes in the library (JsonInput, JsonReplace, etc.) - JMESPath handles all type conversions automatically ## Use Cases - **API Response Processing**: Extract specific fields from API responses - **Data Filtering**: Get only the data you need from large JSON objects - **Configuration Access**: Extract specific settings from configuration files - **Data Transformation**: Prepare data for other nodes in your workflow ## Related Nodes - [JSON Input](https://docs.griptapenodes.com/nodes/json/json_input/index.md) - Create JSON data from inputs - [JSON Replace](https://docs.griptapenodes.com/nodes/json/json_replace/index.md) - Replace values in JSON - [Display JSON](https://docs.griptapenodes.com/nodes/json/display_json/index.md) - Display and format JSON data - [To JSON](https://docs.griptapenodes.com/nodes/convert/to_json/index.md) - Convert other data types to JSON # JSON Find Find items in JSON arrays based on search criteria. ## Description The JSON Find node allows you to search through JSON arrays and find items that match specific criteria. It supports multiple search modes, case sensitivity options, and can return either the first match or all matches. Perfect for filtering and searching through complex data structures. ## Parameters ### Input Parameters | Parameter | Type | Description | Default | | ---------------- | --------------- | ---------------------------------------------------------------------- | --------- | | `json` | json, str, dict | The JSON data to search through (should be an array or contain arrays) | `[]` | | `search_field` | str | Dot notation path to the field to search (e.g., 'attributes.content') | `""` | | `search_value` | str | Value to search for | `""` | | `search_mode` | str | Search mode: 'exact', 'contains', or 'starts_with' | `"exact"` | | `return_mode` | str | Return mode: 'first' for first match, 'all' for all matches | `"first"` | | `case_sensitive` | bool | Whether the search should be case sensitive | `true` | ### Output Parameters | Parameter | Type | Description | | ------------- | ---- | ------------------------------------------------------------------------- | | `found_item` | json | The found item(s) - single item if return_mode is 'first', array if 'all' | | `found_count` | int | Number of items found | | `found_index` | int | Index of the first found item (or -1 if not found) | ## Features - **Flexible Search Modes**: Exact match, contains, or starts with - **Case Sensitivity Control**: Choose case-sensitive or case-insensitive search - **Multiple Return Options**: Get first match or all matches - **Nested Field Access**: Use dot notation to search deep into JSON structures - **Array Auto-Detection**: Automatically finds arrays in common field names - **Real-time Updates**: Automatically updates when input changes ## Search Modes ### Exact Match (`exact`) Finds items where the field value exactly matches the search value. ``` # Search for: "Design" # Matches: "Design" # Does not match: "Design Task", "design", "My Design" ``` ### Contains (`contains`) Finds items where the field value contains the search value as a substring. ``` # Search for: "Design" # Matches: "Design", "Design Task", "My Design Work" # Does not match: "design" (if case_sensitive=true) ``` ### Starts With (`starts_with`) Finds items where the field value starts with the search value. ``` # Search for: "Design" # Matches: "Design", "Design Task", "Designer" # Does not match: "My Design", "design" ``` ## Field Path Syntax The `search_field` parameter uses the same dot notation as JSON Extract Value: ### Basic Object Access ``` search_field = "name" # Search in root name field search_field = "attributes.content" # Search in nested content field search_field = "user.profile.email" # Search in deeply nested email field ``` ### Array Indexing ``` search_field = "items[0].title" # Search in title of first item search_field = "users[2].name" # Search in name of third user ``` ## Examples ### Find Task by Content ``` # Input JSON: [ # {"attributes": {"content": "Design", "status": "wtg"}}, # {"attributes": {"content": "Model", "status": "wtg"}}, # {"attributes": {"content": "Design Review", "status": "ip"}} # ] # search_field: "attributes.content" # search_value: "Design" # search_mode: "exact" # return_mode: "first" # Result: {"attributes": {"content": "Design", "status": "wtg"}} ``` ### Find All Tasks with Status ``` # Input JSON: [ # {"attributes": {"content": "Design", "status": "wtg"}}, # {"attributes": {"content": "Model", "status": "wtg"}}, # {"attributes": {"content": "Review", "status": "ip"}} # ] # search_field: "attributes.status" # search_value: "wtg" # search_mode: "exact" # return_mode: "all" # Result: [{"attributes": {"content": "Design", "status": "wtg"}}, # {"attributes": {"content": "Model", "status": "wtg"}}] ``` ### Case-Insensitive Search ``` # Input JSON: [ # {"name": "Design Task"}, # {"name": "design review"}, # {"name": "DESIGN WORK"} # ] # search_field: "name" # search_value: "design" # search_mode: "contains" # case_sensitive: false # return_mode: "all" # Result: All three items (matches "Design", "design", "DESIGN") ``` ### Find Items Starting With ``` # Input JSON: [ # {"title": "Design Task"}, # {"title": "Design Review"}, # {"title": "My Design Work"} # ] # search_field: "title" # search_value: "Design" # search_mode: "starts_with" # return_mode: "all" # Result: [{"title": "Design Task"}, {"title": "Design Review"}] ``` ### Complex Nested Search ``` # Input JSON: [ # { # "relationships": { # "entity": {"data": {"name": "convertible", "type": "Asset"}} # } # } # ] # search_field: "relationships.entity.data.name" # search_value: "convertible" # search_mode: "exact" # return_mode: "first" # Result: The matching item ``` ## Array Auto-Detection If your input JSON is an object containing arrays, the node will automatically look for common array field names: - `data` - `items` - `results` - `list` ``` # Input JSON: {"data": [{"name": "Item 1"}, {"name": "Item 2"}]} # The node will automatically search in the "data" array ``` ## Use Cases - **Task Management**: Find specific tasks by content, status, or assignee - **Data Filtering**: Filter large datasets based on specific criteria - **API Response Processing**: Search through API response arrays - **Configuration Lookup**: Find specific configuration items - **Content Discovery**: Search through content collections - **User Management**: Find users by name, email, or role ## Related Nodes - [JSON Extract Value](https://docs.griptapenodes.com/nodes/json/json_extract_value/index.md) - Extract single values by path - [JSON Input](https://docs.griptapenodes.com/nodes/json/json_input/index.md) - Create JSON data from inputs - [JSON Replace](https://docs.griptapenodes.com/nodes/json/json_replace/index.md) - Replace values in JSON - [Display JSON](https://docs.griptapenodes.com/nodes/json/display_json/index.md) - Display and format JSON data - [To JSON](https://docs.griptapenodes.com/nodes/convert/to_json/index.md) - Convert other data types to JSON # JSON Input Creates a JSON node from input data with automatic repair capabilities. ## Description The JSON Input node takes various input types and converts them to proper JSON data. It uses `json-repair` to handle malformed JSON strings and provides robust conversion for different data types. ## Parameters ### Input Parameters | Parameter | Type | Description | Default | | --------- | --------------- | --------------------------------- | ------- | | `json` | json, str, dict | The input data to convert to JSON | `{}` | ### Output Parameters | Parameter | Type | Description | | --------- | ---- | ----------------------- | | `json` | json | The processed JSON data | ## Features - **Automatic JSON Repair**: Handles malformed JSON strings using `json-repair` - **Multiple Input Types**: Accepts dictionaries, strings, and other data types - **Error Handling**: Graceful fallbacks when parsing fails - **Real-time Processing**: Updates automatically when input changes ## Examples ### Basic Usage ``` # Input: {"name": "John", "age": 30} # Output: {"name": "John", "age": 30} ``` ### Malformed JSON Repair ``` # Input: '{"name": "John", age: 30}' # Missing quotes around age # Output: {"name": "John", "age": 30} # Repaired JSON ``` ### String to JSON Conversion ``` # Input: '{"user": {"name": "Alice", "active": true}}' # Output: {"user": {"name": "Alice", "active": true}} ``` ### Dictionary Input ``` # Input: {"status": "active", "count": 5} # Output: {"status": "active", "count": 5} # Used as-is ``` ## Use Cases - **Data Validation**: Ensure input data is valid JSON - **API Integration**: Process JSON responses from external APIs - **Data Cleaning**: Repair malformed JSON data - **Workflow Integration**: Convert various data types to JSON format ## Related Nodes - [JSON Extract Value](https://docs.griptapenodes.com/nodes/json/json_extract_value/index.md) - Extract specific values from JSON - [JSON Replace](https://docs.griptapenodes.com/nodes/json/json_replace/index.md) - Replace values in JSON - [Display JSON](https://docs.griptapenodes.com/nodes/json/display_json/index.md) - Display and format JSON data - [To JSON](https://docs.griptapenodes.com/nodes/convert/to_json/index.md) - Convert other data types to JSON # JSON Replace Replace values in JSON using dot notation paths. ## Description The JSON Replace node allows you to modify JSON data by replacing values at specific paths using dot notation. It creates a deep copy of the original data to avoid modifying the source, and can automatically create missing paths if they don't exist. ## Parameters ### Input Parameters | Parameter | Type | Description | Default | | ------------------- | --------------- | ------------------------------------------------------------------ | ------- | | `json` | json, str, dict | The JSON data to modify | `{}` | | `path` | str | Dot notation path to replace (e.g., 'user.name', 'items[0].title') | `""` | | `replacement_value` | json, str, dict | The new value to put at the specified path | `""` | ### Output Parameters | Parameter | Type | Description | | --------- | ---- | -------------------------------------------- | | `output` | json | The modified JSON with the replacement value | ## Features - **Deep Copy Protection**: Creates a deep copy to avoid modifying the original data - **Path Creation**: Automatically creates missing paths if they don't exist - **Array Support**: Handles array indexing like `items[0].name` - **Real-time Updates**: Updates automatically when any input parameter changes - **Safe Operations**: Graceful handling of invalid paths or data types ## Path Syntax ### Basic Object Replacement ``` # Replace nested object properties path = "user.name" # Replace user's name path = "user.profile.email" # Replace nested email ``` ### Array Element Replacement ``` # Replace array elements path = "items[0]" # Replace first item path = "items[0].title" # Replace title of first item path = "users[2].name" # Replace name of third user ``` ### Complex Path Replacement ``` # Replace in nested arrays path = "orders[0].items[1].price" # Replace price of second item in first order ``` ## Examples ### Basic Value Replacement ``` # Original JSON: {"user": {"name": "John", "age": 30}} # Path: "user.name" # Replacement Value: "Jane" # Output: {"user": {"name": "Jane", "age": 30}} ``` ### Array Element Replacement ``` # Original JSON: {"items": [{"title": "Book", "price": 25}, {"title": "Magazine", "price": 10}]} # Path: "items[0].title" # Replacement Value: "Novel" # Output: {"items": [{"title": "Novel", "price": 25}, {"title": "Magazine", "price": 10}]} ``` ### Creating New Paths ``` # Original JSON: {"user": {"name": "John"}} # Path: "user.email" # Replacement Value: "john@example.com" # Output: {"user": {"name": "John", "email": "john@example.com"}} ``` ### Nested Array Replacement ``` # Original JSON: {"orders": [{"items": [{"name": "Product A"}, {"name": "Product B"}]}]} # Path: "orders[0].items[1].name" # Replacement Value: "Product C" # Output: {"orders": [{"items": [{"name": "Product A"}, {"name": "Product C"}]}]} ``` ### Extending Arrays ``` # Original JSON: {"items": [{"title": "Book"}]} # Path: "items[2].title" # Replacement Value: "Magazine" # Output: {"items": [{"title": "Book"}, null, {"title": "Magazine"}]} ``` ## Use Cases - **Data Updates**: Modify specific fields in configuration or user data - **API Integration**: Update request payloads with new values - **Data Transformation**: Modify JSON structure for different systems - **Template Processing**: Fill in template JSON with dynamic values - **Configuration Management**: Update settings in JSON configuration files ## Related Nodes - [JSON Input](https://docs.griptapenodes.com/nodes/json/json_input/index.md) - Create JSON data from inputs - [JSON Extract Value](https://docs.griptapenodes.com/nodes/json/json_extract_value/index.md) - Extract values from JSON - [Display JSON](https://docs.griptapenodes.com/nodes/json/display_json/index.md) - Display and format JSON data - [To JSON](https://docs.griptapenodes.com/nodes/convert/to_json/index.md) - Convert other data types to JSON # AddToList ## What is it? The AddToList node is a utility node that adds an item to an existing list at a specified position. It allows you to insert new items into lists at the beginning, end, or at a specific index. ## When would I use it? Use the AddToList node when: - You need to add new items to an existing list - You want to insert items at specific positions in a list - You're building dynamic lists that need to be updated during workflow execution - You need to maintain ordered collections of items - You want to modify lists without creating new ones ## How to use it ### Basic Setup 1. Add an AddToList node to your workflow 1. Connect an existing list to the "items" input 1. Connect the item you want to add to the "item" input 1. Choose where to add the item using the "position" parameter 1. If using "index" position, specify the index value ### Parameters - **items**: The list to add the item to (list input) - **item**: The item to add to the list (any type) - **position**: Where to add the item (choices: "start", "end", "index") - **index**: The specific position to add the item (only visible when position is "index") - **output**: The modified list with the new item added ### Outputs - **output**: The list containing the original items plus the newly added item ## Example Imagine you want to add a new item to a list of tasks: 1. Add an AddToList node to your workflow 1. Connect your existing task list to the "items" input 1. Connect the new task to the "item" input 1. Set "position" to "end" to add it at the end of the list 1. The output will be the original list with the new task appended ## Important Notes - The node creates a new list rather than modifying the original - When using "index" position, the index must be within the list's bounds - The "index" parameter is only visible when "position" is set to "index" - Items can be of any type, but be aware of type compatibility with the receiving node ## Common Issues - **Invalid Index**: If using "index" position, ensure the index is within the list's bounds - **Empty List**: If the input list is empty, the item will be added as the first element - **Type Mismatch**: Be aware of how the receiving node handles different data types in the list # Combine Lists The Combine Lists node takes two lists and combines them into a single flattened list. ## Inputs - **List A** (list): The first list to combine - **List B** (list): The second list to combine ## Outputs - **Output** (list): The combined list containing all items from both input lists in order ## Example ``` # List A: [1, 2] # List B: [3, 4] # Output: [1, 2, 3, 4] # List A: ["a", "b"] # List B: ["c", "d"] # Output: ["a", "b", "c", "d"] # List A: [1, 2] # List B: [] # Output: [1, 2] ``` ## Notes - The order of items is preserved in the output list - If either input is not a list, it will be treated as an empty list - The original lists are not modified; a new list is returned - The operation is equivalent to list concatenation (list_a + list_b) # Create Bool List The Create Bool List node allows you to create a list of boolean values. ## Inputs - **Items** (bool[]): A list of boolean values to include in the output list ## Outputs - **Output** (list): The created list of boolean values ## Example ``` # Items: [True, False, True] # Output: [True, False, True] # Items: [False, True] # Output: [False, True] ``` ## Notes - All items in the input list must be boolean values - The order of items is preserved in the output list - The node can accept both input and property modes for the items parameter # Create Float List The Create Float List node allows you to create a list of float values. ## Inputs - **Items** (float[]): A list of float values to include in the output list ## Outputs - **Output** (list): The created list of float values ## Example ``` # Items: [1.5, 2.7, 3.14] # Output: [1.5, 2.7, 3.14] # Items: [-0.5, 0.0, 42.0] # Output: [-0.5, 0.0, 42.0] ``` ## Notes - All items in the input list must be float values - The order of items is preserved in the output list - The node can accept both input and property modes for the items parameter # Create Int List The Create Int List node allows you to create a list of integer values. ## Inputs - **Items** (int[]): A list of integer values to include in the output list ## Outputs - **Output** (list): The created list of integer values ## Example ``` # Items: [1, 2, 3] # Output: [1, 2, 3] # Items: [-5, 0, 42] # Output: [-5, 0, 42] ``` ## Notes - All items in the input list must be integer values - The order of items is preserved in the output list - The node can accept both input and property modes for the items parameter # CreateList ## What is it? The CreateList node is a utility node that creates a list from provided items. It allows you to build lists dynamically in your workflow by combining multiple inputs into a single list output. ## When would I use it? Use the CreateList node when: - You need to combine multiple items into a single list - You want to create a list from various inputs in your workflow - You're building data structures that require list format - You need to prepare data for nodes that require list inputs - You want to consolidate multiple values into a single list structure ## How to use it ### Basic Setup 1. Add a CreateList node to your workflow 1. Connect individual items to the "items" input or add them directly in the node's properties 1. The node will combine all inputs into a single list output ### Parameters - **items**: A list of items to combine (supports multiple inputs of any type) - **output**: The combined list containing all input items ### Outputs - **output**: A list containing all the items provided as input ## Example Imagine you want to create a list of different data types: 1. Add a CreateList node to your workflow 1. Connect various inputs to the "items" parameter (e.g., text, numbers, or other data types) 1. The output will be a list containing all these items in the order they were connected ## Important Notes - The CreateList node can handle items of any type - Items are added to the list in the order they are connected - The node automatically updates its output whenever input items change - You can add items both through connections and directly in the node's properties ## Common Issues - **Empty List**: If no items are provided, the output will be an empty list - **Type Mismatch**: The node accepts items of any type, but be aware of how the receiving node handles different data types # Create Text List The Create Text List node allows you to create a list of text values. ## Inputs - **Items** (string[]): A list of text values to include in the output list ## Outputs - **Output** (list): The created list of text values ## Example ``` # Items: ["Hello", "World", "!"] # Output: ["Hello", "World", "!"] # Items: ["First", "Second", "Third"] # Output: ["First", "Second", "Third"] ``` ## Notes - All items in the input list must be text values - The order of items is preserved in the output list - The node can accept both input and property modes for the items parameter # DisplayList ## What is it? The DisplayList node is a utility node that takes a list as input and creates individual output parameters for each item in the list. It automatically determines the type of each item and creates appropriately typed output parameters. ## When would I use it? Use the DisplayList node when: - You need to access individual items from a list separately - You want to display or process list items individually - You're working with lists of mixed types and need type-specific handling - You need to connect list items to different nodes - You want to visualize the contents of a list in a more detailed way ## How to use it ### Basic Setup 1. Add a DisplayList node to your workflow 1. Connect a list to the "items" input 1. The node will automatically create output parameters for each item 1. Connect the individual item outputs to other nodes as needed ### Parameters - **items**: The list to display (list input) - **item_0, item_1, etc.**: Dynamic output parameters for each item in the list ### Outputs - **item_0, item_1, etc.**: Individual outputs for each item in the list, with types matching the items ## Example Imagine you have a list of mixed data types that you want to process separately: 1. Add a DisplayList node to your workflow 1. Connect your list to the "items" input 1. The node will create separate outputs for each item 1. Connect each output to the appropriate processing node based on its type ## Important Notes - The node automatically determines the type of each item - Output parameters are created dynamically based on the list contents - Supported types include: string, boolean, integer, float, and image artifacts - The node updates its outputs whenever the input list changes - Output parameters are named sequentially (item_0, item_1, etc.) ## Common Issues - **Empty List**: If the input list is empty, no output parameters will be created - **Invalid Input**: If the input is not a list, no output parameters will be created - **Type Limitations**: Some complex types may be handled as "any" type - **Dynamic Updates**: The number of output parameters changes with the list length # Get From List The Get From List node takes a list and an index as input and returns the item at that index in the list. ## Inputs - **items** (list): The list to get an item from - **index** (int): The index to get the item from ## Outputs - **item** (any): The item at the specified index in the list. Returns None if the index is invalid or inputs are missing. ## Example ``` # Input list: [1, 2, 3, 4, 5] # Input index: 2 # Output: 3 ``` ## Notes - If the index is out of range or invalid, the node will return None - If either the list or index input is missing, the node will return None - The output type is "any" since the list can contain items of any type - The index parameter can be set either as an input connection or as a property value # GetIndexOfItem ## What is it? The GetIndexOfItem node is a utility node that finds the position (index) of a specific item in a list. It returns the zero-based index of the first occurrence of the item in the list. ## When would I use it? Use the GetIndexOfItem node when: - You need to find the position of an item in a list - You want to locate specific items in a collection - You're building workflows that need to reference items by their position - You need to validate item positions in lists - You want to track the location of items in dynamic lists ## How to use it ### Basic Setup 1. Add a GetIndexOfItem node to your workflow 1. Connect a list to the "items" input 1. Connect the item you want to find to the "item" input 1. The node will output the index of the item in the list ### Parameters - **items**: The list to search in (list input) - **item**: The item to find in the list (any type) - **index**: The position of the item in the list (integer output) ### Outputs - **index**: An integer representing the position of the item (-1 if not found) ## Example Imagine you want to find the position of a specific task in a task list: 1. Add a GetIndexOfItem node to your workflow 1. Connect your task list to the "items" input 1. Connect the task you want to find to the "item" input 1. The "index" output will show the position of the task in the list ## Important Notes - The node returns -1 if the item is not found in the list - Index positions start at 0 (first item is at index 0) - The node finds the first occurrence of the item if it appears multiple times - The node automatically updates when either the list or the item changes ## Common Issues - **Item Not Found**: If the item is not in the list, the output will be -1 - **No Input**: If either the list or item is not connected, the output will be -1 - **Invalid Input**: If the input is not a list, the output will be -1 - **Type Mismatch**: The item type must match the list item types for proper comparison - **Case Sensitivity**: String comparisons are case-sensitive # GetListContainsItem ## What is it? The GetListContainsItem node is a utility node that checks whether a specific item exists in a list. It provides a boolean output indicating if the item is found in the input list. ## When would I use it? Use the GetListContainsItem node when: - You need to check if a specific item exists in a list - You want to validate that required data is present in a collection - You're building conditional workflows based on item presence - You need to filter or process lists based on item existence - You want to monitor for specific items in dynamic lists ## How to use it ### Basic Setup 1. Add a GetListContainsItem node to your workflow 1. Connect a list to the "items" input 1. Connect the item you want to check for to the "item" input 1. The node will automatically check and output whether the item exists in the list ### Parameters - **items**: The list to check (list input) - **item**: The item to look for in the list (any type) - **contains_item**: Boolean indicating if the item was found (true) or not (false) ### Outputs - **contains_item**: A boolean value (true if the item is found, false if not) ## Example Imagine you want to check if a specific task exists in a task list: 1. Add a GetListContainsItem node to your workflow 1. Connect your task list to the "items" input 1. Connect the task you want to check for to the "item" input 1. The "contains_item" output will be true if the task is found, false if not ## Important Notes - The node automatically updates when either the list or the item changes - If either the list or item is not provided, the output will be false - The node uses exact matching to find items - The output is always a boolean value ## Common Issues - **No Input**: If either the list or item is not connected, the output will be false - **Invalid Input**: If the input is not a list, the output will be false - **Type Mismatch**: The item type must match the list item types for proper comparison - **Case Sensitivity**: String comparisons are case-sensitive # GetListIsEmpty ## What is it? The GetListIsEmpty node is a utility node that checks whether a list is empty or not. It provides a boolean output indicating if the input list contains any items. ## When would I use it? Use the GetListIsEmpty node when: - You need to check if a list has any items - You want to validate that a list is not empty before processing - You're building conditional workflows based on list content - You need to ensure data is available before proceeding - You want to monitor the state of dynamic lists ## How to use it ### Basic Setup 1. Add a GetListIsEmpty node to your workflow 1. Connect any list to the "items" input 1. The node will automatically check and output whether the list is empty ### Parameters - **items**: The list to check (list input) - **is_empty**: Boolean indicating if the list is empty (true) or not (false) ### Outputs - **is_empty**: A boolean value (true if the list is empty, false if it contains items) ## Example Imagine you want to check if a task list has any pending tasks: 1. Add a GetListIsEmpty node to your workflow 1. Connect your task list to the "items" input 1. The "is_empty" output will be true if there are no tasks, false if there are tasks ## Important Notes - The node automatically updates when the input list changes - If no list is connected, the output will be true (empty) - The node works with lists of any type of items - The output is always a boolean value ## Common Issues - **No Input**: If no list is connected, the output will be true (empty) - **Invalid Input**: If the input is not a list, the output will be true (empty) - **Type Mismatch**: The node only works with list inputs, other types will result in true (empty) # GetListLength ## What is it? The GetListLength node is a utility node that calculates and outputs the number of items in a list. It provides a simple way to determine the size of any list in your workflow. ## When would I use it? Use the GetListLength node when: - You need to know how many items are in a list - You want to check if a list is empty or has a specific number of items - You're building workflows that depend on list size - You need to validate list lengths before processing - You want to monitor the size of dynamic lists ## How to use it ### Basic Setup 1. Add a GetListLength node to your workflow 1. Connect any list to the "items" input 1. The node will automatically calculate and output the list's length ### Parameters - **items**: The list to measure (list input) - **length**: The number of items in the list (integer output) ### Outputs - **length**: An integer representing the number of items in the input list ## Example Imagine you want to check how many tasks are in a task list: 1. Add a GetListLength node to your workflow 1. Connect your task list to the "items" input 1. The "length" output will show the total number of tasks in the list ## Important Notes - The node automatically updates the length when the input list changes - If the input list is empty or not provided, the length will be 0 - The length is always a non-negative integer - The node works with lists of any type of items ## Common Issues - **Empty List**: If no list is connected, the length will be 0 - **Invalid Input**: If the input is not a list, the length will be 0 - **Type Mismatch**: The node only works with list inputs, other types will result in a length of 0 # RemoveFromList ## What is it? The RemoveFromList node is a utility node that removes items from an existing list based on different criteria. It can remove items by their position (first, last, or specific index) or by matching a specific item value. ## When would I use it? Use the RemoveFromList node when: - You need to remove items from an existing list - You want to filter out specific items from a collection - You're maintaining dynamic lists that need items removed during workflow execution - You need to clean up or modify lists by removing unwanted elements - You want to extract items from specific positions in a list ## How to use it ### Basic Setup 1. Add a RemoveFromList node to your workflow 1. Connect an existing list to the "items" input 1. Choose how to remove items using the "remove_item_by" parameter 1. Depending on the removal method, provide either an index or item value 1. The output will be the list with the specified item removed ### Parameters - **items**: The list to remove items from (list input) - **remove_item_by**: How to remove the item (choices: "first", "last", "index", "item") - **item**: The specific item to remove (only visible when remove_item_by is "item") - **index**: The position to remove from (only visible when remove_item_by is "index") - **output**: The modified list with the item removed ### Outputs - **output**: The list containing the original items minus the removed item ## Example Imagine you want to remove a specific task from a list of tasks: 1. Add a RemoveFromList node to your workflow 1. Connect your task list to the "items" input 1. Set "remove_item_by" to "item" 1. Connect the task you want to remove to the "item" input 1. The output will be the original list with the specified task removed ## Important Notes - The node creates a new list rather than modifying the original - When using "index" removal, the index must be within the list's bounds - When using "item" removal, the item must exist in the list - The "index" and "item" parameters are only visible when their respective removal methods are selected ## Common Issues - **Invalid Index**: If using "index" removal, ensure the index is within the list's bounds - **Item Not Found**: When using "item" removal, the item must exist in the list - **Empty List**: If the input list is empty, no items can be removed - **Type Mismatch**: When using "item" removal, ensure the item type matches the list items # Replace In List The Replace In List node allows you to replace an item in a list either by matching the item or by its index. ## Inputs - **Items** (list): The list to modify - **Item To Replace** (any): The item to replace in the list (shown when Replace By is "item") - **Index To Replace** (int): The index of the item to replace (shown when Replace By is "index") - **New Item** (any): The new item to replace with ## Properties - **Replace By** (str): How to identify the item to replace - Options: - "item": Replace by matching the item value - "index": Replace by index position ## Outputs - **Output** (list): The modified list with the item replaced ## Example ``` # Input list: [1, 2, 3, 4] # Replace by item: # Item To Replace: 3 # New Item: "three" # Output: [1, 2, "three", 4] # Replace by index: # Index To Replace: 1 # New Item: "two" # Output: [1, "two", 3, 4] ``` ## Notes - When replacing by item, the first occurrence of the item will be replaced - When replacing by index, the index must be within the bounds of the list - If the item is not found when replacing by item, no changes will be made - The original list is not modified; a new list is returned # Select From List Select an item from a list of strings with a dropdown interface. ## Description The Select From List node allows users to choose a single item from a list of strings using a dropdown interface. It automatically updates its choices when the input list changes and preserves the current selection when possible. ## Parameters ### Input Parameters | Parameter | Type | Description | Default | | --------- | ---- | ---------------------------- | ------- | | `list` | list | List of items to select from | `[]` | ### Output Parameters | Parameter | Type | Description | | --------------- | ------ | --------------------------- | | `selected_item` | string | The currently selected item | ## Features - **Dynamic Dropdown**: Automatically populates dropdown with items from the input list - **Selection Preservation**: Maintains current selection when list updates (if item still exists) - **String Conversion**: Converts all list items to strings for consistent comparison - **Real-time Updates**: Updates immediately when input list changes - **Safe Handling**: Gracefully handles empty or invalid lists ## Examples ### Basic List Selection ``` # Input list: ["Apple", "Banana", "Cherry", "Date"] # User selects: "Banana" # Output: "Banana" ``` ### Status Selection ``` # Input list: ["In Progress", "Not Started", "Waiting", "Completed", "Blocked"] # User selects: "Completed" # Output: "Completed" ``` ### Dynamic List Updates ``` # Initial list: ["Option A", "Option B", "Option C"] # User selects: "Option B" # List updates to: ["Option A", "Option B", "Option D", "Option E"] # Selection preserved: "Option B" (still exists in new list) # If list updates to: ["Option X", "Option Y", "Option Z"] # Selection resets to: "Option X" (first item in new list) ``` ## Use Cases - **Status Selection**: Choose from predefined status values (e.g., "In Progress", "Completed") - **Category Selection**: Select from a list of categories or types - **Configuration Choices**: Pick from available configuration options - **User Interface**: Provide dropdown selection in workflows - **Data Filtering**: Select specific items from generated lists ## Workflow Examples ### Status Management Workflow ``` Create Text List → Select From List → Process Selected Status ↓ ↓ ["In Progress", "Completed" → Update task status "Not Started", "Waiting", "Completed", "Blocked"] ``` ### Dynamic Option Selection ``` API Response → Extract Options → Select From List → Use Selection ↓ ↓ ↓ JSON data → ["Option 1", → "Option 2" → Process choice "Option 2", "Option 3"] ``` ## Behavior ### Selection Logic 1. **Initial Load**: Selects the first item in the list 1. **List Update**: - If current selection exists in new list → keeps current selection - If current selection doesn't exist → selects first item in new list 1. **Empty List**: Clears selection (empty string) 1. **Invalid Input**: Clears selection (empty string) ### String Conversion All list items are automatically converted to strings for consistent comparison: - Numbers: `123` → `"123"` - Booleans: `True` → `"True"` - Objects: `{"key": "value"}` → `"{'key': 'value'}"` ## Related Nodes - [Create Text List](https://docs.griptapenodes.com/nodes/lists/create_text_list/index.md) - Generate a list of text items - [Create List](https://docs.griptapenodes.com/nodes/lists/create_list/index.md) - Create lists from various inputs - [Get From List](https://docs.griptapenodes.com/nodes/lists/get_from_list/index.md) - Get item by index from list - [Get Index Of Item](https://docs.griptapenodes.com/nodes/lists/get_index_of_item/index.md) - Find index of specific item - [Display List](https://docs.griptapenodes.com/nodes/lists/display_list/index.md) - Display list contents # Split List The Split List node allows you to split a list into two parts either by index or by matching an item value. ## Inputs - **Items** (list): The list to split - **Split Index** (int): The index to split the list at (shown when Split By is "index") - **Split Item** (any): The item to split the list at (shown when Split By is "item") ## Properties - **Split By** (str): How to split the list - Options: - "index": Split at a specific index position - "item": Split at a specific item value - **Keep Split Item** (bool): Whether to keep the split item in the second list (shown when Split By is "item") ## Outputs - **Output A** (list): The first part of the split list - **Output B** (list): The second part of the split list ## Example ``` # Input list: [1, 2, 3, 4] # Split by index: # Split Index: 2 # Output A: [1, 2] # Output B: [3, 4] # Split by item: # Split Item: 3 # Keep Split Item: True # Output A: [1, 2] # Output B: [3, 4] # Split by item: # Split Item: 3 # Keep Split Item: False # Output A: [1, 2] # Output B: [4] ``` ## Notes - When splitting by index, the index is included in the second list - When splitting by item, you can choose whether to keep the split item in the second list - If the item is not found when splitting by item, no split will occur - The index must be within the bounds of the list when splitting by index - The original list is not modified; new lists are returned # Askulator ## What is it? The Askulator is a natural language calculator node that can understand and solve mathematical problems expressed in plain English. It uses an AI model to interpret the question, perform the necessary calculations, and provide both the reasoning and the final answer. ## When would I use it? Use this node when you want to: - Solve mathematical problems expressed in natural language - Get step-by-step reasoning for calculations - Handle complex or ambiguous mathematical questions - Perform calculations that require interpretation or estimation - Get creative or approximate answers when exact numbers aren't available ## How to use it ### Basic Setup 1. Add the Askulator to your workflow 1. Enter your mathematical question in the "instruction" parameter 1. The node will process the question and provide: - A detailed reasoning of how it solved the problem - The final answer in the "result" output ### Parameters - **instruction**: The mathematical question or problem to solve (supports natural language) - **model**: The AI model to use for processing (default: gpt-4.1-mini) - **result**: The final calculated answer - **output**: The detailed reasoning and steps taken to solve the problem ### Outputs - **result**: The final calculated answer - **output**: The detailed reasoning and steps taken to solve the problem ## Example Imagine you want to calculate something complex like "If I have 3 dozen eggs and give away a third, how many do I have left?": 1. Add an Askulator to your workflow 1. Enter the question in the "instruction" parameter 1. The node will: - Interpret that 3 dozen = 36 eggs - Calculate that a third of 36 is 12 - Subtract 12 from 36 - Provide the reasoning in the "output" - Give the final answer (24) in the "result" ## Implementation Details The Askulator uses: - A natural language processing model to understand questions - The Calculator tool to perform precise calculations - JSON output format to separate reasoning from the final answer - Streaming updates to show progress in real-time The node is designed to be creative and helpful, making reasonable estimates when exact numbers aren't available and providing clear explanations of its reasoning. # BoolInput The BoolInput node allows you to create a boolean value. ## Inputs - **Value** (bool): The boolean value to create - Default: False ## Outputs - **Output** (bool): The created boolean value ## Example ``` # Value: True # Output: True # Value: False # Output: False ``` ## Notes - The node can accept both input and property modes for the value parameter - The default value is False if not specified - The value is immediately forwarded to the output when changed # FloatInput ## What is it? The FloatInput node is a simple way to input floating-point number values. ## When would I use it? Use this node when you want to: - Define a numerical value with decimal precision - Create a configurable number parameter for your workflow - Set up values for mathematical operations - Establish thresholds, rates, or other decimal values ## How to use it ### Basic Setup 1. Add a FloatInput node to your workflow 1. Set your desired float value ### Parameters - **float**: A floating-point number value (default is 0.0) ### Outputs - **float**: The floating-point value that can be used by other nodes ## Example Imagine you want to set a temperature value for an AI model: 1. Add a FloatInput node to your workflow 1. Set the "float" value to 0.7 1. Connect the output to a model's temperature parameter 1. The AI model will now use 0.7 as its temperature setting ## Important Notes - This node creates a single floating-point value - The default value is 0.0 if none is specified - You can update the value through the node's properties - Floating-point numbers can represent decimal values like 0.5, 3.14159, or -2.75 ## Common Issues - None # IntegerInput ## What is it? The IntegerInput node is a simple way to input integer (whole) number values. ## When would I use it? Use this node when you want to: - Define a whole number value without decimals - Create a configurable integer parameter for your workflow - Set up counters, indices, or quantity values - Establish limits, thresholds, or other whole-number values ## How to use it ### Basic Setup 1. Add an IntegerInput node to your workflow 1. Set your desired integer value ### Parameters - **integer**: A whole number value (default is 0) ### Outputs - **integer**: The integer value that can be used by other nodes ## Example Imagine you want to set a maximum token limit for an AI response: 1. Add an IntegerInput to your workflow 1. Set the "integer" value to 500 1. Connect the output to a model's max_tokens parameter 1. The AI model will now limit its responses to 500 tokens ## Important Notes - This node creates a single integer value - The default value is 0 if none is specified - You can update the value through the node's properties - Integers represent whole numbers like 1, 42, -7, or 0 # DisplayFloat ## What is it? The DisplayFloat node simply displays a float parameter. ## When would I use it? When you want to inspect a floating-point number value in your graph ## How to use it ### Basic Setup 1. Add the DisplayFloat to your workflow 1. Connect any other node's float output to this input ### Parameters - **float**: A single floating-point number that is displayed by this node (default is 0.0) ### Outputs - **None** ## Example Imagine you want to visualize the temperature setting used in your workflow: 1. Add a DisplayFloat to your workflow 1. Connect the "temperature" output from another node to the DisplayFloat's input 1. The DisplayFloat will now show the current temperature value in your graph ## Important Notes - This node is for visualization purposes only - It doesn't modify the value, just displays it - Useful for debugging and monitoring your workflow ## Common Issues - None # DisplayInteger ## What is it? The DisplayInteger node simply displays an integer parameter. ## When would I use it? Use this node/class when you want to: - When you want to inspect an integer value in your graph ## How to use it ### Basic Setup 1. Add the DisplayInteger to your workflow 1. Connect any other node's integer output to this input ### Parameters - **integer**: A single integer value that is displayed by this node (default is 0) ### Outputs - **None** ## Example Imagine you want to visualize the token count used in your workflow: 1. Add a DisplayInteger to your workflow 1. Connect the "token_count" output from another node to the DisplayInteger's input 1. The DisplayInteger will now show the current token count in your graph ## Important Notes - This node is for visualization purposes only - It doesn't modify the value, just displays it - Useful for debugging and monitoring your workflow ## Common Issues - None # Math Operations ## What is it? The Math node is a versatile mathematical operations node that can perform various arithmetic and mathematical functions on numbers. It supports both unary operations (single input) and binary operations (two inputs). ## When would I use it? Use this node when you need to: - Perform basic arithmetic operations (addition, subtraction, multiplication, division) - Calculate mathematical functions (square root, absolute value, sine) - Round numbers (round, ceil, floor) - Find minimum/maximum values - Calculate averages - Perform modulo operations - Calculate powers ## How to use it ### Basic Setup 1. Add the Math node to your workflow 1. Select the operation you want to perform from the dropdown menu 1. Connect input values to parameters A and B (B is only needed for binary operations) 1. The result will be available in the "result" output ### Available Operations #### Binary Operations (require both A and B inputs) - Add (A + B) - Subtract (A - B) - Multiply (A * B) - Divide (A / B) - Modulo (A % B) - Power (A ^ B) - Average (avg(A, B)) - Min (min(A, B)) - Max (max(A, B)) #### Unary Operations (only require A input) - Square Root (√A) - Round (round(A)) - Ceiling (⌈A⌉) - Floor (⌊A⌋) - Absolute Value (|A|) - Sine (sin(A)) ### Outputs - **result**: The calculated result of the mathematical operation ## Example Imagine you want to calculate the average of two numbers: 1. Add a Math node to your workflow 1. Select "average [avg(A, B)]" from the operation dropdown 1. Connect two number inputs to parameters A and B 1. The average will be available in the "result" output ## Implementation Details The Math node automatically handles: - Type conversion for inputs - Division by zero (returns infinity) - Modulo by zero (returns infinity) - Dynamic parameter visibility (hides B parameter for unary operations) # Ruleset ## What is it? The Ruleset Node allows you to create and output a Ruleset object. Rulesets consist of a name and a list of rules, which can be used with other nodes like agents in your workflow. ## When would I use it? Use this node when you want to: - Constrain or direct the output of another node that generates responses from an LLM ## How to use it ### Basic Setup 1. Add the Ruleset to your workflow 1. Set the "name" parameter to define the name for your ruleset (optional, defaults to "Behavior") 1. Set the "rules" parameter to define the list of rules for your ruleset (optional, defaults to an empty string, comma separated) 1. Connect it to your flow (specifically into nodes that have a Ruleset parameter) ### Parameters - **name**: A single parameter that is created and output by this node - **rules**: A single parameter that is created and output by this node ### Outputs - **ruleset**: The output Ruleset object as a property or input for another node ## Example Imagine you want to create a new ruleset with the following name and rules: 1. Add a Ruleset to your workflow 1. Set the "name" parameter to: ``` MyBehavioralRules ``` 1. Set the "rules" parameter to: ``` Rule 1: This is my first rule. Rule 2: This is my second rule. Rule 3: This is my third rule. ``` 1. Connect the output of this node to an agent's Ruleset parameter ## Important Notes - The Ruleset Node supports string values only - If no initial value is provided, default values are used for name and rules - This node can be used in conjunction with other nodes to create dynamic Ruleset objects based on other parameters or properties # RulesetList ## What is it? The RulesetList node combines multiple rulesets into a single list. This allows for more complex behaviors to be defined for an agent by grouping multiple rulesets together. ## When would I use it? Use the RulesetList node when: - You need to combine multiple rulesets to create more complex agent behaviors - You want to organize different sets of rules into a single collection - You're building an agent that needs to follow multiple sets of rules simultaneously - You want to modularly combine different rule systems ## How to use it ### Basic Setup 1. Add a RulesetList node to your workflow 1. Connect up to four individual ruleset nodes to the input parameters (ruleset_1, ruleset_2, ruleset_3, ruleset_4) 1. Connect the output (rulesets) to any node that accepts a list of rulesets as input ### Parameters - **ruleset_1**: The first ruleset to add to the combined list - **ruleset_2**: The second ruleset to add to the combined list - **ruleset_3**: The third ruleset to add to the combined list - **ruleset_4**: The fourth ruleset to add to the combined list ### Outputs - **rulesets**: A combined list containing all non-null input rulesets ## Example A common use case is combining multiple specialized rulesets for an agent: 1. Add a Ruleset node to your workflow, name it "Conversation Rules" 1. Add a Ruleset node to your workflow, name it "Task Rules" 1. Add a RulesetList node to your workflow 1. Connect a "Conversation Rules" ruleset to your RulesetList node's ruleset_1 1. Connect a "Task Rules" ruleset to your RulesetList node's ruleset_2 1. Connect the output (rulesets) to an Agent node's ruleset input # TextInput ## What is it? The TextInput node is a simple way to input text for use in workflows. ## When would I use it? Use the TextInput node when: - You need to create text content - You want to prepare text input for other nodes in your workflow - You need to define prompts, instructions, or other multi-paragraph content - You're building workflows that require structured text input ## How to use it ### Basic Setup 1. Add a TextInput node to your workflow 1. Set the "text" parameter with your desired multiline content 1. Connect the output to nodes that accept text input ### Parameters - **text**: The multiline text content (string, defaults to empty string) ### Outputs - **text**: The multiline text content as a string ## Example A workflow to create a prompt for an AI assistant: 1. Add a TextInput node to your workflow 1. Set the "text" parameter to: ``` You are a helpful tour guide assistant. Please provide information about the following location: - History of the place - Main attractions - Best time to visit - Local cuisine ``` 1. Connect the output to an Agent node's prompt parameter ## Important Notes - Line breaks are preserved in the output - The node supports any valid string content - Empty strings are valid input and will produce an empty string output - There is no character limit, but extremely large text blocks may impact performance ## Common Issues - Text formatting issues when copying from different sources (check for unexpected characters) - Line breaks might render differently in different env # Date and Time ## What is it? The Date and Time tool is a utility that helps you format and manipulate date and time information as text. ## When would I use it? Use this node when you want to: - Format dates and times in specific text formats - Convert between different date/time formats - Generate date/time strings for use in text - Create time-based text content ## How to use it ### Basic Setup 1. Add the Date and Time tool to your workflow 1. Configure the date/time parameters 1. Connect its output to nodes that need formatted date/time text ### Parameters - **prompt**: The thing you'd like to get the date for (e.g. "Christmas, 2027", "Next friday") - **format**: The format for the date/time (e.g., "%Y-%m-%d %H:%M:%S", "Fri 25, March, 9pm") ### Outputs - **output**: The formatted date/time as text ## Example Imagine you want to format a date in a specific way: 1. Add a Date and Time tool to your workflow 1. Set the desired date format 1. Connect the "output" to another node that needs the formatted date/time ## Implementation Details The Date and Time tool provides flexible date and time formatting capabilities. It supports various format strings and timezone handling to generate properly formatted date/time text. # DisplayText ## What is it? The DisplayText node simply displays text content in your workflow. ## When would I use it? Use the DisplayText node when: - You need to display results or information in your workflow - You want to show text output from previous processing steps - You need a placeholder for text content that will be updated during workflow execution - You want to create readable labels or descriptions within your workflow - You need to visualize string data at specific points in your process ## How to use it ### Basic Setup 1. Add a DisplayText node to your workflow 1. Set the initial text value if desired 1. Connect inputs to the text parameter or manually enter text 1. Connect the text output to other nodes that require text input ### Parameters - **text**: The text content to display (string input, supports multiline text) ### Outputs - **text**: The same text content, available as output to connect to other nodes ## Example Imagine you're building a workflow that processes and displays information: 1. Add a DisplayText node to your workflow 1. Connect the output from a RunAgent node to the DisplayText node's text parameter 1. When the workflow runs, the AI-generated content will replace the initial text ## Important Notes - The DisplayText node is for visualization and doesn't modify the text content - The node passes through text exactly as received, without any processing or formatting # Evaluate Text Result The Evaluate Text Result node allows you to evaluate text outputs against specific criteria using Griptape's Eval Engine. This node is useful for validating AI-generated content, checking factual accuracy, or assessing the quality of text outputs. ## Inputs - **Examples** (Property): Choose from preset examples or create your own evaluation - Options: - Choose a preset.. - Paraphrase - Factual - Analogy - **Input** (Input/Property): The input text to be evaluated - Supports multiline text input - **Expected Output** (Input/Property): The expected or reference output text - Single line text input - **Actual Output** (Input/Property): The actual output text to be evaluated - Single line text input - **Criteria** (Input/Property): The evaluation criteria to use - Supports multiline text input - Example: "Does the output accurately paraphrase the input without losing meaning?" ## Outputs - **Score** (Output): A float value between 0 and 1 representing the evaluation score - 1.0 indicates perfect match - 0.0 indicates complete mismatch - **Reason** (Output): A detailed explanation of the evaluation result - Provides feedback on why the score was given - Explains any discrepancies found ## Example Usage ### Paraphrase Evaluation ``` Input: "The quick brown fox jumps over the lazy dog." Expected Output: "A swift brown fox leaps above a sleeping dog." Actual Output: "A fast fox jumps over a dog that's not awake." Criteria: "Does the output accurately paraphrase the input without losing meaning?" ``` ### Factual Evaluation ``` Input: "The capital of France is Paris." Expected Output: "Paris is the capital city of France." Actual Output: "France's capital is Paris." Criteria: "Is the output factually correct based on the input?" ``` ### Analogy Evaluation ``` Input: "A bird is to sky as a fish is to ______." Expected Output: "water" Actual Output: "concrete" Criteria: "Does the output correctly complete the analogy?" ``` ## Notes - The node uses Griptape's Eval Engine to perform the evaluation - The evaluation is based on the provided criteria - The score is normalized between 0 and 1 - The reason provides detailed feedback about the evaluation - You can use preset examples or create your own custom evaluations # LoadText ## What is it? The LoadText node is a utility node that reads text content from a file on your local system. It allows you to import existing text documents into your workflow for processing. ## When would I use it? Use the LoadText node when: - You need to import content from existing text files - You want to process local documents with AI agents - You're working with saved text data like logs, notes, or reports - You need to feed external content into your workflow ## How to use it ### Basic Setup 1. Add a LoadText node to your workflow 1. Set the "path" parameter to the location of your text file 1. Connect the output to nodes that can process text content ### Parameters - **path**: The file path to the text file you want to load (string) ### Outputs - **output**: The content of the file as a text string - **path**: The path to the loaded file (same as the input) ## Example A workflow to analyze the content of a local text file: 1. Add a LoadText node to your workflow 1. Set the "path" parameter to something like "C:/Users/username/Documents/project_notes.txt" 1. Connect the "output" to an Agent node # MergeTexts ## What is it? The MergeTexts node is a utility node that combines multiple text strings into a single unified text output. It allows you to consolidate separate pieces of text with a configurable separator between them. ## When would I use it? Use the MergeTexts node when: - You need to combine text from multiple sources or nodes - You want to join separate pieces of text with consistent formatting - You're building a workflow that generates content from multiple components - You need to create a comprehensive document from individual sections ## How to use it ### Basic Setup 1. Add a MergeTexts node to your workflow 1. Connect multiple text outputs from other nodes to this node's inputs 1. Optionally configure the separator string 1. Connect the output to nodes that require the combined text ### Parameters - **input_1 through input_4**: Fixed number of text inputs to be combined - **merge_string**: The separator to place between text segments (defaults to "\\n\\n" when trim_whitespace is False) - **whitespace**: Boolean option to trim whitespace from each input and the final result ### Outputs - **output**: The combined text result as a single string ## Example A workflow to create a complete document from separate sections: 1. Add a MergeTexts node to your workflow 1. Connect outputs from three different text nodes (e.g., title, body, conclusion) 1. Set the merge_string parameter to "\\n\\n" for paragraph separation 1. The output will contain all text segments combined with the specified separator ## Important Notes - When `whitespace` is enabled (trim mode), each input is trimmed of whitespace before joining - When `whitespace` is enabled and the separator is empty, inputs are concatenated without any separator - When `whitespace` is disabled, empty inputs are filtered out, and the separator defaults to "\\n\\n" if not specified - The separator is only added between inputs, not at the beginning or end - **Recommendation**: Consider using the **JoinText** node for more flexible input handling ## Common Issues - **Unexpected formatting**: Check that your merge_string contains appropriate whitespace - **Missing content**: Verify all input connections are properly established # Random Text The Random Text node allows you to select random content from input text or generate random content when no input is provided. It supports selecting random characters, words, sentences, or paragraphs. ## Parameters ### Input Text - **Type**: String - **Mode**: Input/Property - **Description**: The text to select random content from. If empty, the node will generate random content based on the selection type. - **UI Options**: Multiline enabled ### Seed - **Type**: Integer - **Mode**: Property - **Description**: A seed value (0-10,000) for reproducible random selection. Using the same seed will produce the same random selection. - **UI Options**: Slider with range 0-10,000 ### Selection Type - **Type**: String - **Mode**: Property - **Description**: The type of content to select or generate: - `character`: Selects a random character - `word`: Selects a random word - `sentence`: Selects a random sentence or generates a new one - `paragraph`: Selects a random paragraph or generates a new one ### Output - **Type**: String - **Mode**: Output - **Description**: The randomly selected or generated content - **UI Options**: Multiline enabled ## Behavior - When input text is provided: - The node selects random content from the input based on the selection type - For sentences and paragraphs, it splits the input on appropriate delimiters - If no matching content is found, it falls back to generating new content - When no input text is provided: - For characters: Generates a random character from letters, digits, and punctuation - For words: Generates a random English word - For sentences: Uses an AI agent to generate a natural-sounding sentence - For paragraphs: Uses an AI agent to generate a coherent paragraph ## Examples ### Selecting Random Content ``` # Input text: "Hello world! This is a test. How are you today?" # Selection type: word # Output: "world" (randomly selected word) ``` ### Generating Random Content ``` # Input text: "" (empty) # Selection type: sentence # Output: "The quick brown fox jumps over the lazy dog." (AI-generated sentence) ``` ## Notes - The seed parameter ensures reproducible results when using the same value - The node will show a loading message while generating content with the AI agent # SaveText ## What is it? SaveText is a utility node that allows you to save text content to a file on your local system. This node provides a simple interface for writing text data to disk with a customizable output path. ## When would I use it? Use this node when you want to: - Save generated text content to a file - Export results from your workflow to a text file - Store conversation logs or outputs for later reference - Create text files as part of your automated workflow - Persist data between workflow runs ## How to use it ### Basic Setup 1. Add the SaveText node to your workflow 1. Connect a text source to the "text" input or enter text directly 1. Specify an output path or use the default 1. Run your workflow to save the text to the specified file ### Parameters - **text**: The text content you want to save to a file (supports multiline text) - **output_path**: The file path where the text will be saved (default is "griptape_output.txt") ### Outputs - **output_path**: The path to the saved file (confirms successful save operation) ## Example Imagine you want to save the output of an AI agent to a text file: 1. Add an Agent node to your workflow 1. Set up the agent with a prompt to generate some text 1. Add a SaveText node 1. Connect the Agent's "output" to the SaveText "text" input 1. Set the "output_path" to "my_agent_response.txt" 1. Run the workflow 1. The agent's response will be saved to "my_agent_response.txt" on your local system ## Important Notes - The node will create the file if it doesn't exist, or overwrite it if it does - The output_path parameter includes a save button in the UI for easy file selection - You can use either relative or absolute paths for the output file - The node will create any necessary parent directories if they don't exist - The text is saved with UTF-8 encoding ## Common Issues - **Permission Errors**: Ensure you have write permissions for the specified directory - **Invalid Path**: Make sure the path is valid for your operating system - **File Already Exists**: Be aware that existing files will be overwritten without confirmation # Scrape Web ## What is it? The Scrape Web tool is a utility that allows you to extract text content from web pages. ## When would I use it? Use this node when you want to: - Extract text content from specific web pages - Gather information from websites - Collect data from online sources - Automate web content extraction ## How to use it ### Basic Setup 1. Add the Scrape Web tool to your workflow 1. Configure the scraping parameters 1. Connect its output to nodes that need the scraped content ### Parameters - **url**: The URL of the web page to scrape ### Outputs - **output**: The text content extracted from the web page ## Example Imagine you want to extract content from a specific web page: 1. Add a Scrape Web tool to your workflow 1. Set the URL of the page you want to scrape 1. Connect the "output" to another node that needs the scraped content ## Implementation Details The Scrape Web tool uses web scraping capabilities to extract text content from web pages. It can target specific content using CSS selectors and handles timeouts and errors gracefully. # SearchReplaceText ## What is it? The SearchReplaceText node allows you to perform search and replace operations on multiline text content. It supports both simple text replacement and regular expression-based search and replace. ## When would I use it? Use the SearchReplaceText node when: - You need to modify multiline text by replacing specific patterns - You want to perform case-sensitive or case-insensitive text replacement - You need to use regular expressions for complex pattern matching - You want to replace either all occurrences or just the first occurrence of a pattern - You need to work with text that contains multiple lines or paragraphs ## How to use it ### Basic Setup 1. Add a SearchReplaceText node to your workflow 1. Connect or set the input text (can be multiline) 1. Set the search pattern 1. Set the replacement text (can be multiline) 1. Configure additional options as needed 1. Connect the output to nodes that accept text input ### Parameters - **input_text**: The multiline text to perform search and replace on (string) - **search_pattern**: The text or pattern to search for (string) - **replacement_text**: The multiline text to replace the search pattern with (string) - **case_sensitive**: Whether the search should be case sensitive (boolean, default: true) - **use_regex**: Whether to treat the search pattern as a regular expression (boolean, default: false) - **replace_all**: Whether to replace all occurrences or just the first one (boolean, default: true) ### Outputs - **output**: The multiline text after performing search and replace (string) ## Examples ### Simple Text Replacement 1. Add a SearchReplaceText node to your workflow 1. Set the input text to: ``` Hello World Welcome to Griptape ``` 1. Set the search pattern to: "World" 1. Set the replacement text to: "Griptape" 1. The output will be: ``` Hello Griptape Welcome to Griptape ``` ### Case-Insensitive Replacement 1. Add a SearchReplaceText node to your workflow 1. Set the input text to: ``` Hello WORLD Welcome to the WORLD ``` 1. Set the search pattern to: "world" 1. Set the replacement text to: "Griptape" 1. Set case_sensitive to: false 1. The output will be: ``` Hello Griptape Welcome to the Griptape ``` ### Regular Expression Examples Basic Regex Patterns ``` Input: "Line 1\nLine 2\nLine 3" Search Pattern: "Line \\d" Replacement: "Item" Use Regex: true Output: "Item\nItem\nItem" ``` This pattern matches "Line" followed by any digit (`\d`). Removing Numbers ``` Input: "Product123, Item456, Order789" Search Pattern: "\\d+" Replacement: "" Use Regex: true Output: "Product, Item, Order" ``` This pattern matches one or more digits (`\d+`). Word Boundaries ``` Input: "cat in the hat" Search Pattern: "\\bcat\\b" Replacement: "dog" Use Regex: true Output: "dog in the hat" ``` This pattern matches the word "cat" only when it appears as a complete word. Multiple Lines ``` Input: "First line\nSecond line\nThird line" Search Pattern: "^.*$" Replacement: "New line" Use Regex: true Output: "New line\nNew line\nNew line" ``` This pattern matches entire lines (`^` start, `.*` any characters, `$` end). ## Regex Reference Common Regex Patterns | Pattern | Description | Example | | ------- | -------------------------- | --------------------------------------- | | `\n` | Match a newline | `Line 1\nLine 2` | | `\s` | Match any whitespace | `Hello\sWorld` | | `\d` | Match any digit | `\d+` matches "123" | | `[a-z]` | Match any lowercase letter | `[a-z]+` matches "hello" | | `[A-Z]` | Match any uppercase letter | `[A-Z]+` matches "WORLD" | | `.` | Match any character | `a.c` matches "abc" | | `*` | Match 0 or more | `a*` matches "", "a", "aa" | | `+` | Match 1 or more | `a+` matches "a", "aa" | | `?` | Match 0 or 1 | `a?` matches "", "a" | | `\b` | Word boundary | `\bcat\b` matches "cat" but not "catch" | Regex Mode When using regex mode, special characters in the search pattern are treated as regex syntax. Make sure to escape special characters if you want to match them literally. Plain Text Mode When not using regex mode, the search pattern is treated as literal text, and special characters are escaped automatically. This is safer for simple text replacements. ## Important Notes - When using regular expressions, make sure to properly escape special characters - Case-insensitive search works for both plain text and regular expressions - If the search pattern is not found, the original text is returned unchanged - Invalid regular expressions will result in the original text being returned - The node preserves the original case of the text when doing case-insensitive replacements - Newlines are preserved in both input and output text - When using regex mode, you can use `\\n` to match newlines in the search pattern ## Common Issues - Regular expression syntax errors when use_regex is enabled - Unexpected results when mixing case-sensitive and case-insensitive operations - Performance impact with very large texts and complex regular expressions - Incorrect handling of newlines when not using regex mode ## Additional Resources For more comprehensive regex examples and patterns, check out: - [Python Regular Expression HOWTO](https://docs.python.org/3/howto/regex.html) - [Regex101](https://regex101.com/) - Interactive regex testing and debugging - [Regular-Expressions.info](https://www.regular-expressions.info/) - Detailed regex tutorials # Search Web ## What is it? The Search Web tool is a utility that allows you to search the web and retrieve text content from search results. ## When would I use it? Use this node when you want to: - Search the web for specific information - Retrieve text content from search results - Gather information from multiple web sources - Research topics online ## How to use it ### Basic Setup 1. Add the Search Web tool to your workflow 1. Connect its output to nodes that need web search results ### Parameters - **prompt**: The search query to use - **summarize**: Whether or not you want an LLM to summarize the text - **search_engine**: The search engine to use. Note: some engines require an API key. ### Outputs - **output**: The text content retrieved from the search results ## Example Imagine you want to search for information about a specific topic: 1. Add a Search Web tool to your workflow 1. Set the search query 1. Connect the "output to another node that needs the search results 1. The tool will return the text content from the search results ## Implementation Details The Search Web tool uses web search capabilities to find relevant information and extracts text content from the search results. It's designed to help you gather information from the web in a structured way. # Summarize Text ## What is it? The Summarize Text Task tool is a utility that summarizes text for you. ## When would I use it? Use this node when you want to: - Create tasks for summarizing text content - Define summarization requirements and parameters - Set up structured summarization workflows - Manage text summarization processes ## How to use it ### Basic Setup 1. Add the Summarize Text Task tool to your workflow 1. Configure the summarization parameters 1. Connect its output to nodes that need summarization tasks ### Parameters - **text**: The text content to be summarized ### Outputs - **output**: The summary of the text ## Example Imagine you want to create a summarization task for a long text: 1. Add a Summarize Text Task tool to your workflow 1. Connect the text input to your source text 1. Connect the "output" to a node that can process the summarization task ## Implementation Details The Summarize Text Task tool helps you summarize long amonts of text. # Load3D ## What is it? The Load3D is a building block that lets you bring a GLTF file into your workflow. It allows you to load 3D models from various sources to be used and displayed within your project. ## When would I use it? Use this node when you want to: - Use a GLTF model from a file - Pass a GLTF model to other nodes in your workflow - Display a GLTF model as part of your project - Create a screenshot of a GLTF model to use as part of your workflow ## How to use it ### Basic Setup 1. Add the Load3D node to your workflow 1. Connect it to a source of GLTF data (file browser, URL, or another node output) ### Parameters - **gltf**: The GLTF file or URL to load. This can be connected to an output from another node or selected via the file browser. ### Outputs - **gltf**: The loaded GLTF artifact that can be used by other nodes in your flow. - **image**: An image snapshot of the loaded GLTF model (available after the model is loaded and a snapshot is saved). ## Important Notes - The node can load GLTF files from your computer using the file browser. - Once a GLTF model is loaded, you can save a snapshot of it as an image using the 'Save Snapshot' button (if available in the UI). - The 'image' output parameter will only be available after a snapshot has been saved. ## Common Issues - **No Model Showing**: Make sure you have properly connected a GLTF source to this node or selected a valid file. - **Invalid File Type**: Ensure you are providing a valid GLTF file or URL. - **Image Output Not Available**: The image output is only generated when explicitly saving a snapshot from the UI. Ensure you have triggered this action. # Calculator ## What is it? The Calculator tool is a mathematical calculation tool that can be given to an agent to help it perform precise calculations. ## When would I use it? Use this node when you want to: - Enable agents to perform precise mathematical calculations - Solve complex math problems within your workflow - Process numerical data without writing custom calculation code ## How to use it ### Basic Setup 1. Add the Calculator to your workflow 1. Connect its output to nodes that need calculation capabilities (like an Agent) ### Outputs - **tool**: The configured calculator tool that other nodes can use ## Example Imagine you want to create an agent that can perform calculations: 1. Add a Calculator to your workflow 1. Connect the "tool" output to an Agent's "tools" input 1. Now that agent can perform calculations when needed in conversations ## Implementation Details The Calculator tool is implemented using Griptape's `CalculatorTool` class and provides a simple interface for performing mathematical calculations. The tool is designed to be used by agents to handle numerical operations accurately and efficiently. # Date Time ## What is it? The Date Time tool is a utility that can be given to an agent to help it perform date and time operations. ## When would I use it? Use this node when you want to: - Enable agents to work with dates and times - Perform date/time calculations and manipulations - Get current time information - Format dates and times in different ways ## How to use it ### Basic Setup 1. Add the Date Time tool to your workflow 1. Connect its output to nodes that need date/time capabilities (like an Agent) ### Outputs - **tool**: The configured date time tool that other nodes can use ## Example Imagine you want to create an agent that can work with dates and times: 1. Add a Date Time tool to your workflow 1. Connect the "tool" output to an Agent's "tools" input 1. Now that agent can perform date and time operations when needed in conversations ## Implementation Details The Date Time tool is implemented using Griptape's `DateTimeTool` class and provides a comprehensive interface for handling date and time operations. The tool is designed to be used by agents to manage temporal data and perform time-based calculations. # File Manager ## What is it? The File Manager tool is a utility that can be given to an agent to help it perform file operations in your workspace directory. ## When would I use it? Use this node when you want to: - Enable agents to read and write files - Manage files in your workspace directory - Perform file operations like copying, moving, or deleting files ## How to use it ### Basic Setup 1. Add the File Manager tool to your workflow 1. Connect its output to nodes that need file operation capabilities (like an Agent) ### Outputs - **tool**: The configured file manager tool that other nodes can use ## Example Imagine you want to create an agent that can manage files: 1. Add a File Manager tool to your workflow 1. Connect the "tool" output to an Agent's "tools" input 1. Now that agent can perform file operations when needed in conversations ## Implementation Details The File Manager tool is implemented using Griptape's `FileManagerTool` class with the `LocalFileManagerDriver`. The tool provides a simple interface for managing files in your workspace directory. Note: Cloud storage functionality is currently disabled and will be available in a future update. # InfoRetriever ## What is it? The InfoRetriever uses Retrieval Augmented Generation or "RAG" capabilities to your workflows. Think of it as a smart researcher that can find and use relevant information to enhance AI responses. ## When would I use it? Use this node when you want to: - Ground AI responses in your own data sources - Enable agents to access and reference specific information - Improve response accuracy with relevant context - Connect knowledge bases to your conversational agents ## How to use it ### Basic Setup 1. Add the InfoRetriever to your workflow 1. Connect its output to nodes that need RAG capabilities (like an Agent) ### Parameters - **description**: A description of what information this tool provides (default is "Contains information") - **off_prompt**: Whether to run RAG operations outside the main prompt (default is true) - **rag_engine**: The engine used to retrieve information (required) ### Outputs - **tool**: The configured RAG tool that other nodes can use - **rules**: The ruleset used by the RAG tool ## Example Imagine you want to create an agent that can answer questions using your company documentation: 1. Add an InfoRetriever to your workflow 1. Connect a vector store containing your documentation to the "rag_engine" input 1. Connect the "tool" output to an Agent's "tools" input 1. Now that agent can retrieve and reference specific information from your documentation when answering questions # ToolList ## What is it? The ToolList node combines multiple tools into a single list. This allows you to connect directly to a "tools" input parameter. ## When would I use it? Use this node when you want to: - Combine multiple tools into a single list - Create a workflow that leverages the strengths of different tools - Integrate tools from various sources in your Griptape workflow ## How to use it ### Basic Setup 1. Add the ToolList to your workflow 1. Connect it to other nodes that provide necessary input parameters (e.g., tools) 1. Run the flow to see the combined list of tools ### Parameters - **tools**: A list of tools to combine into a single list. ### Outputs - **tool_list**: The combined list of tools. ## Example Imagine you have a workflow that generates and saves text: 1. Create a flow with several nodes (like an agent that generates text and a node that saves it) 1. Add the ToolList, connecting it to nodes that provide tools parameters 1. Run the flow to see the combined list of tools in action ## Important Notes - The ToolList requires input tools to be provided when running the flow. - Using nested lists for tool inputs will result in flattened output. ## Common Issues - **Invalid Input Tools**: Ensure that you're providing a valid list of tools when connecting this node to other nodes in your workflow. - **Nested Lists**: Be aware that nested lists for tool inputs will be flattened into a single list. # Web Scraper ## What is it? The Web Scraper tool is a utility that can be given to an agent to help it extract information from web pages. ## When would I use it? Use this node when you want to: - Enable agents to extract data from websites - Scrape content from web pages - Gather information from online sources - Automate web data collection ## How to use it ### Basic Setup 1. Add the Web Scraper tool to your workflow 1. Connect its output to nodes that need web scraping capabilities (like an Agent) ### Outputs - **tool**: The configured web scraper tool that other nodes can use ## Example Imagine you want to create an agent that can scrape web content: 1. Add a Web Scraper tool to your workflow 1. Connect the "tool" output to an Agent's "tools" input 1. Now that agent can perform web scraping operations when needed in conversations ## Implementation Details The Web Scraper tool is implemented using Griptape's `WebScraperTool` class and provides a simple interface for extracting content from web pages. The tool is designed to be used by agents to gather information from websites in a structured way. ## Important Notes - The tool respects website terms of service and robots.txt files - Performance may vary depending on the structure and complexity of websites - Some websites may block automated scraping attempts - The tool works best with text-based content rather than dynamic JavaScript-heavy sites - Consider rate limiting and ethical use to avoid overloading websites ## Common Issues - **Access Denied**: Some websites actively block web scrapers - **Content Not Found**: Dynamic content loaded via JavaScript might not be accessible - **Rate Limiting**: Excessive requests may trigger rate limiting from websites - **Changing Layouts**: Website structure changes can affect scraping reliability - **Processing Large Pages**: Very large web pages may take longer to process or exceed token limits # Web Search ## What is it? The Web Search tool is a utility that can be given to an agent to help it search the web. It supports multiple search engines including DuckDuckGo, Google, and Exa. ## When would I use it? Use this node when you want to: - Enable agents to search the web for information - Access real-time web data - Perform research tasks - Get up-to-date information from the internet ## How to use it ### Basic Setup 1. Add the Web Search tool to your workflow 1. Connect its output to nodes that need web search capabilities (like an Agent) ### Parameters - **search_engine**: The search engine to use (default is "DuckDuckGo") - Options: - DuckDuckGo: Free, no API key required - Google: Requires Google API key and Search ID - Exa: Requires Exa API key ### Outputs - **tool**: The configured web search tool that other nodes can use ## Example Imagine you want to create an agent that can search the web: 1. Add a Web Search tool to your workflow 1. Connect the "tool" output to an Agent's "tools" input 1. Now that agent can perform web searches when needed in conversations ## Implementation Details The Web Search tool is implemented using Griptape's `WebSearchTool` class and supports multiple search engine drivers: - `DuckDuckGoWebSearchDriver`: Free, no API key required - `GoogleWebSearchDriver`: Requires Google API key and Search ID - `ExaWebSearchDriver`: Requires Exa API key When using Google or Exa search engines, you'll need to set up the appropriate API keys in the configuration. The tool will automatically handle authentication and search operations with the selected engine. # Note ## What is it? The Note node allows you to create descriptive notes throughout your workflow. They are not really operative, but more meant for display and documentation within a flow. ## When would I use it? Use the Note node when: - You need to describe what a section of your workflow does. - You want to give yourself a hint as to how to use a particular node. - You want to instruct someone else what to do with your workflow. ## How to use it ### Basic Setup 1. Add a Note node to your workflow 1. Set the "note" parameter with your desired multiline content ### Parameters - **note**: The text content (string, defaults to empty string) ## Example A workflow to create a note for how to use `prompt_context` on an Agent. 1. Add a Note node to your workflow 1. Move the Note near your Agent node 1. Enter the following text ``` To use the prompt_context, create a new KeyValuePair node. Give it something like a key of "style" and a value of "haiku" Then, connect the dictionary output to the prompt_context on your Agent. In the Agent prompt, use the {{ }} character to specify where you want to replace "style" with "haiku". Example: Tell me about skateboards in the style of a {{style}} ``` 1. Position the Note where it is easy to read and helps you understand your workflow ## Important Notes - Line breaks are preserved - The node supports any valid string content - Empty strings are valid - There is no character limit ## Common Issues - There is no control on the font size yet - There is no control for the color of the node yet # AddColorCurves ## What is it? The AddColorCurves node applies color grading effects to video using FFmpeg's `curves` filter. This filter allows for sophisticated color manipulation by adjusting the tonal curves of the video, providing both built-in presets and custom curve options for advanced color grading. ## When would I use it? Use the AddColorCurves node when: - You want to apply cinematic color grading effects to your video - You need to match the color style of multiple video clips - You're creating content that requires a specific visual aesthetic - You want to enhance the mood and atmosphere of your video - You need professional color correction and grading - You're working on projects that require consistent color treatment ## How to use it ### Basic Setup 1. Add an AddColorCurves node to your workflow 1. Connect a video source to the "video" input 1. Select a curve preset or define custom curves 1. Run the workflow to apply the color grading ### Parameters - **video**: The video content to apply color curves to (supports VideoArtifact and VideoUrlArtifact) - **curve_preset**: Built-in curve preset for color grading effects (default: "none") - **none**: No curves applied - **color_negative**: Color negative film look - **cross_process**: Cross-processed film effect - **darker**: Darker overall tone - **increase_contrast**: Moderate contrast boost - **lighter**: Lighter overall tone - **linear_contrast**: Linear contrast adjustment - **medium_contrast**: Medium contrast enhancement - **negative**: Black and white negative effect - **strong_contrast**: High contrast dramatic look - **vintage**: Classic vintage film look - **processing_speed**: Balance between processing speed and output quality (default: "balanced") - **fast**: Fastest processing, lower quality (ultrafast preset, CRF 30) - **balanced**: Good balance of speed and quality (medium preset, CRF 23) - **quality**: Highest quality, slower processing (slow preset, CRF 18) ### Outputs - **video**: The video with color curves applied, available as output to connect to other nodes ## Examples ### Example 1: Apply Vintage Effect 1. Connect the video output from a LoadVideo node to the AddColorCurves's "video" input 1. Set "curve_preset" to "vintage" 1. Run the workflow - the video will have a classic vintage film look 1. The output filename will be `{original_filename}_curves_vintage.{format}` ### Example 2: Apply Strong Contrast 1. Connect a video to the AddColorCurves node 1. Set "curve_preset" to "strong_contrast" 1. Run the workflow - the video will have dramatic contrast enhancement 1. The output filename will be `{original_filename}_curves_strong_contrast.{format}` ### Example 3: Apply Cross-Process Effect 1. Connect a video to the AddColorCurves node 1. Set "curve_preset" to "cross_process" 1. Run the workflow - the video will have a cross-processed film look 1. The output filename will be `{original_filename}_curves_cross_process.{format}` ## Important Notes - The AddColorCurves node uses FFmpeg's curves filter for high-quality color grading - Built-in presets provide quick access to common color grading effects - Processing time depends on video length and resolution - Curves can significantly affect the visual mood and style of your video - For best results, apply curves after other color adjustments ## Parameter Recommendations ### For Cinematic Look - Use "vintage" or "cross_process" presets - Try "color_negative" for a unique film look ### For High Contrast - Use "strong_contrast" or "negative" - Try "increase_contrast" for subtle enhancement # AddFilmGrain ## What is it? The AddFilmGrain node adds realistic film grain to video using sophisticated noise generation and luminance masking. It creates authentic film-like texture that varies based on the brightness of different areas in the video. ## When would I use it? Use the AddFilmGrain node when: - You want to add a cinematic, film-like quality to digital video - You need to create vintage or retro video effects - You want to add texture and character to clean digital footage - You're creating content that needs to look like it was shot on film - You want to enhance the visual depth and atmosphere of your video ## How to use it ### Basic Setup 1. Add an AddFilmGrain node to your workflow 1. Connect a video source to the "video" input 1. Adjust the grain intensity, luminance threshold, and grain scale parameters 1. Run the workflow to add film grain to your video ### Parameters - **video**: The video content to add film grain to (supports VideoArtifact and VideoUrlArtifact) - **grain_intensity**: Film grain intensity (0.05-1.0, default: 0.15) - 0.05 = very subtle grain - 0.15 = moderate grain (default) - 0.5 = heavy grain - 1.0 = maximum grain intensity - **luminance_threshold**: Luminance level where grain is most visible (50-100, default: 75) - 50 = grain visible in darker areas - 75 = grain visible in mid-tones (default, good for lighter areas) - 100 = grain visible in brighter areas - **grain_scale**: Grain scale factor (1.0-4.0, default: 2.0) - 1.0 = fine grain particles - 2.0 = medium grain (default) - 4.0 = larger grain particles - **processing_speed**: Balance between processing speed and output quality (default: "balanced") - **fast**: Fastest processing, lower quality (ultrafast preset, CRF 30) - **balanced**: Good balance of speed and quality (medium preset, CRF 23) - **quality**: Highest quality, slower processing (slow preset, CRF 18) ### Outputs - **video**: The video with film grain added, available as output to connect to other nodes ## Example Imagine you want to add a subtle film grain to a digital video to give it a cinematic look: 1. Add an AddFilmGrain node to your workflow 1. Connect the video output from a LoadVideo node to the AddFilmGrain's "video" input 1. Set the "grain_intensity" to 0.12 for subtle grain 1. Set the "luminance_threshold" to 75 for optimal grain visibility on lighter areas 1. Set the "grain_scale" to 2.0 for medium-sized grain particles 1. Run the workflow - the video will have realistic film grain added 1. The output filename will be `{original_filename}_grain_0.12.{format}` ## Important Notes - The AddFilmGrain node uses FFmpeg with sophisticated noise generation algorithms - Grain intensity is automatically adjusted based on the luminance of each pixel - The effect creates temporal grain that changes between frames for realism - Processing time depends on video length and resolution - The original audio track is preserved - Logs are available for debugging processing issues ## Parameter Recommendations - **For subtle film look**: Use grain_intensity 0.08-0.15, luminance_threshold 75 - **For vintage/retro effect**: Use grain_intensity 0.3-0.6, grain_scale 2.5-3.0 - **For heavy film grain**: Use grain_intensity 0.7-1.0, grain_scale 3.0-4.0 - **For portraits**: Use luminance_threshold 70-80 for optimal grain on lighter areas - **For landscapes**: Use luminance_threshold 60-70 for grain in mid-tones ## Common Issues - **Processing Timeout**: Large videos may take longer to process; the node has a 5-minute timeout - **Too Much Grain**: Reduce grain_intensity if the effect is too strong - **Grain Not Visible**: Increase grain_intensity or adjust luminance_threshold - **No Video Input**: Make sure a video source is connected to the "video" input - **FFmpeg Errors**: Check the logs parameter for detailed error information if processing fails # AddOverlay ## What is it? The AddOverlay node composites two videos together by overlaying one video on top of another using FFmpeg's blend modes. It provides control over the blend mode, channel selection, and sizing of the overlay video. The node supports various blend modes that match industry-standard compositing software. ## When would I use it? Use the AddOverlay node when: - You want to add film grain or noise effects to your video - You need to composite multiple video layers together - You're creating videos with watermarks or logos - You want to add visual effects that require layering - You're working on projects that need video compositing - You want to create vintage or textured video effects ## How to use it ### Basic Setup 1. Add an AddOverlay node to your workflow 1. Connect a base video to the "video" input 1. Connect an overlay video to the "overlay_video" input 1. Choose a blend mode and adjust the amount parameter 1. Run the workflow to composite the videos ### Parameters - **video**: The base video content (supports VideoArtifact and VideoUrlArtifact) - **overlay_video**: The video or image to overlay on top of the base video (supports VideoArtifact, VideoUrlArtifact, ImageArtifact, and ImageUrlArtifact) - **blend_mode**: How to blend the overlay with the base video (default: "overlay") - **overlay**: Original overlay with alpha blending - **screen**: Brighten (good for dust/scratches) - **lighten**: Only brighten where overlay is brighter - **softlight**: Natural blending for film grain - **grainmerge**: Merge grain/noise (recommended for noise effects) - **grainextract**: Extract grain/noise - **glow**: Glow effect - **hardlight**: Hard light blending - **amount**: Strength of the overlay effect (0.0-1.0, default: 0.5) - 0.0 = no effect (original video unchanged) - 0.5 = 50% effect strength (default) - 1.0 = full effect strength - **processing_speed**: Balance between processing speed and output quality (default: "balanced") - **fast**: Fastest processing, lower quality (ultrafast preset, CRF 30) - **balanced**: Good balance of speed and quality (medium preset, CRF 23) - **quality**: Highest quality, slower processing (slow preset, CRF 18) ### Outputs - **video**: The composited video with overlay applied, available as output to connect to other nodes ## Examples ### Example 1: Film Grain Overlay 1. Connect a base video to the AddOverlay's "video" input 1. Connect a film grain video to the "overlay_video" input 1. Set "blend_mode" to "grainmerge" for noise effects 1. Set "amount" to 0.3 for subtle grain effect 1. Run the workflow - the video will have a film grain overlay 1. The output filename will be `{original_filename}_overlay_grainmerge_amount0.30.{format}` ### Example 2: Logo Overlay 1. Connect a base video to the AddOverlay's "video" input 1. Connect a PNG logo with transparency to the "overlay_video" input 1. Set "blend_mode" to "overlay" for alpha blending 1. Set "amount" to 0.8 for visible logo 1. Run the workflow - the video will have a positioned logo ### Example 3: Vintage Effect with Film Grain 1. Use ChangeSpeed to speed up your video 1. Use AddColorCurves with "vintage" preset 1. Use AddOverlay to add film grain texture 1. Set "blend_mode" to "grainmerge" 1. Set "amount" to 0.4 for vintage grain effect 1. Run the workflow for a complete vintage look ## Important Notes - The overlay video will be automatically looped and trimmed to match the base video's duration - PNG images with transparency are supported as overlays - The node uses linear RGB blending for consistent results that match industry standards - For noise effects, use "grainmerge" blend mode with "luminance" channel - For logos with transparency, use "overlay" blend mode with "rgba" channel - The overlay is always centered on the base video for simplicity ## Technical Details The node uses FFmpeg's blend modes and supports both overlay filter (for alpha blending) and blend filter (for other blend modes). It automatically converts to linear RGB space for consistent blending behavior that matches industry-standard compositing software. # AddRGBShift ## What is it? The AddRGBShift node adds RGB shift (chromatic aberration) effects to video, creating visual distortions where the red, green, and blue color channels are offset from each other. It can also add a tear effect that splits the video horizontally and applies different RGB shifts to each part. ## When would I use it? Use the AddRGBShift node when: - You want to create glitch art or digital distortion effects - You need to simulate chromatic aberration or lens distortion - You're creating cyberpunk or retro-futuristic content - You want to add visual interest to otherwise clean footage - You need to create unsettling or disorienting visual effects - You want to add a tear effect that splits the video horizontally ## How to use it ### Basic Setup 1. Add an AddRGBShift node to your workflow 1. Connect a video source to the "video" input 1. Adjust the RGB shift parameters for each color channel 1. Optionally enable the tear effect and configure its position and offset 1. Run the workflow to add the RGB shift effect ### Parameters #### RGB Shift Settings - **red_horizontal**: Red channel horizontal shift (-20 to 20 pixels, default: -6) - **red_vertical**: Red channel vertical shift (-20 to 20 pixels, default: 0) - **green_horizontal**: Green channel horizontal shift (-20 to 20 pixels, default: 6) - **green_vertical**: Green channel vertical shift (-20 to 20 pixels, default: 0) - **blue_horizontal**: Blue channel horizontal shift (-20 to 20 pixels, default: 0) - **blue_vertical**: Blue channel vertical shift (-20 to 20 pixels, default: 0) - **intensity**: Overall intensity of the RGB shift effect (0.0-1.0, default: 1.0) #### Tear Effect Settings - **tear_enabled**: Enable tear effect (default: False) - **tear_position**: Vertical position of tear (0.0-1.0, where 0.5 is center, default: 0.5) - **tear_offset**: Horizontal offset amount for tear effect (-50 to 50 pixels, default: 10) - **processing_speed**: Balance between processing speed and output quality (default: "balanced") - **fast**: Fastest processing, lower quality (ultrafast preset, CRF 30) - **balanced**: Good balance of speed and quality (medium preset, CRF 23) - **quality**: Highest quality, slower processing (slow preset, CRF 18) ### Outputs - **video**: The video with RGB shift effects added, available as output to connect to other nodes ## Example Imagine you want to create a glitch effect on a video: 1. Add an AddRGBShift node to your workflow 1. Connect the video output from a LoadVideo node to the AddRGBShift's "video" input 1. Set red_horizontal to -8, green_horizontal to 8 for classic RGB separation 1. Set intensity to 0.8 for a moderate effect 1. Enable tear_enabled and set tear_position to 0.6 and tear_offset to 15 1. Run the workflow - the video will have RGB shift effects with a tear at 60% down the frame 1. The output filename will include all the effect parameters ## Important Notes - The AddRGBShift node uses FFmpeg with the rgbashift filter for reliable effects - The tear effect splits the video horizontally and applies different RGB shifts to each part - The original audio track is preserved - Processing time depends on video length and effect complexity ## Effect Recommendations - **Subtle RGB shift**: Use small values (2-4 pixels) for red/green horizontal shifts - **Heavy glitch effect**: Use larger values (10-20 pixels) for dramatic separation - **Tear effect**: Position the tear at 0.3-0.7 for best visual impact - **Cyberpunk aesthetic**: Combine red/green horizontal shifts with tear effects - **Retro video look**: Use moderate RGB shifts with intensity around 0.7-0.9 ## Common Issues - **Processing Timeout**: Complex effects may take longer to process; the node has a 5-minute timeout - **Effect Too Strong**: Reduce intensity or RGB shift values if the effect is overwhelming - **No Video Input**: Make sure a video source is connected to the "video" input - **FFmpeg Errors**: Check the logs parameter for detailed error information if processing fails # AddVignette ## What is it? The AddVignette node adds a vignette effect to video, creating a gradual darkening or lightening around the edges of the frame. This effect can enhance the visual focus on the center of the frame and add a cinematic quality to your video. ## When would I use it? Use the AddVignette node when: - You want to draw attention to the center of your video frame - You need to create a cinematic, film-like look - You want to add depth and atmosphere to your video - You're creating content that needs a professional, polished appearance - You want to simulate the natural light falloff of camera lenses - You need to enhance the mood or emotional impact of your video ## How to use it ### Basic Setup 1. Add an AddVignette node to your workflow 1. Connect a video source to the "video" input 1. Adjust the vignette angle, center position, and aspect ratio 1. Choose between "forward" (darken edges) or "backward" (lighten edges) mode 1. Run the workflow to add the vignette effect ### Parameters - **video**: The video content to add vignette to (supports VideoArtifact and VideoUrlArtifact) - **angle**: Lens angle for vignette effect (0.1-3.14, default: 0.628) - Smaller values = less vignette effect - 0.1 = very subtle vignette - 0.628 = moderate vignette (default) - 3.14 = very strong vignette - **center_x**: Center X offset (-1.0 to 1.0, default: 0.0) - -1.0 = far left - 0.0 = center (default) - 1.0 = far right - **center_y**: Center Y offset (-1.0 to 1.0, default: 0.0) - -1.0 = top - 0.0 = center (default) - 1.0 = bottom - **aspect**: Aspect ratio of vignette (0.1-10.0, default: 1.0) - 0.1 = very wide vignette - 1.0 = circular vignette (default) - 10.0 = very tall vignette - **mode**: Vignette mode (default: "forward") - "forward" = darken edges (traditional vignette) - "backward" = lighten edges (inverse vignette) - **processing_speed**: Balance between processing speed and output quality (default: "balanced") - **fast**: Fastest processing, lower quality (ultrafast preset, CRF 30) - **balanced**: Good balance of speed and quality (medium preset, CRF 23) - **quality**: Highest quality, slower processing (slow preset, CRF 18) ### Outputs - **video**: The video with vignette effect added, available as output to connect to other nodes ## Example Imagine you want to add a subtle vignette to a portrait video to draw attention to the subject: 1. Add an AddVignette node to your workflow 1. Connect the video output from a LoadVideo node to the AddVignette's "video" input 1. Set the "angle" to 0.5 for a moderate vignette effect 1. Keep "center_x" and "center_y" at 0.0 to center the vignette 1. Set "aspect" to 1.0 for a circular vignette 1. Choose "forward" mode to darken the edges 1. Run the workflow - the video will have a subtle vignette effect 1. The output filename will be `{original_filename}_vignette_a0.50_x0.00_y0.00_r1.00_forward.{format}` ## Important Notes - The AddVignette node uses FFmpeg with high-quality vignette algorithms - The effect creates smooth, gradual transitions from center to edges - The original audio track is preserved - Processing time depends on video length and resolution - Logs are available for debugging processing issues ## Parameter Recommendations - **For portraits**: Use angle 0.4-0.6, center at (0,0), aspect 1.0 - **For landscapes**: Use angle 0.3-0.5, center at (0,0), aspect 1.5-2.0 - **For cinematic look**: Use angle 0.5-0.8, forward mode - **For subtle enhancement**: Use angle 0.8-1.2 for very gentle vignette - **For dramatic effect**: Use angle 2.0-3.0 for strong vignette - **For off-center subjects**: Adjust center_x and center_y to match subject position ## Common Issues - **Processing Timeout**: Large videos may take longer to process; the node has a 5-minute timeout - **Vignette Too Strong**: Decrease the angle value to make the effect more subtle - **Vignette Not Visible**: Increase the angle value to make the effect stronger - **No Video Input**: Make sure a video source is connected to the "video" input - **FFmpeg Errors**: Check the logs parameter for detailed error information if processing fails # AdjustMaskSize ## What is it? The AdjustMaskSize node adjusts the size of masks in a video by dilating (expanding) or eroding (shrinking) the mask boundaries. It processes each frame individually using morphological operations while maintaining video properties. ## When would I use it? Use the AdjustMaskSize node when: - You need to expand mask boundaries to include more surrounding area (dilation) - You want to shrink masks to remove edge artifacts or thin borders (erosion) - You need to refine mask edges from segmentation or tracking results - You want to adjust mask coverage by a precise number of pixels ## How to use it ### Basic Setup 1. Add an AdjustMaskSize node to your workflow 1. Connect a mask video source to the "mask_video" input 1. Adjust the "adjustment" slider to set the desired expansion or shrinkage 1. Run the workflow to process the mask video ### Parameters - **mask_video**: The mask video to adjust (supports VideoArtifact and VideoUrlArtifact) - **adjustment**: Mask size adjustment in pixels (-25 to +25, default: 0) - Positive values (1 to 25): Expand the mask (dilation) - Negative values (-25 to -1): Shrink the mask (erosion) - Zero (0): Pass through unchanged (no processing) ### Outputs - **output_mask**: The adjusted mask video, available as output to connect to other nodes ## How it works The node applies morphological operations frame-by-frame: - **Dilation (positive adjustment)**: Expands white regions in the mask by applying a maximum filter. Each white pixel expands outward by the specified number of pixels using a circular kernel. - **Erosion (negative adjustment)**: Shrinks white regions in the mask by applying a minimum filter. Each white pixel shrinks inward by the specified number of pixels using a circular kernel. - **Zero adjustment**: When adjustment is 0, the input video is returned unchanged without processing. The circular kernel ensures natural-looking, uniform expansion or contraction in all directions. A progress indicator shows processing status (0-90% for frame processing, 90-100% for video reassembly). ## Example To expand a mask by 5 pixels to include more surrounding area: 1. Connect your mask video to the **mask_video** input 1. Set **adjustment** to `5` 1. Run the workflow 1. The output mask will have boundaries expanded by 5 pixels in all directions To remove a 3-pixel border from a mask: 1. Connect your mask video to the **mask_video** input 1. Set **adjustment** to `-3` 1. Run the workflow 1. The output mask will have boundaries shrunk by 3 pixels in all directions ## Important Notes - The AdjustMaskSize node uses FFmpeg for video frame extraction and reassembly - Morphological operations use PIL (Pillow) MaxFilter and MinFilter for efficient processing - The circular kernel provides smooth, natural-looking results - Input masks are automatically converted to grayscale during processing - The output video maintains the same resolution, frame rate, and duration as the input - Zero adjustment skips all processing for efficiency - Logs are available for debugging processing issues # AdjustVideoEQ ## What is it? The AdjustVideoEQ node allows you to adjust video brightness, contrast, saturation, and gamma settings. It provides precise control over the visual appearance of your video content using FFmpeg's high-quality equalization filters. ## When would I use it? Use the AdjustVideoEQ node when: - You need to correct exposure issues in your video - You want to enhance the visual quality of poorly lit footage - You need to adjust color saturation for artistic or technical reasons - You want to match the look of multiple video clips - You're creating content that needs specific visual adjustments - You want to improve the overall visual appeal of your video ## How to use it ### Basic Setup 1. Add an AdjustVideoEQ node to your workflow 1. Connect a video source to the "video" input 1. Adjust the brightness, contrast, saturation, and gamma parameters 1. Run the workflow to apply the EQ adjustments ### Parameters - **video**: The video content to adjust (supports VideoArtifact and VideoUrlArtifact) - **brightness**: Brightness adjustment (-1.0 to 1.0, default: 0.0) - -1.0 = very dark - 0.0 = normal brightness (default) - 1.0 = very bright - **contrast**: Contrast adjustment (0.0 to 3.0, default: 1.0) - 0.0 = no contrast (flat image) - 1.0 = normal contrast (default) - 3.0 = very high contrast - **saturation**: Saturation adjustment (0.0 to 3.0, default: 1.0) - 0.0 = black and white - 1.0 = normal saturation (default) - 3.0 = very saturated colors - **gamma**: Gamma adjustment (0.1 to 10.0, default: 1.0) - 0.1 = very bright mid-tones - 1.0 = normal gamma (default) - 10.0 = very dark mid-tones - **processing_speed**: Balance between processing speed and output quality (default: "balanced") - **fast**: Fastest processing, lower quality (ultrafast preset, CRF 30) - **balanced**: Good balance of speed and quality (medium preset, CRF 23) - **quality**: Highest quality, slower processing (slow preset, CRF 18) ### Outputs - **video**: The video with EQ adjustments applied, available as output to connect to other nodes ## Example Imagine you want to brighten a dark video and increase its saturation: 1. Add an AdjustVideoEQ node to your workflow 1. Connect the video output from a LoadVideo node to the AdjustVideoEQ's "video" input 1. Set "brightness" to 0.3 to brighten the video 1. Set "contrast" to 1.2 to enhance contrast 1. Set "saturation" to 1.5 to make colors more vibrant 1. Keep "gamma" at 1.0 for normal mid-tone response 1. Run the workflow - the video will be brighter and more saturated 1. The output filename will be `{original_filename}_eq_b0.30_c1.20_s1.50_g1.00.{format}` ## Important Notes - The AdjustVideoEQ node uses FFmpeg with high-quality equalization algorithms - All parameters work together to create the final visual effect - The original audio track is preserved - Processing time depends on video length and resolution - Logs are available for debugging processing issues ## Parameter Recommendations - **For dark footage**: Use brightness 0.2-0.5, contrast 1.1-1.3 - **For overexposed footage**: Use brightness -0.2 to -0.5, contrast 0.8-0.9 - **For flat footage**: Use contrast 1.2-1.5, saturation 1.1-1.3 - **For vibrant colors**: Use saturation 1.3-1.8, gamma 0.9-1.1 - **For cinematic look**: Use contrast 1.1-1.2, saturation 0.8-0.9, gamma 1.1-1.2 - **For vintage effect**: Use saturation 0.7-0.8, gamma 1.2-1.4 ## Common Issues - **Processing Timeout**: Large videos may take longer to process; the node has a 5-minute timeout - **Too Bright/Dark**: Adjust brightness value - use negative values to darken, positive to brighten - **Colors Too Saturated**: Reduce saturation value below 1.0 - **No Video Input**: Make sure a video source is connected to the "video" input - **FFmpeg Errors**: Check the logs parameter for detailed error information if processing fails # ChangeSpeed ## What is it? The ChangeSpeed node allows you to change the playback speed of a video. It uses FFmpeg's setpts filter to adjust video timing and automatically adjusts audio speed to match, creating smooth speed changes while maintaining audio-video synchronization. ## When would I use it? Use the ChangeSpeed node when: - You want to create fast-motion effects (e.g., 2x, 3x faster) - You need to create slow-motion effects (e.g., 0.5x, 0.25x slower) - You want to speed up long videos for quick review - You need to slow down fast action for analysis - You're creating time-lapse or slow-motion content - You want to adjust video pacing for different audiences ## How to use it ### Basic Setup 1. Add a ChangeSpeed node to your workflow 1. Connect a video source to the "video" input 1. Set your desired speed multiplier 1. Run the workflow to change the video's playback speed ### Parameters - **video**: The video content to change speed (supports VideoArtifact and VideoUrlArtifact) - **speed**: Playback speed multiplier (0.1-10.0x, default: 1.0) - 0.1 = 10x slower (very slow motion) - 0.5 = 2x slower (slow motion) - 1.0 = normal speed (default) - 2.0 = 2x faster (fast motion) - 5.0 = 5x faster (very fast motion) - 10.0 = 10x faster (maximum speed) - **include_audio**: When enabled, includes audio in the output file (default: true) - When enabled: Audio is speed-adjusted to match video speed - When disabled: Creates a silent video (no audio track) - **processing_speed**: Balance between processing speed and output quality (default: "balanced") - **fast**: Fastest processing, lower quality (ultrafast preset, CRF 30) - **balanced**: Good balance of speed and quality (medium preset, CRF 23) - **quality**: Highest quality, slower processing (slow preset, CRF 18) ### Outputs - **video**: The video with changed playback speed, available as output to connect to other nodes ## Example Imagine you want to create a fast-motion effect by speeding up a video 3x: 1. Add a ChangeSpeed node to your workflow 1. Connect the video output from a LoadVideo node to the ChangeSpeed's "video" input 1. Set "speed" to 3.0 for 3x faster playback 1. Keep "include_audio" enabled (default) to include speed-adjusted audio 1. Run the workflow - the video will play 3x faster with audio adjusted to match 1. The output filename will be `{original_filename}_speed3.0.{format}` ## Important Notes - The ChangeSpeed node uses FFmpeg's setpts filter for high-quality speed changes - Audio is automatically speed-adjusted using the atempo filter to maintain synchronization (when include_audio is enabled) - Speed changes affect both video and audio simultaneously - Processing time depends on video length and resolution - Extreme speed changes (very fast or very slow) may affect audio quality - When include_audio is disabled, the output video will be silent ## Parameter Recommendations - **For fast motion**: Use 2.0-5.0 for sped-up effects - **For slow motion**: Use 0.25-0.75 for slow-motion effects - **For time-lapse**: Use 5.0-10.0 for very fast playback - **For analysis**: Use 0.5-0.75 to slow down fast action - **For quick review**: Use 2.0-3.0 to speed up long content ## Common Issues - **Processing Timeout**: Large videos may take longer to process; the node has a 5-minute timeout - **Audio Quality**: Extreme speed changes may affect audio clarity - **No Video Input**: Make sure a video source is connected to the "video" input - **FFmpeg Errors**: Check the logs parameter for detailed error information if processing fails ## Speed Examples - **0.25x**: 4x slower - Good for analyzing fast action - **0.5x**: 2x slower - Classic slow motion - **1.0x**: Normal speed - No change - **2.0x**: 2x faster - Fast motion - **3.0x**: 3x faster - Very fast motion - **5.0x**: 5x faster - Time-lapse effect # CombineMasksVideo ## What is it? The CombineMasksVideo node merges multiple mask videos into a single combined mask video by taking the pixel-wise maximum value across all input masks. It handles videos of different durations by padding shorter videos with black frames. ## When would I use it? Use the CombineMasksVideo node when: - You need to merge multiple mask videos into a single mask - You want to combine object masks from different sources - You're working with multiple segmentation outputs that need to be unified - You need to create composite masks from multiple tracking results ## How to use it ### Basic Setup 1. Add a CombineMasksVideo node to your workflow 1. Connect mask video sources to the "mask_videos" input (accepts multiple videos as a list) 1. Run the workflow to combine the masks ### Parameters - **mask_videos**: A list of mask videos to combine (supports VideoArtifact and VideoUrlArtifact) ### Outputs - **combined_mask**: The combined mask video, available as output to connect to other nodes ## How it works The node processes videos frame-by-frame using a pixel-wise maximum operation: - Each frame is converted to grayscale (if not already) - For each pixel position, the maximum value across all input masks is selected - The result is a mask where any white pixel from any input mask appears white in the output - If input videos have different durations, shorter videos are padded with black frames to match the longest video A progress indicator shows processing status (0-90% for frame processing, 90-100% for video reassembly). ## Example To combine two mask videos tracking different objects: 1. Connect both mask videos to the **mask_videos** input 1. Run the workflow 1. The output will contain white pixels wherever either input mask had white pixels ## Important Notes - The CombineMasksVideo node uses FFmpeg for video processing - All input masks are converted to grayscale during processing - The output video resolution matches the first input video - Videos with different durations are automatically handled through black frame padding - The output video maintains the frame rate of the reference (first) video - Logs are available for debugging processing issues # ConcatenateVideos ## What is it? The ConcatenateVideos node combines multiple videos into a single continuous video file with configurable format and codec options. It automatically handles videos of different dimensions by resizing them to match the first video's dimensions, ensuring smooth concatenation without visual discontinuity. ## When would I use it? Use the ConcatenateVideos node when: - You want to combine multiple video clips into one continuous video - Creating video compilations or montages from separate clips - Joining video segments that were split or recorded separately - Building longer videos from shorter generated clips (like from AI video models) - Creating playlists or sequences from individual video files - Combining videos with different resolutions (automatic resizing handles dimension mismatches) ## How to use it ### Basic Setup 1. Add a ConcatenateVideos node to your workflow 1. Connect multiple video inputs to the **video_inputs** parameter (can connect individual videos or a list) 1. Configure the output format and codec settings as needed 1. Connect the **output** to other nodes that require video input ### Parameters - **video_inputs**: List of videos to concatenate (supports VideoArtifact, VideoUrlArtifact, and lists of both types) - **output_format**: Output video format - mp4, avi, mov, mkv, or webm (default: mp4) - **video_codec**: Video codec for output - libx264, libx265, libvpx-vp9, or copy (default: libx264) - **audio_codec**: Audio codec for output - aac, mp3, libmp3lame, or copy (default: aac) - **output_frame_rate**: Frame rate control (inherited from BaseVideoProcessor) - **processing_speed**: Quality vs speed trade-off (inherited from BaseVideoProcessor) ### Outputs - **output**: The concatenated video file as a VideoUrlArtifact - **logs**: Processing logs with detailed information about downloading, dimension checking, and resizing operations ## Automatic Video Resizing The ConcatenateVideos node intelligently handles videos with different dimensions by automatically resizing them: ### How It Works 1. **Dimension Detection**: The node checks the dimensions of all input videos using FFprobe 1. **Reference Selection**: The first video's dimensions are used as the target size for all videos 1. **Smart Resizing**: Videos that don't match the target dimensions are automatically resized using FFmpeg 1. **Aspect Ratio Preservation**: Resizing maintains the original aspect ratio and adds black padding if needed 1. **Detailed Logging**: All resizing operations are logged with before/after dimensions ### Resizing Behavior - **Same Size Videos**: If all videos have matching dimensions, no resizing occurs - **Mixed Size Videos**: Videos are resized to match the first video's dimensions - **Quality Preservation**: Uses high-quality scaling with aspect ratio preservation - **Audio Preservation**: Audio tracks are copied without re-encoding during resize operations ### Log Examples When videos have matching dimensions: ``` ✅ All videos have matching dimensions - no resizing needed ``` When resizing is needed: ``` 🔄 Videos have different dimensions - resizing to match first video... Target dimensions (from first video): 1920x1080 • Video 1: 1920x1080 (reference) • Video 2: 1280x720 → 1920x1080 (resizing...) ✅ Video 2 resized successfully • Video 3: 1920x1080 (no resize needed) ``` ## Example When creating a longer video from multiple AI-generated clips: 1. Add a ConcatenateVideos node to your workflow 1. Connect multiple video generation nodes to the **video_inputs** parameter 1. Set the **output_format** to "mp4" and **video_codec** to "libx264" for wide compatibility 1. Set the **audio_codec** to "aac" for high-quality audio 1. When the node runs, all input videos will be joined into a single continuous video ## Advanced Usage ### Format and Codec Selection - **mp4 + libx264**: Best for web compatibility and general use - **mov + libx264**: Good for professional video editing workflows - **mkv + libx265**: Excellent compression for storage efficiency - **webm + libvpx-vp9**: Optimized for web streaming - **copy codecs**: Fastest processing when all inputs have the same codec ### Performance Tips - Use "copy" for video and audio codecs when all input videos have identical formats for fastest processing - Choose "fast" processing speed for quick previews, "quality" for final output - Consider using libx265 for smaller file sizes with similar quality - **For best performance**: Use videos with matching dimensions to avoid resizing overhead - **Video ordering**: Place your highest-quality or target-dimension video first to set the reference resolution ## Important Notes - **Minimum Input**: Requires at least 2 videos to concatenate - **Format Support**: Supports common video formats (mp4, avi, mov, mkv, webm) - **Automatic Download**: Videos are automatically downloaded and prepared for concatenation - **Automatic Resizing**: Videos with different dimensions are automatically resized to match the first video - **Dimension Reference**: The first video in the list determines the target dimensions for all videos - **Quality Preservation**: Resizing uses high-quality FFmpeg scaling with aspect ratio preservation - **Frame Rate Handling**: Can automatically adjust frame rates or preserve original rates - **Audio Synchronization**: Maintains audio sync throughout the concatenated video - **Web Optimization**: Adds faststart flag for optimized web streaming - **Processing Logs**: Detailed logs show all dimension checking and resizing operations ## Common Issues - **"At least 2 videos required"**: Make sure you've connected at least two video inputs to the node - **Format Mismatch**: If videos have very different formats, avoid using "copy" codecs and let the node re-encode - **Large File Processing**: For very large videos, increase timeout or use "fast" processing speed - **Audio Issues**: If source videos have different audio formats, avoid "copy" audio codec and use aac or mp3 - **Memory Usage**: Processing many large videos simultaneously may require sufficient system memory - **Slow Resizing**: Large videos with different dimensions will take longer to process due to resizing operations - **Unexpected Dimensions**: The first video determines output dimensions - reorder videos if you want different target dimensions - **Quality Loss**: Multiple resizing operations may reduce video quality - use videos with matching dimensions when possible - **FFprobe Errors**: If dimension detection fails, ensure videos are valid and not corrupted # DisplayVideo ## What is it? The DisplayVideo node simply displays video content in your workflow. ## When would I use it? Use the DisplayVideo node when: - You need to display video results or information in your workflow - You want to show video output from previous processing steps - You need a placeholder for video content that will be updated during workflow execution - You want to create readable labels or descriptions within your workflow - You need to visualize video data at specific points in your process ## How to use it ### Basic Setup 1. Add a DisplayVideo node to your workflow 1. Set the initial video value if desired 1. Connect inputs to the video parameter or manually select a video 1. Connect the video output to other nodes that require video input ### Parameters - **video**: The video content to display (supports VideoArtifact and VideoUrlArtifact) ### Outputs - **video**: The same video content, available as output to connect to other nodes ## Example Imagine you're building a workflow that processes and displays video information: 1. Add a DisplayVideo node to your workflow 1. Connect the output from a LoadVideo node to the DisplayVideo node's video parameter 1. When the workflow runs, the video content will be displayed in the node ## Important Notes - The DisplayVideo node is for visualization and doesn't modify the video content - The node passes through video exactly as received, without any processing or formatting - Supports common video formats (mp4, avi, mov, etc.) - The video player controls allow you to play, pause, and scrub through the video ## Common Issues - **No Video Showing**: Make sure you've properly connected a video source to this node - **Video Won't Play**: Check that your video file is in a supported format and not corrupted # ExtractAudio ## What is it? The ExtractAudio node extracts the audio track from a video and outputs it as an audio file for use in your workflow. It supports multiple audio formats and quality settings, with options for both lossless copying and re-encoding. ## When would I use it? Use the ExtractAudio node when: - You need to separate audio from video content for audio processing workflows - You want to convert video audio to different formats (MP3, WAV, AAC, etc.) - You're creating audio-only content from video sources - You need to extract audio for transcription, analysis, or editing - You want to preserve the original audio quality by copying without re-encoding ## How to use it ### Basic Setup 1. Add an ExtractAudio node to your workflow 1. Connect a video input to the video parameter or manually select a video file 1. Choose your desired audio format and quality settings 1. Connect the extracted_audio output to other nodes that require audio input ### Parameters #### Input - **video**: The video content to extract audio from (supports VideoArtifact and VideoUrlArtifact) #### Audio Settings - **audio_format**: Output audio format - `mp3` (default) - Widely compatible, good compression - `wav` - Uncompressed, highest quality - `aac` - Good compression, modern standard - `flac` - Lossless compression - `ogg` - Open-source alternative with good compression - `m4a` - Apple's audio format, similar to AAC - **audio_quality**: Audio quality/bitrate setting - `high` (128k) - High quality for most purposes (default) - `medium` (96k) - Balanced quality and file size - `low` (64k) - Smaller files, lower quality - `copy` - Copy original audio without re-encoding (fastest, preserves original quality) ### Outputs - **extracted_audio**: The audio extracted from the video as an AudioUrlArtifact - **logs**: Processing logs with detailed information about the extraction process ## Audio Format and Quality Guide ### Quality Settings - **Copy**: Fastest option, preserves original audio quality and format exactly. Recommended when you don't need format conversion. - **High (128k)**: Excellent quality for most use cases, good balance of quality and file size. - **Medium (96k)**: Good quality for voice content, smaller file size. - **Low (64k)**: Acceptable for voice-only content where file size is critical. ### Format Recommendations - **MP3**: Most universally compatible, good for general use - **WAV**: Use for highest quality or when working with audio editing software - **AAC**: Modern standard with efficient compression, good for streaming - **FLAC**: Use when you need lossless compression (smaller than WAV but larger than lossy formats) - **OGG**: Open-source alternative to MP3, good compression - **M4A**: Good for Apple ecosystem compatibility ## Example Extracting audio from a video for transcription: 1. Add an ExtractAudio node to your workflow 1. Connect a video source to the **video** input 1. Set **audio_format** to `wav` for high compatibility with transcription services 1. Set **audio_quality** to `high` for best transcription accuracy 1. Connect the **extracted_audio** output to a transcription node 1. When the workflow runs, the audio will be extracted and available for transcription ## Performance Tips - Use **copy** quality when you don't need to change the audio format - it's much faster - Choose **MP3** with **high** quality for a good balance of quality, compatibility, and file size - Use **WAV** format when working with audio editing software that requires uncompressed audio - For long videos, the extraction process may take some time - check the logs for progress ## Important Notes - **Audio Detection**: The node automatically detects if the video contains audio streams - **No Audio Error**: If the input video has no audio track, the node will report an error - **Format Support**: Supports all common video formats (mp4, avi, mov, mkv, webm, etc.) - **Quality Preservation**: Using "copy" quality preserves the original audio exactly without any quality loss - **Processing Speed**: Copy mode is fastest, while re-encoding takes longer but allows format conversion ## Common Issues - **"No audio stream found"**: The input video doesn't contain any audio tracks. Check that your video file has audio. - **Extraction Failed**: Make sure the input video is valid and accessible - **Large File Processing**: For very large videos, extraction may take several minutes - monitor the logs for progress - **Format Issues**: If you encounter format-specific issues, try using "copy" mode first to preserve the original audio format # ExtractLastFrame ## What is it? The ExtractLastFrame node extaracts the last frame from a video as an image for use in your workflow. ## When would I use it? Use the ExtractLastFrame node when: - You need to extract the final frame from a video for use in your workflow. ## How to use it ### Basic Setup 1. Add a ExtractLastFrame node to your workflow 1. Connect an input to the video parameter or manually select a video 1. Connect the last_frame_image output to other nodes that require image input ### Parameters - **video**: The video content to extract the last frame from (supports VideoArtifact and VideoUrlArtifact) ### Outputs - **last_frame_image**: The the last frame from the video, output as an ImageUrlArtifact ## Example When working with image to video models you might want to chain several short video clips together to make one longer video. 1. Add a ExtractLastFrame node to your workflow 1. Connect the output from an image to video node to the **video** input on the ExtractLastFrame node 1. When the ExtractLastFrame node or workflow runs, the last frame from the video will be displayed in the node ## Important Notes - Supports common video formats (mp4, avi, mov, etc.) - The video player controls allow you to play, pause, and scrub through the video ## Common Issues - **No Video Showing**: Make sure you've properly connected a video source to this node # FlipVideo ## What is it? The FlipVideo node flips your video horizontally, vertically, or both directions. This creates a mirror effect that can be useful for correcting orientation or creating artistic effects. ## When would I use it? Use the FlipVideo node when: - You need to correct video that was recorded upside down or backwards - You want to create mirror effects for artistic purposes - You need to flip footage to match other video clips - You're creating content that requires flipped orientation - You want to create symmetrical video effects - You need to correct camera orientation issues ## How to use it ### Basic Setup 1. Add a FlipVideo node to your workflow 1. Connect a video source to the "video" input 1. Choose the flip direction (horizontal, vertical, or both) 1. Run the workflow to flip the video ### Parameters - **video**: The video content to flip (supports VideoArtifact and VideoUrlArtifact) - **direction**: Flip direction (default: "horizontal") - "horizontal" = flip left to right (mirror effect) - "vertical" = flip top to bottom (upside down) - "both" = flip both horizontally and vertically - **processing_speed**: Balance between processing speed and output quality (default: "balanced") - **fast**: Fastest processing, lower quality (ultrafast preset, CRF 30) - **balanced**: Good balance of speed and quality (medium preset, CRF 23) - **quality**: Highest quality, slower processing (slow preset, CRF 18) ### Outputs - **video**: The flipped video, available as output to connect to other nodes ## Example Imagine you want to create a mirror effect on a video: 1. Add a FlipVideo node to your workflow 1. Connect the video output from a LoadVideo node to the FlipVideo's "video" input 1. Set "direction" to "horizontal" to create a mirror effect 1. Run the workflow - the video will be flipped horizontally 1. The output filename will be `{original_filename}_flipped_horizontal.{format}` ## Important Notes - The FlipVideo node uses FFmpeg with high-quality flip processing - Horizontal flip creates a mirror effect (left becomes right) - Vertical flip turns the video upside down - Both directions flip the video in both axes - The original audio track is preserved - Processing time depends on video length and resolution - Logs are available for debugging processing issues ## Direction Recommendations - **For mirror effects**: Use "horizontal" - creates left-to-right mirror - **For upside down correction**: Use "vertical" - flips top to bottom - **For complete rotation**: Use "both" - flips in both directions - **For camera correction**: Use appropriate direction based on how the camera was oriented ## Common Issues - **Processing Timeout**: Large videos may take longer to process; the node has a 5-minute timeout - **Wrong Direction**: Make sure you've selected the correct flip direction for your needs - **No Video Input**: Make sure a video source is connected to the "video" input - **FFmpeg Errors**: Check the logs parameter for detailed error information if processing fails # HoldVideoFrames ## What is it? The HoldVideoFrames node creates a stepped video effect by holding each frame for a specified number of frames. This creates a stop-motion or frame-stepping effect that can add artistic interest to your video content. ## When would I use it? Use the HoldVideoFrames node when: - You want to create a stop-motion animation effect - You need to slow down fast-moving content for better visibility - You're creating artistic video effects with frame manipulation - You want to add a retro or vintage feel to your video - You need to create dramatic slow-motion effects - You're making content that requires frame-by-frame analysis ## How to use it ### Basic Setup 1. Add a HoldVideoFrames node to your workflow 1. Connect a video source to the "video" input 1. Set the "hold_frames" parameter to specify how many frames to hold 1. Run the workflow to create the frame-holding effect ### Parameters - **video**: The video content to apply frame holding to (supports VideoArtifact and VideoUrlArtifact) - **hold_frames**: Number of frames to hold (1-10, default: 2) - 1 = no effect (normal playback) - 2 = each frame held for 2 frames (slower playback) - 5 = each frame held for 5 frames (much slower playback) - 10 = each frame held for 10 frames (very slow playback) - **processing_speed**: Balance between processing speed and output quality (default: "balanced") - **fast**: Fastest processing, lower quality (ultrafast preset, CRF 30) - **balanced**: Good balance of speed and quality (medium preset, CRF 23) - **quality**: Highest quality, slower processing (slow preset, CRF 18) ### Outputs - **video**: The video with frame holding effect applied, available as output to connect to other nodes ## Example Imagine you want to create a stop-motion effect on a video: 1. Add a HoldVideoFrames node to your workflow 1. Connect the video output from a LoadVideo node to the HoldVideoFrames's "video" input 1. Set "hold_frames" to 3 to hold each frame for 3 frames 1. Run the workflow - the video will have a stepped, stop-motion effect 1. The output filename will be `{original_filename}_hold_3_frames.{format}` ## Important Notes - The HoldVideoFrames node uses FFmpeg with high-quality frame processing - The effect reduces the effective frame rate by the hold_frames value - The original audio track is preserved but may not sync perfectly with the visual effect - Processing time depends on video length and hold_frames value - Logs are available for debugging processing issues ## Effect Recommendations - **Subtle slow motion**: Use hold_frames 2-3 for gentle frame holding - **Stop-motion effect**: Use hold_frames 4-6 for noticeable stepping - **Dramatic slow motion**: Use hold_frames 7-10 for very slow playback - **Frame analysis**: Use hold_frames 2-3 to slow down fast content for review ## Common Issues - **Processing Timeout**: Large videos may take longer to process; the node has a 5-minute timeout - **Effect Too Strong**: Reduce hold_frames value if the effect is too dramatic - **Audio Sync Issues**: The frame holding may affect audio synchronization - **No Video Input**: Make sure a video source is connected to the "video" input - **FFmpeg Errors**: Check the logs parameter for detailed error information if processing fails # LoadVideo ## What is it? The LoadVideo is a simple building block that lets you bring a video into your workflow. Think of it as picking up a video file so you can use it in your project. ## When would I use it? Use this node when you want to: - Use a video that was created by another node - Pass a video to other nodes in your workflow - Display a video as part of your project - Load video files from disk into your workflow ## How to use it ### Basic Setup 1. Add the LoadVideo to your workflow 1. Connect it to a source of videos or use the file browser to select a video file ### Parameters - **video**: The video to load (this can be connected to an output from another node or loaded from a file) ### Outputs - **video**: The loaded video that can be used by other nodes in your flow ## Example Imagine you want to load a video file from your computer: 1. Add a LoadVideo node to your workflow 1. Click the file browser icon to select a video file from your computer 1. The LoadVideo will make the video available to use in the rest of your workflow ## Important Notes - The LoadVideo simply passes the video through - it doesn't change the video itself - You can click the file browser icon to select a video from your computer - The video preview can be expanded by clicking the expander icon - Supports common video formats (mp4, avi, mov, etc.) ## Common Issues - **No Video Showing**: Make sure you've properly connected a video source to this node or selected a file - **Unsupported Format**: Check that your video file is in a supported format # ResizeVideo ## What is it? The ResizeVideo node resizes video content using multiple scaling modes (width, height, percentage, or width and height) with FFmpeg. It can preserve aspect ratio, pad with a background color, or crop to fill depending on your fit mode. ## When would I use it? Use the ResizeVideo node when: - You need to scale videos by a precise percentage - You want to target a specific width or height while preserving aspect ratio - You need to fit videos into exact dimensions with padding or cropping - You want consistent resizing controls across video workflows ## How to use it ### Basic Setup 1. Add a ResizeVideo node to your workflow 1. Connect a video source to the "video" input 1. Choose a "resize_mode" 1. Configure the size parameters for that mode 1. Run the workflow to resize the video ### Parameters - **video**: The video content to resize (supports VideoArtifact and VideoUrlArtifact) - **resize_mode**: How the video should be resized - `width`: Resize to a target width while preserving aspect ratio - `height`: Resize to a target height while preserving aspect ratio - `percentage`: Resize by a percentage of the original size - `width and height`: Resize to exact dimensions with fit options - **target_size**: Target size in pixels for width/height modes (1-8000) - **percentage**: Resize percentage (1-500, default: 100) - **target_width**: Target width in pixels for width and height mode - **target_height**: Target height in pixels for width and height mode - **fit_mode**: How to fit the video within the target dimensions - `fit`: Preserve aspect ratio and add padding (letterboxing) - `fill`: Preserve aspect ratio and crop to fill the target - `stretch`: Ignore aspect ratio and stretch to the target - **background_color**: Background color for letterboxing in fit mode (hex) - **scaling_algorithm**: The scaling algorithm used for resizing (default: "bicubic") - `neighbor`, `bilinear`, `bicubic`, `lanczos` - **lanczos_parameter**: Fine-tune the Lanczos scaling algorithm (1.0-10.0, default: 3.0) Only affects the output when `scaling_algorithm` is set to "lanczos". ### Outputs - **resized_video**: The resized video content, available as output to connect to other nodes ## Example To resize a video to 1280x720 while preserving aspect ratio with padding: 1. Set **resize_mode** to `width and height` 1. Set **target_width** to `1280` 1. Set **target_height** to `720` 1. Set **fit_mode** to `fit` 1. Set **background_color** to `#000000` ## Important Notes - The ResizeVideo node uses FFmpeg for video processing - Dimensions are automatically adjusted to be divisible by 2 for codec compatibility - The node supports common video formats (mp4, avi, mov, etc.) - The resized video maintains the original audio track - Logs are available for debugging processing issues # ReverseVideo ## What is it? The ReverseVideo node reverses the playback direction of your video, making it play backwards. It can also handle audio reversal or muting depending on your needs. ## When would I use it? Use the ReverseVideo node when: - You want to create backwards playback effects - You need to reverse footage for artistic or creative purposes - You're creating special effects that require reverse motion - You want to add surreal or dreamlike qualities to your video - You need to reverse specific segments for editing purposes - You're making content that requires backwards motion analysis ## How to use it ### Basic Setup 1. Add a ReverseVideo node to your workflow 1. Connect a video source to the "video" input 1. Choose how to handle audio (reverse, mute, or keep original) 1. Run the workflow to reverse the video ### Parameters - **video**: The video content to reverse (supports VideoArtifact and VideoUrlArtifact) - **audio_handling**: How to handle audio (default: "reverse") - "reverse" = reverse both video and audio (recommended) - "mute" = reverse video only, no audio - "keep" = reverse video, keep original audio (not recommended) - **processing_speed**: Balance between processing speed and output quality (default: "balanced") - **fast**: Fastest processing, lower quality (ultrafast preset, CRF 30) - **balanced**: Good balance of speed and quality (medium preset, CRF 23) - **quality**: Highest quality, slower processing (slow preset, CRF 18) ### Outputs - **video**: The reversed video, available as output to connect to other nodes ## Example Imagine you want to create a backwards playback effect: 1. Add a ReverseVideo node to your workflow 1. Connect the video output from a LoadVideo node to the ReverseVideo's "video" input 1. Set "audio_handling" to "reverse" to reverse both video and audio 1. Run the workflow - the video will play backwards 1. The output filename will be `{original_filename}_reversed_reverse.{format}` ## Important Notes - The ReverseVideo node uses FFmpeg with high-quality reverse processing - Reversing both video and audio creates the most natural backwards effect - Muting audio is recommended if you want to avoid strange backwards audio - The "keep" option may create audio sync issues - Processing time depends on video length - Logs are available for debugging processing issues - The node automatically detects if the video has audio and handles it appropriately - Videos without audio will be processed correctly without errors ## Audio Handling Recommendations - **For natural backwards effect**: Use "reverse" - both video and audio play backwards - **For silent backwards effect**: Use "mute" - video plays backwards with no audio - **For custom audio**: Use "mute" and add your own audio track separately ## Common Issues - **Processing Timeout**: Large videos may take longer to process; the node has a 5-minute timeout - **Strange Audio**: If audio sounds odd, try using "mute" instead of "reverse" - **Audio Sync Issues**: The "keep" option may cause audio/video synchronization problems - **No Video Input**: Make sure a video source is connected to the "video" input - **FFmpeg Errors**: Check the logs parameter for detailed error information if processing fails # SaveVideo ## What is it? The SaveVideo node allows you to save video content from your workflow to a file on your computer. ## When would I use it? Use the SaveVideo node when: - You want to export video results from your workflow - You need to save processed video content to disk - You want to create video files that can be used outside of your workflow - You need to archive video outputs for later use ## How to use it ### Basic Setup 1. Add a SaveVideo node to your workflow 1. Connect a video source to the "video" input 1. Optionally specify the output filename and path 1. Run the workflow to save the video file ### Parameters - **video**: The video content to save (supports VideoArtifact, VideoUrlArtifact, and dict) - **output_path**: The filename and path where the video should be saved (default: "griptape_nodes.mp4") ### Outputs - **output_path**: The path where the video was saved ## Example Imagine you want to save a video that was processed in your workflow: 1. Add a SaveVideo node to your workflow 1. Connect the video output from another node to the SaveVideo's "video" input 1. Set the "output_path" to your desired filename (e.g., "my_video.mp4") 1. Run the workflow - the video will be saved to the specified location ## Important Notes - The SaveVideo node supports common video formats (.mp4, .avi, .mov, etc.) - The output path can include a directory structure (e.g., "videos/output.mp4") - If no extension is provided, .mp4 will be used by default - The node will create directories if they don't exist - You can use the save button in the UI to browse for a save location ## Common Issues - **Permission Denied**: Make sure you have write permissions to the specified directory - **Invalid Path**: Check that the output path is valid and the directory exists - **Unsupported Format**: Ensure you're using a supported video file extension - **No Video Input**: Make sure a video source is connected to the "video" input # SplitVideo ## What is it? The SplitVideo node allows you to split a video into multiple segments using timecodes. It uses AI-powered parsing to understand various timecode formats and creates separate video files for each segment while maintaining high quality. ## When would I use it? Use the SplitVideo node when: - You need to break a long video into smaller, manageable segments - You want to extract specific scenes or sections from a video - You're creating video clips for social media or different platforms - You need to separate different parts of a recording (intro, main content, outro) - You want to create highlight reels from longer footage - You're preparing video content for different audiences or purposes ## How to use it ### Basic Setup 1. Add a SplitVideo node to your workflow 1. Connect a video source to the "video" input 1. Enter timecodes in the "timecodes" parameter 1. Run the workflow to split the video into segments ### Parameters - **video**: The video to split (supports VideoArtifact and VideoUrlArtifact) - **timecodes**: Timecodes to split the video at (required) Supports various formats: **Simple timecode ranges:** ``` 00:00:00:00-00:01:00:00 00:01:00:00-00:02:30:00 ``` **Timecodes with titles:** ``` 00:00:00:00-00:00:04:07|Segment 1: Introduction 00:00:04:08-00:00:08:15|Segment 2: Main Content ``` **JSON format:** ``` [ {"start": "00:00:00:00", "end": "00:01:00:00", "title": "Intro"}, {"start": "00:01:00:00", "end": "00:02:30:00", "title": "Main Content"} ] ``` ### Outputs - **split_videos**: List of split video segments (VideoUrlArtifact array) - **logs**: Processing logs and detailed events ## Example Imagine you want to split a 10-minute video into three segments: 1. Add a SplitVideo node to your workflow 1. Connect the video output from a LoadVideo node to the SplitVideo's "video" input 1. Enter timecodes in the "timecodes" parameter: ``` 00:00:00:00-00:02:30:00|Segment 1: Introduction 00:02:30:00-00:07:30:00|Segment 2: Main Content 00:07:30:00-00:10:00:00|Segment 3: Conclusion ``` 1. Run the workflow - the video will be split into three separate files 1. Each segment will be available as a separate output in the split_videos list ## Important Notes - The SplitVideo node uses AI-powered parsing to understand various timecode formats - Timecodes should be in SMPTE format (HH:MM:SS:FF) where FF represents frames - The node automatically detects video frame rate and drop frame settings - Each segment maintains the original video quality and audio - Processing time depends on video length and number of segments - The node uses FFmpeg for high-quality video processing - Logs provide detailed information about the splitting process ## Timecode Format Guidelines - **Frame rates**: The node automatically detects 24fps, 25fps, 29.97fps, 30fps, 50fps, 59.94fps, and 60fps - **Drop frame**: Automatically detected for 29.97fps and 59.94fps videos - **Frame numbers**: Use 00-23 for 24fps, 00-24 for 25fps, 00-29 for 30fps, etc. - **Separators**: Use hyphens (-) between start and end times, pipes (|) for titles - **Titles**: Optional but recommended for better organization ## Common Issues - **Invalid Timecodes**: Make sure timecodes are in correct SMPTE format (HH:MM:SS:FF) - **Overlapping Segments**: Ensure segments don't overlap in time - **Processing Timeout**: Large videos may take longer to process; the node has a 5-minute timeout - **No Video Input**: Make sure a video source is connected to the "video" input - **Empty Segments**: Check that start time is before end time for each segment - **FFmpeg Errors**: Check the logs parameter for detailed error information if processing fails # VideoColorMatch ## What is it? The VideoColorMatch node transfers color characteristics from a reference image to a video using advanced color matching algorithms. By default, it uses a fast HALD CLUT (Color Lookup Table) method that applies the color transformation to the entire video in one pass, making it 10-50x faster than traditional frame-by-frame processing. This allows you to apply color grading to videos by matching the color palette of a reference image, useful for creating consistent visual styles, matching footage to a specific aesthetic, or applying cinematic color grading. ## When would I use it? Use the VideoColorMatch node when: - You want to apply color grading from a reference image to your video - You need to match the color palette of multiple video clips for consistency - You want to give your video a cinematic look based on a movie still or poster - You're creating content that needs to match a specific aesthetic or mood - You want to apply vintage or stylized color effects to modern footage - You need to transfer the color characteristics from a photograph to video footage ## How to use it ### Basic Setup 1. Add a VideoColorMatch node to your workflow 1. Connect a reference image to "reference_image" (the image with the color palette you want to transfer) 1. Connect your video to "target_video" (the video you want to modify) 1. Select a color transfer method 1. Adjust the strength parameter to control the intensity 1. Run the workflow to apply the color transfer ### Parameters #### Inputs - **reference_image**: The reference image providing the color palette to transfer (supports ImageArtifact and ImageUrlArtifact) - **target_video**: The video to apply color transfer to (supports VideoArtifact and VideoUrlArtifact) #### Color Match Settings - **transfer_method**: The processing method to use - **ffmpeg-haldclut**: Fast HALD CLUT-based transfer (10-50x faster, default) - Applies color matching once to a lookup table, then uses ffmpeg to process the entire video in one pass - **frame-by-frame**: Process each frame individually (slower, more memory intensive) - Extracts and processes each frame separately using the color-matcher library - **transfer_algorithm**: The color transfer algorithm to use - **mkl**: Monge-Kantorovich Linearization (fast, good quality, default) - **hm**: Histogram Matching (fast, moderate quality) - **reinhard**: Reinhard et al. color transfer (fast, good quality) - **mvgd**: Multi-Variate Gaussian Distribution (medium speed, good quality) - **hm-mvgd-hm**: Compound method (slower, best quality) - **hm-mkl-hm**: Alternative compound method (slower, best quality) - **strength** (0.0-10.0, default: 1.0): Controls the intensity of the color transfer - 0.0 = no change (original video colors) - 1.0 = full color transfer (default) - Values > 1.0 exaggerate the effect ### Outputs - **output**: The color-matched video with transferred color characteristics (VideoUrlArtifact) The output video preserves: - Original format (MP4, MOV, etc.) - Original resolution and aspect ratio - Original frame rate - Original audio track (encoded as AAC at 192kbps) ## Example ### Basic Color Grading Apply the color palette from a cinematic movie poster to your video: 1. Add a VideoColorMatch node to your workflow 1. Connect a LoadImage node with your reference movie poster to "reference_image" 1. Connect a LoadVideo node with your video to "target_video" 1. Keep the default "mkl" transfer_algorithm 1. Set "strength" to 1.0 for full color transfer 1. Run the workflow - your video will match the color palette of the movie poster ### Subtle Vintage Effect Apply a subtle vintage film look: 1. Load a vintage photograph as the reference image 1. Connect your modern video as the target 1. Set "transfer_algorithm" to "reinhard" for natural luminance preservation 1. Set "strength" to 0.7 for a subtle effect 1. Run the workflow - the result will have a nostalgic vintage aesthetic ### Experimenting with Methods Try different algorithms to see which aesthetic you prefer: 1. Process a short clip (2-3 seconds) with "mkl" algorithm 1. Process the same clip with "hm-mvgd-hm" algorithm 1. Compare the results to choose your preferred look 1. Apply to the full video ## Important Notes - **Processing Time**: - **ffmpeg-haldclut (default)**: Very fast processing - a 10-second 1080p video at 30fps typically processes in seconds. Recommended for most use cases. - **frame-by-frame**: Slower processing - the same 10-second video may take 3-5 minutes. Useful if you need pixel-perfect control or encounter issues with the HALD CLUT method. - **Progress Bar**: - **ffmpeg-haldclut**: Shows 10% for HALD generation, 20% for color matching, and 70% for video processing. - **frame-by-frame**: Shows 0-90% for frame processing and 90-100% for video reassembly. - **Video Properties**: All original video properties (format, resolution, frame rate, duration) are preserved in the output. - **Audio Preservation**: The original audio track is preserved (copied with ffmpeg-haldclut, re-encoded with AAC at 192kbps for frame-by-frame). - **Memory Usage**: Both methods process efficiently - ffmpeg-haldclut streams through video, frame-by-frame processes sequentially. ## Algorithm Details Different algorithms produce distinct aesthetic results: - **mkl**: Fast optimal transport-based method. Good balance of speed and quality. Best for general use. - **hm**: Matches color distributions using histograms. Can produce more dramatic color shifts. - **reinhard**: Statistical color transfer that preserves luminance structure. Good for natural-looking results. - **mvgd**: Models colors as Gaussian distributions. Better at capturing subtle color relationships. - **hm-mvgd-hm**: Compound method applying histogram matching, then MVGD, then histogram matching again for refined results. - **hm-mkl-hm**: Compound method using MKL as the middle step. Alternative approach for high-quality results. ## Tips and Best Practices ### Choosing a Reference Image ✓ **Good reference images:** - Clear, well-exposed images with the desired color palette - Images with similar lighting conditions to your video - Reference images that represent the final look you want ✗ **Avoid:** - Extremely dark or overexposed images - Images with very different lighting scenarios than your video - References with unnatural or oversaturated colors (unless that's your goal) ### Strength Guidelines | Strength | Use Case | | -------- | ---------------------------------------------------- | | 0.3-0.5 | Very subtle color correction, slight mood adjustment | | 0.6-0.8 | Noticeable but natural color grading | | 0.9-1.1 | Full color transfer, dramatic look change | | 1.2-2.0 | Stylized, artistic effects | | > 2.0 | Experimental, heavily exaggerated results | ### Transfer Method Selection **When to use ffmpeg-haldclut (default):** ✓ For most use cases - it's fast and produces excellent results ✓ When processing longer videos or high-resolution footage ✓ When you want to preview results quickly ✓ For batch processing multiple videos **When to use frame-by-frame:** - If you encounter unexpected visual artifacts with HALD CLUT (rare) - For troubleshooting or comparison purposes - When you need to verify processing against the legacy method ### Workflow Recommendations 1. **Test with short clips first**: Process a 2-3 second clip before running on full videos to preview the effect 1. **Algorithm comparison**: Try 2-3 different color algorithms with the same reference to see which aesthetic you prefer 1. **Batch processing**: Use the same reference image across multiple video clips for consistent look 1. **Strength refinement**: Start at 1.0, then adjust up or down based on results ## Common Issues ### Colors look too extreme - **Solution**: Reduce "strength" to 0.6-0.8 - Or choose a less saturated reference image - Or try "reinhard" algorithm which preserves more original luminance ### Not enough color change - **Solution**: Increase "strength" to 1.2-1.5 - Or choose a reference with more distinct colors - Or try "hm-mvgd-hm" algorithm for stronger transfer ### Processing takes too long - **Solution**: Ensure you're using "ffmpeg-haldclut" transfer method (default, 10-50x faster) - Or use "mkl" color algorithm (fastest color transfer algorithm) - Or pre-trim your video to the desired section using Split Video node - Or reduce video resolution before processing using Resize Video node ### Output looks different than expected - **Solution**: Try different color matching algorithms - each produces different results - Ensure your reference image has the color characteristics you actually want - Adjust strength to find the sweet spot for your specific content ## Performance Considerations ### HALD CLUT Method (Default - Recommended) Processing time with **ffmpeg-haldclut** is very fast and depends primarily on: - **Video length**: Scales linearly with duration - **Resolution**: Higher resolutions take longer but still much faster than frame-by-frame - **Color algorithm**: All algorithms are fast since color matching happens once on the HALD CLUT Approximate processing time for 1080p video at 30fps on modern hardware: - 5 seconds: ~2-5 seconds - 10 seconds: ~3-8 seconds - 30 seconds: ~10-20 seconds - 60 seconds: ~20-40 seconds ### Frame-by-Frame Method (Legacy) Processing time with **frame-by-frame** depends on: - **Video length**: Longer videos take proportionally longer to process - **Resolution**: Higher resolution videos (4K vs 1080p) take significantly longer - **Frame rate**: Higher frame rates mean more frames to process - **Color algorithm**: Compound algorithms (hm-mvgd-hm, hm-mkl-hm) take longer than simple algorithms (mkl, hm) Approximate processing time for 1080p video at 30fps on modern hardware: - 5 seconds (150 frames): ~1-2 minutes - 10 seconds (300 frames): ~3-5 minutes - 30 seconds (900 frames): ~8-12 minutes ## Related Nodes - **Color Match** (Image category) - Single image color transfer, same algorithms - **Adjust Video EQ** (Video category) - Manual brightness/contrast/saturation adjustments - **Add Color Curves** (Video category) - Apply preset color grading curves to video ## Use Case Examples ### Cinematic Grading - **Reference**: Movie still with teal/orange color grading - **Algorithm**: hm-mvgd-hm - **Strength**: 0.9 - **Result**: Professional cinematic look with complementary color palette ### Vintage Film Effect - **Reference**: 1970s photograph with warm, faded colors - **Algorithm**: reinhard - **Strength**: 0.7 - **Result**: Nostalgic vintage aesthetic with authentic film look ### Day-to-Night Conversion - **Reference**: Nighttime city photo with cool blue tones - **Algorithm**: mkl - **Strength**: 1.2 - **Result**: Daytime video with nighttime color palette and mood ### Consistent Multi-Clip Grading - **Reference**: Single color-graded frame from your best clip - **Algorithm**: mkl - **Strength**: 1.0 - **Result**: All clips match the color palette for seamless editing