Overview
CEL (Common Expression Language) enables dynamic decision-making in your Inworld Agent Runtime graphs. Use CEL expressions to control when edges fire, creating intelligent routing based on data content, user properties, confidence scores, and more.Basic Syntax
CEL expressions are used in theconditionExpression parameter when adding edges to your graph:
Understanding the Input Object
In CEL expressions,input refers to the data output from the source node that the edge is connected to. When an edge is evaluated, CEL receives the previous node’s output as the input object.
Example flow
Key Points
input= output data from the source node of the edge- Structure varies based on what the source node produces
- Always verify the source node’s output format before writing CEL expressions
The Data Store Object
In addition toinput, CEL expressions also have access to a data_store object that provides persistent storage across graph execution. The data store allows you to share state between different nodes and maintain context throughout the graph’s lifecycle.
Data Store Operations
Checking if a Variable Exists
Getting a Variable Value
text property, so you need to access .text to get the actual value:
Data Store vs Input
| Aspect | input | data_store |
|---|---|---|
| Scope | Current edge only | Entire graph execution |
| Source | Previous node’s output | Persistent storage across nodes |
| Lifecycle | Per edge evaluation | Graph lifetime |
| Use Case | Node-to-node data flow | Cross-node state management |
Data Type Limitations
Important: The Inworld Agent Runtime only supports simple data types in node inputs and outputs. Complex JavaScript objects and class instances are not supported.Supported Types:
- Primitives:
string,number,boolean - Arrays of primitives:
string[],number[],boolean[] - Plain objects with primitive properties:
{ name: string, age: number } - Nested plain objects:
{ user: { id: string, tier: string } }
Unsupported Types:
- Class instances (e.g.,
new Date(), custom classes) - Functions
- Symbols
- Complex objects with methods
1. Fundamental Operations
Boolean Comparisons
Equality Checks
Expression:input.is_safe == trueUse Case: Safety Gate - Route conversation through safety checker before proceeding to main chat flow. If content is flagged as unsafe, redirect to the safety response node.
input.status == "approved"Use Case: Content Moderation - Only allow approved content to proceed to LLM generation. Rejected content gets routed to the moderation review queue.
Inequality Checks
Expression:input.user_tier != "banned"Use Case: User Access Control - Block banned users from accessing premium features. Route them to account restriction notice instead. Expression:
input.language != "en"Use Case: Multi-language Routing - Route non-English inputs to translation service first, then proceed to the main processing pipeline.
Numeric Comparisons
Greater Than / Less Than
Expression:input.confidence_score > 0.8Use Case: High-Confidence Intent Routing - Only route to specialized handlers if confidence is high enough. Low confidence goes to the general fallback handler.
input.token_count < 100Use Case: Token Limit Enforcement - Short queries go to fast processing pipeline. Long queries get chunked or use different LLM settings.
Greater/Less Than or Equal
Expression:input.user_credits >= 10Use Case: Premium Feature Access - Users with sufficient credits access premium LLM models. Others get routed to standard models. Expression:
input.response_time_ms <= 5000Use Case: Performance SLA Monitoring - Fast responses proceed normally. Slow responses trigger performance alerts and fallback routes.
String Operations
String Contains/StartsWith/EndsWith
Expression:input.message.startsWith("Hello")Use Case: Greeting Detection - Route greeting messages to warm welcome flow. Other messages go to standard conversation flow. Expression:
input.email.endsWith("@company.com")Use Case: Internal User Detection - Company employees get access to internal features. External users follow standard customer journey. Expression:
input.query.contains("urgent")Use Case: Priority Request Handling - Urgent requests bypass normal queue and go to fast-track processing. Regular requests follow a standard processing timeline.
Boolean Logic
AND Operations
Expression:input.verified == true && input.age >= 18Use Case: Age-Gated Content Access - Only verified adult users can access mature content. Others get redirected to age-appropriate alternatives. Expression:
input.is_premium && input.feature_enabledUse Case: Feature Flag + Subscription Check - Premium users with beta features enabled get new functionality. Others continue with the standard feature set.
OR Operations
Expression:input.role == "admin" || input.role == "moderator"Use Case: Administrative Access - Admins and moderators get access to management tools. Regular users follow standard user flow. Expression:
input.emergency == true || input.priority == "high"Use Case: Emergency Response Routing - Emergency or high-priority requests bypass normal processing. Route directly to rapid response handlers.
NOT Operations
Expression:!input.maintenance_modeUse Case: Maintenance Mode Check - During maintenance, route to “service unavailable” message. Normal operations proceed when not in maintenance.
2. Data Structure Navigation
Object Property Access
Simple Property Access
Expression:input.user.tierUse Case: User Tier Routing - Route based on subscription level: free, premium, enterprise. Each tier gets different LLM models and response limits.
input.session.languageUse Case: Language Preference - Route to language-specific LLM models and response templates. Maintain conversation context in user’s preferred language.
Nested Property Access
Expression:input.user.profile.preferences.voice_enabledUse Case: Voice Feature Toggle - Users who enabled voice in their profile settings get TTS output. Others receive text-only responses. Expression:
input.conversation.metadata.sentiment.scoreUse Case: Sentiment-Based Response Adjustment - Negative sentiment routes to empathetic response templates. Positive sentiment continues with standard conversation flow.
Array Operations
Array Size
Expression:size(input.intent_matches) > 0Use Case: Intent Detection Success - If intents were detected, route to intent-specific handlers. No intents detected routes to general conversation flow.
size(input.knowledge_results) >= 3Use Case: Knowledge Base Confidence - Multiple knowledge matches indicate high-confidence answers. Route to direct answer generation vs. “I’m not sure” response.
Array Element Access
Expression:input.intents[0].name == "booking"Use Case: Primary Intent Classification - Top-ranked intent determines conversation flow. Booking intents route to reservation system integration. Expression:
input.search_results[0].score > 0.9Use Case: High-Confidence Search Results - Highly relevant results get featured answer treatment. Lower confidence results trigger clarification questions.
Array Contains/Membership
Expression:"admin" in input.user.rolesUse Case: Role-Based Access Control - Users with admin role get access to system management features. Regular users are limited to standard functionality. Expression:
input.detected_language in ["en", "es", "fr"]Use Case: Supported Language Check - Supported languages proceed with native processing. Unsupported languages route to translation service first.
Complex Data Filtering
Filter with Conditions
Expression:size(input.intent_matches.filter(x, x.score >= 0.8)) > 0Use Case: High-Confidence Intent Filtering - Only consider intents with high confidence scores. Route to specialized handlers vs. general conversation.
size(input.knowledge_records.filter(r, r.verified == true)) >= 2Use Case: Verified Knowledge Validation - Only use verified knowledge sources for answers. Insufficient verified sources trigger “needs verification” flow.
Complex Object Filtering
Expression:input.messages.filter(m, m.role == "user" && m.timestamp > input.last_hour)Use Case: Recent User Message Analysis - Analyze only recent user messages for context. Ignore older conversation history for focused responses. Expression:
input.messages.filter(m, m.role == "user" && m.timestamp.seconds > input.current_time.seconds)Use Case: Recent User Message Analysis - Analyze only messages sent after session start. Requires timestamp objects with seconds/nanos fields. Expression:
input.features.filter(f, f.enabled == true && f.beta == false)Use Case: Stable Feature Selection - Only route to production-ready features. Beta features require special opt-in handling.
Existence Checks
Property Existence
Expression:has(input.user.subscription)Use Case: Subscription Status Check - Subscribed users get premium features. Non-subscribers see upgrade prompts and limited functionality. Expression:
has(input.conversation.context)Use Case: Conversation Context Availability - Continue existing conversation if context exists. Start a fresh conversation flow if there is no prior context.
Complex Existence Patterns
Expression:has(input.user.preferences) && has(input.user.preferences.notifications)Use Case: Notification Preference Check - Users with notification preferences get personalized alerts. Others receive default notification settings. Expression:
has(input.session.auth_token) && input.session.auth_token != ""Use Case: Authentication Validation - Authenticated users access full functionality. Unauthenticated users limited to public features only.
Production Examples from Character Engine
Safety Pipeline Routing
Intent Confidence Thresholding
Behavior Action Discrimination
Message Validation
Best Practices
- Keep expressions simple and readable, Complex logic should be broken into multiple edges when possible
- Use meaningful variable names,
input.user.tieris clearer thaninput.t - Handle edge cases, Always provide fallback routes for when conditions aren’t met
- Test thoroughly, Verify your expressions work with different input data types
- Performance considerations, Avoid expensive operations in frequently evaluated expressions
- Understand your data flow - Always know what structure the source node outputs before writing CEL expressions
- Document expected input formats - Comment your edge conditions with expected input structure for maintainability
- Use only simple data types - Ensure all node outputs use primitives and plain objects, not class instances
- Convert complex types - Transform timestamps, custom classes, and complex objects to plain object representations
- Validate data serialization - Test that your data can be JSON.stringify’d without loss of information
- Always check data_store.contains() first - Prevent runtime errors by verifying variables exist before accessing them
- Remember the .text property - Data store values are objects; always access .text to get the actual stored value
- Use data_store for cross-node communication - When you need to share state between non-adjacent nodes, use data_store instead of passing through intermediate nodes
Common Patterns
Confidence-Based Routing
Multi-Condition Validation
Array Processing
Data Store State Management
Debugging Tips
- Use simple expressions first, Start with basic comparisons and add complexity gradually
- Check data types, Ensure your input data structure matches your CEL expression expectations
- Test with sample data, Create test cases with known input values to verify expression behavior
- Use parentheses, Make complex boolean logic explicit with parentheses:
(A && B) || (C && D) - Inspect actual input data - Log or debug the actual
inputobject structure before writing complex expressions - Validate source node outputs - Ensure source nodes produce the expected data structure that your CEL expressions assume
- Check for unsupported types - If CEL expressions fail unexpectedly, verify that input data contains only supported primitive types
- Use JSON.stringify for validation - Test your node outputs with
JSON.stringify()- if it fails or loses data, the types aren’t supported - Debug data store state - Log data_store contents to understand what variables are available and their current values
- Test data store persistence - Verify that data store variables persist correctly across different graph execution paths