## Agent components
An agent consists of the following components:
* **UI**: the interface that users interact with.
* UIs are designed using elements like input fields, buttons, and embeds, including support for guided workflows with pagination.
* They give users a structured, intuitive way to engage with agents, from simple chat interfaces to rich, interactive tools.
* The UI is optional. If you do not need to receive user input or display output, you can build an agent without a UI by only using the blueprint.
* **Blueprint**: a visual map of the agent's business logic and behavior.
* Blueprints are created using a library of configurable blocks for tool calling, built-in RAG, text generation, classification, state management, and more.
* They define how the agent processes input, makes decisions, and takes action—enabling it to reliably orchestrate work across people, data, systems, and even other agents built in Writer.
An agent can also include:
* Custom Python code to extend its capabilities with more complex logic.
* [Secrets](/agent-builder/secrets) to store sensitive information like API keys, passwords, and other credentials and use them in your agent.
Check out the [demo agent walkthrough](/agent-builder/demo-agent) to see how these components work together in practice.
### Connecting components
Agents work by passing data between the UI and blueprint. The following variables are available to each block in the blueprint:
* `@{result}`: Access the output from the previous block in your blueprint
* `@{payload}`: Access data from UI interactions that triggered the blueprint, like button clicks or user messages
* `@{state_variable}`: Access values stored in your agent's state
* `@{vault.secret_name}`: Access secrets stored in your agent's secrets
These variables let you chain blocks together and create dynamic workflows. Learn more in [Using Data from Previous Blocks](/agent-builder/execution-environment).
### Key capabilities
Agent Builder enables agentic workflows with automation and intelligence by providing the following capabilities:
* **Control Flow**: Blueprints support control structures including:
* Conditional branching based on input analysis or state conditions
* Iterative loops for processing collections of data or retrying operations
* Decision trees that route execution based on classification results or business rules
* **Memory**: Agents can use state, environment variables, and Knowledge Graphs to store and retrieve information across blocks and across agent sessions.
* **Knowledge Graph Integration**: Writer's Knowledge Graph provides:
* Contextual information that enhances agent responses and decision making
* Semantic search and content discovery
* Data storage and retrieval across agent sessions
* **Tool calling**: Agents can dynamically analyze requests and intelligently orchestrate external tools, supporting advanced reasoning patterns like ReAct for step-by-step problem solving.
* **Multi-agent workflows**: Built-in blocks enable integration with other Writer agents for complex multi-agent workflows.
### Agent state
Agents use the agent's state to communicate information across components. The state is a shared memory for each part of the agent: the UI, blueprint, and custom Python code can all read and write data to the state.
Learn more in [Agent state](/agent-builder/state).
## Blueprint-only agents
If you don't need to receive user input or display output, you can build an agent without a UI by only using the blueprint.
To run the blueprint without a UI Trigger, press the **Run blueprint** button in the top right of the blueprint.
Here's what happens when you add an API Trigger block:
### Endpoint creation
The block creates two HTTP endpoints using your agent and blueprint IDs:
* A synchronous endpoint for real-time responses
* An asynchronous endpoint for long-running tasks
### Request handling
When an external system calls your endpoint:
* The request body must contain an `inputs` field with your data
* Your blueprint receives this data as `@{result.inputs}`
* Your blueprint executes with this input data
You can also test your API trigger by sending a POST request to the API endpoint.
## Troubleshooting
### Common issues and solutions
#### I can't see or trigger my agent via the API
**Problem**: Your agent doesn't appear in the API or you get errors when trying to trigger it.
**Solution**: Check that your agent is deployed. Only deployed agents are accessible via the API.
#### I don't see my blueprint when looking via the API
**Problem**: Your blueprint doesn't show up in the list of available blueprints for your agent.
**Solution**: Ensure your blueprint starts with an **API Trigger** block. Only blueprints that begin with an API Trigger block are exposed via the API.
#### The blueprint isn't returning anything
**Problem**: Your blueprint executes but doesn't return any data to the API caller.
**Solution**: Make sure you have a [**Return Value**](/blueprints/returnvalue) block at the end of your blueprint workflow. This block is required to return data to the API caller.
#### My blueprint isn't receiving any input values
**Problem**: You can't access the data you sent in your API request.
**Solution**: Remember that your data is nested under the `inputs` key. Use `@{result.inputs.field_name}` instead of `@{result.field_name}`.
### Debugging tips
Since there are limited logs and execution visibility, use the **Return Value** block for debugging:
**Test payload structure**: Create a simple blueprint with just `API Trigger → Return Value` and set the return value to `@{result}`. This will show you exactly what data structure the blueprint is receiving.
**Test specific values**: Use `@{result.inputs}` to see just the inputs portion of your payload.
**Test state variables**: Return `@{state[key]}` to see the value of a specific state variable in your blueprint execution.
**Note**: Since `@{result}` is only available in the block immediately following the API Trigger, consider using a Set State block as your first block to store the payload data for debugging purposes.
## Next steps
* Learn how to [add file inputs and parse PDFs](/agent-builder/summarize-pdfs)
* Explore [tool calling](/agent-builder/tool-calling) for intelligent integrations with external tools
* Build [data tables with DataFrames](/agent-builder/dataframes) to display structured data
* Add [custom Python code](/agent-builder/python-code) to extend your agent's capabilities
This tutorial demonstrates how to build a chatbot that's connected to the Palmyra X5 model and integrates with a Knowledge Graph for domain-specific knowledge. It covers the following steps:
1. Adding a [**Chatbot** block](/components/chatbot) to the UI
2. Adding a [**Chat reply** block](/blueprints/chatreply) to the blueprint
3. Connecting a Knowledge Graph to the chatbot
## Add notes to your agents
You can add notes anywhere in an agent's Interface or Blueprint view.
To add a note or view existing notes, click the **Add note** button in the top right corner of the interface or blueprint.
Click anywhere in the interface or blueprint to add a note at that location. Type the note in the text box that appears and then click the **Send** button.
To view a specific note, click the note icon on the blueprint or interface. This opens the **Note** view to that particular note.
## See where users are working in real-time
If other users are working on the same interface or blueprint, you can see an icon on top of the block they're editing.
See [Style Agent Builder components with CSS](#style-agent-builder-components-with-css) for more information about specifically styling Agent Builder components.
The first argument is the name you want to give to the stylesheet, which you can reference if there are multiple stylesheets. The second argument is the path to the CSS file.
You can also import stylesheets during runtime by calling the `import_stylesheet` method from the `state` object. See [Switch stylesheets during runtime](#switch-stylesheets-during-runtime) for more information.
### Build the blueprint
Open the **Blueprint** tab to build the agent's blueprint. The blueprint contains:
* A **UI Trigger** that triggers blueprint execution when the user clicks the **Upload CSV** button.
* A **Python** block that processes the uploaded file and stores the data in a state variable.
### Preview the agent
Navigate to the **Preview** tab to see the agent in action. You should see a button to upload a CSV file and a table to display the processed data.
Upload a CSV file; see the [beginning of this example](#example%3A-csv-file-upload-and-processing) for a sample CSV file.
Once you click the **Process CSV** button, the agent processes the data and displays it in the DataFrame component.
## Best practices
1. **Use pandas for data processing**: Clean, aggregate, and transform data before display
2. **Keep DataFrames reasonably sized**: Use "Display row count" to control how many rows show simultaneously
3. **Enable appropriate features**: Only enable editing if users should modify data
4. **Consider text wrapping**: Toggle "Wrap text" based on your data content
5. **Use meaningful column names**: Pandas column names become the table headers
## Next steps
Try extending this example by:
* Adding more sophisticated pandas operations such as `groupby` and `pivot_tables`
* Connecting to real databases or APIs
* Creating calculated columns based on business rules
* Styling the component with custom CSS classes
# Agent Builder Demo Application
Source: https://dev.writer.com/agent-builder/demo-agent
This walkthrough covers the following topics:
* How to navigate the Agent Builder interface
* How the UI and blueprint work together through state variables
* How to use the **Classification** block to route workflows
* How to connect blocks using `@{result}`
* How to modify an existing agent to add new behavior
Once the agent is created, a new tab will open in your browser with the Agent Builder interface.
* **Interface**: The agent's UI, where you can edit the agent's appearance.
* **Blueprints**: The agent's blueprint, where you can edit the agent's behavior.
* **Vault**: The agent's secrets, where you can add secrets like API keys or passwords.
* **Preview**: Test the agent's behavior and preview what the user sees.
You can switch between views by clicking the tabs at the top of the page.
### Preview the agent's behavior
Before you start editing, preview the agent to see how it works:
* [**Interface**](#interface-view): What the user sees and interacts with.
* [**Blueprint**](#blueprints-view): A flowchart-like interface where you connect blocks of logic.
* [**Agent state**](#agent-state): A set of values that's shared between the UI and the blueprint. Both the UI and the blueprint can reference and update the state.
* **UI trigger**: A trigger from the UI that starts the agent's blueprint. In this case, the UI trigger is attached to click events on the **Draft response** button.
### Agent state
The [agent's state](/agent-builder/state) is a core component of the agent's behavior. It contains values that the UI and blueprint can access and update.
You can view the agent's state from any view by clicking the **State explorer** icon in the top right of the page.
#### Hidden sections
You won't see the hidden section for the results in the UI when you first load the agent. You can see that the section is in the UI from the **Interface Layers** view, which shows all the components in the UI. The hidden section has an icon next to it indicating that it's not visible.
Click that element in the Component tree to see its settings. Under **Visibility**, it's set to only show up once there is a value in the `review_response` state variable.
### Connecting the UI and the blueprint with state variables
To build full-featured agents, you must be able to connect the UI to the blueprint.
The agent's state is the main way to pass data between the UI and the blueprint. The state contains a set of values that the UI and blueprint can both reference and set. You can also use triggers to connect the UI to the blueprint.
Here are the places where the demo agent connects the UI to the blueprint and passes data between them:
The UI should now look like this:
### Example from a blueprint
Consider the following section of a blueprint that generates text based on a specific prompt. It shows two connected blocks:
* A **Text generation** block that generates text based on a specific prompt
* A **Set state** block that sets a state variable with the results of the **Text generation** block
* The **Text generation** block uses the state variable `product` to access information that the user input via the UI. It then generates text based on that information.
* The **Set state** block's execution environment includes the `result` variable from the **Text generation** block, which is the summary of the file. It sets a state variable with that result so that the UI can display the summary.
## Variables available to each block
The following table shows the variables that are available to each block in the execution environment. Not all variables are available in all blocks; for example, the `api_calls` variable is only available when the block makes an API call.
Many of these variables are primarily useful for internal use, debugging, and advanced use cases. However, the `result` variable is particularly useful for building workflows.
| Variable | Type | Description | Example |
| ---------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `api_calls` | `array[dict]` | Any Writer API calls that the agent made at this block, including the request and the response. For example, API calls from a **Text generation** block to the Writer [chat completions](/api-reference/completion-api/chat-completion) endpoint. | `[{'request': {'method': 'POST', ...}, 'response': {'status_code': 200, ...}}]` |
| `call_stack` | `dict` | The call stack of the agent. The key is the index of the block in the call stack and the value is the block ID. | `{0: "123abc", 1: "456def"}` |
| `context` | `dict` | The ID of the block that triggered the blueprint execution and the event that triggered it. | `{'target': '123abc', 'event': 'wf-click'}` |
| `httpx_requests` | `array[dict]` | Any HTTP requests that the agent made at this block, including the request and the response. | `[{'request': {'method': 'POST', ...}, 'response': {'status_code': 200, ...}}]` |
| `item` | Any | An individual item in a **For-each** block loop. The type of the item varies based on the values provided to the **For-each** block. For a dictionary, this is the value of the item. For a list, this is the item itself. | `file123` |
| `itemId` | `str` | The ID of the individual item in a **For-each** block loop. For a list, this is its index in the loop, starting at 0. For a dictionary, this is the key of the item. | `0` |
| `message` | `str` | The error message if a block failed with an error. | `"Invalid input: age must be a number"` |
| `result` | varies | The result of the preceding block. The type of the result varies based on the block. | `Thank you so much for your wonderful review! We're thrilled to hear that you've found the perfect tailored blazer with us.` |
| `results` | `dict` | The full list of results from all blocks in the blueprint. It's a dictionary where the key is the block ID and the value is the result of the block. If the block hasn't run yet, the value is `null`. | `{'123abc': 'Thank you so much...', '456def': '%Generating response...', '789ghi': null}` |
| `session` | `dict` | Session information, such as the session ID, cookies, headers, and user information. | `{'id': '123', 'cookies': {...}, 'headers': {...}, 'userInfo': {}}` |
| `state` | `dict` | The agent's [state variables](/agent-builder/state). | `{'user_name': 'John', 'persona': 'sales'}` |
| `target` | `str` | The ID of the next block to run. | `'123abc'` |
| `ui` | `object` | The UI of the agent as a WriterUIManager object. This is a Python object that allows you to interact with the UI of the agent via custom code. | |
## Access execution environment variables
### In blueprint blocks
You can read the execution environment variables in blueprint blocks using `@{variable_name}` syntax. For example, `@{result}` accesses the result of the preceding block.
This example shows a **Set state** block that uses the `@{result}` variable to access the result of the preceding block. The block sets a state variable `message` with the string `Result: ` and the output of the preceding block.
#### Nested variable syntax
You can access nested variables in blueprint blocks using dot notation. For example, `@{result.0.id}` accesses the `id` field of the first result of the preceding block.
This example shows a **Set state** block to set a `file_id` state variable from an **Add files to Writer Cloud** block. It uses `@{result.0.id}` to access the `id` field of the first item in the result of the preceding block.
Here is the what the `result` variable looks like in the execution environment. It's possible to add multiple files to Writer Cloud, so the `result` variable from this block is an array of dictionaries. The `0` index accesses the first item in the array.
The **Set state** block uses `@{result.0.id}` to access the `id` field of the first item in the result of the preceding block.
### In custom Python code
You can access the execution environment variables in custom Python code directly as variables.
For example, this Python block in the blueprint accesses the output of the preceding block and prepends the string `Result: ` to it. It then sets state variable `message` with the final result.
#### Environment variables
Your `WRITER_API_KEY` is also available when you run custom Python code. It's available as an environment variable (as opposed to an execution environment variable), so you can access it using `os.getenv('WRITER_API_KEY')`.
```python theme={null}
import os
api_key = os.getenv('WRITER_API_KEY')
```
## Add tool calling for invoice validation
You can enhance this agent by adding [tool calling](/agent-builder/tool-calling) to validate invoices and flag potential issues. Here are some ways to extend the functionality:
* Check for missing required fields like tax ID or payment terms
* Compare against previous invoices from the same vendor to detect price discrepancies
* Validate amounts against purchase orders and company spending policies
* Flag duplicate invoice numbers or unusual payment terms
* Verify vendor details against your approved vendor list
To add these capabilities:
1. Add a tool calling block after the structured output to analyze the extracted data
2. Configure validation rules and checks in your custom Python code
3. Update the Slack message to include any validation warnings or approvals
4. Optionally trigger different workflows based on the validation results
Check out the [tool calling tutorial](/agent-builder/tool-calling-tutorial) to learn how to add these validation capabilities to your invoice processing agent.
Agents created in Agent Builder are fully composable and reusable—from the backend logic to frontend experience—making them faster to build and easier to maintain. Each agent consists of two core components: a blueprint and a UI.
* **Blueprint**: a visual map of the agent's business logic and behavior.
* Blueprints are created using a library of configurable blocks for tool calling, built-in RAG, text generation, classification, state management, and more.
* They define how the agent processes input, makes decisions, and takes action—enabling it to reliably orchestrate work across people, data, systems, and even other agents built in Writer.
* **UI**: the interface that users interact with.
* UIs are designed using elements like input fields, buttons, and embeds, including support for guided workflows with pagination.
* They give users a structured, intuitive way to engage with agents, from simple chat interfaces to rich, interactive tools.
Agent Builder includes other features to help you build and deploy agents:
* **Custom code**: add custom Python code to the agent to extend its capabilities with more complex logic.
* **Deploy and supervise**: deploy the agent to the Writer cloud, grant access to specific teams, and monitor the agent's performance.
### How to use Python code blocks
1. **Add a Python block** to your blueprint from the blocks library
2. **Write your code** in the Code field
3. **Access variables** directly from the [execution environment](/agent-builder/execution-environment), [state](/agent-builder/state), and [secrets](/agent-builder/secrets)
4. **Return results** using the `set_output()` function
### Returning values
Use `set_output(value)` to pass data to the next block:
```python theme={null}
# Calculate something
total = sum(state["numbers"])
# Pass it to the next block
set_output(total)
```
The value becomes available as `@{result}` in subsequent blocks.
## Code editor for custom functions
The **code editor** lets you create reusable Python functions and manage files for your agent. This is where you build the foundation that your Python blocks can use. Any functions you define in the code editor are available to use in Python code blocks.
### Accessing the code editor
1. **Open your agent** in Agent Builder
2. **Look for the code editor** at the bottom of the interface
3. **Click to expand** and start editing your `main.py` file
### File structure
Your agent starts with these files:
* **`main.py`**: your main Python code file.
* **`requirements.txt`**: shows the version of the `writer` package that your agent is using.
* **`README.md`**: documentation for your agent.
* **`static/`**: folder for static assets like images.
### Managing files
**Add new files**: click **+ Add file** to create Python modules, data files, or configuration files.
**Upload files**: click **Upload** to add external files like CSV data, images, or documents.
#### Creating folders and moving files
To move a file into a folder, click **+ Add file** and rename the file to `
## Create a secret
Secrets are strings stored as key-value pairs. To create a secret, go to the **Vault** tab in the Agent Builder UI and click **+Add a pair**.
The example below creates a secret with the name `WRITER_API_KEY`. When you type the value, it's masked in the UI.
Click **Save** to store the secret.
You can also delete and update secrets from the **Vault** tab.
## Where secrets are available
Within Python code, Vault is a runtime-only feature that's injected into specific execution contexts, not into the main module scope. It isn't a global variable and is only available in the execution context of the blueprint. This design provides security benefits:
* Secrets are only loaded when needed
* Access is limited to proper execution contexts
* No global exposure of sensitive data
The vault is only available in these specific contexts:
* **Event handlers and blueprint code blocks**: Secrets are injected into event handlers and blueprint code blocks when they run. This includes:
* Button click handlers
* Form submission handlers
* Page load handlers
* Custom event handlers
* **Blueprint execution environment**: Secrets are injected into the blueprint execution environment when the blueprint runs and can be referenced from any blueprint block.
### Access your Writer API key
Each Agent Builder agent in the online editor has a Writer API key set as an environment variable called `WRITER_API_KEY`. If you want to use this API key with other Writer API calls, you can access it with `os.getenv('WRITER_API_KEY')`. This allows you to access the API key outside of event handlers and blueprint code blocks, and means you don't need to set a new secret in Vault for your API key.
```python theme={null}
import os
api_key = os.getenv('WRITER_API_KEY')
```
## Examples
### Secrets in Python code blocks
You can reference secrets in [Python code blocks](/blueprints/pythoncode) within blueprints using the `vault` object. `vault` is a dictionary that contains all the secrets in your blueprint.
For example, to access a secret called `ACME_API_KEY` and use it in an HTTP request, you would use the following code:
```python theme={null}
headers = {
"Authorization": f"Bearer {vault['ACME_API_KEY']}"
}
```
### Event handler with vault access
Vault is also provided as an argument to event handlers. Here's an example of an event handler that's triggered when a button is clicked and accesses the vault.
```python theme={null}
def handle_button_click(state, payload, context, session, ui, blueprint_runner, vault):
# Access vault secrets
api_key = vault.get('ACME_API_KEY')
# Use the secret
headers = {"Authorization": f"Bearer {api_key}"}
```
Once the agent is created, a new tab will open in your browser with the Agent Builder interface.
Then click the three vertical dots on the Page's settings menu to find the **Delete** option.
Then click the three vertical dots on the blueprint's settings menu to find the **Delete** option.
### Add the text generation block
The [**Text generation** block](/blueprints/textgeneration) generates a summary of the meeting notes based on the selected format using Palmyra LLMs.
To deploy the agent, toggle the bar. Writer deploys the agent to the Writer cloud, which takes a moment to complete.
When the deployment is complete, the toggle bar shows **Deployed**. It also shows a list of the teams in your organization, which you can use to grant access to the agent. You must select at least one team before you can view the URL for the deployed agent.
You can also choose to deploy the agent to the Writer Agent Library for the teams you've granted access to. These teams can see the agent you've created in the main [Ask Writer app](https://app.writer.com).
To help teams find the agent, select **Edit the agent guide**, where you can add a description and other information to help teams use the agent.
## Agentic enhancements
This tutorial shows outcome-driven design in action: you define what kind of summary you want, and the agent adapts. To make this agent even more versatile, consider adding:
* [Send summaries to Slack](/agent-builder/invoice-processing): Automatically post to relevant channels
* [HTTP request blocks](/blueprints/httprequest) and [tool calling](/agent-builder/tool-calling) to integrate with third party APIs to perform actions like:
* **Emailing participants**: Send summaries via your email API such as SendGrid or Mailgun
* **Calendar integrations**: Schedule follow-up meetings and block time for tasks
* **CRM updates**: Log meeting outcomes in your customer database
* **Task automation**: Create Jira tickets or Asana tasks for each action item
These integrations transform a summarizer into a true meeting assistant that takes action on your behalf.
## Next steps
* Learn how to [add file inputs and parse PDFs](/agent-builder/summarize-pdfs)
* Learn how to [style the agent's UI](/agent-builder/component-styles)
* Learn how to [send messages to Slack](/agent-builder/invoice-processing)
* Add [tool calling for intelligent integrations with external tools](/agent-builder/tool-calling)
See the [tutorial](#tutorial%3A-build-a-recipe-generator-with-repeaters) below for more details about building this recipe generator.
### When to use Repeaters
Repeaters are useful for displaying:
* Lists of files, tasks, or messages
* Search results or product catalogs
* User-generated content like comments or reviews
* Data from APIs or structured output
* Any collection where you don't know the exact number of items
## Example: Display a list of items
This example starts with a small use case to dynamically display whatever a user selects from a select input.
To start, you should have a new Agent Builder agent and clear the demo agent so you can start from scratch. See the instructions for [clearing the demo agent](/agent-builder/quickstart#clear-the-demo-agent) in the Agent Builder quickstart.
### Set up the select input
The select input allows the user to select multiple items from a list. You'll use it to select the items to display in the Repeater.
Navigate to the **Interface Layers** sidebar within the agent's **Interface** tab. Then, click the Repeater component to open its configuration menu.
Under **Visibility** in the Repeater's configuration menu, select **Custom**. Set the condition to `list`. This ensures the Repeater is only visible when the user has selected at least one item from the select input and the `list` state variable isn't empty.
The recipe generator:
* Accepts a list of dish names from the user
* Generates complete recipes in the blueprint using AI structured output
* Displays each recipe in an organized, tabbed layout using nested Repeaters
### Part 1: Set up a minimal UI and the blueprint
First, you'll set up a minimal UI and the blueprint. This UI won't use any Repeaters yet, but it will be the foundation for the recipe generator.
#### Create the input interface
#### Build the blueprint
The blueprint contains three blocks:
* **UI Trigger** to trigger the blueprint when a user clicks the **Get recipes** button
* **Structured Output** block to generate the recipes and provide structured output that the Repeaters can use
* **Set State** block to store the recipes in the state variable `recipes`
#### Test the blueprint
Now you can test that the blueprint works before you add the Repeaters.
Navigate to the **Preview** tab to test the blueprint.
Enter something like "Pizza, Chocolate Chip Cookies" in the text input and click the **Get recipes** button.
You should see the `request` and the structured output in the `result` state variables in your state before moving to the next step.
### Part 2: Add the main recipe Repeater
Navigate back to the **Interface** tab to continue building the recipe generator's UI.
At this point, when there are no recipes, the Repeater still displays placeholder text. At the end of the tutorial you'll update the Repeater to be invisible when there are no recipes.
### Part 3: Add nested Repeaters for ingredients and steps
Now you'll add nested Repeaters for ingredients and steps. You'll use a **Tab Container** to display the ingredients and steps in separate tabs.
## Create a secret
Secrets are strings stored as key-value pairs. To create a secret, go to the **Vault** tab in the Agent Builder UI and click **+Add a pair**.
The example below creates a secret with the name `WRITER_API_KEY`. When you type the value, it's masked in the UI.
Click **Save** to store the secret.
You can also delete and update secrets from the **Vault** tab.
## Use a secret
To use a secret, reference it in a block in your blueprint with the prefix `@{vault}`. For example, to use the `WRITER_API_KEY` secret, you would use it as `@{vault.WRITER_API_KEY}`.
The example below shows adding an authorization header to an HTTP request block using the `WRITER_API_KEY` secret.
When you reference a secret, it's automatically masked in the UI.
## Use a secret in Python code
You can access secrets in Python code blocks within blueprints using the `vault` object. `vault` is a dictionary that contains all the secrets in your blueprint.
## Inspect the state
You can view the agent's state from any view by clicking the **State explorer** icon in the top right of the page. This is helpful for debugging and understanding how the agent is working.
### Set state block
The [**Set state** block](/blueprints/setstate) allows you to set a state variable within a blueprint. Provide the name of the state variable and the value you want to set. When the block runs, the state variable is created or updated with the provided value.
You can set state variables as `text` or `JSON`. Under the **Value type** dropdown, select the type of value you want to set. In the **Value** field, enter the value you want to set.
If you set the value as `text`, the value of the state variable is stored as a string. If you set the value as `JSON`, the value of the state variable is stored as a JSON object.
See how to [access nested state variables that are set as JSON](#nested-state-variables) below.
### Set state with custom Python
You can also set state variables with custom Python code.
To initialize the state, use the `wf.init_state` function:
```python theme={null}
import writer as wf
wf.init_state({
"counter": 0
})
```
To update the state, define a function that takes the state as an argument and updates the state. The state is similar to a Python dictionary, so you can update it by accessing the key and assigning a new value.
```python theme={null}
def increment(state):
state["counter"] += 1
```
#### Private state variables
You can create private state variables with custom Python code. Private state variables are not visible to the agent's UI and are only accessible via code.
To create a private state variable, prefix the variable with an underscore (`_`).
```python theme={null}
wf.init_state({
"_user_name": "John Doe"
})
```
## Access state variables
You can access state variables in the agent's blueprint, UI, and custom Python code.
### Access state variables in a UI element
You can access state variables in a UI element by using the `@` syntax.
### Access state variables in a blueprint
Access state variables in a blueprint by using the `@` syntax.
### Access state variables in custom Python
You can access state variables in custom Python code by referencing the `state` variable. The `state` variable functions like a Python dictionary, so you can access and update state variables by referencing the key.
Pass the `state` variable to the function when you call it.
```python theme={null}
def increment(state):
state["counter"] += 1
```
## Nested state variables
Agent Builder allows you to create and access nested state variables. This is useful when you need to store complex data that requires multiple levels of organization.
### Set nested state variables
You can set nested state variables in the **Set state** block using `JSON` as the value type and a JSON object as the value.
You can also set nested state variables in custom Python code.
### Access nested state variables
In the UI or a blueprint, use `.` to access the different levels of nested state variables. For example, consider a state variable with the following structure:
```json theme={null}
"tasks": [
{
"title": "Write a blog post"
}
]
```
To access the `title` of the first task in a blueprint or UI block, use the following syntax:
```
@{tasks.0.title}
```
If you encounter any issues, refer to the [Troubleshooting](/agent-builder/troubleshooting) guide for debugging information.
Check the box to acknowledge that you're replacing your existing agent and click **Import** to continue.
## Deploy your agent
After syncing your agent to the cloud, deploy it following the [standard cloud workflow](/agent-builder/quickstart#deploy-the-agent).
From the agent editor interface in Agent Builder, click **Configure deployment** in the top right corner of the Agent Builder interface. You can also access the deployment configuration by going to the [AI Studio homepage](https://app.writer.com/aistudio) and selecting the agent you created.
If the agent isn't deployed, you see a toggle bar that says **Draft**.
To deploy the agent, toggle the bar. Writer deploys the agent to the Writer cloud, which takes a moment to complete.
When you deploy the agent, the toggle bar shows **Deployed**. It also shows a list of the teams in your organization, which you can use to grant access to the agent. You must select at least one team before you can view the URL for the deployed agent.
## Considerations for dual-environment development
* **Local-only libraries**: If you install Python packages locally that aren't listed in [Python libraries installed in Agent Builder](/agent-builder/python-libraries), your agent won't run in the cloud version of Agent Builder because those packages aren't available there.
* **Environment file visibility**: Your `.env` file is visible in plain text in the cloud editor when you sync your local project. Any file you add to your local project is included in the export file to the cloud. Before syncing, move your `.env` file to a different location or delete it to prevent it from showing in the cloud editor, and add your secrets to Vault.
* **Vault availability**: Vault is only available in the cloud version of Agent Builder. For local development, you must use environment variables. Structure your code to handle both scenarios gracefully.
* **Keep your local Writer Framework package up to date**: When you sync your agent to the cloud, the cloud version of Agent Builder uses the latest version of the Writer Framework package. If you're using a different version of the Writer Framework package in your local project, you might encounter errors when you sync to the cloud. To avoid this, keep your local Writer Framework package up to date by running the following command:
```bash theme={null}
pip install --upgrade writer
```
### Handle environment variables and secrets
The following code shows an example of how to look for secrets in Vault first, then fall back to environment variables:
```python theme={null}
import os
def get_secret(key, default=None):
"""Get a secret from Vault if available, otherwise from environment variables"""
try:
# Try to get from Vault first (cloud environment). The `vault` object is available in Python code blocks and in event handlers.
return vault[key]
except KeyError:
# Fall back to environment variables (local development)
return os.getenv(key, default)
# Usage example
api_key = get_secret("WRITER_API_KEY", "default_key")
database_url = get_secret("DATABASE_URL")
```
## Next steps
* Learn about [local development](/agent-builder/local-development) workflows
* Understand how to [deploy your agent](#deploy-your-agent) from the cloud
* Explore [custom Python code](/agent-builder/python-code) for advanced functionality
Both blocks allow you to either connect to Knowledge Graphs or define the functions you want to provide to the model as [JSON Schema](https://json-schema.org/) objects. Then, when the model decides to use a tool, it either queries the Knowledge Graphs or sends the call to your blueprint, which executes the tool and sends the results back to the model.
## Add tool calling to your blueprint
To add tool calling to your blueprint, you need to:
1. [Add the **Tool calling** block to your blueprint](#add-the-tool-calling-block)
2. [Write a prompt that explains what the tool calling block is trying to accomplish](#write-a-prompt)
3. [Define the tools you want to provide to the model](#define-your-tools)
4. [Connect the tool calling block to the blocks that execute the tools](#connect-the-tool-calling-connectors-to-the-blocks-that-execute-the-tools)
5. [Add a **Return value** block to the end of the tool calling block](#add-a-return-value-block)
### Add the tool calling block
Add either the [**Tool calling** block](/blueprints/toolcalling) to your blueprint or the [**Chat reply** block](/blueprints/chatreply) to your chatbot conversation.
### Write a prompt
The prompt you write for the tool calling block in the **Prompt** field guides the model's behavior and decision making. It should explain what the tool calling block is trying to accomplish and what tools are available to use.
### Define your tools
Define the tools you want to provide to the **Tool calling** block, so that it knows what tools are available to use.
To add a new tool, click the **Add tool+** button in the **Tool calling** block's configuration menu.
In the **Add tool** modal that appears, select the **Tool type** from the dropdown, either:
* **Function**: A function that the model can call
* **Knowledge Graph**: A Knowledge Graph that the model can query
The example above shows the following setup for a tool that, among other functions, can check the status of a package:
The trace shows the series of thoughts, actions, and tool calls that the model made to complete the task.
This architecture provides several benefits:
* **Security and control**: You maintain full control over your data and system access
* **Flexibility**: You can easily add new tools or modify existing ones without changing the AI
* **Reliability**: Each system component can be optimized and maintained independently
* **Compliance**: Sensitive operations remain within your controlled infrastructure
## Next steps
Now that you understand why tool calling matters and how it works behind the scenes, [learn how to use tool calling in Agent Builder](/agent-builder/tool-calling).
You can see the agent's progress and reasoning in the **Logs** tab.
## Next steps
This tutorial demonstrates the basics of tool calling with external APIs. To continue improving on this agent, you might consider adding a **Structured Output** block after the Tool Calling block to ensure the agent returns the results in the correct format. You could also add more components to the UI to display the results in a more user-friendly way, such as a [**Metric**](/components/metric) block to display the weather conditions, a [**Google Maps**](/components/googlemaps) block to display the recommended locations, or different **Text** blocks to display the different sections of the results.
Next, you might want to learn more about tool calling:
* [Learn more about tool calling](/agent-builder/tool-calling)
* [Learn more about the Tool Calling block](/blueprints/toolcalling)
* [Learn more about the HTTP Request block](/blueprints/httprequest)
You can also see the progress of the agent in the **Log** status bar. Learn more below.
## View error and info logs
If there are any errors or messages as your agent runs, you'll see an indication in the **Log** bar in the bottom right corner of the page.
You can click the **Log** bar to expand it and see more details.
### Add additional logs with Log Message blocks
You can add additional custom [**Log message** blocks](/blueprints/logmessage) to the agent for debugging purposes. Log messages are helpful to understand the flow of the agent and the value of state and other variables at a given point in the execution.
To add a log message, add a **Log message** block to the canvas. In the block's configuration panel, update the following fields:
* **Type**: `info` or `error`
* **Message**: The message to log
Below is an example of a log message block during a file parsing process. It logs the file ID before beginning the parsing process, to help you debug if the file isn't found or isn't parsed correctly.
When the block runs, you'll see the log message in the **Logs** bar at the bottom of the page.
### Add additional logs with Python code
You can use python `print` statements and the globally available [`logger` object](https://docs.python.org/3/library/logging.html) to add additional logs to the agent.
```python theme={null}
print("This is a log message that shows in logs as 'Captured stdout'")
logger.info("This is an info message that shows in logs under 'Captured logs'")
logger.warning("This is a warning message that shows in logs under 'Captured logs'")
logger.error("This is an error message that shows in logs under 'Captured logs'")
```
The following image shows an example of a Python block that logs messages via print statements and the `logger` object.
When the block runs, the log messages appear in the **Logs** bar at the bottom of the page.
## View traces
The log bar shows information about the agent's execution as it runs and after blocks complete. It includes the following:
* The block name
* The status of the block: success, error, or in progress
* A link to view a trace at that point in the execution
* How long the block took to run
The trace link opens a new tab with a detailed view of the agent's execution at that point in time. It contains:
* The value resulting from the block's execution, which is then added to the execution environment of the following block
* The return value, if the block has one
* The full execution environment at that point in time
* The call stack
Below is an example of a trace after a **File upload** block has completed.
## Inspect agent state
You can use the **State explorer** to view an agent's state variables and their values. This is helpful when you're debugging an agent or need to check the state at a given point in the execution.
To access the state explorer, click the **State explorer** icon in the top right corner of the page.
2. Click the **Create API agent** button in the top right corner of the page.
3. Click the agent's name to rename it to something descriptive, and provide a **short description** of your agent to help you keep track of what it does.
## Create an API key
Each API agent has a default API key, called `Production`. To create additional API keys:
1. Navigate to the API application. From the [AI Studio home page](https://app.writer.com/aistudio), click **API Keys** in the navigation menu.
2. Click the API agent's tile you want to generate a new key for.
3. Click **Generate a new key**.
4. Give the key a name and click **Generate**.
5. Immediately after generating the key, copy the key and save it securely. You can't view the key again after you navigate away from this page.
You can't view the API key after creation. Copy it immediately and store it securely. To replace a lost key, generate a new key from the API agent page.
## Manage API agent permissions
API permissions are set at the agent level. To manage API agent permissions:
1. From the [AI Studio home page](https://app.writer.com/aistudio), click **API Keys** in the navigation menu.
2. Click an individual agent's tile to navigate to the agent.
3. Under **Capabilities**, toggle to enable or turn off a specific capability for the API key.
3. Confirm the deletion by clicking **Delete**.
## Delete an API key
To delete an API key:
1. From the [AI Studio home page](https://app.writer.com/aistudio), click **API Keys** in the navigation menu.
2. Click an individual agent's tile to navigate to the agent.
3. Click the dropdown menu (**...**) next to the key you want to delete, and select **Revoke**.
4. Confirm the deletion by clicking **Revoke key**.
# Application details
Source: https://dev.writer.com/api-reference/application-api/application-details
get /v1/applications/{application_id}
Retrieves detailed information for a specific no-code agent (formerly called no-code applications), including its configuration and current status.
## Overview
The **Add files to Writer Cloud** block uploads files to the Writer cloud so you can use them in your workflows. It accepts a list of file objects as inputs.
## File inputs
The **Add files to Writer Cloud** block accepts a list of file objects as inputs. Each file object must have the following fields:
| Field | Type | Description |
| ------ | ------ | -------------------------------------------------------------------------------------------------------------- |
| `name` | string | The name of the file. |
| `data` | bytes | The content of the file as bytes. |
| `type` | string | The [MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/MIME_types/Common_types) of the file. |
### Adding files from the file input block
If you upload a file with the **File input** interface block, the list of file objects is automatically created for you, so all you have to do is pass the list to the **Add files to Writer Cloud** block. See the [example below](#example%3A-upload-files-to-writer-cloud-from-the-file-input-block) for how to do this.
You can retrieve the file objects list from the **File input** interface block in two ways:
* Connect a UI Trigger to the **File input** block's `wf-file-change` event. Then, use the `@{payload}` environment variable to access the file objects. The trigger will fire when a user adds or removes files from the file input block.
* Under **Binding** in the **File input** interface block, define a state variable that contains the list of file objects. Then reference that variable in the **Add files to Writer Cloud** block.
## Output
Once the files are uploaded to the Writer cloud, the **Add files to Writer Cloud** block returns a list containing the uploaded file IDs and other information about the files.
You can access the output of an **Add files to Writer Cloud** block using the `@{result}` variable in the block that follows it in a blueprint.
Each file object has the following fields:
| Field | Type | Description |
| ------------ | ------ | ----------------------------------------------------------------------- |
| `id` | string | The ID of the file. |
| `name` | string | The name of the file. |
| `status` | string | The status of the file. Can be `in_progress`, `completed`, or `failed`. |
| `created_at` | string | The date and time the file was created. |
| `graph_ids` | array | The IDs of any Knowledge Graphs that the file is associated with. |
```json theme={null}
[
{
"id": "701118ae",
"name": "research_paper.pdf",
"status": "in_progress",
"created_at": "2025-06-17T16:18:42.672407+00:00",
"graph_ids": []
}
]
```
### File processing status
If the uploaded file's status is `in_progress`, the file is being processed and is not ready to be used in a workflow.
Some files are processed quickly, such as PDFs and images, and are ready to be used in a workflow within seconds. Others, such as Word documents, may take a few minutes to process.
There are a few ways you can handle files that are still being processed:
* Split the blueprint into two parts: one that uploads the files when a user adds them to the file input block, and another that uses the files when a user clicks a button in the UI. This way, you can use the files in the workflow as soon as they're uploaded, and wait for the user to click a button to use the files in the second part of the blueprint. See an example in the [Upload, parse, and summarize PDFs](/agent-builder/summarize-pdfs#build-the-blueprints) tutorial.
* Add a Python block that introduces a few seconds of delay before the workflow continues.
* Add a [**Tool calling** block](/agent-builder/tool-calling) that can check the status of the file [using the Writer API](/api-reference/file-api/get-file) and wait for the file to be processed before continuing.
## Example: Upload files to Writer Cloud from the file input block
This example shows how to upload files to Writer Cloud that are stored in a state variable. In this example, the interface contains a file input block that's bound to the state variable `files`. After the UI Trigger fires, the **Add files to Writer Cloud** block uploads the files to Writer Cloud by accessing the `@{files}` state variable.
The `@{files}` state variable looks like this:
```json theme={null}
[
{
"name": "research_paper.pdf",
"data": "...",
"type": "application/pdf"
}
]
```
## Example: Create and upload files from Python processing
This example shows how to manually create file objects in a **Python code** block and then upload them to Writer Cloud. In this scenario, a Python block generates a CSV report from data processing and creates the file object programmatically.
The **Python code** block contains the following code, which creates a CSV file object and returns it as a list for the **Add files to Writer Cloud** block to access.
```python theme={null}
import csv
import io
# Sample data processing
data = [
["Name", "Score", "Category"],
["Product A", 85, "Electronics"],
["Product B", 92, "Home & Garden"],
["Product C", 78, "Electronics"]
]
# Create CSV content
csv_buffer = io.StringIO()
writer = csv.writer(csv_buffer)
writer.writerows(data)
csv_content = csv_buffer.getvalue()
# Convert to bytes
csv_bytes = csv_content.encode('utf-8')
# Create file object
file_object = {
"name": "product_analysis.csv",
"data": csv_bytes,
"type": "text/csv"
}
# Return the file object as a list (required by Add files to Writer Cloud block)
set_output([file_object])
```
The Python block outputs `[file_object]`, which the **Add files to Writer Cloud** block then accesses using `@{result}` to upload the CSV file.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Files | Object | - |
\[]
|
A list of files to be uploaded to the Writer platform. You can use files uploaded via the File input component or specify dictionaries with data, type and name. | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | File successfully uploaded. |
| Error | - | error | If the function raises an Exception. |
## Overview
The **Add to Knowledge Graph** block ingests files into a Writer Knowledge Graph, making the information available for AI-powered search, retrieval, and question answering. You can use this block to build enterprise AI applications that need to reference company documents, policies, or structured data.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Graph Id | Graph Id | - | - | The id for an existing knowledge graph. It has a UUID format. | - | Format: uuid |
| Files | Object | - |
\[]
|
A list of files to be uploaded and added to the knowledge graph. You can use files uploaded via the File input component or specify dictionaries with data, type and name. | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | If the execution was successful. |
| Error | - | error | If the function raises an Exception. |
## Overview
This block adds a string value to a list in the agent's state. It behaves differently depending on the state element's type:
* If the state element doesn't already exist, the block creates it and adds the value to it.
* If the state element already exists as a list, the block adds the value to the list.
* If the state element exists but isn't a list, the block fails.
The **For-each loop** block iterates over a list of values and generates text for each value. Then, the **Add to state list** block adds the result of each iteration to the state list called `text_gen_results`.
On the initial run, the **Add to state list** block creates the state element and adds the first value to it. Every subsequent run, the block adds the next value to the list.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Link Variable | Binding | - | - | Set the variable here and use it across your agent. | - | - |
| Value | Text | Textarea | - | - | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | If the function doesn't raise an Exception. |
| Error | - | error | If the function raises an Exception. |
## Overview
The **AI Studio Agent** block runs a [deployed no-code AI Studio agent](/no-code/introduction). Only agents with [text generation](/no-code/text-generation) and [research](/no-code/research) capabilities are supported.
Under `App Id`, you can select the no-code agent you'd like to run from a pre-populated list of your deployed no-code agents.
### File and image inputs
If the input is a file or an image, you must upload the file to Writer first and then pass the ID to the agent. You can't pass the file or image directly to the agent.
See the [Upload, parse, and summarize PDFs](/agent-builder/summarize-pdfs#first-blueprint%3A-upload-file-to-writer-cloud) tutorial for more information on how to upload files to the Writer cloud.
If the no-code agent accepts URLs to a file or image as input, you can pass a URL to the agent as a value rather than uploading the file to the Writer cloud.
## Output
The **AI Studio Agent** block returns the output of the no-code agent as a string.
You can access the output of an **AI Studio Agent** block using the `@{result}` variable in the block that follows it in a blueprint.
## Example
The following example shows an **AI Studio Agent** block that runs a no-code AI Studio agent.
The no-code agent is an NDA review assistant that accepts a file input with the name `NDA` and runs a text generation workflow. It returns the analysis from the workflow in the `Output formatting` field.
In this example blueprint, the **AI Studio Agent** block provides a file ID for an uploaded file, which is stored in the agent's state as `nda_file_id`. The file must already be uploaded to the Writer cloud.
The **AI Studio Agent** block returns the output of the no-code agent and proceeds to the next block, which stores the `@{result}` in a state variable.
## Errors
If the AI Studio Agent block fails with the error `Failed to acquire proper response for completion from data`, ensure that your no-code agent in AI Studio returns a value in the `Output formatting` field and that you are passing the correct inputs to the agent.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| App Id | App Id | - | - | The agent id can be found in the agent's URL. It has a UUID format. | - | Format: uuid |
| App inputs | Key-Value | - |
{"{}"}
|
- | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | If the execution was successful. |
| Error | - | error | If the function raises an Exception. |
## Overview
The **API Trigger** block enables external systems to trigger your blueprint via HTTP API calls. Use it to integrate your agent with webhooks, external services, or other applications that need to programmatically execute your workflows.
Unlike the [**UI Trigger**](/blueprints/uitrigger) block that responds to user interactions in the interface, the **API Trigger** block accepts HTTP POST requests to trigger blueprint execution.
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Blueprint ID | Blueprint Id | - | - | - | - | - |
| Default result | Code | - | - | The result that is used when the blueprint is triggered from the "Run blueprint" button | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Trigger | - | success | - |
## Overview
The **Ask graph question** block queries one or more Writer Knowledge Graphs to find relevant information and generate answers based on the stored data. Use it to create AI applications that can reference and retrieve information from your uploaded documents and structured data.
You can specify the question, the list of Knowledge Graphs to search, whether to use streaming or subqueries, and a state variable to store the answer in. The block searches through the Knowledge Graphs and returns a relevant response.
## Common use cases
* Building question-answering systems based on company documents
* Creating AI assistants that reference specific knowledge bases
* Creating compliance verification tools that check against policy documents
* Building research assistants that analyze academic papers or reports
## How it works
1. **Question**: Enter the natural language question you want to ask about the stored data.
2. **Graph Ids**: Specify one or more Knowledge Graphs to query from a list of available Graphs.
3. **Link Variable**: The name of the variable that stores the answer in the agent's state.
4. **Use streaming**: Choose whether to stream the answer as it is generated. If you choose `yes`, the answer will be streamed to the UI as it is generated. If you choose `no`, the answer will be generated and then returned to the agent once it is complete.
5. **Use subqueries**: Enable to allow the LLM to ask follow-up questions to the Knowledge Graph for improved answers.
The block searches the selected Knowledge Graph or Graphs for relevant information and generates an answer based on the stored documents and data.
## Examples
### Employee policy assistant
This example shows a complete workflow where employees can ask questions about company policies and receive accurate, up-to-date information.
**Interface:**
1. **Text input** → Employee submits a question and the text input stores it in a state variable called `question`
2. **Button** → Employee clicks a button to submit the question to the agent
3. **Text** → Employee sees the answer stored in the `policy_answer` state variable in a text block
**Blueprint Flow:**
1. **UI Trigger** → Employee submits policy question through form
2. **Ask graph question** → Searches company knowledge base
3. **Text generation** → Formats the answer for the employee with additional context
4. **Set state** → Stores response for display
**Block Configuration:**
* **Graph Ids:** `["Company Policies", "Employee Handbook"]`
* **Question:** `@{question}` (set from a text input in the UI)
* **Link Variable:** `policy_answer`
* **Use streaming:** `yes`
* **Use subqueries:** `yes`
This workflow ensures employees receive accurate, up-to-date policy information directly from company documents.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Question | Text | - | - | The natural language question to ask. | - | - |
| Use streaming | Boolean | - |
yes
|
- | - | - |
| Link Variable | Binding | - | - | Set the variable here and use it across your agent. | - | - |
| Graph Ids | Graph Ids | - |
\[]
|
IDs of the graphs to query. | - | - |
| Use subqueries | Boolean | - |
yes
|
Enables LLM to ask follow-up questions to the knowledge graph. This improves answers, but may be slower. | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | Successfully streamed the answer. |
| Error | - | error | If the function raises an Exception. |
## Overview
The **Change page** block navigates the user to a different page in the application. Use it to control the flow of your app, redirect after actions, or guide users through multi-step processes.
You can specify the target page and optionally pass parameters.
## Common use cases
* Redirecting users after form submission
* Navigating between steps in a multi-step workflow
* Sending users to a confirmation or error page
* Guiding users through onboarding or tutorials
## How it works
1. **Page**: Enter the name or path of the page to navigate to. Each page needs a unique key to identify it in the blueprint.
2. **Parameters**: Optionally specify parameters to pass to the new page.
The block triggers a navigation event, sending the user to the specified page.
## Examples
### Post-submission redirect
**Interface:**
1. A page with text inputs for name and email, and a button to submit the form.
2. A second page with the key `confirmation_page`. It contains a message to confirm the form submission with more details about the form submission.
**Blueprint Flow:**
1. **UI Trigger** → User presses button to submit a form
2. **Change page** → Navigates to the page with the key `confirmation`
**Block Configuration:**
* **Page key:** `confirmation`
This workflow ensures users are directed to the confirmation page after submitting the form.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Page key | Text | - | - | The identifying key of the target page. | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | The page change was successful. |
| Error | - | error | The event handler execution wasn't successful. |
## Overview
The **Chat reply** block is a comprehensive chat solution that can initialize conversations, add messages, and generate replies in a single block. It's designed to handle the complete chat workflow efficiently.
You can specify a conversation state variable to store the chat history, system prompt, message, and various configuration options. The block manages the conversation state and generates appropriate responses. You can also configure tools that the AI can use during the conversation, such as function tools and Knowledge Graphs.
## Common use cases
* Building complete chat applications
* Creating AI assistants with conversation management
* Implementing interactive workflows with chat capabilities
* Developing conversational interfaces with tool calling
## How it works
1. **Conversation Object**: A binding variable that stores the chat history and metadata. If not provided or empty, a new conversation will be created and stored in the state.
2. **System prompt**: Set the context or behavior for the AI. Can be left empty if conversation is already initialized in state.
3. **Message**: Enter the user's message as an object with `role` and `content` properties; for example, `{"role": "user", "content": "What are the best practices for using the product?"}`. If you want to pass the user's message from the Chatbot interface to the Chat reply block, you can use the `@{payload}` variable.
4. **Configuration**: Set model (default: `palmyra-x5`), temperature (0-1), and max tokens (1-16384).
5. **Use streaming**: Choose whether to stream responses as they're generated or wait for complete responses.
6. **Tools configuration**: Configure tools that the AI can use during the conversation:
* **Function tools**: Define custom functions with parameters that the AI can call
* **Graph tools**: Connect to knowledge graphs for enhanced responses
* **Tool routing**: Control when and how tools are called during the conversation
* **Tool responses**: Handle and process the results returned from tool executions
* See [tool calling](/agent-builder/tool-calling) for more information on how to configure tools.
## Examples
### Customer support chatbot
This example shows a customer support chatbot that can handle conversations and route to human agents if needed.
**Interface:**
1. A [**Chatbot** block](/components/chatbot) that displays the chat interface.
**Blueprint flow:**
1. **UI Trigger** → Customer sends message through chat interface
2. **Chat reply** → Processes message and generates AI response
3. **Classification** → Determines if human agent is needed
4. **Conditional routing** → Routes to human agent via HTTP Request block if a human agent is needed. Otherwise, the workflow starts again with the next user message.
**Chat reply block configuration:**
* **Conversation Object:** `@{chat}`
* **System prompt:**
```
You are a helpful customer support assistant. Be friendly and professional.
IMPORTANT: If you encounter any of the following situations, clearly indicate that you need to route to a human agent:
- Complex technical issues beyond your capabilities
- Requests for account modifications or sensitive information
- Complaints that require escalation
- Situations where you're unsure or need human judgment
- Requests for supervisor or manager assistance
When routing to human, use phrases like:
- "I need to connect you with a human agent for this request"
- "Let me escalate this to our support team"
- "This requires human assistance, let me transfer you"
- "I'll need to route this to a specialist"
For routine questions and simple requests, provide helpful responses directly.
```
* **Message:** `@{payload}`. This passes the user's message from the Chatbot interface to the Chat reply block.
This workflow provides automated customer support while ensuring complex issues go to human agents.
### Connect a chatbot to a Knowledge Graph
This example shows a chatbot that can answer questions about domain-specific knowledge within a Knowledge Graph.
**Interface:**
1. A [**Chatbot** block](/components/chatbot) that displays the chat interface.
**Blueprint flow:**
1. **UI Trigger** → Customer sends message through chat interface
2. **Chat reply** → Processes message and generates AI response. Configured with a Knowledge Graph tool to answer questions about the company's products and services.
**Chat reply block configuration:**
* **Conversation Object:** `@{chat}`
* **System prompt:**
```
You are a helpful customer support assistant. Be friendly and professional. You have access to information about the company's products and services.
```
* **Tools:**
* **Tool type:** `Knowledge Graph`
* **Tool name:** A name to help you identify the tool in the response.
* **Graph id(s):** The list of Knowledge Graphs to use. You can select from a list of available Knowledge Graphs.
This workflow provides a chatbot that can answer questions about the company's products and services.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Conversation Object | Binding | - | - | The variable that has your conversation object. | - | - |
| System prompt | Text | Textarea |
""
|
A system prompt to set the context for the conversation. Can be left empty if conversation is already initialized in state. | - | - |
| Message | Object | - | - | An array with messages or a variable to contain save your conversation as an object. | - | - |
| Initial model | Model Id | - |
palmyra-x5
|
- | - | - |
| Initial temperature | Number | - |
0.7
|
- | - | Range: 0 to 2 |
| Initial max tokens | Number | - |
1024
|
- | - | Range: 1 to 16384 |
| Use streaming | Boolean | - |
yes
|
If set to 'yes', the block will stream the reply as it is generated. If set to 'no', it will wait for the entire reply to be generated before returning. | - | - |
| Tools | Tools | - |
{"{}"}
|
- | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Tools | tools | dynamic | Run associated tools. |
| Success | - | success | If the function doesn't raise an Exception. |
| Error | - | error | If the function raises an Exception. |
## Overview
The **Classification** block classifies text into predefined categories using AI. Unlike blocks that have a single "success" path (such as the [Tool calling block](/blueprints/toolcalling)), the Classification block creates multiple execution paths based on your defined categories. The workflow continues down the specific path that matches the classification result, not through a single return value or success state.
This makes the Classification block ideal for:
* Routing workflows based on content type, sentiment, or intent
* Tagging content into predefined categories
* Branching logic where different categories trigger different actions
For example, if you define categories like `urgent`, `normal`, and `low_priority`, the Classification block analyzes the input text and continues execution down the corresponding path (`urgent`, `normal`, or `low_priority`).
**Key difference from other blocks**: the Classification block doesn't have a final `success` state that takes a return value. Instead, it creates separate execution paths for each category you define, and the workflow continues down the path that matches the classification result.
The `@{result}` variable from the **Classification** block contains the name of the selected category.
## Common use cases
* **Customer support routing**: Classify support tickets as "Technical", "Billing", or "General" to route them to appropriate teams
* **Content moderation**: Categorize user submissions as "Appropriate", "Needs Review", or "Inappropriate"
* **Sentiment analysis**: Classify feedback as "Positive", "Negative", or "Neutral"
* **Document organization**: Sort documents by type such as "Invoice", "Contract", or "Report"
* **Lead qualification**: Classify sales inquiries as "Hot Lead", "Warm Lead", or "Information Request"
## How it works
1. **Input text**: Provide the text you want to classify (often from user input or previous block results)
2. **Define categories**: Set up category names and descriptions that help the AI understand what each category represents
3. **AI classification**: The block analyzes the text and determines which category best fits
4. **Routing**: The workflow automatically follows the connection path that matches the chosen category
## Example
The following example shows a **Classification** block that analyzes customer reviews to determine their primary focus, then routes to different response workflows based on the classification.
In this example, a customer review is classified into one of five categories:
* **Packaging**: Issues with how the product was packaged or shipped
* **Pricing**: Concerns about cost, value, or billing
* **Quality**: Problems or praise related to product quality
* **Delivery**: Issues with shipping speed or delivery process
* **Empty**: Reviews that don't contain useful feedback
Based on the classification, the workflow routes to different **Text generation** blocks that create appropriate responses for each category.
### Setting up categories
When configuring the **Classification** block, you define categories as key-value pairs:
**Key**: The category name (this becomes the connection point name)\
**Value**: A description that helps the AI understand what belongs in this category
For the customer review example:
* **Quality**: "The review is about the quality of the product or service"
* **Pricing**: "The review discusses pricing concerns or satisfaction"
* **Delivery**: "The review relates to delivery time or issues"
### Using classification results
After classification, the workflow automatically follows the connection that matches the selected category. You can connect different blocks to each category to create specialized workflows.
The classification result is also available in subsequent blocks using `@{result}`, which contains the name of the selected category.
## Best practices
### Writing effective category descriptions
* **Be specific**: Include concrete examples of what should be classified in each category
* **Use clear language**: Avoid ambiguous terms that could apply to multiple categories
* **Consider edge cases**: Think about borderline cases and which category they should fall into
* **Test with real data**: Use actual examples from your use case to verify classifications are accurate
### Category naming
* **Use descriptive names**: Choose names that clearly indicate the category's purpose
* **Follow naming rules**: Category names should contain only letters, digits, underscores, and spaces
* **Keep it consistent**: Use a consistent naming convention across all categories
* **Avoid special characters**: Stick to alphanumeric characters for reliable connections
### Performance optimization
* **Provide context**: Use the "Additional context" field to give the AI more information for better classification
* **Handle edge cases**: Always include a "Other" or "Unknown" category for inputs that don't fit your main categories
### Adding context for better classification
Use the "Additional context" field to provide information that helps with classification:
```
Additional context: This is a product review for a high-end electronics item. Focus on technical aspects, build quality, and user experience when classifying.
```
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Text | Text | - | - | The text you want to classify. | - | - |
| Categories | Key-Value | - |
{"{}"}
|
The keys should be the categories you want to classify the text in, for example 'valid' and 'invalid', and the values the criteria for each category. Category names should contain only letters of the English alphabet, digits, underscores and spaces. | - | - |
| Additional context | Text | Textarea | - | Any additional information that might help the AI in making the classification decision. | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| - | categories | dynamic | - |
| Error | - | error | If the function raises an Exception. |
## Overview
The **For-each loop** block iterates over a list or dictionary and executes a workflow for each item.
The following variables are available in the workflow when iterating with the **For-each loop** block:
* For a list of items (for example, `["a", "b", "c"]`):
* `@{item}`: The current item in the list (for example, `"a"`, `"b"`, or `"c"`)
* `@{itemId}`: The current item's index in the list, starting at 0 (for example, `0`, `1`, or `2`)
* For a dictionary of items (for example, `{"a": "apple", "b": "banana", "c": "cherry"}`):
* `@{item}`: The current item's value in the dictionary (for example, `"apple"`, `"banana"`, or `"cherry"`)
* `@{itemId}`: The current item's key in the dictionary (for example, "a", "b", or "c")
## Example
The following example shows a **For-each loop** block that loops over a list of file IDs and makes an HTTP request to the Writer API delete each file in the list by ID. It then sets a state variable to display a success message when it's done.
When the **For-each loop** block is done, it moves to the next block connected to the **Success** connection point. In this example, the next block is a **Set state** block that sets a state variable to display a success message when it's done.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Items | Object | Textarea |
\[]
|
The item value will be passed in the execution environment and will be available at @{item_0}, its id at @{itemId_0}. You can use either a list or a dictionary. | - | - |
| Prefix | Text | - | - | If set, the item will be available at @{prefix_item_0} and the item id at @{prefix_itemId_0}. | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Loop | - | dynamic | Connect the branch that you'd like to loop on. The branch plugged in here will be executed once per item. |
| Success | - | success | The branch referenced executed successfully for each item. |
| Error | - | error | The branch referenced has failed for at least one of the items. |
## Overview
The **HTTP Request** block sends an HTTP request to an API endpoint. Use it to fetch data from external services, send data to APIs, or integrate with third-party systems.
You can configure the HTTP method, URL, headers, body, and body type (plain text or JSON). The block supports `GET`, `POST`, `PUT`, `PATCH`, and `DELETE` methods.
## Common use cases
* Fetching data from a REST API
* Sending data to a webhook or external service
* Integrating with third-party tools
* Automating workflows that require external communication
## How it works
1. **Method**: Choose the HTTP method, for example, `GET`, `POST`.
2. **URL**: Enter the endpoint URL. Pass path parameters in the URL; for example, `https://api.openweathermap.org/data/2.5/weather?q=@{payload.city}&appid=@{vault.openweather_key}&units=metric`.
3. **Headers**: Optionally specify headers as key-value pairs. You can also use [secrets](/agent-builder/secrets) to access API keys and other sensitive values.
4. **Body type**: Choose whether the body you are sending is `text` or `JSON`.
5. **Body**: Enter the request body for `POST`, `PUT`, and `PATCH` requests.
The block sends the request and returns the response. If the request fails due to a connection error (for example, network timeout, DNS failure), it outputs a connection error. If the request receives an error response from the server (for example, `4xx` or `5xx` status code), it outputs a response error. You can access the error details in the next block using the `@{message}` variable.
## Examples
### External API integration
This example demonstrates making HTTP requests to an external API using a public API endpoint. It uses the [Open-Notify API](https://open-notify.org/), which provides real-time data about the International Space Station (ISS); it's free and requires no authentication, so you can use this example to test your workflow before you use it with another API.
**Blueprint flow:**
1. **UI Trigger** → Receives request to fetch ISS location
2. **HTTP Request** → Calls Open-Notify API to get current ISS position
3. **Text generation** → Creates location description based on the body of the response (`@{result.body}`), which contains the latitude and longitude of the ISS
4. **Set state** → Stores the result of the text generation block in state variable to display in the UI
**HTTP Request block configuration:**
* **Method:** GET
* **URL:** `http://api.open-notify.org/iss-now.json`
* **Headers:** None
* **Body type:** None
* **Body:** None
This workflow enables integration with external APIs to enhance your agent's capabilities.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Method | Text | - |
GET
|
- |
|
Allowed values: GET, POST, PUT, PATCH, DELETE |
| URL | Text | Textarea | - | - | - | - |
| Headers | Key-Value | - |
{"{}"}
|
- | - | - |
| Body type | Text | - |
text
|
- |
|
- |
| Body | Text | Textarea | - | - | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | The request was successful. |
| Response error | - | error | The connection was established successfully but an error response code was received or the response was invalid. |
| Connection error | - | error | The connection couldn't be established. |
## Overview
The **Log message** block writes a message to the workflow log. Use it to record information, debug workflows, or track the progress of your blueprint execution.
You can specify any message, including variables or state values, to help with troubleshooting or monitoring.
To view the logs, click the **Log** tab in the Agent Builder editor.
## Common use cases
* Debugging workflow execution by logging variable values
* Tracking the progress of a workflow
* Recording errors or important events
* Auditing workflow steps for compliance
## How it works
1. **Message**: Enter the message to log. You can log variables using the `@{variable_name}` syntax. For example, to log the value of the `@{payload}` variable, you can use the message `"Payload: @{payload}"`.
2. **Type**: Choose the type of message to log. You can choose from the following options:
* **info**: Log a message with information.
* **error**: Log a message with an error.
The block writes the message to the log. The message is available in the workflow execution logs for review.
## Examples
### Debug workflow execution
This example shows how to add logging throughout a workflow that processes multiple resumes from job applicants. The workflow extracts key information from each resume PDF and generates a summary report. The log message block is used to record the progress of the workflow.
**Blueprint Flow:**
1. **UI Trigger** → HR manager uploads multiple resumes for processing
2. **For-each loop** → Processes document sections
3. **Log message** → Records section completion and confirms parsing success
4. **Parse PDF** → Parses the PDF file
5. **Text generation** → Assembles final document
**Block Configuration:**
* **Message:** `Processing resume for @{itemId}.`
* **Type:** `info`
The logs display the progress of the workflow as it processes each resume. You can see the logs in the **Log** tab of the Agent Builder editor.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Type | Text | - |
info
|
- |
|
- |
| Message | Text | Textarea | - | - | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | The request was successful. |
| Error | - | error | The blueprint was executed successfully. |
## Overview
The **Parse JSON** block transforms a JSON-formatted string into a structured object that you can work with programmatically. For example, if you have a string like `'{"name": "John", "age": 30}'`, this block converts it into an object where you can access values like `object.name` or `object.age`.
This block is essential when you need to extract and work with data from:
* API responses that return JSON strings
* Form submissions containing JSON data
* Configuration files stored as JSON
* Any text that follows JSON format
The parsed object lets you:
* Access nested values using dot notation (for example, `user.address.city`)
* Iterate through arrays of data
* Pass structured data to other blocks
* Validate and transform the data structure
## Common use cases
* Parsing API responses
* Converting user input into structured data
* Extracting values from JSON payloads
* Validating JSON format before further processing
## How it works
1. **JSON string**: Enter the JSON string to parse. You can use variables or state values.
The block parses the string and outputs the resulting object. If the string is not valid JSON, the block raises an error.
## Examples
### User order form processing
This example shows why parsing JSON is essential for conditional logic and data extraction.
**Scenario:** A user submits an order form with JSON data that looks like this:
```json theme={null}
{
"customer": {
"name": "Sarah Johnson",
"email": "sarah@example.com",
"tier": "premium"
},
"items": [
{"product": "Laptop", "price": 1299.99, "quantity": 1},
{"product": "Mouse", "price": 29.99, "quantity": 2}
],
"total": 1359.97,
"shipping_method": "express"
}
```
**Without Parse JSON (problematic):**
* Text generation block sees: `"{"customer": {"name": "Sarah Johnson"...`
* Can't access specific values like customer name or total
* Can't apply conditional logic based on order value
* Can't calculate discounts for premium customers
**With Parse JSON (powerful):**
1. **UI Trigger** → User submits order form
2. **Parse JSON** → Converts form data to structured object
3. **Set state** → Store parsed data in a state variable called `parsed_order`
4. **Classification** → Check if `@{parsed_order.total}` >= 1000 for free shipping
5. **Text generation** → Use `@{parsed_order.customer.name}` and `@{parsed_order.total}` in response
**Block Configuration:**
* **JSON string:** `@{order}` (the name of the state variable that contains the JSON string from the interface)
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Plain text | Text | Textarea | - | - | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | The request was successful. |
| Error | - | error | The text provided couldn't be parsed. |
## Overview
The **Parse PDF tool** block extracts text content from PDF files stored in the Writer cloud. Use it to convert PDF documents into searchable, analyzable text that can be used in AI workflows, content analysis, or data extraction processes.
## Common use cases
* Extracting text from PDF documents for analysis
* Converting PDF reports into searchable content
* Processing PDF forms and documents
* Building document processing workflows
## How it works
1. **PDF input**: Provide a PDF file UUID from the Writer cloud. The file must be uploaded to Writer first; you can upload a file first using the "Add files to Writer Cloud" block. You can also use files uploaded via the API, no-code agents, or the Writer cloud UI.
2. **Content extraction**: The block extracts text content from the PDF using the Writer API.
3. **Format selection**: Choose to enable markdown formatting for the extracted content.
4. **Text processing**: Converts PDF content into the selected format and returns it as a string.
The block handles PDF parsing, text extraction, and formatting. The extracted text maintains the document's structure.
### When to enable markdown formatting
You can choose to parse the PDF content as plain text or markdown. The default is plain text.
**When to use markdown:**
* Preserving document structure and hierarchy
* Maintaining formatting for readability
* Working with documents that have headings, lists, or emphasis
* Creating summaries that maintain document organization
**When to use plain text:**
* Simple text analysis and processing
* When formatting isn't relevant to your workflow
* Extracting raw text for AI processing
* Reducing complexity in downstream processing
## Examples
### Document analysis workflow
This example shows how to extract and analyze text from PDF documents in a complete workflow.
**Blueprint Flow:**
1. **UI Trigger** → User uploads PDF document
2. **Add files to Writer Cloud** → Uploads PDF document to Writer cloud
3. **Parse PDF tool** → Extracts text from PDF document
4. **Text generation** → Creates analysis and summary
5. **Set state** → Stores analysis for user review
**Block Configuration:**
* **File:** `@{result.0.id}`. This variable is the UUID of the file in Writer cloud. The `Add files to Writer Cloud` block returns a list of file objects that were uploaded to Writer cloud. To process multiple files, you can use the [**For each loop** block](/blueprints/for-eachloop) to iterate over the list of files.
* **Enable markdown:** `disabled`. Extracts content as plain text, because the formatting isn't relevant to the text generation task.
This workflow enables automated document analysis and text extraction for research and content processing.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| File | Text | - |
""
|
UUID of a file object in Files API. | - | Format: uuid |
| Enable markdown | Boolean | - |
yes
|
- | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | If the execution was successful. |
| Error | - | error | If the function raises an Exception. |
## Overview
The Python code block allows you to write Python code to extend your blueprint's functionality. Use it to perform custom data processing, calculations, API integrations, or any other logic that requires Python programming capabilities.
## Common use cases
* Custom data processing and transformations
* Complex calculations and mathematical operations
* API integrations with external services
* File manipulation and data parsing
## How it works
1. **Code execution**: Write Python code in the block's code editor.
2. **Variable access**: Access [state variables](/agent-builder/state), [environment variables](/agent-builder/execution-environment), and [preinstalled libraries](/agent-builder/python-libraries).
3. **Output generation**: Use `set_output()` to return values to subsequent blocks.
4. **Error handling**: Handle exceptions and provide meaningful error messages. If an error occurs, access the error message in the next block using `@{message}`.
5. **Error exit condition**: Raise an `Exception` to trigger the **Error** exit condition and route to error handling blocks.
The block provides a full Python environment with access to [state variables](/agent-builder/state), [execution environment variables](/agent-builder/execution-environment), and [preinstalled libraries](/agent-builder/python-libraries).
## Examples
### Calculate average from list
This example shows how to return a value from the Python code block. It calculates the average of a list of numbers and returns the result to the next block by using the `set_output` function.
**Blueprint flow:**
1. **UI Trigger** → User provides list of numbers
2. **Python code** → Calculates average of numbers
3. **Set state** → Stores result in state variable
```python theme={null}
def calculate_mean(numbers):
return sum(numbers) / len(numbers)
average = calculate_mean(state["numbers"])
set_output(average)
```
### Error handling with exceptions
You can trigger the **Error** exit condition by raising an `Exception` in your Python code. This allows you to route to error handling blocks when specific conditions are met.
**Example:**
```python theme={null}
# Validate user input and raise exception if invalid
user_age = state.get("age", 0)
if user_age < 0 or user_age > 120:
raise Exception("Invalid age: Age must be between 0 and 120")
if not state.get("email"):
raise Exception("Missing required field: Email address")
# Process valid data
set_output({"status": "valid", "age": user_age})
```
When an `Exception` occurs, the Python code block exits with the **Error** condition, allowing you to route to error handling blocks in your blueprint. You can access the error message in the next block by referencing the `@{message}` variable.
## Available variables and libraries
Python code block can access the following global variables and libraries:
* [State variables](/agent-builder/state)
* [Execution environment variables](/agent-builder/execution-environment)
* [Preinstalled Python libraries](/agent-builder/python-libraries)
* Functions you've defined in your `main.py` file
### Return values from the Python code block
To return a value from a Python code block and store it in the `result` execution environment variable, you must use the `set_output` function.
Anything that runs in the block but is not returned in the `set_output` function does not get passed to the next block.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Code | Code | Textarea | - | The code to be executed. | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | The event handler execution was successful. |
| Error | - | error | The event handler execution wasn't successful. |
## Overview
The **Return value** block allows you to chain multiple steps together to perform certain actions, and return a single value at the end of the chain. You can use it in the following scenarios:
* To return a value in [tool calling](/agent-builder/tool-calling)
* To return a value when a blueprint is invoked with the [**Run blueprint** block](/blueprints/runblueprint)
It passes the value specified in the **Value** field at the end of a chain of blocks.
### Return a value in tool calling
When an **Tool calling** block decides to run a tool, the tool can include any number of blocks and return a final value to the **Tool calling** block. Use the **Return value** block to define what the final return value is.
If you do not use the **Return value** block, the tool call executes the blocks that follow it but doesn't return a value to the **Tool calling** block.
In the example above, the two **Return value** blocks provide the values of the "Call shipping API" block and the "Call order status API" HTTP Request blocks to the **Tool calling** block.
Without the **Return value** blocks, the "Call shipping API" and "Call order status API" blocks would execute, but their results would not be returned to the **Tool calling** block.
### Return a value from a blueprint
Use the **Return value** block to pass results to another blueprint.
In the example above, the **Return value** block follows the **Text generation** block. It sets the results from the text generation block as the final return value of the blueprint. The blueprint passes this value when it is invoked with the **Run blueprint** block.
Without the **Return value** block, the blueprint would run and the **Text generation** block would execute, but its result would not be returned to the block that ran the blueprint.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Value | Text | Textarea | - | - | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | If the function doesn't raise an Exception. |
| Error | - | error | If the function raises an Exception. |
## Overview
The **Run blueprint** block allows you to call another blueprint from within your current workflow. This enables modular, reusable logic and lets you break complex workflows into smaller, manageable pieces.
You can specify the blueprint to run and pass input parameters. The output of the called blueprint is available for use in subsequent blocks.
## Common use cases
* Reusing logic across multiple workflows
* Breaking up large blueprints into smaller, maintainable components
* Creating shared utility blueprints
* Running conditional or dynamic workflows
## How it works
1. **Blueprint Key**: Select the blueprint to run by its key. The blueprint must have a key set in its settings. If the blueprint doesn't have a key, it does not show in the list of available blueprints.
2. **Payload**: Provide input data as text that will be available as `@{payload}` in the called blueprint. The payload is treated as plain text.
The block executes the specified blueprint and returns any output that the called blueprint returns with the **Return value** block.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Blueprint Key | Blueprint Key | - | - | - | - | Format: writer#blueprintKey |
| Payload | Text | Textarea |
{"{}"}
|
The value specified will be available using the template syntax i.e. @{payload_0} | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | The request was successful. |
| Error | - | error | The blueprint was executed successfully. |
## Overview
The **Set state** block is a core building block that connects information between different parts of your blueprint, interface, and code. It acts as a bridge, allowing data to flow from one part of your workflow to another - whether that's from blueprint to interface, between different blueprint blocks, from interface back to blueprint, or to and from custom Python code.
You can specify the variable name, value, and value type (plain text or JSON). The value is then available in subsequent blocks via the state variable.
## Common use cases
* Storing user input or intermediate results
* Passing data between blocks in a workflow
* Saving API responses or generated content for later use
* Managing workflow state or flags
* Storing block results to display in interface components
* Persisting data between interface updates and refreshes
## How it works
1. **Link Variable**: Specify the name of the state variable to set.
2. **Value type**: Choose whether the value is plain text or JSON. The default if not specified is plain text.
3. **Value**: Enter the value to store.
The block saves the value in the specified state variable. You can reference this variable in later blocks using the state syntax, for example, `@{state.variable_name}`.
## Examples
### Text generation workflow
This example shows how to generate text and display it in the interface using state management.
**Blueprint Flow:**
1. **UI Trigger** → User clicks "Generate" button
2. **Text generation** → Creates content based on prompt
3. **Set state** → Stores generated text
**Interface** → Has a **Text** component that displays the text from the state variable
**Block Configuration:**
* **Link Variable:** `generated_text`
* **Value type**: `text`. The default if not specified is plain text.
* **Value:** `@{result}`. This variable is the text generated by the text generation block that precedes the **Set state** block.
This workflow enables automated text generation and display in the interface.
### Form submission workflow with JSON value type
This example shows how to package multiple form field responses into a single state variable. It's useful when collecting and storing related data from interface forms.
**Blueprint Flow:**
1. **UI Trigger** → User submits a registration form. All the form fields have Link Variables set to the name of the field.
2. **Set state** → Stores all form fields as JSON
**Block Configuration:**
* **Link Variable:** `form_data`
* **Value type**: `JSON`
* **Value:**
```json theme={null}
{
"firstName": @{firstName},
"lastName": @{lastName},
"email": @{email},
"phoneNumber": @{phoneNumber},
"address": {
"street": @{address.street},
"city": @{address.city},
"state": @{address.state},
"zipCode": @{address.zipCode}
},
"preferences": {
"newsletter": @{preferences.newsletter},
"notifications": @{preferences.notifications}
}
}
```
To access specific fields of the JSON data, use the state syntax, for example `@{form_data.firstName}` or `@{form_data.address.city}`.
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Link Variable | Binding | - | - | Set the variable here and use it across your agent | - | - |
| Value type | Text | - |
text
|
- |
|
- |
| Value | Text | Textarea | - | - | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | If the function doesn't raise an Exception. |
| Error | - | error | If the function raises an Exception. |
## Overview
The **Structured Output** block allows you to define a JSON response format that the AI model uses to structure its output. This is useful when you need the model to return data in a specific schema, such as for downstream processing, API responses, reliable data extraction, or integrations. The block returns the JSON object that matches the schema.
You can specify a prompt describing the data you want, and provide a [JSON Schema](https://json-schema.org/) to enforce the structure of the output. The model attempts to generate a response that matches the schema.
## Common use cases
* Extracting structured data from unstructured text; for example, extracting entities, tables, or key-value pairs
* Generating API responses in a specific format
* Creating objects for further automation or integration
* Enforcing data validation on AI-generated output
## How it works
1. **Prompt**: Describe the data you want the model to generate.
2. **JSON Schema**: Define the required structure for the output. See [JSON Schema](https://json-schema.org/) for more information about how to write a JSON Schema.
3. **Model**: Select the model to use for generation.
4. **Max output tokens**: Set the maximum length of the output.
The block sends the prompt and schema to the model, which returns a JSON object matching the schema. If the output isn't parsable as JSON or doesn't match the schema, the block raises an error.
## Examples
### Data extraction from documents
This example shows how to extract structured data from unstructured documents using AI.
**Blueprint Flow:**
1. **UI Trigger** → User selects a document from the interface
2. **Parse PDF tool** → Extracts text from document. Note: the document must already be uploaded to the Writer cloud.
3. **Structured output** → Extracts specific data fields
4. **HTTP Request** → Sends structured data to external API
**Block Configuration:**
* **Prompt:** "Extract the following information from the document. Document content: @{result}. Return the data in the following format: company name, contact person, email, phone number, and address."
* **JSON Schema:**
```json theme={null}
{
"type": "object",
"properties": {
"company_name": {"type": "string"},
"contact_person": {"type": "string"},
"title": {"type": "string"},
"email": {"type": "string", "format": "email"},
"phone": {"type": "string"},
"address": {"type": "string"}
},
"required": ["company_name", "contact_person", "email"],
"additionalProperties": false
}
```
* **Model:** `palmyra-x5`
This workflow enables automated extraction of structured data from unstructured documents.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Prompt | Text | Textarea | - | Description of a JSON object to be created. | - | - |
| Model | Model Id | - |
palmyra-x5
|
- | - | - |
| JSON Schema | JSON | - |
{"{}"}
|
JSON schema that defines the structure of the response. For example, `{"type": "object", "properties": {...}, "required": [...]}`. | - | - |
| Max output tokens | Number | - |
1024
|
- | - | Range: 1 to 16384 |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | If the function doesn't raise an Exception. |
| Error | - | error | If the function raises an Exception. |
## Overview
The **Text generation** block generates text using a Palmyra model. Use it for text completions, summaries, creative writing, and more. You provide a prompt, and the model generates a response based on your instructions.
You can control the creativity of the output using the temperature setting, and limit the length with the max output tokens field.
## Common use cases
* Generating summaries or explanations
* Creative writing such as stories, marketing copy, and abstracts
* Expanding or rewriting text
* Answering questions based on a prompt
## How it works
1. **Prompt**: Enter the text or instructions for the model.
2. **Model**: Select the model to use for generation. Learn more about [the suite of Palmyra models](/home/models).
3. **Temperature**: Adjusts randomness and creativity. A higher temperature value like 0.7-1.0 makes the output more creative, while a lower temperature value like 0.0-0.3 makes it more deterministic.
4. **Max output tokens**: Sets the maximum length of the generated text.
The block sends the prompt and settings to the model, which returns the generated text as output.
## Examples
### Content creation workflow
This example shows a marketing workflow where user input is transformed into professional marketing copy.
**Blueprint Flow:**
1. **UI Trigger** → Marketing team submits product details through form
2. **Text generation** → Creates product description based on input
3. **Set state** → Stores generated content for review and approval in the interface
**Block Configuration:**
* **Prompt:** "Create a compelling product description for a @{product_name} that highlights its @{key_features}. Target audience: @{target_audience}. Tone: professional but approachable."
* **Model:** `palmyra-x5`
* **Temperature:** `0.7`
This workflow automates content creation while ensuring appropriate tone and messaging for different marketing channels.
### Customer support response system
This example demonstrates an automated support system that generates personalized responses based on customer inquiries.
**Blueprint Flow:**
1. **UI Trigger** → Customer submits support ticket
2. **Classification** → Categorizes the issue type
3. **Text generation** → Creates personalized response based on category
4. **Set state** → Stores response for customer to display in the interface
**Block Configuration:**
* **Prompt:** "Generate a helpful and empathetic response for a customer unable to log into their @{product}. Address common login troubleshooting steps like password reset, browser cache clearing, and two-factor authentication issues. Include links to our password reset tool and support documentation. If these steps don't resolve the issue, provide instructions for escalating to our technical support team."
* **Model:** `palmyra-x5`
* **Temperature:** `0.4`
This workflow provides automated, personalized customer support responses while maintaining human-like empathy and helpfulness.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Prompt | Text | Textarea | - | - | - | - |
| Model | Model Id | - |
palmyra-x5
|
- | - | - |
| Temperature | Number | - |
0.7
|
- | - | Range: 0 to 1 |
| Max output tokens | Number | - |
1024
|
- | - | Range: 1 to 16384 |
| Name | Field | Type | Description |
|---|---|---|---|
| Success | - | success | If the function doesn't raise an Exception. |
| Error | - | error | If the function raises an Exception. |
## Overview
The **Tool calling** block enables your agent to interact with external systems, APIs, and custom functions to gather information, perform actions, or execute complex workflows that extend beyond the model's built-in capabilities.
When the tool calling block executes, the AI model analyzes the prompt and automatically determines which tools to use, when to use them, and how to combine the results to complete the requested task. The model can make multiple tool calls in sequence and use the results from one tool to inform subsequent tool calls.
## How tool calling works
* **Tool selection**: The model analyzes your prompt and determines which available tools are needed
* **Parameter extraction**: The model extracts the necessary parameters from the prompt and context to call each tool
* **Sequential execution**: Tools are called in the optimal order, with results from previous tools informing subsequent calls
* **Result synthesis**: The model combines all tool results with its own knowledge to generate the final response
To learn more about how tool calling works, see the [Tool calling introduction](/agent-builder/tool-calling-intro) documentation.
## Tool types
The tool calling block supports two types of tools:
* **Function tools**: Custom workflows you build within the blueprint using other blocks
* **Built-in tools**: Pre-configured integrations with external services. Currently only [Knowledge Graphs](/home/knowledge-graph) are supported.
### Function tools
Function tools allow you to define custom logic using other blueprint blocks. Each function tool requires:
* **Tool name**: A unique identifier for the tool
* **Description**: Clear explanation of what the tool does and when to use it
* **Parameters**: Input fields the model should provide when calling the tool
* **Implementation**: The workflow of blocks that execute when the tool is called
The definition has the following structure:
| Parameter | Type | Description |
| ----------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | The name of the tool |
| `description` | string | A description of what the tool does and when the model should use it |
| `parameters` | object | An object containing the tool's input parameters |
| `parameters.type` | string | The type of the parameter, which is `object` for a JSON schema |
| `parameters.properties` | object | An object containing the tool's parameters in the form of a [JSON schema](https://json-schema.org/). See below for more details. |
| `parameters.required` | array | An array of the tool's required parameters |
#### Example tool definition
Below is an example of a tool definition that gets the details of a package's shipping status. It takes a tracking number and a carrier name as input parameters and returns the shipping status of the package.
```json theme={null}
{
"name": "check_shipping_status",
"description": "Gets real-time shipping and tracking information for a package",
"parameters": {
"type": "object",
"properties": {
"tracking_number": {
"type": "string",
"description": "The shipping carrier's tracking number"
},
"carrier": {
"type": "string",
"enum": ["fedex", "ups", "usps", "dhl"],
"description": "Shipping carrier name"
}
},
"required": ["tracking_number", "carrier"]
}
}
```
### Knowledge Graph tools
Knowledge Graph tools allow you to connect to one or more [Knowledge Graph](/home/knowledge-graph) to get information about a specific topic.
To add a Knowledge Graph tool, select the Knowledge Graph(s) you want to use from the dropdown of available Knowledge Graphs.
## Prompt engineering for tool calling
While AI models are highly intelligent and can reason about complex tasks, a well-structured prompt is essential for effective tool calling. The prompt serves as a blueprint that clearly defines what tools are available, guides the model through the decision-making workflow, and specifies the expected output format. Without this guidance, the model might miss important steps, use tools inefficiently, or produce results that don't match your requirements.
### Key elements of effective tool calling prompts:
* **Clear tool descriptions**: Explain what each tool does and when to use it
* **Decision logic**: Provide step-by-step workflow guidance
* **Output format**: Specify the structure and format of the expected response
* **Context handling**: Include relevant background information and constraints
## Example
Below is an example of a Tool calling block that is connected to multiple tools that run Python code or call external APIs.
For a complete walkthrough of building an agent with tool calling, see the [Tool calling with external APIs](/agent-builder/tool-calling-tutorial) tutorial.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Prompt | Text | Textarea | - | The task that needs to be carried out. | - | - |
| Model | Model Id | - |
palmyra-x5
|
- | - | - |
| Max iterations | Number | - |
10
|
- | - | - |
| Tools | Tools | - |
{"{}"}
|
- | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Tools | tools | dynamic | Run associated tools. |
| Success | - | success | If the function doesn't raise an Exception. |
| Error | - | error | If the function raises an Exception. |
## Overview
The **UI Trigger** block is the starting point for your blueprint if users interact with the agent's interface.
It starts a workflow in response to a user action in the UI, such as clicking a button or typing in a text area. Use it to connect frontend events to backend logic in your blueprint.
You can specify the event to listen for and any parameters to pass to the workflow.
## How it works
1. **Component Id**: Select the UI component that triggers this workflow; for example, a button click or chatbot message.
2. **Event type**: Specify the type of event to listen for; for example, clicks (`wf-click`) or text area changes (`wf-change`).
3. **Default result**: Optionally provide a default result when testing the blueprint from the "Run blueprint" button. This is useful for testing the workflow without having to interact with the interface. The default result should match the expected payload format for the selected event type. For example:
* For a button click (`wf-click`), provide an object with modifier key states, like:
```json theme={null}
{
"ctrl_key": false,
"shift_key": false,
"meta_key": false
}
```
* For a text input change (`wf-change`), provide the expected text value, like `"Sample text"`
* For a file upload (`wf-file-change`), provide an array of file objects, like:
```json theme={null}
[
{
"name": "example.txt",
"type": "text/plain",
"data": "data:text/plain;base64,U2FtcGxlIHRleHQ="
}
]
```
When the specified event occurs on the selected component, the block starts the workflow. The event payload is available as `@{payload}` in subsequent blocks.
## Examples
### Interactive dashboard updates
This example demonstrates how to handle real-time dashboard interactions and updates.
**Interface:**
1. A [Plotly chart](/components/plotlygraph) that a user can click to trigger the workflow.
**Blueprint Flow:**
1. **UI Trigger** → User clicks on dashboard widget
2. **HTTP Request** → Fetches updated data from external API
3. **Set state** → Updates dashboard state
**Block Configuration:**
* **Component Id:** Dashboard widget component
* **Event type:** `wf-click`
* **Default result:** Optional, but useful for testing the blueprint from the "Run blueprint" button. The default result below mimics the data that would be returned if a user clicked somewhere on a [Plotly chart](/components/plotlygraph) in the interface.
```json theme={null}
[
{
"curveNumber": 0,
"pointNumber": 1,
"x": "Product B",
"y": 150,
"label": "Product B",
"xaxis": {
"anchor": "y"
},
"yaxis": {
"anchor": "x"
}
}
]
```
This workflow enables real-time dashboard interactions with external data sources.
## Fields
| Name | Type | Control | Default | Description | Options | Validation |
|---|---|---|---|---|---|---|
| Component Id | Component Id | - | - | The id of the component that will trigger this branch. | uiComponentsWithEvents | - |
| Event type | Component Event Type | - | - | The type of the event that will trigger this branch. For example, wf-click. | eventTypes | - |
| Default result | Code | - | - | The result that is used when the blueprint is triggered from the "Run blueprint" button | - | - |
| Name | Field | Type | Description |
|---|---|---|---|
| Trigger | - | success | - |
## Overview
The **Annotated Text** component displays text with visual annotations and highlights. It allows users to see text with embedded annotations that are color-coded to distinguish different types of content or sections.
Annotated text is essential for educational content, document review systems, and any interface where text needs visual distinction between different types of content. It provides a way to highlight important terms and sections without cluttering the main display.
## Common use cases
* **Educational content**: Provide explanations and definitions for complex terms
* **Document review**: Show comments, suggestions, and feedback on text
* **Code documentation**: Highlight and explain code snippets with annotations
* **Research papers**: Show citations, references, and additional context
* **Legal documents**: Display legal annotations and explanations
## How it works
1. **Text content**: Displays the main text content with embedded annotations
2. **Annotation markers**: Shows visual indicators for annotated sections
3. **Styling**: Customize appearance of annotations and text with colors and CSS classes
The component displays text with embedded annotations that are visually highlighted using color coding and styling to distinguish different types of content or sections.
## Configuration options
### Basic settings
* **Annotated text**: The main text with embedded annotation markers, must be a JSON string or a state reference to an array
### Advanced settings
* **Enable markdown**: Adds markdown support for automatically sanitizing unsafe elements
* **Enable copy buttons**: Adds a control bar with both copy text and JSON buttons
* **Rotate hue**: Rotates the hue of annotated text colors depending on the content of the string
### Styling options
* **Text color**: Set the color of the main text
* **Annotation colors**: Set colors for different annotation types
* **Custom CSS classes**: Apply additional styling
## Example
### Contract review and analysis
This example shows how to create a contract review interface with highlighted terms and clauses.
**Interface:**
* File input component for file upload
* Annotated text component for annotated contract content
**Annotated Text configuration:**
* **Annotated text**: JSON array containing contract text with embedded annotations in the format `["text", ["highlighted term", "label"], "more text"]`
## Best practices
1. **Clear annotations**: Make annotation content concise and informative
2. **Visual indicators**: Use clear visual cues to show where annotations exist
3. **Performance**: Limit the number of annotations to avoid overwhelming users
4. **Consistent styling**: Use consistent annotation styling throughout your interface
5. **Context relevance**: Ensure annotations provide relevant and helpful information
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Annotated text | Object | Value array with text/annotations. Must be a JSON string or a state reference to an array. | - |
| Reference | Color | The colour to be used as reference for chroma and luminance, and as the starting point for hue rotation. | - |
| Seed value | Number | Choose a different value to reshuffle colours. | - |
| Rotate hue | Boolean | If active, rotates the hue depending on the content of the string. If turned off, the reference colour is always used. | - |
| Enable markdown | Boolean | If active, the output will be sanitized; unsafe elements will be removed. | - |
| Enable copy buttons | Boolean | If active, adds a control bar with both copy text and JSON buttons. | - |
| Button | Color | - | - |
| Button text | Color | - | - |
| Primary text | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
| Name | Type | Description | Options |
|---|---|---|---|
| Name | Text | - | - |
| Image source | Text | A valid URL. Alternatively, you can provide a state reference to a packed file. | - |
| Caption | Text | Add an optional caption under the name, such as the person's job title. | - |
| Size | Text | - |
|
| Orientation | Text | - |
|
| Primary text | Color | - | - |
| Secondary text | Color | - | - |
| Separator | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Text | Text | - | - |
| Disabled | Boolean | Disables all event handlers. | - |
| Button | Color | - | - |
| Button text | Color | - | - |
| Button shadow | Shadow | - | - |
| Separator | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
Connect it to an LLM by handling the `wf-chatbot-message` event, which is triggered every time the user sends a message.
You can add `actions` to messages, which are buttons that trigger the `wf-chatbot-action-click`.
See the stubs for more details.
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Conversation | Object | An array with messages or a variable that contains your conversation as an object. | - |
| Assistant initials | Text | - | - |
| User initials | Text | - | - |
| Enable markdown | Boolean | If active, the output will be sanitized; unsafe elements will be removed. | - |
| Enable file upload | Text | - |
|
| Placeholder | Text | - | - |
| Assistant role | Color | - | - |
| User role | Color | - | - |
| Avatar | Color | - | - |
| Avatar text | Color | - | - |
| Accent | Color | - | - |
| Container background | Color | - | - |
| Primary text | Color | - | - |
| Secondary text | Color | - | - |
| Separator | Color | - | - |
| Button | Color | - | - |
| Button text | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Options | Key-Value | Key-value object with options. Must be a JSON string or a state reference to a dictionary. | - |
| Orientation | Text | Specify how to lay out the options. |
|
| Primary text | Color | - | - |
| Accent | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Color List | Object | List of predefined colors | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Title | Text | - | - |
| Width (factor) | Number | Relative size when compared to other columns in the same container. A column of width 2 will be double the width of one with width 1. | - |
| Sticky | Boolean | - | - |
| Collapsible | Boolean | - | - |
| Start collapsed | Boolean | Only applied when the column is collapsible. | - |
| Separator | Color | - | - |
| Padding | Padding | - | - |
| Content alignment (H) | Align (H) | - | - |
| Content alignment (V) | Align (V) | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Data | Text | Must be a state reference to a Pandas dataframe or PyArrow table. Alternatively, a URL for an Arrow IPC file. | - |
| Show index | Boolean | Shows the dataframe's index. If an Arrow table is used, shows the zero-based integer index. | - |
| Enable search | Boolean | - | - |
| Enable adding a record | Boolean | - | - |
| Enable updating a record | Boolean | - | - |
| Enable download | Boolean | Allows the user to download the data as CSV. | - |
| Actions | Key-Value | Define rows actions | - |
| Enable markdown | Boolean | If active, the output will be sanitized; unsafe elements will be removed. | - |
| Display row count | Number | Specifies how many rows to show simultaneously. | - |
| Wrap text | Boolean | Not wrapping text allows for an uniform grid, but may be inconvenient if your data contains longer text fields. | - |
| Primary text | Color | - | - |
| Secondary text | Color | - | - |
| Accent | Color | - | - |
| Separator | Color | - | - |
| Background | Color | - | - |
| Font style | Text | - |
|
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Allowed file types | Text | Provides hints for browsers to select the correct file types. You can specify extensions and MIME types separated by comma, or leave empty to accept any file. | - |
| Allow multiple files | Boolean | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| API Key | Text | API Key from Google Cloud Console. | - |
| Map ID | Text | ID of map from Google Cloud Console, needed for markers. | - |
| Map type | Text | One of 'roadmap', 'satellite', 'hybrid' or 'terrain'. |
|
| Zoom | Number | - | - |
| Latitude | Number | - | - |
| Longitude | Number | - | - |
| Markers | Object | Markers data | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Text | Text | - | - |
| Primary text | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Text | Text | Add text directly, or reference state elements with @{my_text_0}. | - |
| Heading type | Text | - |
|
| Alignment | Text | - |
|
| Primary text | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Padding | Padding | - | - |
| Content alignment (H) | Align (H) | - | - |
| Content alignment (V) | Align (V) | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
You can configure the element type, styles, and attributes to fit your design requirements. You can link them to state for advanced use cases, such as custom animations.
All valid HTML tags are supported, including tags such as `iframe`, allowing you to embed external sites.
Take into account the potential risks of adding custom HTML to your app, including XSS. Be specially careful when injecting user-generated data.
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Element | Text | Set the type of HTML element to create, e.g., 'div', 'section', 'span', etc. | - |
| Styles | Object | Define the CSS styles to apply to the HTML element using a JSON object or a state reference to a dictionary. | - |
| Attributes | Object | Set additional HTML attributes for the element using a JSON object or a dictionary via a state reference. | - |
| HTML inside | Text | Define custom HTML to be used inside the element. It will be wrapped in a div and rendered after children components. | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Source | Text | A valid URL | - |
| Separator | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
Use your app's static folder to serve images directly. For example, `static/my_image.png`.
Alternatively, pass a Matplotlib figure via state.
`state["my_fig"] = fig` and then setting the *Image* source to `@{fig}`
You can also use packed files or bytes:
`state["img_b"] = wf.pack_bytes(img_bytes, "image/png")`
`state["img_f"] = wf.pack_file(img_file, "image/png")`
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Source | Text | A valid URL. Alternatively, you can provide a state reference to a Matplotlib figure or a packed file. | - |
| Caption | Text | Leave blank to hide. | - |
| Max width (px) | Number | - | - |
| Max height (px) | Number | - | - |
| Secondary text | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Data | Object | - | - |
| Initial depth | Number | Sets the initial viewing depth of the JSON tree hierarchy. Use -1 to display the full hierarchy. | - |
| Hide root | Boolean | Don't show the type of the root node when it's an Object or an Array. | - |
| Enable copy button | Boolean | If active, adds a control bar with copy JSON button. | - |
| JSON indentation | Width | - | - |
| Secondary text | Color | - | - |
| Separator | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| URL | Text | Specify a URL or choose a page. Keep in mind that you can only link to pages for which a key has been specified. | - |
| Target | Text | Specifies where to open the linked document. |
|
| Rel | Text | Specifies the relationship between the current document and the linked document. | - |
| Text | Text | The text to display in the link. | - |
| Primary text | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
For this component you need Mapbox access token: [https://www.mapbox.com/api-documentation/#access-tokens-and-token-scopes](https://www.mapbox.com/api-documentation/#access-tokens-and-token-scopes)
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Access Token | Text | Access token from Mapbox | - |
| Map style | Text | Map style URL | - |
| Zoom | Number | - | - |
| Latitude | Number | - | - |
| Longitude | Number | - | - |
| Markers | Object | - | - |
| Enable controls | Boolean | Show map controls | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Overview
The **Message** component displays status messages, notifications, and feedback to users. It uses prefix characters to determine the message type and styling automatically.
Message components support different types (success, error, warning, loading, info) with appropriate styling and can display dynamic content from state variables. They're essential for creating responsive, user-friendly interfaces that keep users informed.
## Common use cases
* **Status updates**: Show progress messages during processing operations
* **Error notifications**: Display validation errors or system failures
* **Success confirmations**: Confirm successful actions or completions
* **Loading states**: Show animated loading messages during operations
* **Form validation**: Display field validation errors and warnings
## How it works
1. **Message content**: Display static text or reference state variables
2. **Prefix-based types**: Use prefix characters to determine message type automatically. For example, `+` makes the message green, `-` makes it red, and `%` provides a dynamic loading icon.
3. **Dynamic updates**: Content updates automatically when state variables change
4. **Visual styling**: Each message type has distinct colors and styling
5. **Conditional display**: Messages can show or hide based on conditions
The component provides immediate feedback to users about the state of their interactions and system operations.
## Configuration options
### Basic settings
* **Message**: The text content to display (supports state variable references)
* **Prefix characters**: Use prefixes to determine message type automatically. For example, `+` in front of the message makes it green: `+Operation completed successfully`.
### Message types and prefixes
* **Success**: Prefix with `+` (for example, `"+Operation completed successfully"`)
* **Error**: Prefix with `-` (for example, `"-An error occurred"`)
* **Warning**: Prefix with `!` (for example, `"!Please check your input"`)
* **Loading**: Prefix with `%` (for example, `"%Processing your request..."`)
* **Info**: No prefix (for example, `"This is an informational message"`)
### Styling options
* **Success color**: Customize success message background color (default: Green3)
* **Error color**: Customize error message background color (default: Orange2)
* **Warning color**: Customize warning message background color (default: #FFE999)
* **Info color**: Customize info message background color (default: Blue2)
* **Loading color**: Customize loading message background color (default: Blue2)
* **Primary text color**: Set the text color
* **Custom CSS classes**: Apply additional styling
## Example
### Processing status updates
Create a workflow that shows different status messages during file processing.
**Interface:**
* File input to upload a file
* Button component to start file processing
* Message component for status updates
**Message configuration:**
* **Message**: `@{status_message}` (with `%` prefix for loading, `+` for success, `-` for errors)
## Best practices
1. **Clear messaging**: Use concise, clear language that users can understand immediately
2. **Appropriate types**: Choose the correct message type for the content (info, success, warning, error)
3. **Dynamic content**: Use state variables to show relevant, up-to-date information
4. **Conditional display**: Show messages only when relevant to avoid interface clutter
5. **Consistent styling**: Maintain consistent message styling across your interface
6. **Context**: Provide enough context for users to understand and act on the message
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Message | Text | Prefix with '+' for a success message, with '-' for error, '!' for warning, '%' for loading. No prefix for info. Leave empty to hide. | - |
| Success | Color | - | - |
| Error | Color | - | - |
| Warning | Color | - | - |
| Info | Color | - | - |
| Loading | Color | - | - |
| Primary text | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Name | Text | - | - |
| Value | Text | The main value to be displayed. It's not limited to numbers. | - |
| Description | Text | - | - |
| Note | Text | Prefix with '+' for a positive message, with '-' for a negative message. | - |
| Primary text | Color | - | - |
| Secondary text | Color | - | - |
| Positive | Color | - | - |
| Neutral | Color | - | - |
| Negative | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Placeholder | Text | - | - |
| Minimum value | Number | - | - |
| Max value | Number | - | - |
| Step | Number | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Page | Number | The current page number. | - |
| Page Size | Number | The number of items per page. | - |
| Total Items | Number | The total number of items | - |
| Page Size Options | Text | A comma-separated list of page size options. If it's empty, the user can't change the page size. Set your default page size as the first option. | - |
| Show All Option | Boolean | Show an option to show all records. | - |
| Jump To | Boolean | Show an option to jump to a specific page. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| PDF source | Text | A valid URL. Alternatively, you can provide a state reference to a packed PDF file. | - |
| Highlights | Object | A list of highlights to be applied to the PDF as a JSON array of strings. | - |
| Selected highlight match | Number | The index of the selected highlight match. | - |
| Page | Number | The page to be displayed. | - |
| Enable controls | Boolean | Show controls to navigate the PDF. | - |
| Container background | Color | - | - |
| Separator | Color | - | - |
| Primary text | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
You can listen to events triggered by Plotly.js and add interactivity to your charts.
For example, implement cross-filtering.
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Graph specification | Object | Plotly graph specification. Pass it using state, e.g. @{fig_0}, or paste a JSON specification. | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Value | Number | - | - |
| Minimum value | Number | - | - |
| Maximum value | Number | - | - |
| Display percentage | Boolean | - | - |
| Accent | Color | - | - |
| Primary text | Color | - | - |
| Separator | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Options | Key-Value | Key-value object with options. Must be a JSON string or a state reference to a dictionary. | - |
| Orientation | Text | Specify how to lay out the options. |
|
| Primary text | Color | - | - |
| Accent | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Minimum value | Number | - | - |
| Maximum value | Number | - | - |
| Step size | Number | - | - |
| Accent | Color | - | - |
| Popover color | Color | - | - |
| Popover background | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Feedback | Text | - |
|
| Minimum value | Number | Valid values are 0 and 1. | - |
| Max value | Number | Valid values are between 2 and 11. | - |
| Step | Number | Valid values are between 0.25 and 1. | - |
| Accent | Color | - | - |
| Primary text | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Overview
The **Repeater** component dynamically creates copies of its child components based on a dictionary or array. It's similar to a for-each loop in programming, but for UI elements. Use Repeaters when you need to display lists, collections, or any data where you don't know the exact number of items in advance.
Inside a Repeater, you can access:
* `@{item}`: the current item's value
* `@{itemId}`: the current item's index (for arrays) or key (for dictionaries)
## Common use cases
* **Dynamic lists**: Display files, tasks, messages, or any collection of items
* **Search results**: Show filtered or API-fetched data
* **User-generated content**: Display comments, reviews, or form responses
* **Product catalogs**: Render product cards or inventory items
* **Data tables**: Create rows based on database queries or structured output
## How it works
1. **Data source**: Bind the Repeater to a state variable containing an array or dictionary
2. **Child components**: Add components inside the Repeater that define the template
3. **Variable access**: Use `@{item}` and `@{itemId}` to display data for each iteration
4. **Nested Repeaters**: Nest Repeaters to display hierarchical data structures
5. **Input binding**: Bind input components to specific items using `@{itemId}` for interactive lists
## Configuration options
### Basic settings
* **Repeater object**: The state variable containing the array or dictionary to iterate over (for example, `@{myList}`)
* **Key variable name**: The variable name for accessing the current item's key or index (default: `itemId`)
* **Value variable name**: The variable name for accessing the current item's value (default: `item`)
### Visibility
Set custom visibility conditions to show the Repeater only when data is available (for example, `myList` to hide when empty).
## Using inputs within Repeaters
When you add input components (like Text Input, Text Area Input, or other form fields) inside a Repeater, you need to properly bind them to capture user input for each repeated item.
### Binding inputs to dictionary items
The most reliable approach is to use a dictionary as your Repeater data source and bind inputs using the `itemId` to target specific dictionary keys:
```
state_variable[itemId].property_name
```
**Example - Text Area Input inside a Repeater**
If your state variable is `products` and you want each product to have a `description` property that users can edit:
1. Set up your Repeater with **Repeater object**: `@{products}`
2. Add a Text Area Input inside the Repeater
3. Set the Text Area Input's **Link variable** to: `products[itemId].description`
This creates or updates the `description` key for each specific product in your dictionary. As users type in each Text Area Input, the corresponding product's description updates in your state.
### Managing editable lists with temporary state
For more complex scenarios where you need to edit one item at a time:
1. **Create a temporary state variable** to hold the draft being edited (for example, `current_draft`)
2. **Track which item is being edited** using a state variable like `editing_item_id`
3. **Show input fields conditionally** using visibility rules that check if `@{itemId} == @{editing_item_id}`
4. **Display read-only content** for items that aren't being edited
5. **Copy data** from the temporary variable back to your main data structure when the user saves
This pattern prevents multiple items from being edited simultaneously and provides a clearer user experience when editing items in a list.
### Best practices for Repeater inputs
1. **Use dictionaries over arrays**: Dictionaries make it easier to target specific items using `itemId` as the key
2. **Provide clear feedback**: Show which item is being edited with conditional styling or visibility
3. **Validate input**: Check that required fields are filled before processing
4. **Consider performance**: Large lists with many input components can impact performance; use pagination if needed
5. **Test empty states**: Ensure your Repeater handles empty data gracefully
## Example: Dynamic task list with input
This example shows how to create an editable task list where users can update task descriptions.
**Interface:**
* Button to add new tasks
* Repeater bound to `tasks` dictionary
* Text Input inside Repeater for editing task names
* Button inside Repeater to delete tasks
**Repeater configuration:**
* **Repeater object**: `@{tasks}`
* **Key variable name**: `itemId`
* **Value variable name**: `item`
**Text Input configuration (inside Repeater):**
* **Link variable**: `tasks[itemId].name`
* **Placeholder**: "Enter task name"
This setup allows each task to maintain its own editable name field.
## Dynamic tabs
Repeaters can be used to create tabs dynamically based on your data. Place a **Repeater** inside a **Tab Container**, then add a **Tab** component inside the Repeater.
**Example structure:**
* Tab Container
* Repeater (bound to `@{categories}`)
* Tab (with name `@{item.name}`)
* Your content here
Each item in your data generates a new tab. This is useful for category-based views, multi-entity interfaces, or any scenario where the number of tabs depends on your data.
## Dynamic columns
Similarly, you can create dynamic column layouts by placing a **Repeater** inside a **Column Container** with **Column** components inside the Repeater. This allows for responsive, data-driven layouts.
## Working with pagination
For large datasets, combine Repeaters with the **Pagination** component to improve performance. The Pagination component provides events (`wf-change-page`, `wf-change-page-size`) that you can handle in your blueprints to update the data displayed in your Repeater.
**Example pattern:**
1. Add a Pagination component to your interface
2. Handle the `wf-change-page` event to load the appropriate data slice
3. Update the state variable bound to your Repeater with the paginated data
This approach prevents rendering hundreds or thousands of items at once, which can impact performance.
## Additional resources
For a detailed tutorial on building with Repeaters, including nested Repeaters and structured output integration, see [Build dynamic UI components with Repeaters](/agent-builder/repeater).
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Repeater object | Object | Include a state reference to the dictionary used for repeating the child components. Alternatively, specify a JSON object. | - |
| Key variable name | Text | Set the name of the variable that will store the key of the current repeater object entry. | - |
| Value variable name | Text | Set the name of the variable that will store the value of the current repeater object entry. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Component id | Text | The id of the component to reuse. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Title | Text | Leave blank to hide. | - |
| Collapsible | Boolean | - | - |
| Start collapsed | Boolean | Only applied when the component is collapsible. | - |
| Accent | Color | - | - |
| Primary text | Color | - | - |
| Secondary text | Color | - | - |
| Container background | Color | - | - |
| Container shadow | Shadow | - | - |
| Separator | Color | - | - |
| Button | Color | - | - |
| Button text | Color | - | - |
| Button shadow | Shadow | - | - |
| Padding | Padding | - | - |
| Content alignment (H) | Align (H) | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Overview
The **Select Input** component provides a dropdown interface for selecting single or multiple values from a searchable list of options. It offers a clean, accessible way to present multiple choices while maintaining a compact interface.
Select inputs are essential for forms and interfaces where users need to choose from predefined options. They provide better user experience than text inputs for categorical data and help ensure data consistency and validation.
## Common use cases
* **Form selections**: Choose options in registration and configuration forms
* **Category filtering**: Filter content by categories or types with single or multiple selections
* **Configuration settings**: Select preferences and settings from predefined options
* **Data entry**: Choose from standardized options for consistent data entry
## How it works
1. **Label**: Provide a clear label for the select input field
2. **Options**: Define available choices as key-value pairs in JSON format
3. **Dropdown**: Display options in a searchable dropdown interface
4. **Selection**: Allow users to select one or multiple options from the list
5. **Binding**: Connect to a state variable to store the selected value
6. **Events**: Trigger workflows when selections change
The component automatically updates the bound state variable when users make selections, and can trigger events for real-time processing or validation.
## Configuration options
### Basic settings
* **Label**: Text label displayed preceding the select input field
* **Allow Multi-select**: Toggle to enable selection of multiple options from the dropdown (default: off)
* **Options**: Key-value object with available options (must be a JSON string or state reference to a dictionary)
* **Placeholder**: Text to display when no options are selected
* **Maximum count**: The maximum allowable number of selected options (set to zero for unlimited) - only available when multi-select is enabled
### Binding settings
* **Link Variable**: Connect the result of this component to a dynamic variable for use across the agent
### Styling options
* **Accent color**: Set the focus and highlight color for the select input
* **Primary text color**: Set the main text color for options and labels
* **Container background color**: Set the background color of the dropdown container
* **Separator color**: Set the border and divider color
* **Custom CSS classes**: Apply additional styling with custom CSS classes (separated by spaces)
### Events
* **wf-option-change**: Triggered when a single option changes (only available when multi-select is off) - payload contains the selected option
* **wf-options-change**: Triggered when multiple options change (only available when multi-select is enabled) - payload contains array of selected options
## Example
### User registration form
This example shows how to create a registration form with select inputs.
**Interface:**
* Text Input components for personal information
* Select Input components for preferences
* Button component for submission
**Select Input configuration:**
* **Label**: "Country," "Account Type," "Time Zone"
* **Options**: Various country, account type, and timezone options
* **Link Variable**: "country," "account," "timezone"
### Multi-select tag management
Create a tag management interface for content categorization.
**Interface:**
* Select Input component with multi-select enabled
* Button component for saving
**Select Input configuration:**
* **Label**: "Content Tags"
* **Allow Multi-select**: Enabled
* **Options**: JSON object with tag categories
* **Placeholder**: "Select tags for your content"
* **Link Variable**: "tags"
## Best practices
1. **Clear labeling**: Use descriptive labels that explain the selection purpose and context
2. **Logical ordering**: Arrange options in logical order (alphabetical, numerical, or by frequency of use)
3. **Option clarity**: Use clear, concise option text that users understand without ambiguity
4. **Accessibility**: Ensure select inputs are keyboard navigable and screen reader friendly
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Allow Multi-select | Boolean | Select more than one option from the dropdown. | - |
| Options | Key-Value | Key-value object with options. Must be a JSON string or a state reference to a dictionary. | - |
| Placeholder | Text | Text to show when no options are selected. | - |
| Maximum count | Number | The maximum allowable number of selected options. Set to zero for unlimited. | - |
| Accent | Color | - | - |
| Primary text | Color | - | - |
| Chip text | Color | The colour of the text in the chips. | - |
| Container background | Color | - | - |
| Separator | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
*Separator* components are used to separate layout elements. They can be used in most containers, including *Column container* to separate columns.
If the container flows horizontally (like a *Horizontal Stack* or a *Column container*) the *Separator* will be a vertical line. Otherwise, it'll be a horizontal line.
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Separator | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Start collapsed | Boolean | - | - |
| Background | Color | - | - |
| Accent | Color | - | - |
| Primary text | Color | - | - |
| Secondary text | Color | - | - |
| Container background | Color | - | - |
| Container shadow | Shadow | - | - |
| Separator | Color | - | - |
| Button | Color | - | - |
| Button text | Color | - | - |
| Button shadow | Shadow | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Minimum value | Number | - | - |
| Maximum value | Number | - | - |
| Step size | Number | - | - |
| Accent | Color | - | - |
| Popover color | Color | - | - |
| Popover background | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Name | Text | - | - |
| Padding | Padding | - | - |
| Completed | Boolean | Use a state reference to dynamically mark this step as complete. | - |
| Content alignment (H) | Align (H) | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Accent | Color | - | - |
| Primary text | Color | - | - |
| Secondary text | Color | - | - |
| Container background | Color | - | - |
| Container shadow | Shadow | - | - |
| Separator | Color | - | - |
| Button | Color | - | - |
| Button text | Color | - | - |
| Button shadow | Shadow | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Accent | Color | - | - |
| Primary text | Color | - | - |
| Separator | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Name | Text | - | - |
| Padding | Padding | - | - |
| Content alignment (H) | Align (H) | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Accent | Color | - | - |
| Primary text | Color | - | - |
| Secondary text | Color | - | - |
| Container background | Color | - | - |
| Container shadow | Shadow | - | - |
| Separator | Color | - | - |
| Button | Color | - | - |
| Button text | Color | - | - |
| Button shadow | Shadow | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Tags | Key-Value | Key-value object with tags. Must be a JSON string or a state reference to a dictionary. | - |
| Reference | Color | The colour to be used as reference for chroma and luminance, and as the starting point for hue rotation. | - |
| Seed value | Number | Choose a different value to reshuffle colours. | - |
| Rotate hue | Boolean | If active, rotates the hue depending on the content of the string. If turned off, the reference colour is always used. | - |
| Primary text | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Text | Text | Add text directly, or reference state elements with @{my_text_0}. | - |
| Enable markdown | Boolean | The Markdown output will be sanitised; unsafe elements will be removed. | - |
| Alignment | Text | - |
|
| Enable copy button | Boolean | Enable a copy button that lets users to copy the contents in this field to their clipboard | - |
| Primary text | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Overview
The **Text area Input** component allows users to enter multi-line text values. It's designed for longer text content like descriptions, comments, reviews, or any content that requires multiple lines.
## Common use cases
* **Long-form content**: Collect essays, articles, or detailed descriptions
* **User feedback**: Gather reviews, comments, or support requests
* **Data entry**: Allow users to paste large amounts of text or data
* **Configuration**: Collect complex configuration settings or parameters
## How it works
1. **Multi-line input**: Users can enter text across multiple lines
2. **Manual resizing**: Users can resize the text area by dragging the bottom corner
3. **State binding**: Content automatically saves to the specified state variable
4. **Real-time events**: Triggers events as users type or finish editing
5. **Configurable rows**: Set the initial number of rows to display
The component provides a familiar text editing experience with manual resizing for longer content entry.
## Configuration options
### Basic settings
* **Label**: Text label displayed above the input field
* **Placeholder**: Hint text shown when the field is empty
* **Rows**: Number of rows to display (default: `5`)
* **Link variable**: The name of the state variable that stores the input value
### Styling options
* **Custom CSS classes**: Apply additional styling
### Events
* **wf-change**: Triggered as users type (real-time) - payload contains the text area content
* **wf-change-finish**: Triggered when users finish editing (on blur) - payload contains the text area content
## Example
### Customer feedback form
This example shows how to create a customer feedback form with a text area for detailed comments.
**Interface:**
* Text Input for customer name
* Text area Input for feedback details
* Button to submit the form
**Text area Input configuration:**
* Label: "Your feedback"
* Placeholder: "Please share your experience with our product..."
* State element: `customer_feedback`
* Rows: 4
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Placeholder | Text | - | - |
| Rows | Number | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Overview
The **Text Input** component allows users to enter single-line text values. It's the most basic form of user input and is essential for collecting information from users in your agent.
Text inputs automatically bind to state variables, making it easy to capture and use user input in your blueprint workflows. They support various styling options and can be configured for different use cases, including password fields.
## Common use cases
* **User information**: Collect names, emails, phone numbers, and other personal data
* **Search queries**: Allow users to search through data or content
* **Configuration values**: Let users set preferences or parameters
* **Form fields**: Collect data as part of larger forms
* **Password entry**: Secure input for sensitive information
## How it works
1. **Label**: Provide a clear label for the input field
2. **Placeholder**: Add helpful placeholder text to guide users
3. **Binding**: Connect to a state variable to store the input value
4. **Events**: Trigger workflows when users type or finish editing
5. **Styling**: Customize appearance with colors and CSS classes, and add a password mode for sensitive information
The component automatically updates the bound state variable as users type, and can trigger events for real-time processing or validation.
## Configuration options
### Basic settings
* **Label**: The field label displayed above the input
* **Placeholder**: Hint text shown when the field is empty
* **Link variable**: The name of the state variable that stores the input value
### Styling options
* **Password mode**: Toggle to hide input for sensitive data (default is disabled)
* **Accent color**: Set the focus color for the input field
* **Custom CSS classes**: Apply additional styling
### Events
* **wf-change**: Triggered as users type (real-time) - payload contains the input value
* **wf-change-finish**: Triggered when users finish editing (on blur) - payload contains the input value
## Example
### Search interface
This example shows how to create a search interface with a text input. The search query is stored in the `search_query` state variable. For the blueprint workflow, you can access the `search_query` state variable to see the user's query.
**Interface:**
* Text Input component for the search query
* Button component to trigger the search
* Results container to display matches
**Search text input configuration:**
* **Label**: "Search"
* **Placeholder**: "Enter search terms..."
* **Link variable**: `search_query`
## Best practices
1. **Clear labels**: Use descriptive labels that clearly indicate what information is needed
2. **Helpful placeholders**: Provide examples or guidance in placeholder text
3. **Appropriate validation**: Validate input on both client and server side
4. **Real-time feedback**: Use the `wf-change` event for immediate user feedback
5. **Password security**: Use password mode for sensitive information
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Placeholder | Text | - | - |
| Password mode | Boolean | - | - |
| Accent | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Label | Text | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Interval (ms) | Number | How much time to wait between ticks. A tick is considered finished when its event is handled. | - |
| Active | Boolean | Whether the timer should trigger tick events. | - |
| Accent | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
Generate a chart using Altair and pass it via state; it'll be converted to Vega-Lite specification.
`state["my_chart"] = chart`
Afterwards, you can reference the chart in the specification using the syntax `@{my_chart}`.
Alternatively, you can work with Vega-Lite directly.
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Chart specification | Object | Vega-Lite chart specification. Pass a Vega Altair chart using state or paste a JSON specification. | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
Use your app's static folder to serve videos directly. For example, `static/my_video.mp4`.
Alternatively, you can pack bytes or files in state:
`state["vid_b"] = wf.pack_bytes(vid_bytes, "video/mp4")`
`state["vid_f"] = wf.pack_file(vid_file, "video/mp4")`
Afterwards, you can reference the video using the syntax `@{vid_f}`.
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Source | Text | The URL of the video file. Alternatively, you can pass a file via state. | - |
| Enable controls | Boolean | Display Video player controls. | - |
| Autoplay | Boolean | Autoplay the video when the component is loaded. | - |
| Loop | Boolean | Loop the video when it reaches the end. | - |
| Muted | Boolean | Mute the video by default. | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Fields
| Name | Type | Description | Options |
|---|---|---|---|
| Refresh rate (ms) | Number | Set to 0 for manual capture. | - |
| Button | Color | - | - |
| Button text | Color | - | - |
| Button shadow | Shadow | - | - |
| Separator | Color | - | - |
| Custom CSS classes | Text | CSS classes, separated by spaces. You can define classes in custom stylesheets. | - |
## Use OIDC provider
Authentication configuration is done in the `server_setup.py` [module](/framework/custom-server). The configuration depends on your identity provider.
Here is an example configuration for Google.
```python server_setup.py theme={null}
import os
import writer.serve
import writer.auth
oidc = writer.auth.Oidc(
client_id="1xxxxxxxxx-qxxxxxxxxxxxxxxx.apps.googleusercontent.com",
client_secret="GOxxxx-xxxxxxxxxxxxxxxxxxxxx",
host_url=os.getenv('HOST_URL', "http://localhost:5000"),
url_authorize="https://accounts.google.com/o/oauth2/auth",
url_oauthtoken="https://oauth2.googleapis.com/token",
url_userinfo='https://www.googleapis.com/oauth2/v1/userinfo?alt=json'
)
writer.serve.register_auth(oidc)
```
### Use pre-configured OIDC
The Writer Framework provides pre-configured OIDC providers. You can use them directly in your application.
| Provider | Function | Description |
| -------- | -------------------- | --------------------------------------------------------------------------------------- |
| Google | `writer.auth.Google` | Allow your users to login with their Google Account |
| Github | `writer.auth.Github` | Allow your users to login with their Github Account |
| Auth0 | `writer.auth.Auth0` | Allow your users to login with different providers or with login password through Auth0 |
#### Google
You have to register your application into [Google Cloud Console](https://console.cloud.google.com/).
```python server_setup.py theme={null}
import os
import writer.serve
import writer.auth
oidc = writer.auth.Google(
client_id="1xxxxxxxxx-qxxxxxxxxxxxxxxx.apps.googleusercontent.com",
client_secret="GOxxxx-xxxxxxxxxxxxxxxxxxxxx",
host_url=os.getenv('HOST_URL', "http://localhost:5000")
)
writer.serve.register_auth(oidc)
```
#### Github
You have to register your application into [Github](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app#registering-a-github-app)
```python server_setup.py theme={null}
import os
import writer.serve
import writer.auth
oidc = writer.auth.Github(
client_id="xxxxxxx",
client_secret="xxxxxxxxxxxxx",
host_url=os.getenv('HOST_URL', "http://localhost:5000")
)
writer.serve.register_auth(oidc)
```
#### Auth0
You have to register your application into [Auth0](https://auth0.com/).
```python server_setup.py theme={null}
import os
import writer.serve
import writer.auth
oidc = writer.auth.Auth0(
client_id="xxxxxxx",
client_secret="xxxxxxxxxxxxx",
domain="xxx-xxxxx.eu.auth0.com",
host_url=os.getenv('HOST_URL', "http://localhost:5000")
)
writer.serve.register_auth(oidc)
```
### Authentication workflow
### App static assets
Static assets in your application are inaccessible. You can use the `app_static_public` parameter to allow their usage.
When `app_static_public` is set to `True`, the static assets in your application are accessible without authentication.
```python theme={null}
oidc = writer.auth.Auth0(
client_id="xxxxxxx",
client_secret="xxxxxxxxxxxxx",
domain="xxx-xxxxx.eu.auth0.com",
host_url=os.getenv('HOST_URL', "http://localhost:5000"),
app_static_public=True
)
```
## User information in event handler
When the `user_info` route is configured, user information will be accessible
in the event handler through the `session` argument.
```python theme={null}
def on_page_load(state, session):
email = session['userinfo'].get('email', None)
state['email'] = email
```
## Unauthorize access
It is possible to reject a user who, for example, does not have the correct email address.
| Parameter | Description |
| ------------ | ---------------------- |
| status\_code | HTTP status code |
| message | Error message |
| more\_info | Additional information |
## Modify user info
User info can be modified in the callback.
```python theme={null}
from fastapi import Request
import writer.serve
import writer.auth
oidc = ...
def callback(request: Request, session_id: str, userinfo: dict):
userinfo['group'] = []
if userinfo['email'] in ['fabien@example.com']:
userinfo['group'].append('admin')
userinfo['group'].append('user')
else:
userinfo['group'].append('user')
writer.serve.register_auth(oidc, callback=callback)
```
## Custom unauthorized page
You can customize the access denial page using your own template.
```python theme={null}
import os
from fastapi import Request, Response
from fastapi.templating import Jinja2Templates
import writer.serve
import writer.auth
oidc = ...
def unauthorized(request: Request, exc: writer.auth.Unauthorized) -> Response:
templates = Jinja2Templates(directory=os.path.join(os.path.dirname(__file__), "templates"))
return templates.TemplateResponse(request=request, name="unauthorized.html", status_code=exc.status_code, context={
"status_code": exc.status_code,
"message": exc.message,
"more_info": exc.more_info
})
writer.serve.register_auth(oidc, unauthorized_action=unauthorized)
```
## Enable in edit mode
Authentication is disabled in edit mode. To activate it, you must trigger the loading of the server\_setup module in edition mode.
```bash theme={null}
writer edit --enable-server-setup
```
# Backend-driven UI
Source: https://dev.writer.com/framework/backend-driven-ui
Framework facilitates backend-initiated user interface modifications. These changes are made possible through **Code-Managed Components** (CMCs), distinct from the *Builder-Managed Components* (BMCs).
CMCs, unlike BMCs, are dynamically created and modified via back-end code, and cannot be edited (but still can be viewed) within the application builder. It's important to also note that CMCs do not persist in your application's files and exist only during the application runtime, supporting dynamic UI adjustments.
Framework adds notifications when a runtime error takes place. You can add your own notifications using the `add_notification` method, which takes the `type`, `title` and `message` arguments. `type` must be one of `error`, `warning`, `info`, `success`.
```py theme={null}
def notify_of_things_that_happened(state):
state.add_notification("error", "An Error", "Something bad happened.")
state.add_notification("warning", "A Warning", "Be aware that something happened.")
state.add_notification("info", "Some Info", "Something happened.")
state.add_notification("success", "A Success", "Something good happened.")
```
## Opening a URL
Open a URL in a new tab using the `open_url` method, which takes the `url` argument.
```py theme={null}
def handle_open_website(state):
state.open_url("https://writer.com")
```
The URL will be safely opened with `noopener` and `noreferrer` options.
2. **Preview mode:** This mode lets you preview the application, experiencing it almost as your end users would. The tool overlay is hidden, but you can still edit the app’s code and view its log in this mode.
You can switch between UI mode and Preview mode by using the **UI**/**Preview** selector near the upper left corner of the page.
## Building your app in UI mode
### The different areas of UI mode
In UI mode, Writer Framework Builder’s page is divided into these areas:
1. **Canvas:** This is where you lay out components to build your app’s user interface. It displays the UI as your users will see it.
2. **Core toolkit:** A “palette” of UI components that can be added to your app’s user interface. To add a component to your app’s UI, drag it onto the Canvas or into the Component tree.
3. **Component tree:** This provides an alternative way to view your app’s user interface: as a hierarchical tree structure. It’s useful for ensuring that UI components are located in the right place or in the correct container object, and is handy for selecting UI components in complex layouts.
4. **Component settings:** This panel lets you view and edit the settings for the currently selected UI component. You can hide the Component settings panel if you need more screen space to work on the Canvas.
5. **Top bar:** Contains the “high level” editing controls: switching between UI and Preview mode, undoing and redoing the most recent change to the UI, and viewing the application’s state.
6. **Bottom bar:** Contains the “low level” editing controls, which toggle the Code and Log panels.
### Defining your app’s user interface
Writer Framework Builder provides a selection of over 50 UI components that you can use to build your app’s user interface:
You define your app’s user interface by dragging components from the Core toolkit and dropping them either onto the Canvas or into the Component tree (remember, the Canvas and Component tree provide different ways to look at your app’s user interface). If you simply drag and drop a UI component onto an empty spot on the Canvas, it will be placed at the “end,” below all other UI components in the user interface. To place a UI component at a specific location, drag them over the desired location or parent component until you see the insertion lines.
It can sometimes be difficult to find the component you’re looking for, so the Core toolkit has a search field. You can use it to find the narrow down the list of components or find a specific one.
Some UI components, such as Section, can act as “parents,” which are UI components that can contain other UI components. Others, such as Text, cannot. Additionally, certain components have placement restrictions — for instance, a Column must be added to a Column Container, and a Sidebar can only be added to a Page.
The Component tree provides a hierarchical view of your app’s user interface. It shows the top-down layout of UI components in your app, as well as the parent-child relationships between components, making it easier to understand your app’s structure and ensure that components are correctly nested.
You will find the Component tree useful when trying to reorganize the order of components in the UI, especially those located inside a parent UI component. For more flexibility and finer control, you can use the Component tree as a source or a destination for your drag and drop actions.
### Discovering components
The Builder is designed to allow easy discoverability of components. Rather than scouring specifications every time you need to use a component, you can rely on the visual editor to guide you.
1. **Short description:** You can hover on the component type to get a tooltip with a short description.
2. **Available properties and events:** Looking at *Settings* will allow you to see which of its properties are configurable.
3. **Built-in docs:** Components have short docs built into them. You can expand it by clicking the help icon in Settings.
4. **Event handler stub code:** Different events need to be handled differently. The built-in stub handlers, which can be found next to each event, can help you get started when writing event handlers.
### Selecting and editing components
To move or edit a component in your UI, you need to select it first. You can do this by clicking on the component either in the Canvas or in the Component tree. The selected component will be highlighted in both the Canvas and Component tree.
Selecting a UI component will allow you to view and edit its settings in the Component settings panel on the right side of the page.
To hide the Component settings panel to get a better view of the Canvas, click on **»** in the button bar. To show the panel, click on **⚙** in the button bar.
The settings in the Component settings panel are divided into the following sections:
These settings are divided into two categores:
1. **General**, which specifies the component’s content
2. **Style**, which defines the component’s appearance.
Values for these settings can include:
1. Literals, e.g. `monkey`
2. References to application state using the template syntax `@{}`, e.g. `@{my_favourite_animal}`.
3. A combination of both, e.g. `My favourite animal is @{my_favourite_animal}`.
4. Nested states can be accessed with `.` (dot), e.g. `@{building.height}`.
5. Nested elements can be dynamically accessed with `[]`, e.g. `@{building[dynamic_prop]}` will be equivalent to `@{building.height}` when `dynamic_prop` equals `height`.
The values for these settings can be of different types, such as *Text*, *Color* and *Number*. The values are stored as text and are cast to the correct type when evaluated.
Only input components have a **Binding** section, whose settings are used to bind the component to a variable in the application’s state. The binding is two-way; if the user changes the component’s value, the state variable will change to match, and any change the code makes to the bound state variable will also change the component’s value.
For example, a *Slider Input* component can be bound to a state variable `my_var`. If the value of the slider changes, so does the value of `my_var`. Similarly, if the value of `my_var` changes, the slider is moved automatically to reflect the change.
To bind an input component, specify the state element. For example, `my_var` or `building.height`. Note that this field should not contain the template syntax, e.g. `my_var` and not `@{my_var}`.
The **Events** section lists all the events generated by the selected component, with the option of setting event handlers for them. For example, one of the events that the Text Input component generates is the `wf-change` event, which occurs whenever the user changes the contents of the component.
For more about event handlers, consult the [*Event handlers*](/framework/event-handlers) section of the Writer Framework documentation.
The **Visibility** settings control Whether the component should be displayed. There are three visibility options:
1. **Yes**: The component is displayed.
2. **No**: The component is *not* displayed. Note that hidden components are still part of the HTML code but aren't shown.
3. **Custom**: The component’s visibility depends on the value of a given state or context element. For example, if set to `my_var`, visibility will depend on the value of the `my_var` state element. Note that this field, similarly to Binding, should only contain the state element, e.g. `my_var` and not `@{my_var}`.
Options will be grayed out when they're not applicable to the relevant component. Most shortcuts can also be activated using the keyboard; hover the cursor over a shortcut to see its keyboard shortcut.
The shortcuts are:
* **Add**: Adds a child of a specified type to the selected component.
* **Move up**: Decrements the position index of the selected component, used to sort children within the parent container.
* **Move down**: Increments the position index of the selected component.
* **Cut**: Cuts the selected component and places it in the clipboard.
* **Copy**: Copies the selected component to the clipboard.
* **Paste**: Pastes the content of the internal clipboard using the selected component as a parent.
* **Go to parent**: Selects the parent of the selected component.
* **Delete**: Deletes the selected component.
### The Code editor and the Log
Writer Framework Builder provides a built-in Code editor for the `main.py` file, which defines the behavior of the app. You can toggle the Code panel by clicking the **Code** control near the lower left corner of the page:
Any changes made to the code do not take effect until you click the **Save and run** button, located at the upper right corner of the code editor.
The log is a useful debugging tool that displays any messages sent to standard output via the `print()` function, as well as any error messages. You can toggle the Log panel by clicking the **Log** control near the lower right corner of the page:
Note that both the Code editor and Log panes can be displayed at the same time:
## Testing your app in Preview mode
In Preview mode, the overlays that let you build the user interface — the Core toolkit, Component tree, and Component settings — are invisible and unavailable. You see the app *almost* as your users would see it; the Top bar and Bottom bar, which your users would not see, are still available to you in Preview mode.
You can still use the Code editor and Log in Preview mode:
# Chat assistant
Source: https://dev.writer.com/framework/chat-assistant
In this tutorial, you'll use the Writer Framework to create a simple yet powerful chat assistant that can engage in conversations on various topics, provide answers to your questions, and maybe even help you when you're experiencing writer's block!
The process will take only minutes using a drag-and-drop visual editor to build the user interface and Python for the back-end code.
Here's what the finished project will look like:
## Prerequisites
Before starting, ensure you have:
* **A Writer account:** You don't need an account to use Writer Framework, but you'll need one to use the AI module. [Sign up for a free account here](https://app.writer.com/register).
* **Python 3.9.2 or later**: Use the installer from [python.org](https://www.python.org/downloads/).
* **pip:** This command-line application comes with Python and is used for installing Python packages, including those from Writer.
* **A basic understanding of Python:** You should be familiar with the basics of the language.
* **Your favorite code editor (optional):** There's a code editor built into Writer for editing back-end code, but you can also use Visual Studio Code, Notepad++, Vim, Emacs, or any text editor made for programming if you prefer.
## Setting up your project
### Create a Writer app and get its API key
First, you'll need to create a new app within Writer.
The **Start building** menu will appear, presenting options for the types of apps you can create.
Select **Framework**, located under **Developer tools**. This will create a brand new app based on Writer Framework.
You'll see the following:
* The **canvas** is in the center. It displays the app's user interface.
* The column on the left contains:
* The **Core toolkit**, which contains all the UI components. You define the user interface by dragging components from the Toolkit and placing them on the canvas.
* The **Component tree**, which shows the arrangement of the UI components on the canvas. It's also useful for selecting items on the canvas, especially when it has a lot of UI components.
It's time to build the UI!
The first property you'll see in the panel is the **Text** property, which defines the text that appears as the header's title. It should contain the value `@{my_app.title}`. The `@{` and `}` indicate that `my_app.title` is a variable and that its contents should be the text displayed instead of the literal text "my\_app.title". You'll set the value of this variable soon.
A pane with the name **Code** will appear at the bottom half of the screen, displaying an editor for the the contents of `main.py`.
Click the "toggle code" button to hide the code editor.
The value `@{conversation}` specifies that the **Chatbot** component should get its information from the value corresponding to the `conversation` key in the application's `state` variable.
To get a better sense of what the experience will be like for the user, switch to the preview by changing the edit mode (located near the upper left corner of the page) from *UI* mode to *Preview* mode by selecting the **Preview** option:
Here’s what the app looks like in *Preview* mode:
You can see the output of any `print()` functions and error messages by clicking on the **Log** button located near the upper right corner of the page:
Here’s what the app looks like when displaying the log:
It's very helpful to be able to test the application while editing it. As you continue to work with Writer Framework, you'll find yourself alternating between making changes to your application and testing those changes without having to leave the project editor.
## Run the application locally
Once you've tested the application, it's time to run it locally.
Switch back to your terminal application. Stop the project editor with ctrl-c, then run the application by entering the following command:
```
writer run chat-assistant
```
Note that the command starts with `writer run` as opposed to `writer edit`. This launches the application as your users will see it, without any of the editing tools. Even though you can preview your applications in the project editor, it's still a good idea to test it by running it on your computer, outside the project editor, before deploying it.
You'll be able to access the application with your browser at the URL that appears on the command line. It should look like this:
Dependencies are [provided](https://vuejs.org/api/composition-api-dependency-injection.html) using injection symbols and can be *injected* to be used by the component template. These include `evaluatedFields`, which contain the current values of the editable fields. Injected dependencies are fully typed, making development easier.
[Rollup's external feature](https://rollupjs.org/configuration-options/#external), invoked via Vite, allows for extensions to be compiled without dependencies and link those during runtime. Therefore, extensions aren't bundled to be standalone, but rather to work as a piece of a puzzle.
## Anatomy of a template
A template defines how a certain component is rendered. For example, `corebutton` defines how *Button* components are rendered.
Framework component templates are purely front-end. They are Vue 3 templates that extend the Vue specification via a [custom option](https://vuejs.org/api/utility-types.html#componentcustomoptions), `writer`. This custom option defines all the Framework-specific behaviour of the component. For example, its `fields` property establishes which fields will be editable via the Builder.
### Simple example
This example shows a template for *Bubble Message*, a simple demo component with one editable field, `text`.
```js theme={null}
## Developing templates
### Run a local server
DataFrames built-in features that users expect, such as headers that can be clicked to change the sort order and resizable columns...
...and with the simple change of a parameter, you can enable features such as the Search field, which lets the user find the data they’re looking for (or filter out unwanted data), and the Download button, which downloads the data currently being displayed as a .csv file:
You can find the full list of DataFrame properties and fields on the [*DataFrame* component page](/components/dataframe).
### “DataFrame” has multiple meanings
**Writer Framework has two objects with the name *DataFrame*:**
1. **UI DataFrame:** In Writer Framework's UI, "DataFrame" refers to a ***user interface component*** that displays data in rows and columns in a way similar to a spreadsheet or SQL table.
2. **Code Dataframe:** In code that you write for a Writer Framework application, `DataFrame` refers to a ***data structure*** that stores data in rows and columns in a way similar to a spreadsheet or SQL table.
To present a data table in Writer Framework, you create a `DataFrame` data structure in your code and then bind it to a UI Dataframe.
## Displaying a static DataFrame
A static DataFrame is one whose content does not change. The user can change its sort order, but the data within the DataFrame remains constant.
### Download button
To enable the Download button, select the DataFrame, open the Component settings panel, and set **Enable download** to **yes**.
# Event handlers
Source: https://dev.writer.com/framework/event-handlers
Events originate in the front-end, for example, when a user clicks a *Button* component. Using the Builder, these events can be linked to event handlers.
## Plain Python functions
Event handlers are Python functions accessible from `main.py`. They can be defined in that same file or imported. No decorators or special syntax are required.
```py theme={null}
# This event handler will add an entry to the log
def handle_click()
print("Hello")
```
To specify that a function isn't an event handler and should remain hidden to the front-end, prefix it with a `_` (underscore).
```py theme={null}
# This function won't be visible in the front-end
# because its name starts with an underscore
def _reticulate(splines):
r_splines = np.random.normal(size=(splines,100))
return r_splines
```
### External handlers
If your `main.py` file has become cluttered with too many handler functions, you can organize them more effectively using the `init_handlers` method. This method allows you to register handler functions from other modules. You can pass a single imported module or a list of modules to the init\_handlers method to register multiple handlers simultaneously:
The binding above establishes a two-way link between the component and the state element `name`. If `name` changes in the back-end, the component changes. If the component changes, the value of `name` changes.
## Using events and bindings simultaneously
Bindings can be used together with events. This is useful for triggering recalculations or applying dynamic filters. For example, you may want to have three *Number Input* components bound to `a`, `b` and `c` and display a value `n`. This easily done by binding the components and linking the same recalculation event handler to all three components.
```py theme={null}
def recalculate(state):
state["n"] = state["a"]*state["b"]*state["c"]
```
## Handling inputs safely
Framework automatically sanitises the payloads it provides for its built-in events, those that start with `wf-`.
For example, if a *Dropdown Input* component lists options `high` and `low`, you're guaranteed you won't get a value like `"Robert'); DROP TABLE students;--"` when handling `wf-option-change`. You'll get `"high"`, `"low"` or `None`.
# Writer Framework
Source: https://dev.writer.com/framework/introduction
The Writer Framework lets you build feature-rich apps by using a drag-and-drop visual editor called **the Builder** and writing the back-end code in Python. It's fast and flexible, with clean, easy-to-test syntax. It provides separation of concerns between UI and business logic, enabling more complex apps.
Build AI apps with the Writer Framework when:
1. You need to incorporate external data sources, such as external APIs
2. You have complex user input that requires custom logic, such as conditions that trigger the use of different prompts
3. You want to quickly analyze and visualize data using an LLM
The Writer Framework offers:
## Setting up your project
### Creating a Writer app and getting your API key
From the Home screen, click on **Build an app**.
Select Framework as the app type you’d like to create, enabling you to generate keys and build your app with the Writer Framework.
On the next screen, you can edit your Writer application name in the upper left. Underneath “Authenticate with an API key,” click on “Reveal” to see and copy your API key.
### Creating the application
Next, open your terminal and navigate to the directory where you want to create your application directory.
### Setting up the code
First, integrate new functionality into your code for generating product descriptions.
To display the chart, drag a Plotly graph component from the Content section of the toolkit into your new tab. Click on the component to bring up the component settings. The Plotly graph component accepts a graph specification. Add `@{seo_analysis}` to pass the LLM-generated graph specification to the component.
Click preview, add some data to the form, and click generate. You should see a new SEO analysis tab appear with a nicely formatted and labeled chart.
## Extending the application: user-added outlet
Finally, you can extend this application even further by allowing users to add their own custom food outlet and derive a new description from a custom prompt.
### Adding the new form
Start by building the UI for this new form. From the Layout section of the Toolkit, drag a new Section component into the column where the current form is and drop it above or below it. Click on the Section and change the Name to “Add an outlet.”
To create the inputs for the form, drag a Text Input and a Number Input from the Input section of the Toolkit into the newly created section. Click on the Text Input component to change the Label to “Outlet name.” Click on the Number Input and change the label to “Character max.”
Finally, add a Button from the Other section of the toolkit to the bottom of the new section. Click on the button and change the text to “Add and Generate.” You can also add `laps` or another [Material Symbols](https://fonts.google.com/icons) ID to the Icon input if you wish.
### Updating the code
In the code, you next need to add corresponding state elements for the new form components to `wf.init_state()`. Add the following to the state dictionary:
```python theme={null}
"outlet_form": {
"name": "",
"character_max": "",
},
```
Don't forget to check your commas when adding to the state dictionary. Your completed state should look like this:
```python theme={null}
wf.init_state({
"form": {
"title": "",
"description": "",
"keywords": ""
},
"outlet_form": {
"name": "",
"character_max": "",
},
"message": "Fill in the inputs and click \"Generate\" to get started.",
"product_descriptions": {
"visible": False,
"outlets": {}
}
})
```
The `outlet_form` state elements will bind to the form elements.
Next, add the click handler for the new button. Copy and paste this `handle_add_outlet` method into the code under the `handle_click` method:
```python theme={null}
def handle_add_outlet(state):
# Create a new base prompt for the new outlet
new_outlet_name = state["outlet_form"]["name"]
product_info = {**state["outlet_form"].to_dict(), **state["form"].to_dict()}
base_prompt = user_prompt.format(**product_info)
# Add the new base prompt to the base_prompts dictionary
base_prompts[new_outlet_name] = base_prompt
# Generate the product description for the new outlet
state["message"] = f"% Generating product description for {new_outlet_name}..."
product_description = _generate_product_description(base_prompt, state["form"].to_dict())
state["product_descriptions"]["outlets"][new_outlet_name] = product_description
# Update the SEO analysis
state["message"] = "Updating SEO analysis..."
outlets = state["product_descriptions"]["outlets"]
state["seo_analysis"] = _generate_seo_keywords(outlets)
state["message"] = ""
```
This method formats the input from both forms into the imported `user_prompt` and adds the formatted prompt to the `base_prompts` dictionary. It then generates the product description for the new food outlet, updates the SEO analysis, and clears the status message.
### Binding the elements and handler to the UI
Finalize your setup by binding the state elements and configuring the click handler to the UI components.
You can add whatever additional form inputs you wish to the outlet form, but be sure to update `user_prompt` in the `prompts.py` file using your favorite editor.
## Conclusion
You’ve now built a full application with the Writer Framework and the Writer AI module. Congratulations! This application not only demonstrates the platform's capabilities but also provides a foundation on which you can build more complex applications. To learn more, explore the rest of the Writer Framework documentation and the API documentation.
# Quickstart
Source: https://dev.writer.com/framework/quickstart
## Overview
In this guide, you'll learn how to get started with the Writer Framework by building a simple "Hello, World" application. This beginner-friendly guide will help you install the necessary tools, set up an app, and deploy it to the Writer Cloud.
## Step 1: Install Writer Framework and run the demo app
## Setting up your project
### Creating a Writer app and getting your API key
From the Home screen, click on **Build an app**.
Select Framework as the app type you want to create, enabling you to generate keys and build your app with the Writer Framework.
On the next screen, you can edit your Writer application name at the top left. Underneath "Authenticate with an API key," click "Reveal" to view and copy your API key.
### Creating the application
Next, open your terminal and navigate to the directory where you want to create your application directory.
When using the sample data located in `sample-input/test-data.csv`, the Raw CSV section will display the uploaded CSV file:
## Generating release notes
Now that you've set up the file upload functionality, you can generate release notes based on the uploaded CSV file.
### Defining text completion functions
Using the prompts provided, define functions to get the category, release notes summary, and release notes description using AI completion. You'll use these functions to process the uploaded CSV file and generate release notes.
#### Release notes tab
Finally, you'll add a Dataframe component to the second tab to display the detailed release notes.
You can [see the finished code on GitHub](https://github.com/writer/framework-tutorials/tree/main/release-notes-generator/end) or in `framework-tutorials/release-notes-generator/end` in the `tutorials` repo you cloned at the beginning of the tutorial.
## Conclusion
By following these steps, you've created a complete Release notes generator application using Writer Framework. To learn more, explore the rest of the Writer Framework documentation and the API documentation.
# Repeater
Source: https://dev.writer.com/framework/repeater
The *Repeater* component allows you to repeat a group of components according to a list or dictionary.
## How it works
*Repeater* repeats its contents for every item of a given list or dictionary. It's similar to a `for each` construct.
Each iteration is rendered with a different **context**, a dictionary containing the key and value for the relevant item. By default, in the variables `itemId` and `item`.
### Food Selector example
Given the state below, the contents of *Repeater* will be repeated 3 times. For the first iteration, `itemId` will equal `Banana`, and `item` will equal `{"type": "fruit", "colour": "yellow"}`. Components inside *Repeater* will be able to access this data using references such as `@{itemId}` and `@{item.type}`.
```py theme={null}
wf.init_state({
"articles": {
"Banana": {
"type": "fruit",
"colour": "yellow"
},
"Lettuce": {
"type": "vegetable",
"colour": "green"
},
"Spinach": {
"type": "vegetable",
"colour": "green"
}
},
"order_list": []
})
```
## Event context
When working with *Repeater* components, you can get the context data using the `context` argument in the event handler.
Continuing with the Food Selector example above, the following handler can be linked to a *Button* —allowing users to add items to a list.
```py theme={null}
# The event handler below adds the itemId
# of the relevant article to the order list
def handle_add_to_list(state, context):
state["order_list"] += [context["itemId"]]
```
## Prerequisites
Before starting, ensure you have:
* **A Writer account:** You don't need an account to use Writer Framework, but you'll need one to use the AI module. [Sign up for a free account here](https://app.writer.com/register).
* **Python 3.9.2 or later**: Use the installer from [python.org](https://www.python.org/downloads/).
* **pip:** This command-line application comes with Python and is used for installing Python packages, including those from Writer.
* **A basic understanding of Python:** You should be familiar with the basics of the language.
* **Your favorite code editor (optional):** There's a code editor built into Writer for writing back-end code, but you can also use Visual Studio Code, Notepad++, Vim, Emacs, or any text editor made for programming if you prefer.
## Setting up your project
### Create a Writer app and get its API key
First, you'll need to create a new app within Writer.
The **Start building** menu will appear, presenting options for the types of apps you can create.
Select **Framework**, located under **Developer tools**. This will create a brand new app based on Writer Framework.
You'll see the following:
* The **canvas** is in the center. It displays the app's user interface.
* The column on the left contains:
* The **Core toolkit**, which contains all the UI components. You define the user interface by dragging components from the Toolkit and placing them on the canvas.
* The **Component tree**, which shows the arrangement of the UI components on the canvas. It's also useful for selecting items on the canvas, especially when it has a lot of UI components.
It's time to build the UI!
The first property you'll see in the panel is the **Text** property, which defines the text that appears as the header's title. It should contain the value `@{my_app.title}`. The `@{` and `}` indicate that `my_app.title` is a variable and that its contents should be the text displayed instead of the literal text "my\_app.title". You'll set the value of this variable soon.
Select the **Section** you just added. In the **properties** panel:
* Find the **Title** property and clear it its value to remove the **Section**'s title.
* Scroll down to the **Style** section and look for the **Container background** property, which sets the **Section**'s background color.
* Click its **CSS** button, which will cause a text field to appear below it.
* Enter this color value into the text field: `#F6EFFD`.
A pane with the name **Code** will appear at the bottom half of the screen, displaying an editor for the the contents of `main.py`.
Click the "toggle code" button to hide the code editor.
...and soon after that, you should see some results:
To get a better sense of what the experience will be like for the user, switch to the preview by changing the edit mode (located near the upper left corner of the page) from *UI* mode to *Preview* mode by selecting the **Preview** option:
Here’s what the app looks like in *Preview* mode:
You can see the output of any `print()` functions and error messages by clicking on the **Log** button located near the upper right corner of the page:
Here’s what the app looks like when displaying the log:
It's very helpful to be able to test the application while editing it. As you continue to work with Writer Framework, you'll find yourself alternating between making changes to your application and testing those changes without having to leave the project editor.
## Run the application locally
Once you've tested the application, it's time to run it locally.
Switch back to your terminal application. Stop the editor with ctrl-c, then run the application by entering the following command:
```
writer run social-post-generator
```
Note that the command starts with `writer run` as opposed to `writer edit`. This launches the application as your users will see it, without any of the editing tools. Even though you can preview your applications in the project editor, it's still a good idea to test it by running it on your computer, outside the project editor, before deploying it.
You'll be able to access the application with your browser at the URL that appears on the command line. It should look like this:
## Tips for effective stylesheets
The CSS code for the class used earlier, `bubblegum`, can be found below. Note how the `!important` flag is used when targetting style attributes that are configurable via the Builder. If the flag isn't included, these declarations will not work, because built-in Framework styling is of higher specificity.
```css theme={null}
.bubblegum {
background: #ff63ca !important;
line-height: 1.5;
transform: rotate(-5deg);
}
/* Targeting an element inside the component root element */
.bubblegum > h2 {
color: #f9ff94 !important;
}
```
## Targeting component types
Framework components have root HTML elements with a class linked to their type. For example, *Dataframe* components use the class *CoreDataframe*. When writing a stylesheet, you can target all *Dataframe* components as shown below.
```css theme={null}
.CoreDataframe {
line-height: 2.0;
}
```
## Implementing themes
It's possible to switch stylesheets during runtime, by specifying the same `stylesheet_key` in subsequent calls. This allows you to implement a "theme" logic if desired, targeting the whole or a specific part of your app.
```py theme={null}
def handle_cyberpunk(state):
state.import_stylesheet("theme", "/static/cyberpunk_theme.css")
def handle_minimalist(state):
state.import_stylesheet("theme", "/static/minimalist_theme.css")
```
# Testing
Source: https://dev.writer.com/framework/testing
Testing a Framework application is easy. Given that event handlers are plain Python functions that take arguments such as `state` and `payload`, you can inject your own and test whether the outcome is correct. This section will use `pytest` examples.
## State
### Accessing the initial state
To get started, import your app's entry point, `main`. This will initialise state and make event handlers available. The initial state is available in the module, at `main.wf.initial_state` provided you imported `writer` as `wf`.
### Creating states
For testing purposes, you can create your own state using the `WriterState` class in `writer.core`. Pass a dictionary when constructing it.
```py theme={null}
from writer.core import WriterState
artificial_state = WriterState({
"a": 3,
"b": 6
})
```
## Example
The code of a Framework application basically consists of two things:
1. Initial state
2. Event handlers
It's straightforward to test both, as shown below.
### The app
```py theme={null}
import writer as wf
def handle_multiplication(state):
state["n"] = state["a"]*state["b"]
wf.init_state({
"counter": 0,
"a": 0,
"b": 0
})
```
### The tests
```py theme={null}
from writer.core import WriterState
import main
class TestApp:
initial_state = main.wf.initial_state
artificial_state = WriterState({
"a": 3,
"b": 2
})
def test_counter_must_start_from_zero(self):
assert self.initial_state["counter"] == 0
def test_handle_multiplication(self):
main.handle_multiplication(self.artificial_state)
assert self.artificial_state["n"] == 6
```
# Account management
Source: https://dev.writer.com/home/account_management
# Agent Builder
Source: https://dev.writer.com/home/agent-builder-link
# Agent observability
Source: https://dev.writer.com/home/agent-observability
AI Studio provides session logs and observability metrics for agents so you can understand how your agents are performing.
The following observability views are available for individual agents:
* [Session logs](#session-logs)
* [Agent observability](#agent-observability)
You can also view [global usage and spend](/home/observability) for your organization.
## Session logs
From there, you can enable session logging for your organization.
Once enabled, you can choose the retention period for session logs for each agent. The default is no retention. The possible retention periods are 7 days, 30 days, 90 days, or 180 days.
Whenever someone updates an agent's session log settings, all organization admins receive an email notification.
### View session logs
To view session logs, navigate to specific agent from the [AI Studio homepage](https://app.writer.com/aistudio). Select the **Observability** tab from the top of the page and then select the **Session logs** view. Click on an individual session to view the logs.
#### Flagged responses
In a chat with a no-code agent, an end user can flag a response as inappropriate or inaccurate. An organization admin can then view the flagged responses within the session logs.
To flag a response in a chat, the user clicks the **Flag** button in the bottom right corner of the response.
In the session logs for a particular conversation, you can see any flagged responses by clicking the **View flagged responses** button in the top right corner of the session log.
## Agent observability
Agent observability provides a detailed view of an agent's engagement, performance, and usage. Anyone can view these metrics for agents in your organization. Session logs are also available, but [only for organization admins if the agent's session log settings are enabled](#view-session-logs).
These metrics are only available for deployed agents.
To view metrics for deployed agents, navigate to the specific agent from the [AI Studio homepage](https://app.writer.com/aistudio). Select the **Observability** tab from the top of the page and then choose the specific metric you want to view.
* **Engagement metrics**: Show the top five users for the agent over the specified time period and the total number of user interactions for the agent.
* **Performance metrics**: Show the average response time as well as the P90, P95, and P99 response times for the agent over the specified time period. It also shows the response codes broken down by `200`, `400`, and `500` status codes.
* **Usage metrics**: Show the total number of tokens and the total spend for the agent over the specified time period.
# Detect AI content
Source: https://dev.writer.com/home/ai-detect
### Template categories
**Starter templates** are core building blocks for common agent types:
* **Text Generation**: Build [no-code agents](/no-code/text-generation) that collect user inputs and generate customized text
* **Chat**: Construct [conversational no-code agents](/no-code/chat) with specific personas connected to your data sources
* **Research**: Create [no-code agents](/no-code/research) for conducting tailored research and analysis
* **New Agent**: Start completely from scratch with [Agent Builder](/agent-builder/overview) when no template fits your needs
**Industry-specific templates** are specialized agents designed for particular sectors:
* **Financial Services**: From broker research Q\&A and 10-K summaries to fund compliance and market research
* **Healthcare**: Medical article summaries, FDA guidance assistants, and regulatory document analysis
* **Legal**: Contract review, compliance checking, and regulatory document processing
* **Retail and e-commerce**: Product descriptions, competitive analysis, and SEO optimization
**Function-based templates** are templates organized by business function:
* **Marketing**: Campaign briefs, content creation, social media posts, and brand messaging
* **Sales**: Outbound emails, prospect research, lead follow-ups, and client relationship management
* **Customer Support**: Help center Q\&A, review responses, and sentiment analysis
* **Content Creation**: Blog outlines, thought leadership articles, case studies, and presentation slides
* **Operations**: Meeting summaries, document processing, and workflow automation
**Custom Agents** are organization-specific templates from your Agent Library. These are specialized agents that other users within your organization have created and published, offering tailored solutions and workflows that align with your company's specific needs and processes.
### Filtering and discovery
You can filter templates by industry, function, and task.
* **Industry**: Browse sector-specific templates across Finance, Healthcare, Legal, Marketing, Retail, and more
* **Function**: Filter by business area such as Customer Support, Sales, Content Creation, or Operations
* **Task**: Find templates by specific capabilities like Analyzing, Brainstorming, Researching, Summarizing, or Generating
Each template includes a detailed preview and description of its capabilities so that you can select the best starting point for your agent development project.
### Agent Builder templates
The majority of prebuilt agent templates are [no-code agent](/no-code/introduction) templates.
The following prebuilt templates use [Agent Builder](/agent-builder/overview):
* **Content lifecycle**: Transform a single campaign brief into a fully built, ready-to-launch campaign. This agent extracts goals and summaries, generates project tasks, creates copy and visuals, and runs the final content through customizable legal checks, streamlining every step of the marketing content supply chain.
* **SDR outreach**: Generate personalized outreach emails tailored to a target account. This agent combines prospect research with product positioning to craft a multi-email sequence that aligns with the prospect's business priorities and challenges.
# Manage files
Source: https://dev.writer.com/home/files
The File API allows you to manage files in your account. You can upload, download, list, and delete files.
After you upload a file, you can use it to perform actions such as attaching it to a [Knowledge Graph](/home/knowledge-graph) or using it as an input in a [no-code agent](/home/applications).
This guide shows you how to perform the following actions:
* [Upload a file](#upload-a-file)
* [Get a file](#get-a-file)
* [List all files](#list-all-files)
* [Delete a file](#delete-a-file)
## What is OpenLLMetry?
OpenLLMetry is an open source project that enables monitoring and debugging of LLM application execution. It provides non-intrusive tracing built on top of OpenTelemetry, allowing you to export traces to your existing observability stack.
Since it's built on [OpenTelemetry](https://opentelemetry.io/), you can use any OpenTelemetry compatible backend (Jaeger, Zipkin, Datadog, New Relic, etc.) or the hosted Traceloop platform.
## What you get with the Writer integration
The Writer integration with OpenLLMetry provides enhanced observability beyond standard OpenTelemetry traces. You'll see detailed information about your Writer API calls as additional span attributes:
* **`gen_ai.request.model`**: the model requested (for example, `palmyra-x5`)
* **`gen_ai.prompt`**: array of prompts sent to the Writer model
* **`gen_ai.completion`**: array of completions returned from Writer
* **`gen_ai.usage.total_tokens`**: total tokens used
* **`gen_ai.usage.prompt_tokens`**: number of tokens used for prompts
* **`gen_ai.usage.completion_tokens`**: number of tokens used for completions
This gives you comprehensive visibility into your Writer API usage, including prompt engineering insights, token consumption patterns, and performance characteristics that aren't available in standard OpenTelemetry traces.
## Data sources and formats
Knowledge Graph supports multiple ways to add data:
| Source type | Description | Supported formats/features | Learn more |
| ----------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| **File uploads** | Upload documents directly | - PDF, TXT, DOC/DOCX, PPT/PPTX
You can switch between a cost view and an activity view, which shows the number of tokens used for each agent type. See below for more details about the data available in the [cost view](#cost-view) and the [activity view](#activity-view).
The image below shows the cost view for a sample organization.
### Cost view
The cost view shows the following information:
* **Monthly usage-based spend**: The total cost of usage for the month, with a comparison to the previous month.
* **Spend by agent type**: A day-by-day breakdown of the cost of usage by agent type (API, Framework, and No-Code).
* **Spend breakdown**:
* **LLM models**: The usage cost for each LLM model.
* **Others**: The usage cost for all other services, such as Knowledge Graph hosting, web access, and optical character recognition (OCR).
### Activity view
The activity view shows the following information:
* **Monthly token usage**: The total number of tokens used for the month, with a comparison to the previous month.
* **Top users by token usage**: The top five users with the most token usage.
* **Top agents by token usage**: The top five agents with the most token usage.
* **Token usage by agent type**: A day-by-day breakdown of the token usage by agent type (API, Framework, and No-Code).
* **Token usage by model**: The total number of tokens used for each model.
# Palmyra Med vocabularies and entities
Source: https://dev.writer.com/home/palmyra-med-vocabularies
Palmyra Med supports the following medical vocabularies:
* Foundational Model of Anatomy
* Gene Ontology
* HUGO Gene Nomenclature Committee
* Human Phenotype Ontology
* ICD-10 Procedure Coding System
* ICD-10-CM (available for US users only)
* ICD-9-CM
* LOINC
* MeSH
* MedlinePlus Health Topics
* Metathesaurus Names
* NCBI Taxonomy
* NCI Thesaurus
* National Drug File
* Online Mendelian Inheritance in Man
* RXNORM
* SNOMED CT (available for US users only)
## Extracting entities, relations, and contextual attributes
Palmyra-Med uses context-aware models to extract medical entities, relations, and contextual attributes. Each text entity is extracted into a medical dictionary entry.
{prompt}
### Instruction ###
Write a poem in the style of
Robert Frost.
### Question ###
What's the poem about?
Provide a list of idioms in English, along with their meanings and example
sentences.
Here is an example for the output:
An idiom: 'Break a leg'
Meaning: Good luck
Example sentence: 'I'm sure you'll do great in your interview.
Break a leg!'
System instruction: Create a product detail page for a
fictional innovative smartphone by a retailer known as "TechTrend
Electronics."
Prompt 1: Start by describing the unique features of the smartphone,
such as its solar-powered battery, triple-lens camera system, and foldable
screen technology.
Prompt 2: Next, outline the benefits of these features
for users, like extended battery life, exceptional photo quality, and
enhanced device portability.
Prompt 3: Conclude with crafting compelling product descriptions
and a call-to-action that entices customers to make a purchase during the upcoming
holiday sale.
Summarize a series of healthcare claims documents for a fictional
healthcare company, 'HealthFirst Solutions', using the following delimiter `\n`
to separate different sections of the summary:
Claim Number: 123456789 `\n`
Date of Service: January 1, 2024 `\n`
Diagnosis: Acute sinusitis `\n`
Total Claimed: \$300 `\n`
Status: Pending review `\n`
## Building an agent with chat
To build an agent with chat capabilities, choose the **Chat** option after clicking **Build an agent** in AI Studio.
## Write a welcome message
Next, create a welcome message. A welcome message is the first message your users see. Greet them, tell them how to use the chat, and include any special instructions or anything else they should know.
## Create an avatar
Choose an avatar for your chat. This can be your company logo or any other image you prefer.
## Provide instructions
All of the modes allow you to provide instructions. Instructions are the system messages that reach the LLM to provide context or structure around how your chat responds to requests.
For example, you can use instructions to:
1. Request answers in a specific language by providing examples.
2. Patch stale data in your Knowledge Graph that's hard to retrieve by providing additional information.
3. Provide context about the users and how to address them.
4. Set limits on the topics that the agent can answer.
Your instructions tell the LLM what answers to generate and how to format them.
## Configure Knowledge Graph settings
When you enable Knowledge Graph mode for a chat agent, you can access advanced configuration options to fine-tune how the agent searches, ranks, and retrieves content from your Knowledge Graph. These settings control the balance between keyword and semantic search, how closely responses match source material, relevance thresholds, and response length.
### Access configuration options
To configure Knowledge Graph settings for your chat agent:
1. **Enable Knowledge Graph mode**: In your chat agent configuration, turn on the **Knowledge Graph mode** toggle
2. **Open configuration**: Click the configuration slider in the top right corner of the Knowledge Graph mode box when Knowledge Graph mode is enabled
3. **Adjust settings**: Use the sliders and options in the configuration panel to customize behavior
### Configuration parameters
#### Grounding level
Controls how closely responses must match source material. This setting determines how much creative interpretation the AI can apply to Knowledge Graph content.
**How it works**:
* **Lower values (closer to 0)**: Grounded outputs - stick closely to source material with minimal creativity
* **Higher values (closer to 1)**: Higher creativity - allow more creative interpretation of source material
**Examples**:
* `0.0`: "According to the documentation, the API supports JSON responses" (direct quote/paraphrase)
* `0.5`: "The API documentation indicates that JSON responses are supported, which suggests additional capabilities" (interpretive)
* `1.0`: "Based on the available information, users can expect JSON responses, though other formats might be possible" (highly interpretive)
**When to adjust**:
* **Increase** for higher creativity when you want more interpretive responses
* **Decrease** for grounded outputs when factual reporting or accuracy is critical
#### Top-k
Controls the balance between keyword and semantic search in ranking results. This parameter determines how the system prioritizes different types of matching.
**How it works**:
* **Higher values (closer to 100)**: Prioritize keyword-based matching
* **Lower values (closer to 0)**: Prioritize semantic similarity matching
**When to adjust**:
* **Increase** for searches where exact keyword matches matter most
* **Decrease** for searches where conceptual similarity is more important
#### Max tokens
Maximum number of tokens the model can generate in the response. This controls the length of the AI's answer.
**How it works**:
* **Higher values**: Allow longer, more detailed responses
* **Lower values**: Generate shorter, more concise responses
**When to adjust**:
* **Increase** for detailed analysis or comprehensive answers
* **Decrease** for quick summaries or when you need faster responses
#### Sub-question count
Maximum number of sub-questions to generate when processing complex queries. Higher values allow the system to break down complex questions into more detailed sub-queries.
**How it works**:
* **Higher values**: Improve detail by generating more sub-questions for thorough analysis
* **Lower values**: Reduce response time by generating fewer sub-questions
**When to adjust**:
* **Increase** for complex, multi-part questions that need thorough analysis
* **Decrease** for simple, direct questions to reduce processing time
#### Weight distribution
Controls how many text snippets to retrieve from the Knowledge Graph for context. This works together with the Top-k setting to control best matches vs broader coverage.
**How it works**:
* **Lower values (5-15)**: Best matches - retrieve fewer, more relevant snippets
* **Higher values (15-25)**: Broader coverage - retrieve more snippets for comprehensive context
* **Values below 5**: May return no results due to RAG implementation limitations
**Important notes**:
* **Recommended range**: 5-25 (default is 30, which is higher than recommended)
* Due to RAG system behavior, you may see more snippets than requested
**When to adjust**:
* **Increase** for broader coverage when you need comprehensive research or more context
* **Decrease** for best matches when you want focused queries or concise results
### Common configuration patterns
The default configuration for Knowledge Graph mode handles most use cases. You can adjust the configuration to your specific needs. Below are some common configuration patterns for different use cases.
#### Research and analysis
Use this configuration for comprehensive research tasks where you need thorough analysis. Higher sub-questions and broader coverage provide more context.
**Settings**:
* **Grounding level**: 0.1 (very grounded)
* **Top-k**: 60 (balanced search)
* **Max tokens**: 7000 (detailed responses)
* **Sub-question count**: 8 (thorough analysis)
* **Weight distribution**: 25 (broad coverage)
#### Quick answers
Use this configuration for fast, focused responses where speed and precision matter more than comprehensive analysis.
**Settings**:
* **Grounding level**: 0.0 (completely grounded)
* **Top-k**: 80 (keyword-focused)
* **Max tokens**: 2000 (concise responses)
* **Sub-question count**: 3 (quick processing)
* **Weight distribution**: 15 (focused results)
#### Creative content generation
Use this configuration when you want the AI to interpret and build upon source material creatively.
**Settings**:
* **Grounding level**: 0.6 (interpretive)
* **Top-k**: 40 (semantic-focused)
* **Max tokens**: 5000 (moderate length)
* **Sub-question count**: 6 (balanced analysis)
* **Weight distribution**: 30 (comprehensive context)
### How parameters work together
Some parameters interact in ways that affect both results and performance:
* **Weight distribution and Top-k**: Work together to control best matches vs broader coverage
* **Weight distribution and Max tokens**: Weight distribution controls input context size, while Max tokens controls output response length
* **Grounding level and Sub-question count**: Both affect how thoroughly the system processes queries
### Performance considerations
* **Higher Max tokens**: Increases processing time and cost
* **Higher Sub-question count**: Increases processing time for complex queries
* **Lower Weight distribution**: May return fewer results but with higher relevance
* **Higher Grounding level**: May require more processing to balance creativity with accuracy
### Best practices
1. **Start with defaults**: Begin with the default configuration and adjust based on your specific needs
2. **Test incrementally**: Change one parameter at a time to understand its effect
3. **Consider your use case**: Different applications, like research, Q\&A, and content generation, benefit from different configurations
4. **Monitor performance**: Track how different configurations affect response quality and processing time
5. **Balance precision and recall**: Higher Top-k values give more precise results but may miss relevant content
### Apply default settings
If you've made changes and want to return to the recommended default configuration, click **Apply default settings** in the configuration panel. This resets all parameters to their optimal values for general Knowledge Graph queries.
# Chat with Knowledge Graph
Source: https://dev.writer.com/no-code/chat-kg
export const PromptComponent = ({prompt}) => {prompt}
The **Open** button will take you directly to this agent through the Playground, while the **Copy link** option will generate a shareable link that you can send to any tester or end user to get feedback on your agent.
Below are the steps to create this earnings call agent. To follow along, first log in to [AI Studio](https://app.writer.com/aistudio).
You can build no-code agents for:
1. **Recurring** **tasks** that require repetitive processing, such as creating
recurring content.
2. **Time-intensive tasks** that require extensive back-and-forth communication between
contributors to complete.
3. **Structured** **tasks** that require the same information
to be requested and always produce the same result.
4. **Voice-specific** **tasks** that require a particular tone, style, or format.
Below are the steps to create this newsletter agent. To follow along, first log in to [AI Studio](https://app.writer.com/aistudio).
## Building an agent with research capabilities
To build an agent with research capabilities, first choose the **Research assistant** option after clicking **Build an agent** in AI Studio.
## Selecting the type of research
The first step is to select the type of research from the **Research** menu to incorporate into your agent. You have the option of choosing from a selection of pre-built research that specialize in popular topics, or creating a custom one that can be tailored to a specific topic and writing style.
### Pre-built research
The **Research** menu starts with this selection of pre-built research:
* **Auto research**: automatically selects the most appropriate research type based on the topic
* **Finance research**: use this research for topics related to finance and investing
* **Business research**: helps with business-related topics
* **Travel research**: for topics about travel and tourism, use this research
### Custom research
**Custom research**, the final option in the **Research** menu, allows you to create research tailored to your specific needs. When you select this option, the **Edit instructions** link will appear.
Click this link to open the **Custom instructions** window, where you can specify how the research should behave, what topics it should cover, and how it should respond to user queries.
## Giving the research search parameters
The **Search type** options allow you to specify how the research should search for information.
You can choose from the following:
* **Web search**: The research will search the web for information.
* **Specific URLs**: The research will limit its search to the web sites or pages you specify. When you select this option, a text field will appear where you can enter one or more URLs of the sources you want the research to search. Note that the URLs you provide must start with `http://` or `https://`.
# Text generation
Source: https://dev.writer.com/no-code/text-generation
Agents with the text generation capability can generate text based on specific inputs. These no-code agents are ideal for long- or short-form structured content, such as blog posts, FAQs, press releases, and newsletters.
## Building an agent with text generation
To build an agent with text generation capabilities, choose the **Text generation** option after clicking **Build an agent** in AI Studio.
An agent with text generation capabilities consists of:
* [Adding inputs](/no-code/text-generation#inputs)
* [Writing prompts](/no-code/text-generation#prompts)
* [Formatting outputs](/no-code/text-generation#output-formatting)
* [Choosing a voice](/no-code/text-generation#voice)
## Inputs
An input is content information for your text generation. It generates the desired output—the content your text generation will produce.
For example, if you're building a blog post, the input would include the blog post topic, outline, keywords, reference materials, research sources, and existing knowledge about the topic.
### Types of inputs
There are four types of inputs:
1. Text input: A text input allows users to enter text directly into the text generation.
2. Dropdown: A dropdown input allows users to select one option from a list.
3. File upload: A file upload input allows users to upload a file, such as an image or PDF.
4. Image upload: An image upload input allows users to drag-and-drop or choose an image.
### Adding an input
Once you've decided what type of input you want to add, you'll need to:
### Renaming prompts
You can rename prompts from generic names like "Prompt 1" and "Prompt 2" to any custom name.
This will help you to:
1. Stay organized and keep track of your prompts
2. Know exactly what each prompt accomplishes
3. Write your prompts
4. Format your output
### Referencing inputs
To reference your input, `@` it within the prompt.
For example, if you're writing a prompt to generate social posts based on a blog post, do the following:
1. **Input:** `Blog post`
2. **Prompt 1:** instructions for creating a social post
In your prompt, reference `@Blog post` so that Writer understands that the social post should be based on the blog input.
## Output formatting
Next, format your output—the final assembled text generation. To do this, take all your prompt outputs (if you have more than one), format their style, add any static text such as a disclaimer, and combine them together.
You can use Markdown or HTML to specify how you'd like your output to be formatted.
For example, you can use the following formatting:
You don't have to include every prompt in your output formatting. For example, a prompt might be used only to summarize and then to create a LinkedIn post (based on the summary). If you don't want to include the summary in your output, you don't have to.