Skip to Content
ConceptsVariable Interpolation

Variable Interpolation

Variable interpolation allows you to dynamically insert values into step content, request bodies, and assertions. This is essential for building realistic test flows that depend on runtime data.

Syntax Overview

The test runner supports two interpolation syntaxes:

SyntaxExampleUse Case
${path}${bag.var.accountId}Standard interpolation with braces
$path$bag.var.accountIdShorthand (no braces needed)

Both syntaxes support nested paths with dots and array brackets.


Variable Bag Structure

All variables are stored in a “bag” object that’s available throughout test execution:

{ bag: { var: {}, // Variables from saveAs/assign steps: {}, // Outputs by step ID last: { assistant: "", // Last assistant response user: "", // Last user message request: {} // Last request result }, transcript: [] // Full conversation history } }

Accessing Variables

From saveAs / assign

When a step uses saveAs or assign, the result is stored in bag.var:

// Step 1: Save request result { "type": "request", "requestId": "accounts.create_test", "saveAs": "newAccount" } // Step 2: Access the saved value { "type": "message", "content": "Show account ${bag.var.newAccount.accountId}" }

From Step Outputs ($steps)

Access outputs by step ID using the $steps prefix:

// Step with ID { "type": "request", "id": "create_acct", "requestId": "accounts.create_test" } // Access by step ID { "type": "message", "content": "Created account: $steps.create_acct.output.accountId" }

Last Response ($bag.last)

Access the most recent values:

{ "type": "message", "content": "You said: ${bag.last.assistant}" }

Available in bag.last:

  • assistant — Last assistant response text
  • user — Last user message text
  • request — Last request response payload

Path Syntax

Dot Notation

Access nested properties with dots:

bag.var.account.details.balance

Array Access

Access array elements with brackets:

bag.var.accounts[0].id bag.transcript[2].content

Mixed Paths

Combine both:

bag.steps.get_users.output.users[0].name

Examples

In Message Content

{ "type": "message", "content": "Transfer ${bag.var.amount} from account ${bag.var.sourceAccount.id} to ${bag.var.targetAccount.id}" }

In Request Input Mappings

{ "type": "request", "requestId": "transfers.create", "inputMappings": { "amount": "${bag.var.transferAmount}", "fromAccount": "$steps.create_source.output.accountId", "toAccount": "$steps.create_target.output.accountId" } }

In Rubrics

{ "type": "assistant_check", "mode": "judge", "rubric": "The assistant should mention the account balance of ${bag.var.expectedBalance}" }

Request Body Templates

In request definitions (requests/*.json), use {key} syntax in the body template:

{ "requests": [ { "id": "accounts.create_test", "transport": { "http": { "method": "POST", "body": { "account_id": "{accountId}", "balance": "{balance}", "status": "{status}" } } } } ] }

When executing this request from a step:

{ "type": "request", "requestId": "accounts.create_test", "inputMappings": { "accountId": "acct-12345", "balance": 5000, "status": "active" } }

The {accountId}, {balance}, and {status} placeholders are replaced with the values from inputMappings.


Environment Variables

Environment variables can be interpolated in configuration files:

{ "env": { "baseUrl": "${BOT_BASE_URL}", "headers": { "x-api-key": "${BOT_API_KEY}" } } }

Set these in your environment:

export BOT_BASE_URL="https://api.example.com" export BOT_API_KEY="your-secret-key"

Special Values

JSON Serialization

When a variable value is an object, it’s automatically JSON-serialized in string contexts:

{ "content": "Account details: ${bag.var.account}" } // Result: "Account details: {\"id\":\"123\",\"balance\":1000}"

Missing Values

  • ${path} — Returns empty string if path doesn’t exist
  • $path — Keeps original text if path doesn’t exist (useful for debugging)

Complete Example

{ "id": "transfer-flow", "name": "Money Transfer Test", "steps": [ { "type": "request", "id": "source", "requestId": "accounts.create_test", "inputMappings": { "balance": 10000, "name": "Source Account" }, "saveAs": "sourceAccount" }, { "type": "request", "id": "target", "requestId": "accounts.create_test", "inputMappings": { "balance": 0, "name": "Target Account" }, "saveAs": "targetAccount" }, { "type": "message", "content": "I want to transfer $500 from ${bag.var.sourceAccount.name} to ${bag.var.targetAccount.name}" }, { "type": "assistant_check", "mode": "judge", "rubric": "The assistant should confirm the transfer between the accounts with IDs $steps.source.output.id and $steps.target.output.id" }, { "type": "message", "content": "Yes, please proceed with the transfer" }, { "type": "extract", "variableName": "transferId", "description": "The transfer confirmation number" }, { "type": "request", "id": "verify_source", "requestId": "accounts.get", "inputMappings": { "accountId": "$steps.source.output.id" } }, { "type": "assistant_check", "mode": "variable_check", "variablePath": "bag.steps.verify_source.output.balance", "expectEquals": "9500" } ] }

Debugging Tips

  1. Check step IDs — Ensure the id property is set on steps you want to reference
  2. Use saveAs — Explicitly save values for clearer access via bag.var
  3. Log intermediate values — Add a message step to echo variables for debugging
  4. Test paths — Use $path syntax to see if values are missing (it preserves the original text)

Next Steps

Last updated on