A Technical Guide to WilmerAI Workflow Variables
This guide provides a comprehensive and validated reference to all dynamic variables available within the WilmerAI workflow system. It has been corrected against the system's source code to ensure accuracy and prevent the generation of invalid workflows.
Core Principle: Dynamic Substitution
The WorkflowVariableManager service is responsible for replacing placeholders in your workflow's string properties
with real-time data. This happens automatically before a node is executed.
Part 1: How to Use Variables
Standard Formatting ({...})
By default, simply place the variable name in curly braces within any valid string property. The system uses Python's
str.format() method for substitution.
{
"title": "Greet User with Time",
"type": "Standard",
"systemPrompt": "Today is {todays_date_pretty}. The current time is {current_time_12h}.",
"prompt": "Please respond to the user's message: {chat_user_prompt_last_one}"
}
Jinja2 Templating (Advanced)
For advanced logic like loops or conditionals, add "jinja2": true to the node's configuration. This allows you to
use the full Jinja2 syntax.
{
"title": "Render Conversation History",
"type": "Standard",
"jinja2": true,
"prompt": "Here is the conversation so far:\n\n{% for message in messages %}{{ message.role | capitalize }}: {{ message.content }}\n{% endfor %}\n\nHow can I help you now?"
}
⚠️ Critical Limitation: Configuration vs. Content
Variable substitution is performed by node handlers on specific fields, not by the core workflow engine. This creates a critical distinction between what can and cannot be a variable.
The principle is: Configuration keys are static, content keys can be dynamic.
✅ Fields that SUPPORT variables (Content)
You CAN use variables in fields that are treated as content for the node to process.
promptsystemPrompttitlepromptToSearch(and similar input fields on specialized nodes)filepath(inGetCustomFileandSaveCustomFile, the handlers process variables for this field)
⚠️ Fields with SPECIAL variable support (Early Substitution)
endpointName and preset now support a LIMITED form of variable substitution. These fields use early variable substitution, which means they are processed BEFORE nodes execute. This creates important limitations:
ONLY these variables work in endpointName and preset:
{agent#Input}- Values passed from parent workflows viascoped_variables{custom_variable}- Static variables defined at the top level of your workflow JSON- Date/time variables like
{todays_date_pretty},{current_time_12h}, etc. {chat_user_prompt_last_one}and other conversation history variables{time_context_summary}(if a discussionId is present)
These variables DO NOT work in endpointName and preset:
- ❌
{agent#Output}- These don't exist yet since nodes haven't executed! - ❌ Any variable that depends on the output of another node
Example of CORRECT usage:
{
"coding_endpoint": "Creative-Fast-Endpoint",
"nodes": [{
"type": "Standard",
"endpointName": "{coding_endpoint}",
"preset": "{agent1Input}"
}]
}
Example of INCORRECT usage:
{
"nodes": [{
"type": "Standard",
"endpointName": "{agent1Output}", // ❌ WILL FAIL - agent1Output doesn't exist yet!
"prompt": "..."
}]
}
❌ Fields that DO NOT SUPPORT variables (Configuration)
You CANNOT use variables in fields that define a node's configuration. These fields are read by the workflow engine before any processing occurs. Always use hardcoded, static string values for them.
typereturnToUser(This is a booleantrue/false, not a string).workflowName- Keys within a
conditionalWorkflowsobject. jinja2(This is a booleantrue/false).
Adding Custom Variables (The Correct Way)
You can add your own reusable variables by adding a new key-value pair to the top level of your workflow JSON file. Any
key that is not "nodes" will automatically become an available variable for use in content fields.
Correct Example my_workflow.json:
{
"shared_persona": "You are a witty AI assistant who loves puns.",
"nodes": [
{
"title": "Respond to User",
"type": "Standard",
"endpointName": "Creative-Fast-Endpoint",
"systemPrompt": "{shared_persona}",
"prompt": "{chat_user_prompt_last_one}",
"returnToUser": true
}
]
}
Part 2: Complete Variable & Placeholder Reference
This is an exhaustive list of all available variables, validated against workflow_variable_manager.py.
Custom Workflow Variables
{custom_variable}: The value of any top-level key in the workflow's JSON file (except for"nodes").
Data Flow Variables
{agent#Output}: The string result from a previous node in the same workflow. The#corresponds to the node's position (1-indexed). For example,{agent1Output}is the result of the first node.{agent#Input}: A value passed from a parent workflow into a child workflow via thescoped_variablesproperty of aCustomWorkflownode. The#corresponds to the value's position in thescoped_variableslist (1-indexed).
Conversation History Variables
{chat_user_prompt_last_one}: The raw text of the last message in the conversation.{chat_user_prompt_last_two}: Raw text of the last 2 turns.{chat_user_prompt_last_three}: Raw text of the last 3 turns.{chat_user_prompt_last_four}: Raw text of the last 4 turns.{chat_user_prompt_last_five}: Raw text of the last 5 turns.{chat_user_prompt_last_ten}: Raw text of the last 10 turns.{chat_user_prompt_last_twenty}: Raw text of the last 20 turns.{chat_user_prompt_n_messages}: Raw text of the last N messages, where N is set by the node propertynMessagesToIncludeInVariable(defaults to 5 if not specified). This is the preferred approach for custom message counts, as it allows any value of N without requiring a new hardcoded variable.{templated_user_prompt_n_messages}: Same as{chat_user_prompt_n_messages}, but formatted with the LLM's chat template. Controlled by the samenMessagesToIncludeInVariableproperty.{chat_user_prompt_estimated_token_limit}: Recent messages as a raw string, selected by estimated token budget instead of message count. The budget is set by the node propertyestimatedTokensToIncludeInVariable(defaults to 2048). Starting from the most recent message and working backwards, messages are accumulated until the estimated token count would exceed the budget. At least one message is always included, even if it alone exceeds the limit. This is useful when you want to fill a prompt with as much conversation context as will fit, without knowing how many messages that corresponds to. Note: token counts are estimated (not exact) using a heuristic that intentionally overestimates.{templated_user_prompt_estimated_token_limit}: Same as{chat_user_prompt_estimated_token_limit}, but formatted with the LLM's chat template. Controlled by the sameestimatedTokensToIncludeInVariableproperty.{chat_user_prompt_min_n_max_tokens}: A combination of the N-messages and token-limit approaches. This variable pulls a minimum number of messages (set by the node propertyminMessagesInVariable, defaults to 5), then continues adding older messages as long as the accumulated estimated token count stays within the budget (set by the node propertymaxEstimatedTokensInVariable, defaults to 2048). The minimum message count always takes precedence: even if the minimum messages exceed the token budget, they are all included. Beyond the minimum, expansion stops when the next message would push the total past the token limit. This is useful when you want at least N messages of context but are willing to include more if the messages are short enough to fit within a token budget. For example, with"minMessagesInVariable": 5and"maxEstimatedTokensInVariable": 5000, you always get at least 5 messages. If those 5 messages only total 2000 estimated tokens, additional older messages are included until the 5000-token budget is reached (without exceeding it).{templated_user_prompt_min_n_max_tokens}: Same as{chat_user_prompt_min_n_max_tokens}, but formatted with the LLM's chat template. Controlled by the sameminMessagesInVariableandmaxEstimatedTokensInVariableproperties.{templated_user_prompt_last_one}: The last message, formatted with the LLM's chat template (e.g.,[INST]...[/INST]).{templated_user_prompt_last_two}: Last 2 turns, templated.{templated_user_prompt_last_three}: Last 3 turns, templated.{templated_user_prompt_last_four}: Last 4 turns, templated.{templated_user_prompt_last_five}: Last 5 turns, templated.{templated_user_prompt_last_ten}: Last 10 turns, templated.{templated_user_prompt_last_twenty}: Last 20 turns, templated.{chat_system_prompt}: The system prompt sent from the front-end client.{system_prompts_as_string}: All system messages from the conversation history concatenated into a single string.{messages}: The entire conversation history as a raw list of dictionaries (e.g.,[{'role': 'user', 'content': '...'}]). Note: This is available for both standard formatting and Jinja2, but it is most useful with Jinja2 for iterating over the conversation history.
Date & Time Variables
{todays_date_pretty}: e.g., "August 30, 2025"{todays_date_iso}: e.g., "2025-08-30"{YYYY_MM_DD}: e.g., "2025_08_30" (underscore-separated format, useful for filenames){current_time_12h}: e.g., "08:00 PM"{current_time_24h}: e.g., "20:00"{current_month_full}: e.g., "August"{current_day_of_week}: e.g., "Saturday"{current_day_of_month}: e.g., "30"
Context & Memory Variables
{Discussion_Id}: The unique identifier for the current conversation/discussion. This is useful for creating per-conversation files or organizing data by session. If no discussion ID is present, this will be an empty string.{time_context_summary}: A natural language summary of the conversation's timeline (e.g., "The user started this conversation a few minutes ago").
Using {Discussion_Id} and {YYYY_MM_DD} for Dynamic File Paths
These variables are particularly useful with the GetCustomFile and SaveCustomFile nodes, which support variable
substitution in their filepath fields. This allows you to create per-conversation or date-based file storage.
Example: Per-Conversation Notes
{
"type": "GetCustomFile",
"filepath": "/data/sessions/{Discussion_Id}_notes.txt"
}
Example: Daily Logs
{
"type": "SaveCustomFile",
"filepath": "/data/logs/{YYYY_MM_DD}_report.txt",
"content": "{agent1Output}"
}
Example: Combined Session and Date
{
"type": "SaveCustomFile",
"filepath": "/data/{YYYY_MM_DD}/{Discussion_Id}_output.txt",
"content": "Generated at {current_time_12h}:\n\n{agent1Output}"
}
See the GetCustomFile and SaveCustomFile node documentation for more details.
{current_chat_summary}: ⚠️ UNAVAILABLE VARIABLE: The helper functiongenerate_chat_summary_variablesthat populates this is not called by the main variable generation logic. Do not use{current_chat_summary}as it will not be substituted. To get the summary, you must use a dedicated node likeGetCurrentSummaryFromFile.
Special Placeholders (Context-Specific)
These are not standard variables but are special keywords replaced within specific node types or sub-workflows. They do
not use curly braces. Their processing logic is not present in the core WorkflowVariableManager and is handled by
specialized workflows (e.g., those called by the QualityMemory node).
[TextChunk]: Represents a block of text to be processed into a memory.[IMAGE_BLOCK]: Represents the AI-generated description of an image within anImageProcessorcontext.[Memory_file],[Full_Memory_file],[Chat_Summary]: Represent various memory files for file-based memory generation.[LATEST_MEMORIES],[CHAT_SUMMARY]: Used specifically by thechatSummarySummarizernode type.