@elqnt/chat
Platform-agnostic chat SDK for building conversational interfaces. Works with React (web) and React Native (mobile). Provides real-time messaging via SSE, chat history management, and monitoring capabilities.
Installation
npm install @elqnt/chat
Entry Points
| Import | Description |
|---|---|
@elqnt/chat | All exports |
@elqnt/chat/hooks | React hooks (primary interface) |
@elqnt/chat/models | TypeScript types |
@elqnt/chat/transport | Transport layer (SSE, fetch-SSE) |
@elqnt/chat/api | Internal API functions (use hooks instead) |
React Hooks
useChat
Primary hook for real-time chat functionality with SSE transport support.
import { useChat } from "@elqnt/chat/hooks";
function ChatComponent() {
const {
// Connection
connect,
disconnect,
connectionState,
isConnected,
// Chat operations
startChat,
loadChat,
sendMessage,
sendEvent,
endChat,
// Typing indicators
startTyping,
stopTyping,
// State
currentChat,
chatKey,
messages,
error,
metrics,
// Events
on,
clearError,
} = useChat({
baseUrl: "https://api.example.com/chat",
orgId: selectedOrgId,
userId: user.id,
clientType: "customer", // "customer" | "humanAgent" | "observer"
autoConnect: true,
debug: false,
});
// Connect to SSE server
useEffect(() => {
connect();
return () => disconnect();
}, []);
// Start a new chat
const handleNewChat = async () => {
const chatKey = await startChat({ source: "website" });
};
// Load existing chat
const handleLoadChat = async (key: string) => {
const chat = await loadChat(key);
};
// Send message
const handleSend = async (content: string) => {
await sendMessage(content, attachments);
};
// Subscribe to specific events
useEffect(() => {
const unsubscribe = on("typing", (event) => {
console.log("User typing:", event);
});
return unsubscribe;
}, [on]);
return (
<div>
{messages.map((msg) => (
<Message key={msg.id} {...msg} />
))}
{isConnected && <MessageInput onSend={handleSend} onTyping={startTyping} />}
</div>
);
}
Options:
| Option | Type | Description |
|---|---|---|
baseUrl | string | Chat server base URL |
orgId | string | Organization ID |
userId | string | User ID |
clientType | "customer" | "humanAgent" | "observer" | Client type for routing |
transport | ChatTransport | "sse" | "sse-fetch" | Transport type |
onMessage | (event: ChatEvent) => void | Callback for all events |
onError | (error: TransportError) => void | Callback for errors |
onConnectionChange | (state: TransportState) => void | Connection state callback |
autoConnect | boolean | Auto-connect on mount |
retryConfig | RetryConfig | Retry configuration |
debug | boolean | Enable debug logging |
Methods:
| Method | Parameters | Returns | Description |
|---|---|---|---|
connect | - | Promise<void> | Connect to chat server |
disconnect | - | void | Disconnect from server |
startChat | metadata?: Record<string, unknown> | Promise<string> | Start new chat, returns chatKey |
loadChat | chatKey: string | Promise<Chat> | Load existing chat |
sendMessage | content: string, attachments?: unknown[] | Promise<void> | Send a message |
sendEvent | event: Omit<ChatEvent, "timestamp"> | Promise<void> | Send custom event |
endChat | reason?: string | Promise<void> | End current chat |
startTyping | - | void | Signal user is typing |
stopTyping | - | void | Signal user stopped typing |
on | eventType: string, handler: Function | Unsubscribe | Subscribe to event type |
clearError | - | void | Clear current error |
State:
| Property | Type | Description |
|---|---|---|
connectionState | TransportState | Current connection state |
isConnected | boolean | Whether connected |
currentChat | Chat | null | Current chat object |
chatKey | string | null | Current chat key |
messages | ChatMessage[] | Chat messages |
error | TransportError | null | Current error |
metrics | ConnectionMetrics | Connection metrics |
useChatHistory
Hook for chat history CRUD operations with loading/error states.
import { useChatHistory } from "@elqnt/chat/hooks";
const {
loading,
error,
getChatHistory,
getChat,
updateChat,
deleteChat,
getChatsByUser,
} = useChatHistory({
baseUrl: apiGatewayUrl,
orgId: selectedOrgId,
});
// Get paginated chat history
const { chats, total, hasMore } = await getChatHistory({ limit: 20, offset: 0 });
// Get single chat
const chat = await getChat("chat-key");
// Update chat (title, pinned)
const updated = await updateChat("chat-key", { title: "New Title", pinned: true });
// Delete chat
const success = await deleteChat("chat-key");
// Get chats by user email
const userChats = await getChatsByUser("user@example.com");
Methods:
| Method | Parameters | Returns | Description |
|---|---|---|---|
getChatHistory | { limit?, offset?, skipCache? } | { chats, total, hasMore } | Get paginated chat history |
getChat | chatKey: string | ChatSummary | null | Get single chat |
updateChat | chatKey: string, { title?, pinned? } | boolean | Update chat metadata |
deleteChat | chatKey: string | boolean | Delete a chat |
getChatsByUser | userEmail: string | ChatSummary[] | Get chats by user |
useChatMonitoring
Hook for monitoring active/waiting chats and queues (helpdesk use case).
import { useChatMonitoring } from "@elqnt/chat/hooks";
const {
loading,
error,
getActiveChats,
getActiveChatsCount,
getWaitingChatsCount,
listQueues,
} = useChatMonitoring({
baseUrl: apiGatewayUrl,
orgId: selectedOrgId,
});
// Get active chats (last N hours)
const activeChats = await getActiveChats(24); // last 24 hours
// Get counts
const activeCount = await getActiveChatsCount();
const waitingCount = await getWaitingChatsCount();
// List all queues with stats
const queues = await listQueues();
// Returns: [{ id, name, agentId, waitingCount, activeCount }, ...]
Methods:
| Method | Parameters | Returns | Description |
|---|---|---|---|
getActiveChats | pastHours?: number | ChatSummary[] | Get active chats |
getActiveChatsCount | - | number | Count of active chats |
getWaitingChatsCount | - | number | Count of waiting chats |
listQueues | - | Queue[] | List all queues with stats |
useHumanAgentSessions
Hook for monitoring human agent sessions.
import { useHumanAgentSessions } from "@elqnt/chat/hooks";
const {
loading,
error,
getOnlineSessions,
getAgentSession,
} = useHumanAgentSessions({
baseUrl: apiGatewayUrl,
orgId: selectedOrgId,
});
// Get all online agent sessions
const sessions = await getOnlineSessions();
// Get specific agent session
const session = await getAgentSession("agent-uuid");
Methods:
| Method | Parameters | Returns | Description |
|---|---|---|---|
getOnlineSessions | - | AgentSession[] | Get all online agent sessions |
getAgentSession | agentId: string | AgentSession | null | Get specific agent session |
Transport Layer
The chat SDK uses a transport abstraction for real-time communication.
SSE Transport (Browser)
import { createSSETransport } from "@elqnt/chat/transport";
const transport = createSSETransport({ debug: true });
const chat = useChat({
baseUrl: "...",
orgId: "...",
userId: "...",
transport,
});
Fetch SSE Transport (React Native)
import { createFetchSSETransport } from "@elqnt/chat/transport";
// For React Native where EventSource isn't available
const transport = createFetchSSETransport();
const chat = useChat({
baseUrl: "...",
orgId: "...",
userId: "...",
transport,
});
Custom Transport
Implement the ChatTransport interface for custom integrations:
import type { ChatTransport, TransportConfig } from "@elqnt/chat/transport";
const customTransport: ChatTransport = {
connect: async (config: TransportConfig) => { /* ... */ },
disconnect: (intentional?: boolean) => { /* ... */ },
send: async (event: ChatEvent) => { /* ... */ },
sendMessage: async (message: ChatMessage) => { /* ... */ },
createChat: async (options) => { /* ... */ },
loadChatData: async (options) => { /* ... */ },
onMessage: (handler) => { /* ... */ },
on: (eventType, handler) => { /* ... */ },
getState: () => "connected",
getMetrics: () => ({ /* ... */ }),
getError: () => undefined,
clearError: () => { /* ... */ },
};
Stream API (Low-level)
Low-level functions for direct HTTP calls (used internally by transport).
import {
createChat,
sendChatMessage,
loadChat,
endChat,
sendTypingIndicator,
sendEvent,
} from "@elqnt/chat/api";
// Create a new chat
const chatKey = await createChat({
baseUrl: "https://api.example.com/chat",
orgId: "org-123",
userId: "user-456",
metadata: { source: "website" },
});
// Send a message
await sendChatMessage({
baseUrl: "https://api.example.com/chat",
orgId: "org-123",
chatKey,
userId: "user-456",
content: "Hello!",
attachments: [],
});
// Load existing chat
const chat = await loadChat({
baseUrl: "https://api.example.com/chat",
orgId: "org-123",
chatKey: "chat-789",
userId: "user-456",
});
// Send typing indicator
await sendTypingIndicator({
baseUrl: "https://api.example.com/chat",
orgId: "org-123",
chatKey,
userId: "user-456",
typing: true,
});
// End chat
await endChat({
baseUrl: "https://api.example.com/chat",
orgId: "org-123",
chatKey,
userId: "user-456",
reason: "User closed chat",
});
Models
Chat
import type { Chat, ChatMessage, ChatUser } from "@elqnt/chat/models";
interface Chat {
orgId: string;
key: string;
title: string;
messages: ChatMessage[];
lastUpdated: number;
startTime: number;
users: ChatUser[];
status: ChatStatusTS;
aiEngaged: boolean;
humanAgentEngaged: boolean;
isWaiting: boolean;
isWaitingForAgent: boolean;
userRating?: number;
metadata?: Record<string, unknown>;
grading?: ChatGrading;
flow?: ChatFlow;
context?: ChatContext;
pinned?: boolean;
chatType?: ChatTypeTS;
}
interface ChatMessage {
id: string;
role: ChatRoleTS;
content: string;
time: number;
status?: string;
senderId?: string;
createdAt: number;
attachments?: ChatAttachment[];
toolCalls?: ToolCall[];
metadata?: Record<string, unknown>;
}
interface ChatUser {
id: string;
role: ChatRoleTS;
name: string;
email: string;
phone?: string;
authProvider: string;
metadata?: Record<string, unknown>;
}
Chat Event
import type { ChatEvent } from "@elqnt/chat/models";
interface ChatEvent {
type: string;
orgId: string;
chatKey?: string;
userId?: string;
timestamp: number;
message?: ChatMessage;
data?: Record<string, unknown>;
}
// Event types
ChatEventTypeMessage; // "message"
ChatEventTypeTyping; // "typing"
ChatEventTypeStoppedTyping; // "stopped_typing"
ChatEventTypeUserJoined; // "user_joined"
ChatEventTypeUserLeft; // "user_left"
ChatEventTypeLoadChat; // "load_chat"
ChatEventTypeLoadChatResponse; // "load_chat_response"
ChatEventTypeChatEnded; // "chat_ended"
ChatEventTypeError; // "error"
Agent Session
import type { AgentSession } from "@elqnt/chat/models";
interface AgentSession {
agentId: string;
orgId: string;
onlineSince: number;
lastActivity: number;
status: AgentStatusTS; // "online" | "away" | "busy" | "offline"
chatsHandled: number;
activeChats: string[];
userAgent?: string;
ipAddress?: string;
metadata?: Record<string, unknown>;
}
Queue
interface Queue {
id: string;
name: string;
agentId: string;
waitingCount: number;
activeCount: number;
}
Transport Types
import type {
TransportState,
TransportError,
TransportConfig,
RetryConfig,
ConnectionMetrics,
ChatTransport,
} from "@elqnt/chat/transport";
type TransportState = "disconnected" | "connecting" | "connected" | "reconnecting";
interface TransportError {
code: "CONNECTION_FAILED" | "PARSE_ERROR" | "SEND_FAILED" | "TIMEOUT" | "NETWORK_ERROR" | "AUTH_FAILED";
message: string;
retryable: boolean;
timestamp: number;
}
interface ConnectionMetrics {
latency: number;
messagesSent: number;
messagesReceived: number;
messagesQueued: number;
reconnectCount: number;
connectedAt?: number;
lastMessageAt?: number;
}
interface RetryConfig {
maxRetries?: number;
intervals?: number[];
backoffMultiplier?: number;
maxBackoffTime?: number;
}
Status Constants
import {
ChatStatusActive,
ChatStatusDisconnected,
ChatStatusAbandoned,
ChatStatusClosed,
ChatStatusArchived,
ChatRoleUser,
ChatRoleAI,
ChatRoleHumanAgent,
ChatRoleSystem,
ChatRoleTool,
AgentStatusOnline,
AgentStatusAway,
AgentStatusBusy,
AgentStatusOffline,
} from "@elqnt/chat/models";
API Gateway Routes
| Function | Method | Gateway Route |
|---|---|---|
| Chat History | ||
| List | POST | /api/v1/chats |
| Get | GET | /api/v1/chats/{chatKey} |
| Update | PATCH | /api/v1/chats/{chatKey} |
| Delete | DELETE | /api/v1/chats/{chatKey} |
| Get by User | GET | /api/v1/chats/user/{userEmail} |
| Active Chats | ||
| List | GET | /api/v1/chats/active |
| Count | GET | /api/v1/chats/active/count |
| Waiting Chats | ||
| Count | GET | /api/v1/chats/waiting/count |
| Queues | ||
| List | GET | /api/v1/queues |
| Agent Sessions | ||
| Online | GET | /api/v1/agents/sessions/online |
| Get | GET | /api/v1/agents/sessions/{agentId} |
| Project Chats | ||
| Add to Project | POST | /api/v1/projects/{projectId}/chats |
| Update Title | PUT | /api/v1/projects/{projectId}/chats/{chatKey}/title |
| Stream Endpoints | ||
| SSE Connect | GET | /chat/events |
| Create | POST | /chat/create |
| Send | POST | /chat/send |
| Load | POST | /chat/load |
| End | POST | /chat/end |
| Typing | POST | /chat/typing |
| Event | POST | /chat/event |
React Native Support
The SDK works with React Native using fetch-based SSE transport:
import { useChat } from "@elqnt/chat/hooks";
import { createFetchSSETransport } from "@elqnt/chat/transport";
function ChatScreen() {
const transport = createFetchSSETransport();
const { messages, sendMessage, isConnected } = useChat({
baseUrl: config.chatUrl,
orgId: config.orgId,
userId: user.id,
transport,
});
return (
<FlatList
data={messages}
renderItem={({ item }) => <MessageBubble message={item} />}
keyExtractor={(item) => item.id}
/>
);
}
Type Generation
Chat types are generated from Go using tygo:
cd backend && tygo generate
| Go Package | TypeScript Output |
|---|---|
blazi/common/chat | @elqnt/chat/models/chat-models.ts |
See Also
- @elqnt/agents - Agent configuration for chat
- SSE Progress - Streaming patterns
- @elqnt/api-client - HTTP client used internally