@devana/ws-tools
v1.0.6
Published
WebSocket client library for Devana Dynamic Tools - Connect any app to Devana AI agents
Downloads
40
Maintainers
Readme
@devana/ws-tools
Bibliothèque JavaScript/TypeScript pour connecter n'importe quelle application à Devana AI via WebSocket et exposer des tools dynamiques que l'agent IA peut utiliser en temps réel.
🌟 Qu'est-ce que @devana/ws-tools ?
Cette bibliothèque permet de :
- Connecter votre application à Devana AI via WebSocket
- Exposer des fonctionnalités de votre app comme des "tools" pour l'IA
- Permettre à l'agent IA d'interagir avec votre application en temps réel
- Supporter multi-sessions et multi-documents avec un seul client
🚀 Installation
npm install @devana/ws-tools
# ou
yarn add @devana/ws-tools
# ou
pnpm add @devana/ws-toolsPour Node.js, installez également :
npm install ws📝 Guide de Démarrage Rapide
1. Créer un Client WebSocket
import { DevanaWSClient } from '@devana/ws-tools';
const client = new DevanaWSClient({
apiUrl: 'https://api.devana.ai',
clientType: 'my-app', // Type de votre application
documentName: 'Document Principal', // Optionnel : nom du document
bearerToken: 'eyJhbGciOi...', // Optionnel : Token JWT (le serveur extrait automatiquement le userId)
metadata: { // Optionnel : métadonnées
version: '1.0.0',
platform: 'web'
},
onConnected: (sessionId) => {
console.log('✅ Connecté avec session:', sessionId);
},
onDisconnected: () => {
console.log('❌ Déconnecté');
},
onError: (error) => {
console.error('⚠️ Erreur:', error);
}
});2. Enregistrer des Tools (Fonctionnalités)
Les tools sont des fonctionnalités que vous exposez à l'agent IA :
// Tool simple
client.registerTool('get_current_time', {
description: 'Obtenir l\'heure actuelle',
schema: {
type: 'object',
properties: {},
required: []
},
handler: async () => {
return new Date().toLocaleTimeString();
}
});
// Tool avec paramètres
client.registerTool('create_document', {
description: 'Créer un nouveau document',
schema: {
type: 'object',
properties: {
title: {
type: 'string',
description: 'Titre du document'
},
content: {
type: 'string',
description: 'Contenu initial'
},
format: {
type: 'string',
enum: ['txt', 'md', 'html', 'json'],
description: 'Format du document'
}
},
required: ['title', 'content']
},
handler: async (data) => {
// Votre logique métier
const doc = await createDocumentInYourApp(data);
return `Document "${data.title}" créé avec succès`;
}
});
// Enregistrer plusieurs tools d'un coup
client.registerTools([
{
name: 'save_file',
description: 'Sauvegarder un fichier',
schema: { /* ... */ },
handler: async (data) => { /* ... */ }
},
{
name: 'delete_file',
description: 'Supprimer un fichier',
schema: { /* ... */ },
handler: async (data) => { /* ... */ }
}
]);3. Se Connecter au Serveur
// Connexion asynchrone
await client.connect();
// Vérifier la connexion
if (client.isConnected()) {
console.log('Session ID:', client.getSessionId());
console.log('Document:', client.getDocumentName());
}4. Utiliser avec l'API Devana
import { callDevanaWithTools } from '@devana/ws-tools';
// Envoyer une requête au LLM avec vos tools
const response = await callDevanaWithTools(
'https://api.devana.ai',
'votre-api-key',
{
agentId: 'devana', // ID de l'agent
message: 'Crée un document test.md', // Message utilisateur
sessionId: client.getSessionId()!, // Session WebSocket
tools: client.getToolsConfig(), // Vos tools
conversationId: 'conv-123', // Optionnel
stream: false // Optionnel
}
);
// L'agent utilisera automatiquement vos tools pour accomplir la tâche !
const result = await response.json();
console.log(result);🎨 Exemples Complets
Exemple 1 : Microsoft Word Add-in
import { DevanaWSClient } from '@devana/ws-tools';
class WordAddIn {
private client: DevanaWSClient;
constructor() {
this.client = new DevanaWSClient({
apiUrl: 'https://api.devana.ai',
clientType: 'word',
documentName: this.getDocumentName(),
onConnected: (sessionId) => {
this.updateUI('connected', sessionId);
}
});
this.registerWordTools();
}
private getDocumentName(): string {
// Obtenir le nom du document Word actuel
return Office.context.document.url || 'Sans titre';
}
private registerWordTools() {
// Ajouter du texte avec style
this.client.registerTool('add_paragraph', {
description: 'Ajouter un paragraphe avec un style spécifique',
schema: {
type: 'object',
properties: {
text: {
type: 'string',
description: 'Texte du paragraphe'
},
style: {
type: 'string',
enum: ['Title', 'Heading1', 'Heading2', 'Heading3', 'Normal', 'Quote'],
description: 'Style du paragraphe'
},
alignment: {
type: 'string',
enum: ['Left', 'Center', 'Right', 'Justified'],
description: 'Alignement du texte'
}
},
required: ['text', 'style']
},
handler: async (data) => {
return await Word.run(async (context) => {
const body = context.document.body;
const paragraph = body.insertParagraph(
data.text,
Word.InsertLocation.end
);
paragraph.style = data.style;
if (data.alignment) {
paragraph.alignment = data.alignment;
}
await context.sync();
return `Paragraphe ajouté avec le style ${data.style}`;
});
}
});
// Insérer un tableau
this.client.registerTool('insert_table', {
description: 'Insérer un tableau dans le document',
schema: {
type: 'object',
properties: {
rows: {
type: 'number',
description: 'Nombre de lignes',
minimum: 1,
maximum: 100
},
columns: {
type: 'number',
description: 'Nombre de colonnes',
minimum: 1,
maximum: 20
},
data: {
type: 'array',
description: 'Données du tableau (optionnel)',
items: {
type: 'array',
items: { type: 'string' }
}
}
},
required: ['rows', 'columns']
},
handler: async (data) => {
return await Word.run(async (context) => {
const body = context.document.body;
const table = body.insertTable(
data.rows,
data.columns,
Word.InsertLocation.end
);
// Remplir avec les données si fournies
if (data.data) {
for (let i = 0; i < data.data.length && i < data.rows; i++) {
for (let j = 0; j < data.data[i].length && j < data.columns; j++) {
table.getCell(i, j).value = data.data[i][j];
}
}
}
await context.sync();
return `Tableau ${data.rows}x${data.columns} inséré`;
});
}
});
// Obtenir les statistiques du document
this.client.registerTool('get_document_stats', {
description: 'Obtenir les statistiques du document',
schema: {
type: 'object',
properties: {},
required: []
},
handler: async () => {
return await Word.run(async (context) => {
const body = context.document.body;
const paragraphs = body.paragraphs;
const tables = body.tables;
body.load('text');
paragraphs.load('items');
tables.load('items');
await context.sync();
const text = body.text;
const words = text.trim().split(/\s+/).filter(w => w.length > 0);
const characters = text.length;
return {
paragraphs: paragraphs.items.length,
tables: tables.items.length,
words: words.length,
characters: characters,
charactersNoSpaces: text.replace(/\s/g, '').length
};
});
}
});
}
async connect() {
await this.client.connect();
}
getClient() {
return this.client;
}
}
// Utilisation
Office.onReady(async () => {
const wordAddIn = new WordAddIn();
await wordAddIn.connect();
// Maintenant l'agent IA peut contrôler Word !
});Exemple 2 : Application IoT / Domotique
import { DevanaWSClient } from '@devana/ws-tools';
class SmartHomeClient {
private client: DevanaWSClient;
private devices: Map<string, any> = new Map();
constructor() {
this.client = new DevanaWSClient({
apiUrl: 'https://api.devana.ai',
clientType: 'smart-home',
bearerToken: 'eyJhbGciOi...',
metadata: {
location: 'Maison principale',
rooms: ['salon', 'cuisine', 'chambres']
}
});
this.registerSmartHomeTools();
}
private registerSmartHomeTools() {
// Contrôle des lumières
this.client.registerTool('control_lights', {
description: 'Contrôler les lumières de la maison',
schema: {
type: 'object',
properties: {
room: {
type: 'string',
enum: ['salon', 'cuisine', 'chambre1', 'chambre2', 'all'],
description: 'Pièce cible'
},
action: {
type: 'string',
enum: ['on', 'off', 'dim'],
description: 'Action à effectuer'
},
brightness: {
type: 'number',
minimum: 0,
maximum: 100,
description: 'Niveau de luminosité (pour dim)'
}
},
required: ['room', 'action']
},
handler: async (data) => {
// Logique de contrôle des lumières
const devices = data.room === 'all'
? this.getAllLights()
: this.getLightsByRoom(data.room);
for (const device of devices) {
switch (data.action) {
case 'on':
await device.turnOn();
break;
case 'off':
await device.turnOff();
break;
case 'dim':
await device.setBrightness(data.brightness || 50);
break;
}
}
return `Lumières ${data.room}: ${data.action}`;
}
});
// Thermostat
this.client.registerTool('set_temperature', {
description: 'Régler la température',
schema: {
type: 'object',
properties: {
zone: {
type: 'string',
enum: ['salon', 'chambres', 'global'],
description: 'Zone de température'
},
temperature: {
type: 'number',
minimum: 15,
maximum: 30,
description: 'Température en Celsius'
},
mode: {
type: 'string',
enum: ['heat', 'cool', 'auto'],
description: 'Mode de climatisation'
}
},
required: ['zone', 'temperature']
},
handler: async (data) => {
const thermostat = this.getThermostat(data.zone);
await thermostat.setTemperature(data.temperature);
if (data.mode) {
await thermostat.setMode(data.mode);
}
return `Température ${data.zone} réglée à ${data.temperature}°C`;
}
});
// Sécurité
this.client.registerTool('security_control', {
description: 'Contrôler le système de sécurité',
schema: {
type: 'object',
properties: {
action: {
type: 'string',
enum: ['arm', 'disarm', 'status', 'lock_doors', 'unlock_doors'],
description: 'Action de sécurité'
},
code: {
type: 'string',
description: 'Code de sécurité (pour arm/disarm)'
}
},
required: ['action']
},
handler: async (data) => {
switch (data.action) {
case 'arm':
return await this.armSecuritySystem(data.code);
case 'disarm':
return await this.disarmSecuritySystem(data.code);
case 'status':
return await this.getSecurityStatus();
case 'lock_doors':
return await this.lockAllDoors();
case 'unlock_doors':
return await this.unlockDoors(data.code);
}
}
});
// Scénarios
this.client.registerTool('run_scenario', {
description: 'Exécuter un scénario prédéfini',
schema: {
type: 'object',
properties: {
scenario: {
type: 'string',
enum: ['morning', 'night', 'away', 'movie', 'party'],
description: 'Scénario à exécuter'
}
},
required: ['scenario']
},
handler: async (data) => {
switch (data.scenario) {
case 'morning':
await this.runMorningRoutine();
break;
case 'night':
await this.runNightRoutine();
break;
case 'away':
await this.runAwayMode();
break;
case 'movie':
await this.runMovieMode();
break;
case 'party':
await this.runPartyMode();
break;
}
return `Scénario "${data.scenario}" activé`;
}
});
}
// Méthodes helper...
private async runMorningRoutine() {
// Ouvrir les volets
// Allumer lumières cuisine
// Régler température à 21°C
// Lancer machine à café
}
// ... autres méthodes
}Exemple 3 : Application de Dessin/Design
import { DevanaWSClient } from '@devana/ws-tools';
class DesignAppClient {
private client: DevanaWSClient;
private canvas: any;
constructor(canvas: any) {
this.canvas = canvas;
this.client = new DevanaWSClient({
apiUrl: 'https://api.devana.ai',
clientType: 'design-app',
documentName: canvas.projectName
});
this.registerDesignTools();
}
private registerDesignTools() {
// Créer des formes
this.client.registerTool('create_shape', {
description: 'Créer une forme géométrique',
schema: {
type: 'object',
properties: {
type: {
type: 'string',
enum: ['rectangle', 'circle', 'triangle', 'polygon', 'star'],
description: 'Type de forme'
},
x: { type: 'number', description: 'Position X' },
y: { type: 'number', description: 'Position Y' },
width: { type: 'number', description: 'Largeur' },
height: { type: 'number', description: 'Hauteur' },
fill: { type: 'string', description: 'Couleur de remplissage' },
stroke: { type: 'string', description: 'Couleur de bordure' },
strokeWidth: { type: 'number', description: 'Épaisseur de bordure' }
},
required: ['type', 'x', 'y', 'width', 'height']
},
handler: async (data) => {
const shape = this.canvas.createShape(data);
return `Forme ${data.type} créée avec ID: ${shape.id}`;
}
});
// Ajouter du texte
this.client.registerTool('add_text', {
description: 'Ajouter du texte sur le canvas',
schema: {
type: 'object',
properties: {
text: { type: 'string', description: 'Texte à afficher' },
x: { type: 'number', description: 'Position X' },
y: { type: 'number', description: 'Position Y' },
font: { type: 'string', description: 'Police' },
size: { type: 'number', description: 'Taille de police' },
color: { type: 'string', description: 'Couleur' },
bold: { type: 'boolean', description: 'Gras' },
italic: { type: 'boolean', description: 'Italique' }
},
required: ['text', 'x', 'y']
},
handler: async (data) => {
const textObj = this.canvas.addText(data);
return `Texte ajouté: "${data.text}"`;
}
});
// Appliquer des filtres
this.client.registerTool('apply_filter', {
description: 'Appliquer un filtre à un élément ou au canvas',
schema: {
type: 'object',
properties: {
target: {
type: 'string',
description: 'ID de l\'élément ou "canvas" pour tout'
},
filter: {
type: 'string',
enum: ['blur', 'brightness', 'contrast', 'grayscale', 'sepia', 'saturate'],
description: 'Type de filtre'
},
value: {
type: 'number',
description: 'Valeur du filtre (0-100)'
}
},
required: ['filter', 'value']
},
handler: async (data) => {
if (data.target === 'canvas') {
this.canvas.applyGlobalFilter(data.filter, data.value);
} else {
const element = this.canvas.getElementById(data.target);
element.applyFilter(data.filter, data.value);
}
return `Filtre ${data.filter} appliqué`;
}
});
}
}📊 Architecture Multi-Sessions
Le système supporte plusieurs sessions simultanées pour un même utilisateur :
// Client 1 : Document Word
const wordClient = new DevanaWSClient({
apiUrl: 'https://api.devana.ai',
clientType: 'word',
documentName: 'Rapport Q4 2024',
bearerToken: 'eyJhbGciOi...'
});
// Client 2 : Document Excel
const excelClient = new DevanaWSClient({
apiUrl: 'https://api.devana.ai',
clientType: 'excel',
documentName: 'Budget 2024',
bearerToken: 'eyJhbGciOi...' // Même utilisateur
});
// Client 3 : Application IoT
const iotClient = new DevanaWSClient({
apiUrl: 'https://api.devana.ai',
clientType: 'smart-home',
bearerToken: 'eyJhbGciOi...' // Même utilisateur
});
// L'agent IA peut interagir avec les 3 en même temps !🔧 Configuration Avancée
Options de Reconnexion
const client = new DevanaWSClient({
apiUrl: 'https://api.devana.ai',
clientType: 'my-app',
// Reconnexion automatique
autoReconnect: true, // Par défaut: true
maxReconnectAttempts: 10, // Par défaut: 5
reconnectDelay: 5000, // Par défaut: 3000ms
// Callbacks
onReconnected: (sessionId) => {
console.log('Reconnecté avec nouvelle session:', sessionId);
// Réenregistrer l'état si nécessaire
}
});Gestion des Erreurs
client.registerTool('risky_operation', {
description: 'Opération qui peut échouer',
schema: { /* ... */ },
handler: async (data) => {
try {
// Opération risquée
const result = await dangerousOperation(data);
return result;
} catch (error) {
// L'erreur sera transmise à l'agent IA
throw new Error(`Échec de l'opération: ${error.message}`);
}
}
});Heartbeat / Keep-Alive
La bibliothèque gère automatiquement un heartbeat (ping/pong) toutes les 30 secondes pour maintenir la connexion active.
📚 API Reference Complète
DevanaWSClient
Constructor Options
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| apiUrl | string | URL de l'API Devana | Required |
| clientType | string | Type de client (word, excel, custom...) | Required |
| documentName | string | Nom du document ou contexte | undefined |
| bearerToken | string | Token JWT pour authentification (le serveur extrait automatiquement le userId) | undefined |
| metadata | object | Métadonnées supplémentaires | {} |
| autoReconnect | boolean | Reconnexion automatique si déconnecté | true |
| maxReconnectAttempts | number | Nombre max de tentatives | 5 |
| reconnectDelay | number | Délai entre tentatives (ms) | 3000 |
| onConnected | (sessionId: string) => void | Callback connexion réussie | () => {} |
| onDisconnected | () => void | Callback déconnexion | () => {} |
| onReconnected | (sessionId: string) => void | Callback reconnexion | () => {} |
| onError | (error: Error) => void | Callback erreur | () => {} |
| onSessionsChanged | () => void | Callback changement de sessions | undefined |
Methods
registerTool(name: string, config: ToolDefinition)
Enregistre un tool avec son handler.
client.registerTool('my_tool', {
description: 'Description du tool',
schema: { /* JSON Schema */ },
handler: async (data) => { /* ... */ }
});registerTools(tools: ToolDefinition[])
Enregistre plusieurs tools en une fois.
getToolsConfig(): ToolConfig[]
Retourne la configuration des tools (sans les handlers) pour envoyer au serveur.
connect(): Promise<string>
Se connecte au serveur WebSocket. Retourne le sessionId.
getSessionId(): string | null
Retourne l'ID de session actuel.
getDocumentName(): string | undefined
Retourne le nom du document associé.
isConnected(): boolean
Vérifie si le client est connecté.
disconnect(): void
Déconnecte proprement le client.
callDevanaWithTools()
Helper pour envoyer une requête à l'API Devana avec support des tools dynamiques.
async function callDevanaWithTools(
apiUrl: string,
apiKey: string,
options: {
agentId: string; // ID de l'agent IA
message: string; // Message utilisateur
sessionId: string; // Session WebSocket
tools: ToolConfig[]; // Configuration des tools
conversationId?: string; // ID de conversation (optionnel)
stream?: boolean; // Mode streaming (optionnel)
}
): Promise<Response>Types TypeScript
interface ToolSchema {
type: "object";
properties: Record<string, {
type: string;
description?: string;
enum?: string[];
minimum?: number;
maximum?: number;
items?: any;
}>;
required?: string[];
}
interface ToolConfig {
name: string;
description: string;
schema: ToolSchema;
}
interface ToolDefinition extends ToolConfig {
handler: (data: Record<string, any>) => Promise<any>;
}
interface DevanaWSClientOptions {
apiUrl: string;
clientType: string;
documentName?: string;
bearerToken?: string; // Token JWT pour authentification
metadata?: Record<string, any>;
autoReconnect?: boolean;
maxReconnectAttempts?: number;
reconnectDelay?: number;
onConnected?: (sessionId: string) => void;
onDisconnected?: () => void;
onReconnected?: (sessionId: string) => void;
onError?: (error: Error) => void;
onSessionsChanged?: () => void;
}🔒 Sécurité
Authentification
- Les sessions WebSocket sont temporaires et uniques
- Chaque session a un ID unique généré côté client
- Le serveur valide les permissions via JWT
Isolation
- Chaque session est isolée
- Pas d'accès cross-session
- Les tools sont limités à leur session
Validation
- Les schémas sont validés côté serveur avec Zod
- Timeout de 30 secondes par exécution de tool
- Rate limiting sur les appels
🐛 Debugging
Activer les logs détaillés :
// Les logs sont automatiquement affichés dans la console
// Format: [DevanaWSClient] Message
// Exemples de logs:
// [DevanaWSClient] ✅ Registered
// [DevanaWSClient] 📥 add_paragraph
// [DevanaWSClient] ▶ add_paragraph
// [DevanaWSClient] ✅ add_paragraph done
// [DevanaWSClient] 📤 Response sent (OK)🚀 Déploiement
Browser (Web)
<script src="https://unpkg.com/@devana/ws-tools"></script>
<script>
const client = new DevanaWSTools.DevanaWSClient({
apiUrl: 'https://api.devana.ai',
clientType: 'web-app'
});
</script>Node.js
import { DevanaWSClient } from '@devana/ws-tools';
// ou
const { DevanaWSClient } = require('@devana/ws-tools');React/Vue/Angular
// Dans un composant React
import { useEffect, useState } from 'react';
import { DevanaWSClient } from '@devana/ws-tools';
function MyComponent() {
const [client, setClient] = useState<DevanaWSClient | null>(null);
useEffect(() => {
const wsClient = new DevanaWSClient({
apiUrl: process.env.REACT_APP_DEVANA_API,
clientType: 'react-app'
});
wsClient.registerTool(/* ... */);
wsClient.connect();
setClient(wsClient);
return () => {
wsClient.disconnect();
};
}, []);
// ...
}📖 Documentation & Ressources
- Architecture Détaillée : DYNAMIC_TOOLS_SYSTEM.md
- Guide Migration : MIGRATION_GUIDE.md
- Examples : /examples
- API Docs : https://docs.devana.ai
🤝 Support & Communauté
- GitHub Issues : github.com/Scriptor-Group/devana.ai/issues
- Discord : discord.gg/devana
- Email : [email protected]
🔄 Changelog
v1.0.0 (2024-01)
- Version initiale
- Support WebSocket avec auto-reconnect
- Système de tools dynamiques
- Support multi-sessions
- Heartbeat automatique
- Support Browser et Node.js
📄 License
MIT © Devana.ai
Fait avec ❤️ par l'équipe Devana.ai
