Skip to content

Inline Resource Blocks (before / after)

before: and after: are expression lists that run around a resource's main action -- before: prepares data before the action runs, after: processes output after it completes. Think of them like setup/teardown blocks around a function call.

  • before: runs before the main action (chat, httpClient, sql, etc.)
  • after: runs after the main action

Both accept bare scalar expressions. Each item executes in sequence and can call set() to store values, perform calculations, or read from memory and session.

Basic Usage

yaml

actionId: preProcessor
name: Pre-Processor
after:
  - set('normalized_input', lower(get('q')))
  - set('timestamp', info('timestamp'))
  - set('user_id', get('userId', 'session'))

chat:
  prompt: "Process: {{ get('normalized_input') }}"

When to Use expr Blocks

1. Data Transformation

Transform data before using it:

yaml
# resources/example.yaml
after:
  - set('cleaned_data', trim(get('rawData')))
  - set('request_time', info('timestamp'))

httpClient:
  url: "https://api.example.com/process"
  data:
    input: get('cleaned_data')
    timestamp: get('request_time')

2. Memory/Session Operations

Store values for later use:

yaml
# resources/example.yaml
after:
  - set('last_query', get('q'), 'memory')
  - set('query_count', (get('query_count', 'memory') ?? 0) + 1, 'memory')
  - set('user_preferences', get('prefs'), 'session')

chat:
  prompt: "User asked {{ get('query_count') }} times. Query: {{ get('q') }}"

3. Calculations

Perform calculations before execution:

yaml
# resources/example.yaml
after:
  - set('total', get('price') * get('quantity'))
  - set('discount', get('total') * 0.1)
  - set('final_price', get('total') - get('discount'))

apiResponse:
  response:
    total: get('total')
    discount: get('discount')
    final_price: get('final_price')

4. Conditional Logic

Set values based on conditions:

yaml
# resources/example.yaml
after:
  - set('mode', get('env') == 'production' ? 'strict' : 'debug')
  - set('cache_ttl', get('mode') == 'strict' ? '1h' : '5m')

httpClient:
  url: "https://api.example.com/data"
  cache:
    ttl: get('cache_ttl')

Execution Order

Expressions in each block run top to bottom. The full per-resource order is:

Requestbefore:expressions run in ordervalues stored via set()Main Actionchat, httpClient, sql, python, exec, apiResponseafter:expressions run in orderoutput accessible via get()response

Common Patterns

Pattern 1: Data Normalization

yaml
# resources/example.yaml
after:
  - set('normalized_email', lower(trim(get('email'))))
  - set('normalized_name', trim(get('name')))

sql:
  query: |
    INSERT INTO users (email, name) 
    VALUES ($1, $2)
  params:
    - get('normalized_email')
    - get('normalized_name')

Pattern 2: Counter/State Management

yaml
# resources/example.yaml
after:
  - set('request_id', info('ID'))
  - set('request_count', (get('request_count', 'memory') ?? 0) + 1, 'memory')
  - set('last_request_time', info('timestamp'), 'memory')

chat:
  prompt: "Request #{{ get('request_count') }}: {{ get('q') }}"

Pattern 3: Data Aggregation

yaml
# resources/example.yaml
after:
  - set('items', get('previousResource'))
  - set('total_items', len(get('items')))
  - set('total_value', sum(map(get('items'), .price)))

apiResponse:
  response:
    summary:
      count: get('total_items')
      total: get('total_value')

Pattern 4: Error Handling Preparation

yaml
# resources/example.yaml
after:
  - set('fallback_value', get('default', 'memory') ?? 'N/A')
  - set('retry_count', get('retry_count', 'memory') ?? 0)

httpClient:
  url: "https://api.example.com/data"
  retry:
    maxAttempts: 3

Expression-Only Resources

You can create resources that only execute expressions (no main action):

yaml

actionId: setupContext
name: Setup Context
after:
  - set('session_id', info('ID'))
  - set('start_time', info('timestamp'))
  - set('environment', get('ENV', 'env') ?? 'development')
  - set('user_context', get('user', 'session') ?? {})

This resource returns {"status": "expressions_executed"} and can be used as a dependency for other resources.

Accessing expr Results

Values set in expr blocks are immediately available via get():

yaml
# resources/example.yaml
after:
  - set('processed_data', lower(trim(get('raw_data'))))

# Use the processed data
chat:
  prompt: "Analyze: {{ get('processed_data') }}"

Best Practices

1. Keep Expressions Simple

yaml
# Good: Simple, clear expressions
after:
  - set('normalized', trim(get('input')))
  - set('count', len(get('items')))

# Avoid: Complex logic (use Python resource instead)
# after:
#   - set('result', get('a') * get('b') / get('c') + get('d') - ...)

2. Use for Side Effects

yaml
# Good: Storing state
after:
  - set('last_action', get('action'), 'memory')
  - set('timestamp', info('timestamp'), 'session')

# Avoid: Complex data processing (use Python)

3. Order Matters

Expressions execute in sequence:

yaml
# Correct order
after:
  - set('step1', trim(get('input')))
  - set('step2', lower(get('step1')))
  - set('final', replace(get('step2'), ' ', '_'))

# Wrong: step2 depends on step1, must come after

Limitations

  • No return values: Expressions don't return values (use set() to store results)
  • Sequential execution: Expressions run one after another
  • Error handling: If an expression fails, the resource fails
  • Complex logic: For complex operations, use Python resources

Examples

Example 1: Request Logging

yaml

actionId: loggedRequest
name: Logged Request
after:
  - set('request_id', info('ID'))
  - set('request_time', info('timestamp'))
  - set('request_log', {
      'id': get('request_id'),
      'time': get('request_time'),
      'query': get('q')
    }, 'memory')

chat:
  prompt: "{{ get('q') }}"

Example 2: Data Validation and Transformation

yaml

actionId: validatedInput
name: Validated Input
after:
  - set('email', lower(trim(get('email'))))
  - set('age', int(get('age')))
  - set('is_valid', get('email') contains '@' && get('age') >= 18)

validations:
  check:
    - get('is_valid') == true
  error:
    code: 400
    message: Invalid email or age

chat:
  prompt: "Process user: {{ get('email') }}, age {{ get('age') }}"

Example 3: Session Management

yaml

actionId: sessionHandler
name: Session Handler
after:
  - set('session_id', get('session_id', 'session') ?? info('sessionId'), 'session')
  - set('visit_count', (get('visit_count', 'session') ?? 0) + 1, 'session')
  - set('last_visit', info('timestamp'), 'session')

apiResponse:
  response:
    session_id: get('session_id')
    visit_count: get('visit_count')
    last_visit: get('last_visit')

See Also

Released under the Apache 2.0 License.