<script>
function preSendHandler(event, instance) {
console.log('pre:send event', event);
if (event?.message?.message?.content) {
event.message.message.content = event.message.message.content.toUpperCase();
}
}
function sendHandler(event, instance) {
console.log('send event', event);
}
function feedbackHandler(event, instance) {
console.log('feedback', event);
if (event.interactionType === 'details') {
// Submit feedback to backend
// Note: watsonx Orchestrate does not persist feedback internally
fetch('/api/store-feedback', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${jwtToken}`
},
body: JSON.stringify({
messageId: event.message.id,
isPositive: event.isPositive,
text: event.text,
categories: event.categories
})
});
}
}
function preReceiveHandler(event, instance) {
console.log('pre-receive event', event);
// Modify response text
event?.message?.content?.forEach((element) => {
if (element?.text?.includes('assistant')) {
element.text = element.text.replace('assistant', 'Agent');
}
element.type = 'user_defined';
});
// Add feedback controls
const lastItem = event?.message?.content?.[event.message.content.length - 1];
if (lastItem) {
lastItem.message_options = {
feedback: {
is_on: true,
show_positive_details: false,
show_negative_details: true,
positive_options: {
categories: ['Helpful', 'Accurate', 'Clear'],
disclaimer: "Your feedback helps us improve.",
},
negative_options: {
categories: ['Inaccurate', 'Incomplete', 'Confusing', 'Other'],
disclaimer: "Please provide details to help us improve.",
},
},
};
}
}
function receiveHandler(event, instance) {
console.log('received event', event);
instance.off('pre:receive', preReceiveHandler);
}
function userDefinedResponseHandler(event, instance) {
console.log('userDefinedResponse event', event);
event.hostElement.innerHTML = `
<div style="background-color:#f4f4f4;padding:15px;border-radius:8px;">
<h4>Custom Response</h4>
<p>${event.contentItem?.text || '[No content]'}</p>
</div>`;
}
function preStreamDeltaHandler(event, instance) {
console.log('pre:stream:delta event', event);
// Filter sensitive content in streaming responses
event?.delta?.content?.forEach((contentItem) => {
if (contentItem.response_type === 'text' && contentItem.text) {
contentItem.text = contentItem.text.replace(/confidential/gi, '[REDACTED]');
}
});
// Track streaming metrics
const deltaLength = event?.delta?.content?.[0]?.text?.length || 0;
console.log('Delta chunk size:', deltaLength);
}
function preRestartConversationHandler(event, instance) {
console.log('pre:restartConversation event', event);
console.log('Restart source:', event.interactionType);
// Handle different restart sources
if (event.interactionType === 'restart_button') {
console.log('User clicked the Restart button');
} else if (event.interactionType === 'new_chat_button') {
console.log('User clicked the New Chat button');
} else if (event.interactionType === 'instance_method') {
console.log('Restart triggered programmatically');
}
}
let hasRestarted = false;
function restartConversationHandler(event, instance) {
console.log('restartConversation event', event);
console.log('Restart source:', event.interactionType);
if (!hasRestarted) {
setTimeout(() => {
instance.send('Hello! Starting a new conversation.');
}, 1000);
hasRestarted = true;
}
}
function preThreadLoadedHandler(event, instance) {
console.log('pre:threadLoaded event', event);
// Add feedback to historical messages
event?.messages.forEach((message) => {
if (message?.sender === 'response') {
const [lastItem] = message.content;
lastItem.message_options = {
feedback: { is_on: true }
};
message.message_state = {
content: {
1: {
feedback: {
text: "",
is_positive: true,
selected_categories: []
}
}
}
};
}
});
}
async function authTokenNeededHandler(event, instance) {
console.log('authTokenNeeded event', event);
// Fetch refreshed token
const newToken = await fetchRefreshedToken();
event.authToken = newToken;
}
async function viewPreChange(event, instance) {
console.log('view:pre:change event', event);
document.body.classList.add('chat-view-changing');
}
async function viewChange(event, instance) {
console.log('view:change event', event);
document.body.classList.remove('chat-view-changing');
if (event.newViewState.mainWindow) {
analytics.track('chat_opened', { reason: event.reason });
}
}
function onChatLoad(instance) {
// Lifecycle events
instance.on('chat:ready', (event, instance) => {
console.log('chat:ready', event);
});
// Message events
instance.once('pre:send', preSendHandler);
instance.on('send', sendHandler);
instance.on('pre:stream:delta', preStreamDeltaHandler);
instance.once('pre:receive', preReceiveHandler);
instance.on('receive', receiveHandler);
// Conversation events
instance.on('pre:restartConversation', preRestartConversationHandler);
instance.on('restartConversation', restartConversationHandler);
instance.on('pre:threadLoaded', preThreadLoadedHandler);
// Customization events
instance.on('feedback', feedbackHandler);
instance.on('userDefinedResponse', userDefinedResponseHandler);
// Security events
instance.on('authTokenNeeded', authTokenNeededHandler);
// View events
instance.on('view:pre:change', viewPreChange);
instance.on('view:change', viewChange);
}
window.wxOConfiguration = {
orchestrationID: '<tenantId>',
hostURL: 'http://localhost:3000',
showLauncher: true,
rootElementID: 'root',
chatOptions: {
agentId: '<agentId>',
agentEnvironmentId: '<agentEnvironmentId>',
onLoad: onChatLoad,
},
};
setTimeout(function () {
const script = document.createElement('script');
script.src = `${window.wxOConfiguration.hostURL}/wxoLoader.js?embed=true`;
script.addEventListener('load', function () {
wxoLoader.init();
});
document.head.appendChild(script);
}, 0);
</script>