Skip to main content
v2

Backend Integration

This page uses your demo repos directly:

  • backend reference: convengine-demo
  • UI reference: zapper-ui

What each demo proves

  • convengine-demo: Spring Boot consumer setup, transport mode config, step hooks, LLM wiring.
  • zapper-ui: conversation client, SSE subscription, audit timeline UI.
1

Enable ConvEngine in backend

Add @EnableConvEngine and provide required beans.

2

Configure transport mode

Use profile yaml (sse, stomp, both) to switch runtime behavior.

3

Connect frontend

Frontend posts to message endpoint and listens to SSE/STOMP for audit updates.

convengine-demo: Core app bootstrap

ConvengineDemoApplication.java
package: com.github.salilvnairfile: convengine-demo/src/main/java/com/github/salilvnair/ConvengineDemoApplication.java
JAVA
@SpringBootApplication
@ComponentScan(basePackages = {"com.github.salilvnair.convengdemo"})
@EntityScan(basePackages = {"com.github.salilvnair.convengdemo.entity"})
@EnableJpaRepositories(basePackages = {"com.github.salilvnair.convengdemo.repo"})
public class ConvengineDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ConvengineDemoApplication.class, args);
}
}
ConvEngineConfig.java
package: com.github.salilvnair.convengdemo.configfile: convengine-demo/src/main/java/com/github/salilvnair/convengdemo/config/ConvEngineConfig.java
JAVA
@Configuration
@EnableConvEngine
@EnableConvEngineAsyncAuditDispatch // optional
@EnableConvEngineStompBrokerRelay // optional
@EnableCcfCore
public class ConvEngineConfig {
@Bean
public RestWebServiceFacade restWebServiceFacade() {
return new RestWebServiceFacade();
}

@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
}

convengine-demo: Runtime config

application.yml
file: convengine-demo/src/main/resources/application.yml
YAML
application-sse.yml
file: convengine-demo/src/main/resources/application-sse.yml
YAML
convengine:
demo:
stream-mode: SSE
transport:
sse:
enabled: true
stomp:
enabled: false
application-stomp.yml
file: convengine-demo/src/main/resources/application-stomp.yml
YAML
convengine:
demo:
stream-mode: STOMP
transport:
sse:
enabled: false
stomp:
enabled: true
application-both.yml
file: convengine-demo/src/main/resources/application-both.yml
YAML
convengine:
demo:
stream-mode: BOTH
transport:
sse:
enabled: true
stomp:
enabled: true
Relay + async audit rollout

Keep broker.mode: SIMPLE and audit.dispatch.async-enabled: false for baseline. Enable relay and async in non-prod first, then tune queue-capacity, rejection-policy, and rate-limit using audit volume.

convengine-demo: Transport + Hook diagnostics

DemoTransportProperties.java
file: convengine-demo/src/main/java/com/github/salilvnair/convengdemo/config/DemoTransportProperties.java
JAVA
@Component
@ConfigurationProperties(prefix = "convengine.demo")
public class DemoTransportProperties {
private StreamMode streamMode = StreamMode.SSE;
private StepHook stepHook = new StepHook();

public enum StreamMode { SSE, STOMP, BOTH }

public static class StepHook {
private boolean enabled = true;
}
}
ConvEngineTransportStartupLogger.java
file: convengine-demo/src/main/java/com/github/salilvnair/convengdemo/config/ConvEngineTransportStartupLogger.java
JAVA
@Component
@RequiredArgsConstructor
public class ConvEngineTransportStartupLogger {
private final DemoTransportProperties demoTransportProperties;
private final ConvEngineTransportConfig transportConfig;
private final Environment environment;

@EventListener(ApplicationReadyEvent.class)
public void logTransportConfiguration() {
boolean sseEnabled = transportConfig.getSse().isEnabled();
boolean stompEnabled = transportConfig.getStomp().isEnabled();
// validates mode and prints endpoints
}
}
DemoEngineStepHook.java
file: convengine-demo/src/main/java/com/github/salilvnair/convengdemo/config/DemoEngineStepHook.java
JAVA
@Component
@RequiredArgsConstructor
public class DemoEngineStepHook implements EngineStepHook {
private static final Set<EngineStep.Name> TRACKED_STEPS = Set.of(
EngineStep.Name.IntentResolutionStep,
EngineStep.Name.SchemaExtractionStep,
EngineStep.Name.RulesStep,
EngineStep.Name.ResponseResolutionStep
);

@Override
public boolean supports(EngineStep.Name stepName, EngineSession session) {
return demoTransportProperties.getStepHook().isEnabled() && TRACKED_STEPS.contains(stepName);
}

@Override
public void beforeStep(EngineStep.Name stepName, EngineSession session) { /* logs */ }
@Override
public void afterStep(EngineStep.Name stepName, EngineSession session, StepResult result) { /* logs */ }
@Override
public void onStepError(EngineStep.Name stepName, EngineSession session, Throwable error) { /* logs */ }
}

zapper-ui: How frontend consumes backend

convengine.api.js (zapper-ui)
file: zapper-ui/src/api/convengine.api.js
JS
const API_BASE = "http://localhost:8080/api/v1/conversation";

export async function sendMessage(conversationId, message, inputParams = {}, reset = false) { /* ... */ }
export async function fetchAudits(conversationId) { /* ... */ }
export function subscribeConversationSse(conversationId, handlers = {}) { /* ... */ }
// STOMP scaffold commented for optional enablement
App.jsx (zapper-ui)
file: zapper-ui/src/App.jsx
JSX
useEffect(() => {
const stream = subscribeConversationSse(conversationId, {
onConnected: () => setAuditVersion(v => v + 1),
onEvent: () => setAuditVersion(v => v + 1),
});
return () => stream.close();
}, [conversationId]);
ChatPanel.jsx (zapper-ui)
file: zapper-ui/src/components/ChatPanel.jsx
JSX
const res = await sendMessage(conversationId, userText);
setMessages(m => [...m, { role: "assistant", text: assistantText }]);
// Typing indicator shown while request is in-flight
Backend + UI runbook
  1. Start backend (convengine-demo) with profile sse, stomp, or both.
  2. Start zapper-ui and point API base to backend URL.
  3. Send message, verify live timeline from stream + detailed payloads from audit API.
Run commands
BASH