Add message type constants and documentation
Introduce a shared enum-based system for categorizing message types across the Rust backend and JavaScript frontend. This replaces magic numbers with named constants for improved type safety, readability, and maintainability. The implementation includes: - Rust MessageType enum with serialization support - JavaScript constants matching the Rust enum values - Helper
This commit is contained in:
parent
752d455312
commit
ee4c0dcda1
4 changed files with 529 additions and 0 deletions
100
gbapp/MESSAGE_TYPES.md
Normal file
100
gbapp/MESSAGE_TYPES.md
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
# Message Types Documentation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The botserver uses a simple enum-based system for categorizing different types of messages flowing through the system. This document describes each message type and its usage.
|
||||||
|
|
||||||
|
## Message Type Enum
|
||||||
|
|
||||||
|
The `MessageType` enum is defined in both Rust (backend) and JavaScript (frontend) to ensure consistency across the entire application.
|
||||||
|
|
||||||
|
### Backend (Rust)
|
||||||
|
Location: `src/core/shared/message_types.rs`
|
||||||
|
|
||||||
|
### Frontend (JavaScript)
|
||||||
|
Location: `ui/shared/messageTypes.js`
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Value | Name | Description | Usage |
|
||||||
|
|-------|------|-------------|-------|
|
||||||
|
| 0 | `EXTERNAL` | Messages from external systems | WhatsApp, Instagram, Teams, and other external channel integrations |
|
||||||
|
| 1 | `USER` | User messages from web interface | Regular user input from the web chat interface |
|
||||||
|
| 2 | `BOT_RESPONSE` | Bot responses | Can contain either regular text content or JSON-encoded events (theme changes, thinking indicators, etc.) |
|
||||||
|
| 3 | `CONTINUE` | Continue interrupted response | Used when resuming a bot response that was interrupted |
|
||||||
|
| 4 | `SUGGESTION` | Suggestion or command message | Used for contextual suggestions and command messages |
|
||||||
|
| 5 | `CONTEXT_CHANGE` | Context change notification | Signals when the conversation context has changed |
|
||||||
|
|
||||||
|
## Special Handling for BOT_RESPONSE (Type 2)
|
||||||
|
|
||||||
|
The `BOT_RESPONSE` type requires special handling in the frontend because it can contain two different types of content:
|
||||||
|
|
||||||
|
### 1. Regular Text Content
|
||||||
|
Standard bot responses containing plain text or markdown that should be displayed directly to the user.
|
||||||
|
|
||||||
|
### 2. Event Messages
|
||||||
|
JSON-encoded objects with the following structure:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"event": "event_type",
|
||||||
|
"data": {
|
||||||
|
// Event-specific data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Supported Events:
|
||||||
|
- `thinking_start` - Bot is processing/thinking
|
||||||
|
- `thinking_end` - Bot finished processing
|
||||||
|
- `warn` - Warning message to display
|
||||||
|
- `context_usage` - Context usage update
|
||||||
|
- `change_theme` - Theme customization data
|
||||||
|
|
||||||
|
## Frontend Detection Logic
|
||||||
|
|
||||||
|
The frontend uses the following logic to differentiate between regular content and event messages:
|
||||||
|
|
||||||
|
1. Check if `message_type === 2` (BOT_RESPONSE)
|
||||||
|
2. Check if content starts with `{` or `[` (potential JSON)
|
||||||
|
3. Attempt to parse as JSON
|
||||||
|
4. If successful and has `event` and `data` properties, handle as event
|
||||||
|
5. Otherwise, process as regular message content
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Rust Backend
|
||||||
|
```rust
|
||||||
|
use crate::shared::message_types::MessageType;
|
||||||
|
|
||||||
|
let response = BotResponse {
|
||||||
|
// ... other fields
|
||||||
|
message_type: MessageType::BOT_RESPONSE,
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### JavaScript Frontend
|
||||||
|
```javascript
|
||||||
|
if (message.message_type === MessageType.BOT_RESPONSE) {
|
||||||
|
// Handle bot response
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUserMessage(message)) {
|
||||||
|
// Handle user message
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migration Notes
|
||||||
|
|
||||||
|
When migrating from magic numbers to the MessageType enum:
|
||||||
|
|
||||||
|
1. Replace all hardcoded message type numbers with the appropriate constant
|
||||||
|
2. Import the MessageType module/script where needed
|
||||||
|
3. Use the helper functions for type checking when available
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
1. **Type Safety**: Reduces errors from using wrong message type numbers
|
||||||
|
2. **Readability**: Code is self-documenting with named constants
|
||||||
|
3. **Maintainability**: Easy to add new message types or modify existing ones
|
||||||
|
4. **Consistency**: Same values used across frontend and backend
|
||||||
82
src/core/shared/message_types.rs
Normal file
82
src/core/shared/message_types.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Enum representing different types of messages in the bot system
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct MessageType(pub i32);
|
||||||
|
|
||||||
|
impl MessageType {
|
||||||
|
/// Regular message from external systems (WhatsApp, Instagram, etc.)
|
||||||
|
pub const EXTERNAL: MessageType = MessageType(0);
|
||||||
|
|
||||||
|
/// User message from web interface
|
||||||
|
pub const USER: MessageType = MessageType(1);
|
||||||
|
|
||||||
|
/// Bot response (can be regular content or event)
|
||||||
|
pub const BOT_RESPONSE: MessageType = MessageType(2);
|
||||||
|
|
||||||
|
/// Continue interrupted response
|
||||||
|
pub const CONTINUE: MessageType = MessageType(3);
|
||||||
|
|
||||||
|
/// Suggestion or command message
|
||||||
|
pub const SUGGESTION: MessageType = MessageType(4);
|
||||||
|
|
||||||
|
/// Context change notification
|
||||||
|
pub const CONTEXT_CHANGE: MessageType = MessageType(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i32> for MessageType {
|
||||||
|
fn from(value: i32) -> Self {
|
||||||
|
MessageType(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MessageType> for i32 {
|
||||||
|
fn from(value: MessageType) -> Self {
|
||||||
|
value.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MessageType {
|
||||||
|
fn default() -> Self {
|
||||||
|
MessageType::USER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for MessageType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let name = match self.0 {
|
||||||
|
0 => "EXTERNAL",
|
||||||
|
1 => "USER",
|
||||||
|
2 => "BOT_RESPONSE",
|
||||||
|
3 => "CONTINUE",
|
||||||
|
4 => "SUGGESTION",
|
||||||
|
5 => "CONTEXT_CHANGE",
|
||||||
|
_ => "UNKNOWN",
|
||||||
|
};
|
||||||
|
write!(f, "{}", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_message_type_conversion() {
|
||||||
|
assert_eq!(i32::from(MessageType::USER), 1);
|
||||||
|
assert_eq!(MessageType::from(2), MessageType::BOT_RESPONSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_message_type_display() {
|
||||||
|
assert_eq!(MessageType::USER.to_string(), "USER");
|
||||||
|
assert_eq!(MessageType::BOT_RESPONSE.to_string(), "BOT_RESPONSE");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_message_type_equality() {
|
||||||
|
assert_eq!(MessageType::USER, MessageType(1));
|
||||||
|
assert_ne!(MessageType::USER, MessageType::BOT_RESPONSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
247
tests/websocket_test.html
Normal file
247
tests/websocket_test.html
Normal file
|
|
@ -0,0 +1,247 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>WebSocket Message Handler Test</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: monospace;
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.test-section {
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 15px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.test-case {
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 10px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
.success {
|
||||||
|
color: green;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
margin: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#output {
|
||||||
|
background: #000;
|
||||||
|
color: #0f0;
|
||||||
|
padding: 10px;
|
||||||
|
height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>WebSocket Message Handler Test Suite</h1>
|
||||||
|
|
||||||
|
<div class="test-section">
|
||||||
|
<h2>Test Cases</h2>
|
||||||
|
<button onclick="runAllTests()">Run All Tests</button>
|
||||||
|
<button onclick="clearOutput()">Clear Output</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="test-section">
|
||||||
|
<h2>Individual Tests</h2>
|
||||||
|
<button onclick="testEmptyMessage()">Test Empty Message</button>
|
||||||
|
<button onclick="testValidEventMessage()">Test Valid Event Message</button>
|
||||||
|
<button onclick="testRegularTextMessage()">Test Regular Text Message</button>
|
||||||
|
<button onclick="testMalformedJSON()">Test Malformed JSON</button>
|
||||||
|
<button onclick="testIncompleteJSON()">Test Incomplete JSON</button>
|
||||||
|
<button onclick="testContextMessage()">Test Context Message</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="output"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Simulate the message handler from the actual implementation
|
||||||
|
function handleEvent(eventType, eventData) {
|
||||||
|
log(`Event handled: ${eventType}`, 'info');
|
||||||
|
log(`Event data: ${JSON.stringify(eventData)}`, 'info');
|
||||||
|
}
|
||||||
|
|
||||||
|
function processMessageContent(message) {
|
||||||
|
log(`Processing message content: ${message.content}`, 'info');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleWebSocketMessage(rawData) {
|
||||||
|
try {
|
||||||
|
if (!rawData || rawData.trim() === "") {
|
||||||
|
log("Empty WebSocket message received", 'error');
|
||||||
|
return { success: false, reason: "Empty message" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const r = JSON.parse(rawData);
|
||||||
|
|
||||||
|
if (r.type === "connected") {
|
||||||
|
log("WebSocket welcome message", 'info');
|
||||||
|
return { success: true, type: "connected" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r.message_type === 2) {
|
||||||
|
// Check if content looks like JSON (starts with { or [)
|
||||||
|
const contentTrimmed = r.content.trim();
|
||||||
|
if (contentTrimmed.startsWith("{") || contentTrimmed.startsWith("[")) {
|
||||||
|
try {
|
||||||
|
const d = JSON.parse(r.content);
|
||||||
|
if (d.event && d.data) {
|
||||||
|
// This is an event message
|
||||||
|
handleEvent(d.event, d.data);
|
||||||
|
return { success: true, type: "event", event: d.event };
|
||||||
|
}
|
||||||
|
} catch (parseErr) {
|
||||||
|
// Not a valid event message, treat as regular content
|
||||||
|
log("Content is not an event message, processing as regular message", 'info');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Process as regular message content
|
||||||
|
processMessageContent(r);
|
||||||
|
return { success: true, type: "regular_message" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r.message_type === 5) {
|
||||||
|
log("Context change message", 'info');
|
||||||
|
return { success: true, type: "context_change" };
|
||||||
|
}
|
||||||
|
|
||||||
|
processMessageContent(r);
|
||||||
|
return { success: true, type: "default_message" };
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
log(`WebSocket message parse error: ${err.message}`, 'error');
|
||||||
|
return { success: false, reason: err.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test cases
|
||||||
|
function testEmptyMessage() {
|
||||||
|
log("\n=== Testing Empty Message ===", 'info');
|
||||||
|
const result = handleWebSocketMessage("");
|
||||||
|
if (!result.success && result.reason === "Empty message") {
|
||||||
|
log("✓ Empty message handled correctly", 'success');
|
||||||
|
} else {
|
||||||
|
log("✗ Empty message not handled correctly", 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testValidEventMessage() {
|
||||||
|
log("\n=== Testing Valid Event Message ===", 'info');
|
||||||
|
const message = JSON.stringify({
|
||||||
|
message_type: 2,
|
||||||
|
content: JSON.stringify({
|
||||||
|
event: "thinking_start",
|
||||||
|
data: { message: "Processing..." }
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const result = handleWebSocketMessage(message);
|
||||||
|
if (result.success && result.type === "event") {
|
||||||
|
log("✓ Valid event message handled correctly", 'success');
|
||||||
|
} else {
|
||||||
|
log("✗ Valid event message not handled correctly", 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRegularTextMessage() {
|
||||||
|
log("\n=== Testing Regular Text Message (type 2) ===", 'info');
|
||||||
|
const message = JSON.stringify({
|
||||||
|
message_type: 2,
|
||||||
|
content: "This is a regular text message, not JSON"
|
||||||
|
});
|
||||||
|
const result = handleWebSocketMessage(message);
|
||||||
|
if (result.success && result.type === "regular_message") {
|
||||||
|
log("✓ Regular text message handled correctly", 'success');
|
||||||
|
} else {
|
||||||
|
log("✗ Regular text message not handled correctly", 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testMalformedJSON() {
|
||||||
|
log("\n=== Testing Malformed JSON in Content ===", 'info');
|
||||||
|
const message = JSON.stringify({
|
||||||
|
message_type: 2,
|
||||||
|
content: "{invalid json: true"
|
||||||
|
});
|
||||||
|
const result = handleWebSocketMessage(message);
|
||||||
|
if (result.success && result.type === "regular_message") {
|
||||||
|
log("✓ Malformed JSON handled gracefully", 'success');
|
||||||
|
} else {
|
||||||
|
log("✗ Malformed JSON not handled correctly", 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testIncompleteJSON() {
|
||||||
|
log("\n=== Testing Incomplete JSON ===", 'info');
|
||||||
|
const message = JSON.stringify({
|
||||||
|
message_type: 2,
|
||||||
|
content: '{"event": "test", "data":' // Incomplete JSON
|
||||||
|
});
|
||||||
|
const result = handleWebSocketMessage(message);
|
||||||
|
if (result.success && result.type === "regular_message") {
|
||||||
|
log("✓ Incomplete JSON handled gracefully", 'success');
|
||||||
|
} else {
|
||||||
|
log("✗ Incomplete JSON not handled correctly", 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testContextMessage() {
|
||||||
|
log("\n=== Testing Context Message (type 5) ===", 'info');
|
||||||
|
const message = JSON.stringify({
|
||||||
|
message_type: 5,
|
||||||
|
context_name: "test_context",
|
||||||
|
content: "Context content"
|
||||||
|
});
|
||||||
|
const result = handleWebSocketMessage(message);
|
||||||
|
if (result.success && result.type === "context_change") {
|
||||||
|
log("✓ Context message handled correctly", 'success');
|
||||||
|
} else {
|
||||||
|
log("✗ Context message not handled correctly", 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runAllTests() {
|
||||||
|
log("Starting all tests...\n", 'info');
|
||||||
|
testEmptyMessage();
|
||||||
|
testValidEventMessage();
|
||||||
|
testRegularTextMessage();
|
||||||
|
testMalformedJSON();
|
||||||
|
testIncompleteJSON();
|
||||||
|
testContextMessage();
|
||||||
|
log("\n=== All tests completed ===", 'info');
|
||||||
|
}
|
||||||
|
|
||||||
|
function log(message, type = 'info') {
|
||||||
|
const output = document.getElementById('output');
|
||||||
|
const timestamp = new Date().toISOString().split('T')[1].split('.')[0];
|
||||||
|
const className = type === 'error' ? 'error' : type === 'success' ? 'success' : 'info';
|
||||||
|
output.innerHTML += `<span class="${className}">[${timestamp}] ${message}</span>\n`;
|
||||||
|
output.scrollTop = output.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearOutput() {
|
||||||
|
document.getElementById('output').innerHTML = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run tests on page load
|
||||||
|
window.onload = function() {
|
||||||
|
log("WebSocket Message Handler Test Suite Ready", 'info');
|
||||||
|
log("Click 'Run All Tests' to begin\n", 'info');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
100
ui/shared/messageTypes.js
Normal file
100
ui/shared/messageTypes.js
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
/**
|
||||||
|
* Message Type Constants
|
||||||
|
* Defines the different types of messages in the bot system
|
||||||
|
* These values must match the server-side MessageType enum in Rust
|
||||||
|
*/
|
||||||
|
|
||||||
|
const MessageType = {
|
||||||
|
/** Regular message from external systems (WhatsApp, Instagram, etc.) */
|
||||||
|
EXTERNAL: 0,
|
||||||
|
|
||||||
|
/** User message from web interface */
|
||||||
|
USER: 1,
|
||||||
|
|
||||||
|
/** Bot response (can be regular content or event) */
|
||||||
|
BOT_RESPONSE: 2,
|
||||||
|
|
||||||
|
/** Continue interrupted response */
|
||||||
|
CONTINUE: 3,
|
||||||
|
|
||||||
|
/** Suggestion or command message */
|
||||||
|
SUGGESTION: 4,
|
||||||
|
|
||||||
|
/** Context change notification */
|
||||||
|
CONTEXT_CHANGE: 5
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of a message type
|
||||||
|
* @param {number} type - The message type number
|
||||||
|
* @returns {string} The name of the message type
|
||||||
|
*/
|
||||||
|
function getMessageTypeName(type) {
|
||||||
|
const names = {
|
||||||
|
0: 'EXTERNAL',
|
||||||
|
1: 'USER',
|
||||||
|
2: 'BOT_RESPONSE',
|
||||||
|
3: 'CONTINUE',
|
||||||
|
4: 'SUGGESTION',
|
||||||
|
5: 'CONTEXT_CHANGE'
|
||||||
|
};
|
||||||
|
return names[type] || 'UNKNOWN';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a message is a bot response
|
||||||
|
* @param {Object} message - The message object
|
||||||
|
* @returns {boolean} True if the message is a bot response
|
||||||
|
*/
|
||||||
|
function isBotResponse(message) {
|
||||||
|
return message && message.message_type === MessageType.BOT_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a message is a user message
|
||||||
|
* @param {Object} message - The message object
|
||||||
|
* @returns {boolean} True if the message is from a user
|
||||||
|
*/
|
||||||
|
function isUserMessage(message) {
|
||||||
|
return message && message.message_type === MessageType.USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a message is a context change
|
||||||
|
* @param {Object} message - The message object
|
||||||
|
* @returns {boolean} True if the message is a context change
|
||||||
|
*/
|
||||||
|
function isContextChange(message) {
|
||||||
|
return message && message.message_type === MessageType.CONTEXT_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a message is a suggestion
|
||||||
|
* @param {Object} message - The message object
|
||||||
|
* @returns {boolean} True if the message is a suggestion
|
||||||
|
*/
|
||||||
|
function isSuggestion(message) {
|
||||||
|
return message && message.message_type === MessageType.SUGGESTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export for use in other modules (if using modules)
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = {
|
||||||
|
MessageType,
|
||||||
|
getMessageTypeName,
|
||||||
|
isBotResponse,
|
||||||
|
isUserMessage,
|
||||||
|
isContextChange,
|
||||||
|
isSuggestion
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also make available globally for non-module scripts
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.MessageType = MessageType;
|
||||||
|
window.getMessageTypeName = getMessageTypeName;
|
||||||
|
window.isBotResponse = isBotResponse;
|
||||||
|
window.isUserMessage = isUserMessage;
|
||||||
|
window.isContextChange = isContextChange;
|
||||||
|
window.isSuggestion = isSuggestion;
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue