Skip to main content
v2

Architecture

Layers

API + Transport Layer

  • ConversationController
  • request ingress + validation + correlation ids
  • SSE / STOMP event transport for audit streams

Runtime Pipeline Layer

  • DefaultConversationalEngine
  • EnginePipelineFactory (DAG sort + wrappers + timing + step hooks)
  • EngineSession (per-turn mutable state)

Decision + Control Layer

  • dialogue act: DialogueActStep
  • policy: InteractionPolicyStep
  • correction routing: CorrectionStep
  • pending action lifecycle: ActionLifecycleStep, DisambiguationStep, PendingActionStep
  • guard + transition validation: GuardrailStep, StateGraphStep
  • core intent/schema/rules/response path

Tooling Layer

  • ToolOrchestrationStep (unified tool request/execute/result contract)
  • McpToolStep (planner loop + CALL_TOOL / ANSWER)

Persistence + Audit Layer

  • JPA repositories on ce_*
  • ce_conversation, ce_audit, ce_memory write path

Request Path (high-level)

1

Controller receives request

Builds EngineContext from conversationId, message, inputParams, optional reset flag.

2

Engine opens session

EngineSessionFactory creates session and history provider injects last turns.

3

Control steps run

Dialogue act + interaction policy + correction routing + pending-action lifecycle decide whether to execute task, clarify, confirm, patch, or continue standard intent flow.

4

Core flow resolves output

Intent/schema/rules/response steps produce deterministic state transition + assistant payload.

5

Persist + return result

Final payload is persisted and returned as TEXT/JSON response, with full audit trail.

Code references

Engine entrypoint
package: com.github.salilvnair.convengine.engine.providerfile: src/main/java/com/github/salilvnair/convengine/engine/provider/DefaultConversationalEngine.java
JAVA
@Override
public EngineResult process(EngineContext engineContext) {
EngineSession session = sessionFactory.open(engineContext);
session.setConversationHistory(historyProvider.lastTurns(session.getConversationId(), 10));
EnginePipeline pipeline = pipelineFactory.create();
return pipeline.execute(session);
}
Pipeline execution loop
package: com.github.salilvnair.convengine.engine.pipelinefile: src/main/java/com/github/salilvnair/convengine/engine/pipeline/EnginePipeline.java
JAVA
for (EngineStep step : steps) {
StepResult r = step.execute(session);
if (r instanceof StepResult.Stop(EngineResult result)) {
return result;
}
}
Intervention point

You can intercept any step via EngineStepHook (beforeStep, afterStep, onStepError) without forking the engine.

Control phases

Rules execute by phase: PRE_RESPONSE_RESOLUTION, POST_AGENT_INTENT, POST_SCHEMA_EXTRACTION, PRE_AGENT_MCP, POST_AGENT_MCP, and POST_TOOL_EXECUTION.

Deterministic boundary

Keep business transitions in ce_rule and state graph config, not hardcoded in steps, so behavior remains data-driven and auditable.