uneeq-state-manager
v1.3.1
Published
Uneeq State Manager SDK - Client library for managing chatbot session state
Readme
Uneeq State Manager SDK
A TypeScript client library for managing chatbot session state with real-time PubSub capabilities.
Table of Contents
- Installation
- Quick Start
- Configuration
- API Reference
- PubSub (Real-time Subscriptions)
- Error Handling
- TypeScript Support
- Examples
- License
Installation
npm install uneeq-state-manageryarn add uneeq-state-managerRequirements
- Node.js 18.0.0 or higher
- TypeScript 5.0 or higher (for development)
Quick Start
import { StateManager } from 'uneeq-state-manager';
// Initialize the State Manager
const stateManager = new StateManager({
endpoint: 'https://api.example.com',
apiKey: 'your-api-key',
});
// Access a workspace and create a session
const workspace = stateManager.workspace('my-workspace');
const session = workspace.session('user-session-123');
// Create the session with a 1 hour TTL
await session.create({ ttl: 3600 });
// Store session state
await session.set('user', { name: 'John', age: 30 });
// Retrieve session state
const user = await session.get('user');
console.log(user); // { name: 'John', age: 30 }
// End the session when done
await session.end();Configuration
StateManagerConfig Options
interface StateManagerConfig {
/** API endpoint URL for the State Manager Engine */
endpoint: string;
/** Authentication key for API requests */
apiKey: string;
/** Optional default workspace ID */
workspaceId?: string;
/** Request timeout in milliseconds (default: 30000) */
timeout?: number;
/** Maximum retry attempts (default: 3) */
maxRetries?: number;
/** Initial retry delay in milliseconds (default: 1000) */
retryDelay?: number;
}Configuration Example
const stateManager = new StateManager({
endpoint: process.env.UNEEQ_ENDPOINT || 'https://api.example.com',
apiKey: process.env.UNEEQ_API_KEY,
timeout: 60000,
maxRetries: 5,
retryDelay: 2000,
});API Reference
StateManager
The main entry point for the SDK.
workspace(workspaceId: string): Workspace
Gets a workspace instance for accessing sessions.
const workspace = stateManager.workspace('my-workspace');Session
Manages session lifecycle and session-scoped state.
create(config?: SessionConfig): Promise<void>
Creates/initializes the session on the server.
await session.create({
ttl: 3600, // Session expires in 1 hour
metadata: { userId: 'user-123' },
});getInfo(): Promise<SessionInfo>
Retrieves session information.
const info = await session.getInfo();
console.log(`Status: ${info.status}`);
console.log(`Expires at: ${info.expiresAt}`);
console.log(`State keys: ${info.stateKeys.join(', ')}`);set<T>(key: string, data: T): Promise<void>
Sets a session state value.
await session.set('user', { name: 'John', age: 30 });
await session.set('conversation-count', 5);
await session.set('active', true);get<T>(key: string): Promise<T | null>
Gets a session state value.
const user = await session.get<{ name: string; age: number }>('user');
if (user) {
console.log(`User: ${user.name}`);
}getAll<T>(): Promise<T>
Gets all session state as a single object.
const allState = await session.getAll();
console.log(allState); // { user: {...}, count: 5, active: true }update<T>(key: string, updateFn: (current: T) => T): Promise<T>
Atomically updates a session state value.
const newCount = await session.update('count', (current) => current + 1);
const updatedUser = await session.update('user', (user) => ({
...user,
lastSeen: new Date(),
}));delete(key: string): Promise<void>
Deletes a session state value.
await session.delete('tempData');has(key: string): Promise<boolean>
Checks if a state key exists.
if (await session.has('user')) {
const user = await session.get('user');
}keepAlive(): Promise<void>
Extends the session's TTL (call on user activity).
await session.keepAlive();end(): Promise<void>
Terminates the session and removes all data.
await session.end();publishToTopic<T>(topicId: string, data: T): Promise<PublishResult>
Publishes a message to a topic (session-scoped).
const result = await session.publishToTopic('messages', {
text: 'Hello from Flowise!',
type: 'assistant',
});
console.log(`Delivered to ${result.deliveredTo} subscribers`);getSessionId(): string / getWorkspaceId(): string
Returns the session or workspace ID.
console.log(`Session: ${session.getSessionId()}`);
console.log(`Workspace: ${session.getWorkspaceId()}`);PubSub (Real-time Subscriptions)
The SDK includes a PubSub client for real-time message subscriptions using Socket.IO.
Creating a PubSub Client
import { PubSubClient } from 'uneeq-state-manager';
const pubsub = new PubSubClient({
endpoint: 'https://api.example.com',
apiKey: 'your-api-key',
workspaceId: 'my-workspace',
sessionId: 'session-123', // Required for session-scoped subscriptions
});PubSubConfig Options
interface PubSubConfig {
/** Socket.IO server endpoint URL */
endpoint: string;
/** Authentication key */
apiKey: string;
/** Workspace identifier */
workspaceId: string;
/** Session ID (required for subscriptions) */
sessionId: string;
/** Auto-connect on creation (default: false) */
autoConnect?: boolean;
/** Enable automatic reconnection (default: true) */
reconnection?: boolean;
/** Max reconnection attempts (default: 10) */
reconnectionAttempts?: number;
/** Reconnection delay in ms (default: 1000) */
reconnectionDelay?: number;
}Subscribing to Topics
Simple handler:
const subscription = pubsub.subscribe('messages', (message) => {
console.log('Received:', message.data);
console.log('Published at:', message.publishedAt);
});
// Unsubscribe when done
subscription.unsubscribe();With full options:
const subscription = pubsub.subscribe('messages', {
onMessage: (message) => {
console.log('Message from Flowise:', message.data);
},
onError: (error) => {
console.error('Subscription error:', error);
},
onSubscribed: (subscriberCount) => {
console.log(`Subscribed! ${subscriberCount} total subscribers`);
},
onUnsubscribed: () => {
console.log('Unsubscribed from topic');
},
});Event Handling
// Connection events
pubsub.on('connect', () => {
console.log('Connected to PubSub server');
});
pubsub.on('disconnect', () => {
console.log('Disconnected from PubSub server');
});
pubsub.on('error', (error) => {
console.error('PubSub error:', error);
});
// Session lifecycle
pubsub.on('session-ended', ({ sessionId, reason }) => {
console.log(`Session ${sessionId} ended: ${reason}`);
});Connection Management
// Manual connection
await pubsub.connect();
// Check connection status
if (pubsub.isConnected) {
console.log('Connected');
}
// Disconnect and cleanup
await pubsub.disconnect();Publishing Messages
Use the session's publishToTopic method to send messages (typically from Flowise to frontend):
// From your session instance (e.g., in Flowise)
const result = await session.publishToTopic('messages', {
text: 'Here is your answer!',
type: 'assistant',
});
console.log(`Message delivered to ${result.deliveredTo} subscribers`);Error Handling
The SDK provides specialized error classes for different scenarios.
Error Types
| Error Class | HTTP Status | Description |
|---|---|---|
| StateManagerError | - | Base error class |
| AuthenticationError | 401 | Invalid API credentials |
| AuthorizationError | 403 | Insufficient permissions |
| NotFoundError | 404 | Resource not found |
| ValidationError | 400 | Invalid request parameters |
| RateLimitError | 429 | Rate limit exceeded |
| NetworkError | - | Network connectivity issues |
| TimeoutError | - | Request timeout |
Error Handling Example
import {
StateManagerError,
AuthenticationError,
NotFoundError,
RateLimitError,
} from 'uneeq-state-manager';
try {
const user = await session.get('user');
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Invalid API key:', error.message);
} else if (error instanceof NotFoundError) {
console.error('Session not found:', error.message);
} else if (error instanceof RateLimitError) {
console.error('Rate limited, retry later');
} else if (error instanceof StateManagerError) {
console.error(`Error (${error.code}):`, error.message);
}
}TypeScript Support
The SDK is built with TypeScript and provides full type safety.
Typed Session State
// Define your state types
interface ConversationState {
messages: Array<{ role: 'user' | 'assistant'; content: string }>;
currentTopic: string;
sentiment: number;
}
// Use with full type safety
const session = workspace.session('conv-123');
await session.create();
// Set typed state
await session.set<ConversationState>('conversation', {
messages: [{ role: 'user', content: 'Hello' }],
currentTopic: 'support',
sentiment: 0.8,
});
// Get with type inference
const conv = await session.get<ConversationState>('conversation');
if (conv) {
console.log(conv.messages.length);
console.log(conv.sentiment);
}
// Type-safe updates
const updated = await session.update<ConversationState>(
'conversation',
(current) => ({
...current,
messages: [...current.messages, { role: 'assistant', content: 'Hi!' }],
sentiment: (current.sentiment + 0.9) / 2,
})
);Typed PubSub Messages
interface FlowiseMessage {
text: string;
type: 'user' | 'assistant';
timestamp?: string;
}
// Typed subscription (frontend receiving from Flowise)
pubsub.subscribe<FlowiseMessage>('messages', (message) => {
console.log(message.data.text); // string
console.log(message.data.type); // 'user' | 'assistant'
});
// Typed publish (Flowise sending to frontend)
await session.publishToTopic<FlowiseMessage>('messages', {
text: 'Hello! How can I help you?',
type: 'assistant',
timestamp: new Date().toISOString(),
});Examples
Basic Session Management
import { StateManager } from 'uneeq-state-manager';
async function basicSessionExample() {
const stateManager = new StateManager({
endpoint: 'https://api.example.com',
apiKey: process.env.API_KEY,
});
const workspace = stateManager.workspace('demo-workspace');
const session = workspace.session('user-123-session');
try {
// Create session with 1 hour TTL
await session.create({ ttl: 3600 });
// Store user information
await session.set('user', {
id: 'user-123',
name: 'Alice',
email: '[email protected]',
});
// Retrieve user information
const user = await session.get('user');
console.log('User:', user);
// Check all session state
const allState = await session.getAll();
console.log('All state:', allState);
// End session
await session.end();
} catch (error) {
console.error('Error:', error);
}
}Chatbot Conversation Flow
import { StateManager } from 'uneeq-state-manager';
interface Message {
role: 'user' | 'assistant';
content: string;
timestamp: string;
}
interface ConversationState {
messages: Message[];
topic: string;
turnCount: number;
}
async function chatbotExample() {
const stateManager = new StateManager({
endpoint: 'https://api.example.com',
apiKey: process.env.API_KEY,
});
const workspace = stateManager.workspace('chatbot-workspace');
const session = workspace.session(`chat-${Date.now()}`);
try {
// Create session
await session.create({
ttl: 7200, // 2 hour conversation
metadata: { type: 'customer-support' },
});
// Initialize conversation
await session.set<ConversationState>('conversation', {
messages: [],
topic: 'support',
turnCount: 0,
});
// Process user message
const userMessage = 'I need help with my order';
await session.update<ConversationState>('conversation', (current) => ({
...current,
messages: [
...current.messages,
{
role: 'user',
content: userMessage,
timestamp: new Date().toISOString(),
},
],
turnCount: current.turnCount + 1,
}));
// Add assistant response
await session.update<ConversationState>('conversation', (current) => ({
...current,
messages: [
...current.messages,
{
role: 'assistant',
content: 'I would be happy to help. What is your order number?',
timestamp: new Date().toISOString(),
},
],
}));
// Keep session alive on activity
await session.keepAlive();
// Retrieve conversation history
const conversation = await session.get<ConversationState>('conversation');
console.log('Messages:', conversation?.messages);
} finally {
await session.end();
}
}Real-time PubSub Example
This example shows how the frontend subscribes to receive messages from Flowise:
import { StateManager, PubSubClient } from 'uneeq-state-manager';
interface FlowiseMessage {
text: string;
type: 'user' | 'assistant';
timestamp?: string;
}
async function frontendPubsubExample() {
const stateManager = new StateManager({
endpoint: 'https://api.example.com',
apiKey: process.env.API_KEY,
});
const workspace = stateManager.workspace('my-workspace');
const session = workspace.session('user-456');
// Create session first
await session.create({ ttl: 3600 });
// Create PubSub client for this session
const pubsub = new PubSubClient({
endpoint: 'https://api.example.com',
apiKey: process.env.API_KEY,
workspaceId: 'my-workspace',
sessionId: 'user-456',
});
// Handle connection events
pubsub.on('connect', () => console.log('Connected to PubSub'));
pubsub.on('error', (err) => console.error('PubSub error:', err));
pubsub.on('session-ended', ({ reason }) => {
console.log('Session ended:', reason);
});
// Subscribe to messages from Flowise
const subscription = pubsub.subscribe<FlowiseMessage>('messages', {
onMessage: (message) => {
// Display message in the UI
console.log(`[${message.data.type}]: ${message.data.text}`);
},
onSubscribed: (count) => {
console.log(`Subscribed to messages (${count} subscribers)`);
},
onError: (error) => {
console.error('Subscription error:', error);
},
});
// Flowise publishes messages like this:
// await session.publishToTopic<FlowiseMessage>('messages', {
// text: 'Hello! How can I help you?',
// type: 'assistant',
// });
// Later: cleanup
subscription.unsubscribe();
await pubsub.disconnect();
await session.end();
}License
This SDK is licensed under the MIT License.
Support
For issues or questions, please visit the GitLab repository.
