Skip to main content
v1

Extension Points and Intervention Scenarios

EngineStepHook - intervene between any steps

Real scenario: For LOG_ANALYSIS, inject additional retrieval hint before schema extraction.

EngineStepHook example
package: com.zapper.convengine.hooks
JAVA
@Component
public class LogAnalysisHintHook implements EngineStepHook {
@Override
public boolean supports(EngineStep.Name stepName, EngineSession session) {
return EngineStep.Name.SchemaExtractionStep == stepName
&& "LOG_ANALYSIS".equalsIgnoreCase(session.getIntent());
}

@Override
public void beforeStep(EngineStep.Name stepName, EngineSession session) {
session.putInputParam("log_source_priority", "APM_FIRST");
}
}
Where to use this

Use hooks when you need low-friction runtime intervention without forking framework steps.

ContainerDataTransformer - reshape container response

Real scenario: CCF returns nested payload; you flatten to schema-friendly map.

ContainerDataTransformer example
package: com.zapper.convengine.transformers
JAVA
@Component
@ContainerDataTransformer(intent = "REQUEST_TRACKER", state = "IDLE")
public class RequestTrackerContainerTransformer implements ContainerDataTransformerHandler {
@Override
public Map<String, Object> transform(ContainerComponentResponse response,
EngineSession session,
Map<String, Object> inputParams) {
Map<String, Object> out = new LinkedHashMap<>();
out.put("ticket_id", inputParams.get("ticketId"));
out.put("status", "IN_REVIEW");
return out;
}
}

ResponseTransformer - post-process final payload

Real scenario: Add support-team escalation footer for high-severity disconnect failures.

ResponseTransformer example
package: com.zapper.convengine.transformers
JAVA
@Component
@ResponseTransformer(intent = "DISCONNECT_ELECTRICITY", state = "FAILED")
public class DisconnectFailureResponseTransformer implements ResponseTransformerHandler {
@Override
public OutputPayload transform(OutputPayload responsePayload,
EngineSession session,
Map<String, Object> inputParams) {
if (responsePayload instanceof TextPayload(String text)) {
return new TextPayload(text + "\nIf this is urgent, call support at +1-800-000-0000.");
}
return responsePayload;
}
}

ContainerDataInterceptor - intercept request/response around CCF

Real scenario: add tenant metadata and redact a field before persistence.

Interceptor scenario (concept)
TEXT
Before Execute:
- inject tenantId/requestId
- attach observability headers

After Execute:
- redact sensitive node from raw container payload
- enrich session input params for downstream rule checks

Rule Action Playbook (SET_TASK, SET_JSON, GET_CONTEXT, GET_SESSION)

These actions execute inside RulesStep.execute(...) and mutate the live EngineSession.

Set ce_rule.phase based on where you want the action to run:

  • PIPELINE_RULES: normal RulesStep pass.
  • AGENT_POST_INTENT: post-intent pass inside AgentIntentResolver.

Action value format (exact runtime behavior)

Actionaction_value formatEngine behavior
SET_TASKbeanName:methodName or beanName:methodA,methodBInvokes Spring bean methods via CeRuleTaskExecutor
SET_JSONtargetKey:jsonPathExtracts JSONPath from session eject and stores into inputParams[targetKey]
GET_CONTEXTtargetKey (optional)Stores session.contextDict() into inputParams[targetKey] (default key=context)
GET_SESSIONtargetKey (optional)Stores session.sessionDict() into inputParams[targetKey] (default key=session)

SET_TASK - execute consumer Java methods from rule

Use this when a rule match must trigger consumer-side business logic (incident raise, tracker lookup, eligibility fetch, etc).

1

Create ce_rule row

Set ce_rule.action to SET_TASK and provide bean/method mapping in action_value.

SET_TASK rule example (SQL)
SQL
INSERT INTO ce_rule
(phase, intent_code, state_code, rule_type, match_pattern, action, action_value, priority, enabled, description)
VALUES
('PIPELINE_RULES', 'REQUEST_TRACKER', 'ANY', 'REGEX', '(?i).*track.*request.*', 'SET_TASK', 'requestTrackerTask:loadStatus,attachEta', 10, true,
'Load tracker status + ETA from consumer service');
2

Implement task bean

Bean must be a Spring bean with the configured bean name and implement CeRuleTask. Methods are invoked with (EngineSession session, CeRule rule).

Consumer task bean (Java)
package: com.zapper.convengine.tasksfile: src/main/java/com/acme/convengine/tasks/RequestTrackerTask.java
JAVA
@Component("requestTrackerTask")
public class RequestTrackerTask implements CeRuleTask {

public void loadStatus(EngineSession session, CeRule rule) {
String requestId = String.valueOf(session.getInputParams().getOrDefault("requestId", ""));
// fetch from your DB/service
session.putInputParam("requestStatus", "APPROVAL_PENDING");
session.putInputParam("lastUpdated", "2026-02-10T11:40:00Z");
}

public void attachEta(EngineSession session, CeRule rule) {
session.putInputParam("eta_hours", 12);
}
}
3

Use in response generation

Downstream prompt/response can read new context/inputParams (for example requestStatus, lastUpdated, eta_hours).

SET_JSON - move JSONPath value into input params

Use this to extract one value from runtime session JSON into a flat prompt var.

SET_JSON rule example (SQL)
SQL
INSERT INTO ce_rule
(phase, intent_code, state_code, rule_type, match_pattern, action, action_value, priority, enabled, description)
VALUES
('PIPELINE_RULES', 'LOG_ANALYSIS', 'ANY', 'JSON_PATH', '$.schemaJson.errorCode != null', 'SET_JSON', 'error_code:$.schemaJson.errorCode', 20, true,
'Expose extracted errorCode as prompt var');
What exactly gets updated

SET_JSON writes to session.putInputParam("error_code", value).
It does not change session.contextJson unless your subsequent task/hook updates context explicitly.

GET_CONTEXT - snapshot context into input params

Use this when prompt templates or tasks need full context as a single variable.

GET_CONTEXT rule example (SQL)
SQL
INSERT INTO ce_rule
(phase, intent_code, state_code, rule_type, match_pattern, action, action_value, priority, enabled, description)
VALUES
('PIPELINE_RULES', 'REQUEST_TRACKER', 'ANY', 'REGEX', '(?i).*status.*', 'GET_CONTEXT', 'ctx_snapshot', 30, true,
'Expose full context to prompt/task layer');

If action_value is blank, engine uses default key context.

GET_SESSION - snapshot session facts into input params

Use this for advanced derived responses that need runtime flags (schemaComplete, intentLocked, missingRequiredFields, etc).

GET_SESSION rule example (SQL)
SQL
INSERT INTO ce_rule
(phase, intent_code, state_code, rule_type, match_pattern, action, action_value, priority, enabled, description)
VALUES
('PIPELINE_RULES', 'DISCONNECT_ELECTRICITY', 'ANY', 'JSON_PATH', '$.schemaComplete == false', 'GET_SESSION', 'session_snapshot', 40, true,
'Expose full runtime session facts for follow-up prompt decisions');

If action_value is blank, engine uses default key session.

Operational guardrails

SET_TASK methods run during rule execution. Keep methods deterministic and idempotent.
For side-effecting calls (ticket creation, webhook dispatch), guard with strict rule conditions and add idempotency keys from conversationId.

Where to trace this in code
Resolvers
engine/rule/type/provider/SetTaskActionResolver.javaengine/rule/type/provider/SetJsonActionResolver.javaengine/rule/type/provider/GetContextActionResolver.javaengine/rule/type/provider/GetSessionActionResolver.java
Task invocation
engine/rule/task/CeRuleTaskExecutor.java
Execution loop
engine/steps/RulesStep.java

Custom action resolver (RuleActionResolver)

Consumer can define a brand-new rule action without changing framework core.

1

Create custom resolver bean

Implement RuleActionResolver and return your action name from action().

Custom RuleActionResolver example
package: com.zapper.convengine.rulesfile: src/main/java/com/acme/convengine/rules/EnrichCustomerTierActionResolver.java
JAVA
@Component
public class EnrichCustomerTierActionResolver implements RuleActionResolver {

@Override
public String action() {
return "ENRICH_TIER";
}

@Override
public void resolve(EngineSession session, CeRule rule) {
// action_value example: customerTier:PREMIUM
String raw = rule.getActionValue() == null ? "" : rule.getActionValue();
String[] parts = raw.split(":", 2);
String key = parts.length > 0 && !parts[0].isBlank() ? parts[0] : "customerTier";
String value = parts.length > 1 ? parts[1] : "STANDARD";

session.putInputParam(key, value);
}
}
2

Use action in ce_rule

Set ce_rule.action to your custom token (case-insensitive lookup), for example ENRICH_TIER.

ce_rule row for custom action
SQL
INSERT INTO ce_rule
(phase, intent_code, rule_type, match_pattern, action, action_value, priority, enabled, description)
VALUES
('PIPELINE_RULES', 'REQUEST_TRACKER', 'REGEX', '(?i).*vip.*', 'ENRICH_TIER', 'customerTier:PREMIUM', 5, true,
'Mark VIP tier for downstream response logic');
Auto-registration behavior

No manual factory config is needed. RuleActionResolverFactory auto-registration behavior auto-discovers all Spring beans implementing RuleActionResolver and maps by action().toUpperCase().