Skip to main content

Integrating Connect Agents into Custom Software

This guide explains how to integrate Connect agents into your custom software using our WebSocket-based communication system. The WebSocket approach provides real-time, bidirectional communication between your application and the Connect platform, enabling a more responsive and interactive user experience.

Table of Contents

  1. Overview
  2. Prerequisites
  3. WebSocket Connection
  4. Authentication
  5. Starting a Session
  6. Sending Messages
  7. Handling Responses
  8. Tool Execution
  9. Error Handling
  10. Session Management
  11. Complete Example
  12. Best Practices

Overview

Connect's WebSocket-based agent integration allows you to:

  • Establish real-time communication with Connect agents
  • Send user messages and file attachments
  • Receive agent responses and tool execution requests
  • Execute tools on the client side and send results back
  • Maintain session state across multiple interactions

This approach offers several advantages over traditional REST API calls:

  • Lower latency for a more responsive user experience
  • Streaming responses for immediate feedback
  • Persistent connections that reduce overhead
  • Better handling of long-running conversations

Prerequisites

Before integrating with Connect, you'll need:

  1. A Connect account with access to agents
  2. The agent ID of the agent you want to integrate (for published agents)
  3. An authentication token (for accessing private agents or user-specific tools)
  4. A WebSocket client library for your platform

WebSocket Connection

To connect to the Connect WebSocket server, use the following endpoint:

wss://api.connect.example.com

Most programming languages and frameworks have WebSocket client libraries available. Here's a basic example using JavaScript:

const socket = new WebSocket('wss://api.connect.example.com');

socket.onopen = () => {
console.log('Connected to Connect WebSocket server');
};

socket.onclose = () => {
console.log('Disconnected from Connect WebSocket server');
};

socket.onerror = (error) => {
console.error('WebSocket error:', error);
};

socket.onmessage = (event) => {
const data = JSON.parse(event.data);
handleMessage(data);
};

Authentication

For accessing private agents or user-specific tools, you'll need to authenticate with a JWT token. This token should be sent when starting a session:

// Get the authentication token from your auth system
const token = getAuthToken();

// Send it when starting a session
socket.send(JSON.stringify({
type: 'start_session',
agentId: 'agent-123',
token: token
}));

For public agents, authentication is optional.

Starting a Session

To start a new session with an agent, send a start_session message:

socket.send(JSON.stringify({
type: 'start_session',
agentId: 'agent-123', // Optional: Specific agent ID
token: 'your-auth-token' // Optional: Authentication token
}));

The server will respond with a session_started message containing a session ID:

{
type: 'session_started',
sessionId: 'session-456'
}

Store this session ID as you'll need it for all subsequent messages.

Sending Messages

To send a user message to the agent, use the user_message event type:

socket.send(JSON.stringify({
type: 'user_message',
sessionId: 'session-456',
message: 'Hello, can you help me with something?',
files: [] // Optional: Array of file attachments
}));

Attaching Files

You can attach files to your messages for the agent to process:

// Read a file and convert to base64
const fileContent = await readFileAsBase64('document.pdf');

socket.send(JSON.stringify({
type: 'user_message',
sessionId: 'session-456',
message: 'Can you analyze this document?',
files: [
{
name: 'document.pdf',
content: fileContent, // Base64-encoded content
type: 'application/pdf'
}
]
}));

The server will extract text from supported file types and include it in the context for the agent.

Handling Responses

The agent will respond with one or more agent_response messages. These can be of different types:

Text Messages

{
type: 'agent_response',
responseType: 'message',
message: 'Hello! I'd be happy to help you with that.'
}

Tool Calls

When the agent needs to use a tool, it will send a tool call that should be executed on the client side:

{
type: 'agent_response',
responseType: 'tool_calls',
toolCalls: [
{
id: 'call-789',
type: 'function',
function: {
name: 'author-12345-search',
arguments: '{"query":"climate change","limit":5}'
}
}
]
}

Tool Execution

When you receive a tool call, you should:

  1. Execute the tool on the client side
  2. Send the result back to the server
// Execute the tool
const toolCall = data.toolCalls[0];
const args = JSON.parse(toolCall.function.arguments);
const result = await executeTool(toolCall.function.name, args);

// Send the result back
socket.send(JSON.stringify({
type: 'tool_execution_result',
sessionId: 'session-456',
toolCallId: toolCall.id,
result: JSON.stringify(result)
}));

If there's an error during tool execution:

socket.send(JSON.stringify({
type: 'tool_execution_result',
sessionId: 'session-456',
toolCallId: toolCall.id,
error: 'Failed to execute search: Network error'
}));

Error Handling

The server may send error messages in case of issues:

{
type: 'error',
message: 'Session not found'
}

Your client should handle these errors appropriately, potentially by retrying operations or notifying the user.

Session Management

Sessions have a timeout period (default: 10 minutes). If a session expires due to inactivity, you'll receive a session_expired message:

{
type: 'session_expired',
sessionId: 'session-456'
}

When this happens, you should start a new session if the user wants to continue the conversation.

You can also explicitly close a session by disconnecting from the WebSocket server.

Complete Example

Here's a complete example in JavaScript:

class ConnectAgentClient {
constructor(serverUrl) {
this.serverUrl = serverUrl;
this.socket = null;
this.sessionId = null;
this.messageHandlers = {
onMessage: () => {},
onToolCall: () => {},
onError: () => {},
onSessionExpired: () => {}
};
}

connect() {
this.socket = new WebSocket(this.serverUrl);

this.socket.onopen = () => {
console.log('Connected to Connect WebSocket server');
};

this.socket.onclose = () => {
console.log('Disconnected from Connect WebSocket server');
};

this.socket.onerror = (error) => {
console.error('WebSocket error:', error);
this.messageHandlers.onError('Connection error');
};

this.socket.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleMessage(data);
};
}

handleMessage(data) {
switch (data.type) {
case 'session_started':
this.sessionId = data.sessionId;
console.log(`Session started: ${this.sessionId}`);
break;

case 'agent_response':
if (data.responseType === 'message' && data.message) {
this.messageHandlers.onMessage(data.message);
} else if (data.responseType === 'tool_calls' && data.toolCalls) {
for (const toolCall of data.toolCalls) {
this.messageHandlers.onToolCall(toolCall);
}
}
break;

case 'error':
console.error('Server error:', data.message);
this.messageHandlers.onError(data.message);
break;

case 'session_expired':
console.log('Session expired');
this.sessionId = null;
this.messageHandlers.onSessionExpired();
break;

default:
console.warn('Unknown message type:', data.type);
}
}

startSession(agentId, token) {
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
throw new Error('WebSocket not connected');
}

this.socket.send(JSON.stringify({
type: 'start_session',
agentId,
token
}));
}

sendMessage(message, files = []) {
if (!this.sessionId) {
throw new Error('No active session');
}

this.socket.send(JSON.stringify({
type: 'user_message',
sessionId: this.sessionId,
message,
files
}));
}

sendToolResult(toolCallId, result, error) {
if (!this.sessionId) {
throw new Error('No active session');
}

this.socket.send(JSON.stringify({
type: 'tool_execution_result',
sessionId: this.sessionId,
toolCallId,
result: error ? undefined : JSON.stringify(result),
error
}));
}

setEventHandlers(handlers) {
this.messageHandlers = { ...this.messageHandlers, ...handlers };
}

disconnect() {
if (this.socket) {
this.socket.close();
this.socket = null;
this.sessionId = null;
}
}
}

// Usage example
const client = new ConnectAgentClient('wss://api.connect.example.com');

client.setEventHandlers({
onMessage: (message) => {
console.log('Agent says:', message);
document.getElementById('responses').innerHTML += `<p>${message}</p>`;
},

onToolCall: async (toolCall) => {
console.log('Tool call:', toolCall);

try {
const args = JSON.parse(toolCall.function.arguments);
const result = await executeTool(toolCall.function.name, args);

client.sendToolResult(toolCall.id, result);
} catch (error) {
client.sendToolResult(toolCall.id, null, error.message);
}
},

onError: (error) => {
console.error('Error:', error);
document.getElementById('errors').innerHTML = `<p class="error">${error}</p>`;
},

onSessionExpired: () => {
console.log('Session expired, starting new session');
client.startSession('agent-123');
}
});

client.connect();

// When the user wants to start a conversation
document.getElementById('start-button').addEventListener('click', () => {
client.startSession('agent-123');
});

// When the user sends a message
document.getElementById('send-button').addEventListener('click', () => {
const messageInput = document.getElementById('message-input');
const message = messageInput.value.trim();

if (message) {
client.sendMessage(message);
messageInput.value = '';
}
});

// When the user attaches a file
document.getElementById('file-input').addEventListener('change', async (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
const base64Content = e.target.result.split(',')[1];
client.sendMessage('I\'m attaching a file', [
{
name: file.name,
content: base64Content,
type: file.type
}
]);
};
reader.readAsDataURL(file);
}
});

// Tool execution function
async function executeTool(name, args) {
// Implement your tool execution logic here
// This is where you would call your actual tool implementations
console.log(`Executing tool ${name} with args:`, args);

// Example implementation for a search tool
if (name.endsWith('-search')) {
return {
results: [
{ title: 'Result 1', url: 'https://example.com/1' },
{ title: 'Result 2', url: 'https://example.com/2' }
]
};
}

throw new Error(`Unknown tool: ${name}`);
}

Best Practices

  1. Error Handling: Implement robust error handling for WebSocket connections, including reconnection logic.

  2. Message Queuing: If the WebSocket connection is temporarily lost, queue messages to be sent when the connection is restored.

  3. Timeouts: Implement timeouts for tool executions to prevent hanging operations.

  4. User Feedback: Provide clear feedback to users about the connection status and ongoing operations.

  5. Security: Store authentication tokens securely and never expose them in client-side code.

  6. Rate Limiting: Implement rate limiting to prevent abuse of the WebSocket connection.

  7. Testing: Test your integration with different agents and tools to ensure compatibility.

  8. Logging: Implement comprehensive logging to help debug issues in production.

  9. Graceful Degradation: Have a fallback mechanism if WebSockets are not supported or blocked.

  10. Session Management: Handle session expiration gracefully and provide a smooth experience for starting new sessions.

By following this guide, you should be able to successfully integrate Connect agents into your custom software using our WebSocket-based communication system. If you encounter any issues or have questions, please contact our support team.