@curaious/uno-converse
v0.1.10
Published
React hooks and utilities for building conversation UIs with Uno Agent Server
Maintainers
Readme
@curaious/uno-converse
React hooks and utilities for building conversation UIs with Uno Agent Server.
Features
- 🎣 React Hook -
useConversationhook for complete conversation state management - 🌊 Streaming Support - Built-in SSE streaming with real-time message updates
- 📦 Type Safe - Full TypeScript support with comprehensive type definitions
- 🔌 Flexible API Client - Bring your own API client or use the built-in one
- 🎯 Zero Dependencies - Only React as a peer dependency
Installation
# npm
npm install @curaious/uno-converse
# yarn
yarn add @curaious/uno-converse
# pnpm
pnpm add @curaious/uno-converseGitHub Packages Registry
This package is published to GitHub Packages. You'll need to configure npm to use the GitHub registry for @curaious scoped packages:
# Create or edit ~/.npmrc
echo "@curaious:registry=https://npm.pkg.github.com" >> ~/.npmrcYou may also need to authenticate with a GitHub personal access token:
echo "//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN" >> ~/.npmrcQuick Start
import { useConversation, createApiClient, MessageType, ContentType } from '@curaious/uno-converse';
// Create an API client
const client = createApiClient({
baseUrl: 'https://your-uno-server.com/api/agent-server',
headers: {
'Authorization': 'Bearer your-api-key',
},
});
function ChatApp() {
const {
allMessages,
isStreaming,
sendMessage,
startNewChat,
conversations,
selectConversation,
} = useConversation({
namespace: 'my-app',
client,
});
const handleSend = async (text: string) => {
await sendMessage(
[{
type: MessageType.Message,
id: Date.now().toString(),
content: [{ type: ContentType.InputText, text }],
}],
{
baseUrl: client.baseUrl,
namespace: 'my-app',
agentName: 'my-agent',
}
);
};
return (
<div>
<button onClick={startNewChat}>New Chat</button>
<div className="messages">
{allMessages.map((msg) => (
<Message key={msg.message_id} message={msg} />
))}
{isStreaming && <LoadingIndicator />}
</div>
<ChatInput onSend={handleSend} disabled={isStreaming} />
</div>
);
}API Reference
useConversation(options)
The main hook for managing conversation state.
Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| namespace | string | required | Namespace for organizing conversations |
| client | UnoApiClient | required | API client for communicating with Uno server |
| autoLoad | boolean | true | Auto-load conversations on mount |
Return Value
interface UseConversationReturn {
// State
conversations: Conversation[];
conversationsLoading: boolean;
threads: Thread[];
threadsLoading: boolean;
currentThread: Thread | null;
messages: ConversationMessage[];
streamingMessage: ConversationMessage | null;
messagesLoading: boolean;
isStreaming: boolean;
currentConversationId: string | null;
currentThreadId: string | null;
allMessages: ConversationMessage[];
// Actions
loadConversations: () => Promise<void>;
selectConversation: (conversationId: string) => void;
loadThreads: (conversationId: string) => Promise<void>;
selectThread: (threadId: string) => void;
sendMessage: (messages: MessageUnion[], config: ConverseConfig) => Promise<void>;
startNewChat: () => void;
}createApiClient(options)
Creates a default API client using fetch.
const client = createApiClient({
baseUrl: 'https://your-server.com/api/agent-server',
headers: { 'Authorization': 'Bearer token' },
projectId: 'optional-project-id',
});Custom API Client
You can implement your own API client:
import type { UnoApiClient } from '@curaious/uno-converse';
const customClient: UnoApiClient = {
baseUrl: 'https://your-server.com',
headers: { 'Authorization': 'Bearer token' },
async getConversations(namespace) {
// Your implementation
},
async getThreads(conversationId, namespace) {
// Your implementation
},
async getMessages(threadId, namespace) {
// Your implementation
},
async getMessage(messageId, namespace) {
// Your implementation
},
};Streaming Utilities
For advanced use cases, you can use the streaming utilities directly:
import { streamSSE, ChunkProcessor } from '@curaious/uno-converse';
// Stream SSE events
await streamSSE(
'https://your-server.com/converse',
{ method: 'POST', body: JSON.stringify(payload) },
{
onChunk: (data) => console.log('Chunk:', data),
onComplete: () => console.log('Done'),
onError: (err) => console.error('Error:', err),
}
);
// Process chunks into conversation messages
const processor = new ChunkProcessor(
'conversation-id',
'thread-id',
(conversation) => updateUI(conversation)
);
processor.processChunk(jsonString);Message Types
The library exports comprehensive types for all message formats:
import {
MessageType,
ContentType,
Role,
type MessageUnion,
type InputMessage,
type FunctionCallMessage,
type ReasoningMessage,
} from '@curaious/uno-converse';
// Create a user message
const userMessage: InputMessage = {
type: MessageType.Message,
role: Role.User,
content: [
{ type: ContentType.InputText, text: 'Hello!' },
],
};Tool Call Approvals
Handle human-in-the-loop scenarios with function call approvals:
const { sendMessage } = useConversation({ ... });
// Approve or reject pending tool calls
const handleApproval = async (approvedIds: string[], rejectedIds: string[]) => {
await sendMessage(
[{
type: MessageType.FunctionCallApprovalResponse,
id: Date.now().toString(),
approved_call_ids: approvedIds,
rejected_call_ids: rejectedIds,
}],
converseConfig
);
};Type Guards
The library exports type guard functions for runtime type checking:
import {
isEasyMessage,
isInputMessage,
isFunctionCallMessage,
isReasoningMessage,
} from '@curaious/uno-converse';
allMessages.forEach((msg) => {
msg.messages.forEach((m) => {
if (isFunctionCallMessage(m)) {
console.log('Function call:', m.name, m.arguments);
} else if (isReasoningMessage(m)) {
console.log('Reasoning:', m.summary);
}
});
});License
MIT
