expression

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 30, 2026 License: EUPL-1.2 Imports: 7 Imported by: 0

Documentation

Overview

Package expression provides boolean expression evaluation for workflow conditions.

This package implements the public API for evaluating conditional expressions in workflow transitions, loop conditions, and agent stop conditions. It uses the expr-lang/expr library for expression parsing and evaluation.

Key features:

  • Boolean expression evaluation against interpolation context
  • Type coercion for string numbers and booleans
  • Helper functions for string operations (has_prefix, has_suffix, contains)
  • Graceful handling of missing keys (evaluates to false, not error)

Core Components

## Evaluator Interface (evaluator.go)

Evaluator defines the contract for expression evaluation:

  • Evaluate: Parse and evaluate expression, return bool result
  • ExprEvaluator: Implementation using expr-lang/expr library

## Expression Context (evaluator.go)

BuildExprContext converts interpolation.Context to expr-compatible map:

  • inputs: Map of workflow input parameters (with type coercion)
  • states: Map of step results with PascalCase properties (Output, Stderr, ExitCode, Status, Response, TokensUsed)
  • workflow: Workflow metadata with PascalCase properties (ID, Name, CurrentState, Duration)
  • env: Environment variables
  • loop: Loop context (Index, Index1, Item, Length, First, Last, Parent)
  • error: Error context (Message, State, ExitCode, Type)
  • context: Runtime context (WorkingDir, User, Hostname)

## Type Coercion (evaluator.go)

Automatic type conversion for input values:

  • "true"/"false" -> bool (case-insensitive)
  • Numeric strings -> int64 or float64
  • Preserves native types (don't convert numbers to strings)

## Expression Preprocessing (evaluator.go)

Transform function-style syntax to expr operators:

  • contains(haystack, needle) -> haystack contains needle
  • has_prefix(a, b) -> has_prefix(a, b) (kept as function)
  • has_suffix(a, b) -> has_suffix(a, b) (kept as function)

Expression Syntax

Expressions use expr-lang syntax with AWF context namespaces:

## Comparison Operators

inputs.count > 10
inputs.count == 5
inputs.enabled == true
states.step.ExitCode == 0
states.step.Status == "completed"

## Logical Operators

inputs.debug == true && inputs.verbose == true
inputs.mode == "dev" || inputs.mode == "test"
!(inputs.skip == true)

## String Operations

states.step.Output contains "success"
has_prefix(states.step.Output, "ERROR:")
has_suffix(inputs.file, ".yaml")

## Numeric Operations

inputs.count >= 1 && inputs.count <= 10
states.step.ExitCode != 0
workflow.Duration contains "5m"

## Missing Keys Behavior

Missing keys evaluate to false without error:

inputs.nonexistent == true          # false (input not provided)
env.MISSING_VAR == "value"          # false (env var not set)
states.not_run.Output == "x"        # false (step not executed yet)

Usage Examples

## Basic Evaluation

Evaluate simple boolean expression:

evaluator := expression.NewExprEvaluator()
ctx := interpolation.NewContext()
ctx.Inputs["count"] = "5"

result, err := evaluator.Evaluate("inputs.count > 3", ctx)
// result: true

## State Transitions

Conditional workflow transitions based on step results:

ctx.States["check_status"] = interpolation.StepStateData{
    ExitCode: 0,
    Output:   "OK",
    Status:   "completed",
}

evaluator.Evaluate("states.check_status.ExitCode == 0", ctx)
// result: true

evaluator.Evaluate("states.check_status.Output contains 'OK'", ctx)
// result: true

## Type Coercion

Automatic type conversion for string inputs:

ctx.Inputs["count"] = "42"        // string
ctx.Inputs["enabled"] = "true"    // string

evaluator.Evaluate("inputs.count > 40", ctx)
// result: true (string "42" coerced to int)

evaluator.Evaluate("inputs.enabled == true", ctx)
// result: true (string "true" coerced to bool)

## Loop Conditions

Evaluate loop continuation conditions:

ctx.Loop = &interpolation.LoopData{
    Index:  5,
    Item:   "test",
    Length: 10,
}

evaluator.Evaluate("loop.Index < loop.Length", ctx)
// result: true

## Agent Stop Conditions

Check if agent conversation should stop:

ctx.States["agent_step"] = interpolation.StepStateData{
    Output:   "Task completed. DONE",
    Response: map[string]any{"status": "success"},
}

evaluator.Evaluate("states.agent_step.Output contains 'DONE'", ctx)
// result: true

## Nested Loops (F043)

Access parent loop context:

ctx.Loop = &interpolation.LoopData{
    Index: 2,
    Item:  "child",
    Parent: &interpolation.LoopData{
        Index: 1,
        Item:  "parent",
    },
}

evaluator.Evaluate("loop.Parent.Index == 1", ctx)
// result: true

## Error Context Conditions

Conditional error handling:

ctx.Error = &interpolation.ErrorData{
    Message:  "Connection timeout",
    State:    "api_call",
    ExitCode: 7,
    Type:     "network",
}

evaluator.Evaluate("error.ExitCode == 7", ctx)
// result: true

evaluator.Evaluate("has_prefix(error.Message, 'Connection')", ctx)
// result: true

Workflow Integration

## Conditional Transitions (workflow transitions field)

transitions:
  - condition: "{{states.check.ExitCode}} == 0"
    target: "success"
  - condition: "{{states.check.ExitCode}} == 404"
    target: "not_found"
  - condition: "true"  # default fallback
    target: "error"

## Loop Conditions (while loops)

loop:
  type: while
  condition: "{{loop.Index}} < 10"
  body:
    - step_name

## Agent Stop Conditions (conversation mode)

agent:
  mode: conversation
  conversation:
    stop_conditions:
      - "{{states.agent.Output}} contains 'DONE'"
      - "{{loop.Index}} >= 10"

Error Handling

## Compilation Errors

Invalid syntax returns compile error:

evaluator.Evaluate("inputs.count >>>", ctx)
// error: "compile expression: syntax error"

## Type Errors

Non-boolean result returns error:

evaluator.Evaluate("inputs.count + 5", ctx)
// error: "expression did not return boolean, got int64"

## Graceful Missing Key Handling

Missing keys evaluate to false (not error):

evaluator.Evaluate("inputs.missing == 'value'", ctx)
// result: false, error: nil

evaluator.Evaluate("states.not_run.Output == 'x'", ctx)
// result: false, error: nil

Helper Functions

Built-in string helper functions:

has_prefix(str, prefix)    # strings.HasPrefix
has_suffix(str, suffix)    # strings.HasSuffix
contains(haystack, needle) # preprocessed to "haystack contains needle"

Property Name Casing (F050)

PascalCase properties (uppercase first letter):

  • states.step.Output, states.step.Stderr, states.step.ExitCode, states.step.Status, states.step.Response, states.step.TokensUsed
  • workflow.ID, workflow.Name, workflow.CurrentState, workflow.Duration
  • loop.Index, loop.Index1, loop.Item, loop.First, loop.Last, loop.Length, loop.Parent
  • error.Message, error.State, error.ExitCode, error.Type
  • context.WorkingDir, context.User, context.Hostname

Design Principles

## Public API Surface

This is a public package (pkg/) for external consumers:

  • Stable API with semantic versioning
  • Clean Evaluator interface for easy mocking
  • Depends on pkg/interpolation (public) for Context types

## Type Safety

Strong typing with automatic coercion:

  • Input strings coerced to native types (int, float, bool)
  • PascalCase property names enforce type structure
  • Boolean result type enforced at compile time

## Graceful Degradation

Missing keys don't fail expressions:

  • Allows forward references to steps not yet executed
  • Simplifies conditional logic (no existence checks needed)
  • expr library's AllowUndefinedVariables option enabled

## Expression Power

expr-lang provides rich expression capabilities:

  • Arithmetic: +, -, *, /, %
  • Comparison: ==, !=, <, >, <=, >=
  • Logical: &&, ||, !
  • String operators: contains, in
  • Array/map access: arr[0], map["key"]

See also:

  • pkg/interpolation: Template variable resolution (provides Context types)
  • internal/domain/workflow: Workflow transition and condition types
  • internal/infrastructure/expression: Infrastructure adapter implementing expression compilation port
  • docs/conditional-transitions.md: Workflow transition syntax
  • External: https://expr-lang.org/ - expr-lang documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildExprContext

func BuildExprContext(ctx *interpolation.Context) map[string]any

BuildExprContext converts an interpolation.Context to a map for expression evaluation. The map structure allows dot-access patterns like inputs.name, states.step.ExitCode.

Types

type Evaluator

type Evaluator interface {
	// Evaluate parses and evaluates the expression against the context.
	// Returns true if the condition is met, false otherwise.
	// Returns an error for invalid expressions or evaluation failures.
	Evaluate(expr string, ctx *interpolation.Context) (bool, error)
}

Evaluator evaluates conditional expressions against a context.

type ExprEvaluator

type ExprEvaluator struct{}

ExprEvaluator implements Evaluator using expr-lang/expr library.

func NewExprEvaluator

func NewExprEvaluator() *ExprEvaluator

func (*ExprEvaluator) Evaluate

func (e *ExprEvaluator) Evaluate(exprStr string, ctx *interpolation.Context) (bool, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL