@sofya-sdk/chat
v3.4.0
Published
Headless, UI-agnostic Chat SDK for real-time messaging
Maintainers
Readme
@sofya-sdk/chat
Headless, UI-agnostic Chat SDK for real-time messaging.
Features
- 🚀 Headless - No UI assumptions, works with any framework
- 🔌 Pluggable - Adapter architecture for transport, protocol, and session management
- 📡 Real-time - WebSocket-based streaming with delta support
- 🔄 Resilient - Auto-reconnection with exponential backoff
- 📘 Type-safe - Full TypeScript support with excellent IDE autocomplete
- 🧪 Mock-first - Built-in mock transport for development and testing
Quick Start
npm install @sofya-sdk/chat
# or
pnpm add @sofya-sdk/chatBasic Usage
import { Chat, StaticSessionProvider } from "@sofya-sdk/chat";
// Create chat client with agent
const chat = new Chat({
sessionProvider: new StaticSessionProvider({
endpoint: "api.example.com", // Will be converted to wss://
agentSlug: "my-agent-123",
token: "your-bearer-token", // Optional
}),
});
// Or use shorthand (creates StaticSessionProvider automatically)
const chat = new Chat({
endpoint: "api.example.com",
agentSlug: "my-agent-123",
token: "your-bearer-token",
});
// Listen for events
chat.on("message", (msg) => {
console.log("Assistant:", msg.text);
console.log("Tools:", msg.tools); // Tools used by agent (if any)
});
chat.on("error", (err) => {
console.error(`[${err.code}]:`, err.message);
});
// Connect and send
await chat.connect();
await chat.sendText("Hello!");With Audio Input
// Record audio (browser)
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const mediaRecorder = new MediaRecorder(stream);
const audioChunks = [];
mediaRecorder.ondataavailable = (e) => audioChunks.push(e.data);
mediaRecorder.onstop = async () => {
const audioBlob = new Blob(audioChunks, { type: "audio/webm" });
const messageId = await chat.sendAudio(audioBlob);
console.log("Audio sent:", messageId);
};
mediaRecorder.start();
// ... later ...
mediaRecorder.stop();With Agents API (Dynamic Configuration)
import { Chat, AgentsApiSessionProvider } from "@sofya-sdk/chat";
// Fetch agent configuration from backend API
const sessionProvider = new AgentsApiSessionProvider({
agentsApiUrl: "https://agents-api.example.com",
apiKey: "your-x-api-key",
provider: "ask_sofya", // REQUIRED: Provider name to connect to
cacheTtlMs: 300000, // Cache config for 5 minutes (default)
});
const chat = new Chat({ sessionProvider });
// First connect: fetches config from API
await chat.connect();
// Subsequent connects within cache TTL: uses cached config
// After cache expires: automatically refetchesListing Available Providers
import { AgentsApiSessionProvider } from "@sofya-sdk/chat";
// Create session provider (you need to specify a provider even to list them)
const sessionProvider = new AgentsApiSessionProvider({
agentsApiUrl: "https://agents-api.example.com",
apiKey: "your-x-api-key",
provider: "ask_sofya", // Temporary - can change later by creating new instance
});
// List available providers
const providers = await sessionProvider.listProviders();
console.log("Available providers:", providers);
// Output: ["pre-triage", "ask_sofya"]
// To switch providers, create a new ChatClient with a new provider
const newSessionProvider = new AgentsApiSessionProvider({
agentsApiUrl: "https://agents-api.example.com",
apiKey: "your-x-api-key",
provider: "pre-triage", // Different provider
});
const newChat = new Chat({ sessionProvider: newSessionProvider });
await newChat.connect();
const newChat = new Chat({ sessionProvider: newSessionProvider });
await newChat.connect();Medical Terminology Provider (Special Case)
The medical-terminology-coder provider supports both WebSocket (streaming) and HTTP POST (request/response) interaction models.
const sessionProvider = new AgentsApiSessionProvider({
agentsApiUrl: "https://agents-api.example.com",
apiKey: "your-x-api-key",
provider: "medical-terminology-coder",
});
const chat = new Chat({ sessionProvider });
await chat.connect();
// Send text via HTTP POST (returns JSON response directly)
// The response is also emitted as a 'message' event
const messageId = await chat.sendText("dor de cabeca");Note: If the specified provider is not found in the API response, an error will be thrown with a list of available providers.
Using Mocks (Development & Testing)
import { Chat, mockScenarios, MockProtocol } from "@sofya-sdk/chat";
const chat = new Chat({
transport: mockScenarios.assistant, // or .echo, .slow, .unreliable
protocol: new MockProtocol(), // Use old event format for mocks
});
await chat.connect();
await chat.sendText("Test message"); // Mock will respond automaticallyAPI Reference
ChatClient
new Chat(options: ChatClientOptions | string)
Create a new chat client.
Parameters:
options- Configuration object or endpoint string
connect(): Promise<void>
Connect to the chat server.
disconnect(code?: number, reason?: string): void
Disconnect from the chat server.
sendText(text: string, meta?: any): Promise<string>
Send a text message. Returns the message ID.
sendAudio(audioBlob: Blob, meta?: any): Promise<string>
Send an audio message. Audio is uploaded via HTTP POST to the audio endpoint with the contextId query parameter. Returns the message ID.
sendControl(kind: 'stop' | 'clear' | 'typing_on' | 'typing_off'): void
Send a control command. Note: Real backend doesn't support control commands - this is a no-op for compatibility.
on(callback: (event: ChatEvent) => void): () => void
Subscribe to all events. Returns an unsubscribe function.
on<T>(eventType: T, callback: (event: EventOfType<T>) => void): () => void
Subscribe to a specific event type.
once<T>(eventType: T, callback: (event: EventOfType<T>) => void): () => void
Subscribe to an event once.
getConnectionState(): ConnectionState
Get the current connection state.
getMessages(): MessageEvent[]
Get all messages in chronological order.
Documentation
- Full API Documentation (Coming soon)
- Error Codes (Coming soon)
- Examples
License
ISC
