@lvx74/openrrouter-ai-agent 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +373 -0
- package/package.json +82 -0
- package/src/Agent.js +495 -0
- package/src/Tool.js +94 -0
- package/src/cli.js +155 -0
- package/src/index.js +59 -0
package/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 AI Agent Toolkit
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
@@ -0,0 +1,373 @@
|
|
1
|
+
# AI Agent Toolkit
|
2
|
+
|
3
|
+
Un toolkit completo per creare agenti AI con tool calling, compatibile con l'interfaccia `@openai/agents`. **Funziona principalmente con OpenRouter** per accesso a modelli multipli con una singola API key.
|
4
|
+
|
5
|
+
## 🚀 Caratteristiche
|
6
|
+
|
7
|
+
- **Compatibile con @openai/agents**: Segue l'interfaccia standard per agenti AI
|
8
|
+
- **OpenRouter Ready**: Ottimizzato per OpenRouter con supporto per modelli multipli
|
9
|
+
- **Tool System Avanzato**: Supporto per tools con validazione Zod
|
10
|
+
- **Chat Interattiva**: Interfacce ready-to-use per console
|
11
|
+
- **NPM Package**: Utilizzabile come dipendenza in altri progetti
|
12
|
+
- **Debug & Verbose Mode**: Modalità dettagliate per sviluppo
|
13
|
+
- **Gestione Errori**: Gestione robusta degli errori nei tool calls
|
14
|
+
|
15
|
+
## 📦 Installazione
|
16
|
+
|
17
|
+
### Come pacchetto npm
|
18
|
+
```bash
|
19
|
+
npm install ai-agent-toolkit
|
20
|
+
```
|
21
|
+
|
22
|
+
### Sviluppo locale
|
23
|
+
```bash
|
24
|
+
# Clona il repository
|
25
|
+
git clone <repo-url>
|
26
|
+
cd simpleAgent
|
27
|
+
|
28
|
+
# Installa le dipendenze
|
29
|
+
npm install
|
30
|
+
|
31
|
+
# Configura le variabili d'ambiente
|
32
|
+
cp .env.example .env
|
33
|
+
# Modifica .env con la tua OpenRouter API key
|
34
|
+
```
|
35
|
+
|
36
|
+
## 🔧 Configurazione
|
37
|
+
|
38
|
+
### OpenRouter (Raccomandato)
|
39
|
+
```env
|
40
|
+
OPENROUTER_API_KEY=your_openrouter_api_key_here
|
41
|
+
```
|
42
|
+
|
43
|
+
### OpenAI (Alternativa)
|
44
|
+
```env
|
45
|
+
OPENAI_API_KEY=your_openai_api_key_here
|
46
|
+
```
|
47
|
+
|
48
|
+
> **💡 Perché OpenRouter?**
|
49
|
+
> - Accesso a modelli multipli (GPT-4, Claude, Gemini, etc.) con una singola API key
|
50
|
+
> - Costi ridotti rispetto alle API dirette
|
51
|
+
> - Stesso formato API di OpenAI
|
52
|
+
> - Failover automatico tra modelli
|
53
|
+
|
54
|
+
## 🔧 Uso Base
|
55
|
+
|
56
|
+
### Importa le classi principali
|
57
|
+
|
58
|
+
```javascript
|
59
|
+
import { Agent, Tool } from 'ai-agent-toolkit'
|
60
|
+
|
61
|
+
// Crea un tool personalizzato
|
62
|
+
const myTool = new Tool({
|
63
|
+
name: 'get_time',
|
64
|
+
description: 'Ottiene l\'ora attuale',
|
65
|
+
parameters: {
|
66
|
+
type: 'object',
|
67
|
+
properties: {
|
68
|
+
timezone: { type: 'string', description: 'Timezone (opzionale)' }
|
69
|
+
}
|
70
|
+
},
|
71
|
+
handler: async ({ timezone = 'UTC' }) => {
|
72
|
+
return new Date().toLocaleString('it-IT', { timeZone: timezone })
|
73
|
+
}
|
74
|
+
})
|
75
|
+
|
76
|
+
// Crea l'agent
|
77
|
+
const agent = new Agent({
|
78
|
+
model: 'qwen/qwen3-coder:free', // Modello gratuito su OpenRouter
|
79
|
+
apiKey: process.env.OPENROUTER_API_KEY,
|
80
|
+
instructions: 'Sei un assistente utile.',
|
81
|
+
tools: [myTool]
|
82
|
+
})
|
83
|
+
|
84
|
+
// Usa l'agent
|
85
|
+
const response = await agent.run('Che ore sono?')
|
86
|
+
console.log(response.content)
|
87
|
+
```
|
88
|
+
|
89
|
+
### Chat interattiva con utilities
|
90
|
+
|
91
|
+
```javascript
|
92
|
+
import { Agent, createChatInterface } from 'ai-agent-toolkit'
|
93
|
+
|
94
|
+
const agent = new Agent({
|
95
|
+
model: 'anthropic/claude-3-haiku', // Claude via OpenRouter
|
96
|
+
apiKey: process.env.OPENROUTER_API_KEY,
|
97
|
+
instructions: 'Sei un assistente AI utile e amichevole.',
|
98
|
+
verbose: true
|
99
|
+
})
|
100
|
+
|
101
|
+
// Avvia chat interattiva
|
102
|
+
createChatInterface(agent, {
|
103
|
+
welcomeMessage: '🤖 Ciao! Come posso aiutarti?',
|
104
|
+
prompt: 'Tu: '
|
105
|
+
})
|
106
|
+
```
|
107
|
+
|
108
|
+
## 🖥️ Modalità d'uso: CLI, API server e Web GUI
|
109
|
+
|
110
|
+
Puoi usare questo toolkit in tre modi principali:
|
111
|
+
|
112
|
+
### 1. Interfaccia CLI (console)
|
113
|
+
|
114
|
+
Esegui la chat interattiva direttamente da terminale:
|
115
|
+
|
116
|
+
```bash
|
117
|
+
node example/basic.js
|
118
|
+
```
|
119
|
+
|
120
|
+
- Chatta con l'agente AI e prova i tool direttamente da console
|
121
|
+
- Ideale per sviluppo, debug e test rapidi
|
122
|
+
|
123
|
+
### 2. API server compatibile OpenAI/OpenRouter
|
124
|
+
|
125
|
+
Avvia il server API compatibile OpenAI (con tool calling):
|
126
|
+
|
127
|
+
```bash
|
128
|
+
node example/server.js
|
129
|
+
```
|
130
|
+
|
131
|
+
- Espone endpoint `/v1/chat/completions` e `/v1/models` compatibili con OpenAI
|
132
|
+
- Puoi collegare qualsiasi client OpenAI, plugin, o frontend custom
|
133
|
+
|
134
|
+
### 3. Web GUI (OpenWebUI)
|
135
|
+
|
136
|
+
Collega il server a una web UI moderna come [OpenWebUI](https://github.com/open-webui/open-webui):
|
137
|
+
|
138
|
+
- Interfaccia grafica user-friendly
|
139
|
+
- Supporta tool calling, history, modelli multipli
|
140
|
+
- **Guida dettagliata**: [openwebui.md](./openwebui.md)
|
141
|
+
|
142
|
+
> Consulta il file [`openwebui.md`](./openwebui.md) per istruzioni dettagliate su come collegare OpenWebUI al server e sfruttare tutte le funzionalità avanzate.
|
143
|
+
|
144
|
+
## 🎯 Modelli Supportati
|
145
|
+
|
146
|
+
Tramite **OpenRouter**, puoi usare tutti questi modelli:
|
147
|
+
|
148
|
+
### Gratuiti
|
149
|
+
- `qwen/qwen3-coder:free` - Ottimo per coding
|
150
|
+
- `microsoft/phi-3-mini-128k-instruct:free` - Veloce e leggero
|
151
|
+
- `mistralai/mistral-7b-instruct:free` - Bilanciato
|
152
|
+
|
153
|
+
### A Pagamento (Costi Ridotti)
|
154
|
+
- `anthropic/claude-3-haiku` - Veloce ed economico
|
155
|
+
- `openai/gpt-4o-mini` - GPT-4 economico
|
156
|
+
- `google/gemini-pro` - Google Gemini
|
157
|
+
- `anthropic/claude-3-sonnet` - Claude bilanciato
|
158
|
+
- `openai/gpt-4` - GPT-4 completo
|
159
|
+
|
160
|
+
### Enterprise
|
161
|
+
- `anthropic/claude-3-opus` - Claude più potente
|
162
|
+
- `openai/gpt-4-turbo` - GPT-4 avanzato
|
163
|
+
|
164
|
+
## 🛠 Tools Disponibili
|
165
|
+
|
166
|
+
### get_weather
|
167
|
+
Restituisce informazioni meteo per una città specifica.
|
168
|
+
|
169
|
+
**Parametri:**
|
170
|
+
- `city` (string): Nome della città
|
171
|
+
- `unit` (string, opzionale): Unità di temperatura ('celsius' o 'fahrenheit')
|
172
|
+
|
173
|
+
**Esempio:**
|
174
|
+
```
|
175
|
+
Che tempo fa a Roma?
|
176
|
+
```
|
177
|
+
|
178
|
+
### echo
|
179
|
+
Ripete il testo fornito dall'utente.
|
180
|
+
|
181
|
+
**Parametri:**
|
182
|
+
- `text` (string): Testo da ripetere
|
183
|
+
- `repeat_count` (number, opzionale): Numero di ripetizioni
|
184
|
+
|
185
|
+
**Esempio:**
|
186
|
+
```
|
187
|
+
Ripeti "Ciao mondo" 3 volte
|
188
|
+
```
|
189
|
+
|
190
|
+
### calculate
|
191
|
+
Esegue calcoli matematici semplici.
|
192
|
+
|
193
|
+
**Parametri:**
|
194
|
+
- `operation` (string): Operazione ('add', 'subtract', 'multiply', 'divide')
|
195
|
+
- `a` (number): Primo numero
|
196
|
+
- `b` (number): Secondo numero
|
197
|
+
|
198
|
+
**Esempio:**
|
199
|
+
```
|
200
|
+
Calcola 15 + 25
|
201
|
+
```
|
202
|
+
|
203
|
+
## 🎮 Comandi della Chat
|
204
|
+
|
205
|
+
- `exit` / `quit` - Esci dalla chat
|
206
|
+
- `reset` - Resetta la conversazione
|
207
|
+
- `history` - Mostra cronologia messaggi
|
208
|
+
- `tools` - Lista dei tools disponibili
|
209
|
+
- `debug on/off` - Attiva/disattiva modalità debug
|
210
|
+
- `help` - Mostra l'aiuto
|
211
|
+
|
212
|
+
## 🏗 Architettura
|
213
|
+
|
214
|
+
### Struttura del Progetto
|
215
|
+
|
216
|
+
```
|
217
|
+
simpleAgent/
|
218
|
+
├── agent.js # Chat interattiva principale
|
219
|
+
├── AgentClass.js # Classe Agent compatibile @openai/agents
|
220
|
+
├── Tool.js # Classe base per tools
|
221
|
+
├── functions.js # Definizione dei tools disponibili
|
222
|
+
├── package.json # Configurazione npm
|
223
|
+
└── README.md # Documentazione
|
224
|
+
```
|
225
|
+
|
226
|
+
### Classe Agent
|
227
|
+
|
228
|
+
La classe `Agent` è compatibile con l'interfaccia `@openai/agents` e supporta:
|
229
|
+
|
230
|
+
```javascript
|
231
|
+
// Inizializzazione con options object
|
232
|
+
const agent = new Agent({
|
233
|
+
model: 'anthropic/claude-3-haiku', // Qualsiasi modello OpenRouter
|
234
|
+
apiKey: process.env.OPENROUTER_API_KEY,
|
235
|
+
instructions: 'Sei un assistente AI...',
|
236
|
+
tools: availableTools,
|
237
|
+
temperature: 0.7,
|
238
|
+
maxIterations: 10,
|
239
|
+
debug: false
|
240
|
+
})
|
241
|
+
|
242
|
+
// Metodi principali
|
243
|
+
await agent.run(message) // Esegue una conversazione completa
|
244
|
+
await agent.step() // Esegue un singolo step
|
245
|
+
agent.reset() // Resetta la conversazione
|
246
|
+
agent.getHistory() // Ottiene la cronologia
|
247
|
+
agent.addTool(tool) // Aggiunge un tool
|
248
|
+
agent.setDebug(true) // Attiva debug
|
249
|
+
```
|
250
|
+
|
251
|
+
### Classe Tool
|
252
|
+
|
253
|
+
La classe `Tool` supporta sia schema Zod che JSON Schema tradizionali:
|
254
|
+
|
255
|
+
```javascript
|
256
|
+
// Con schema Zod
|
257
|
+
const weatherTool = new Tool({
|
258
|
+
name: 'get_weather',
|
259
|
+
description: 'Restituisce il meteo per una città',
|
260
|
+
schema: z.object({
|
261
|
+
city: z.string().describe('Nome della città'),
|
262
|
+
unit: z.enum(['celsius', 'fahrenheit']).optional()
|
263
|
+
}),
|
264
|
+
handler: async ({ city, unit }) => {
|
265
|
+
// Implementazione del tool
|
266
|
+
}
|
267
|
+
})
|
268
|
+
|
269
|
+
// Con JSON Schema
|
270
|
+
const echoTool = new Tool({
|
271
|
+
name: 'echo',
|
272
|
+
description: 'Ripete un testo',
|
273
|
+
parameters: {
|
274
|
+
type: 'object',
|
275
|
+
properties: {
|
276
|
+
text: { type: 'string', description: 'Testo da ripetere' }
|
277
|
+
},
|
278
|
+
required: ['text']
|
279
|
+
},
|
280
|
+
handler: async ({ text }) => {
|
281
|
+
return `Echo: ${text}`
|
282
|
+
}
|
283
|
+
})
|
284
|
+
```
|
285
|
+
|
286
|
+
## 🔄 Aggiungere Nuovi Tools
|
287
|
+
|
288
|
+
1. **Crea il tool:**
|
289
|
+
|
290
|
+
```javascript
|
291
|
+
export const myTool = new Tool({
|
292
|
+
name: 'my_tool',
|
293
|
+
description: 'Descrizione del mio tool',
|
294
|
+
schema: z.object({
|
295
|
+
param1: z.string().describe('Descrizione parametro'),
|
296
|
+
param2: z.number().optional()
|
297
|
+
}),
|
298
|
+
handler: async ({ param1, param2 }) => {
|
299
|
+
// La tua logica qui
|
300
|
+
return 'Risultato del tool'
|
301
|
+
}
|
302
|
+
})
|
303
|
+
|
304
|
+
// Aggiungi all'array dei tools disponibili
|
305
|
+
export const availableTools = [weatherTool, echoTool, calculateTool, myTool]
|
306
|
+
```
|
307
|
+
|
308
|
+
2. **Il tool sarà automaticamente disponibile nella chat!**
|
309
|
+
|
310
|
+
## 🔗 Setup OpenRouter
|
311
|
+
|
312
|
+
1. **Registrati su [OpenRouter.ai](https://openrouter.ai)**
|
313
|
+
2. **Ottieni la tua API key** dal dashboard
|
314
|
+
3. **Aggiungi crediti** (anche solo $5 durano mesi)
|
315
|
+
4. **Usa qualsiasi modello** con la stessa API key
|
316
|
+
|
317
|
+
### Vantaggi di OpenRouter:
|
318
|
+
- ✅ **Una sola API key** per tutti i modelli
|
319
|
+
- ✅ **Costi ridotti** fino al 50% rispetto alle API dirette
|
320
|
+
- ✅ **Modelli gratuiti** disponibili per testing
|
321
|
+
- ✅ **Failover automatico** se un modello non è disponibile
|
322
|
+
- ✅ **Stesso formato** delle API OpenAI
|
323
|
+
- ✅ **Rate limiting** migliore
|
324
|
+
|
325
|
+
## 🐛 Debug
|
326
|
+
|
327
|
+
Attiva la modalità debug per vedere i dettagli delle chiamate API:
|
328
|
+
|
329
|
+
```bash
|
330
|
+
# Nella chat, digita:
|
331
|
+
debug on
|
332
|
+
```
|
333
|
+
|
334
|
+
Questo mostrerà:
|
335
|
+
- Risposte complete del modello
|
336
|
+
- Tool calls con parametri
|
337
|
+
- Risultati dei tools
|
338
|
+
- Numero di iterazioni
|
339
|
+
|
340
|
+
## 📚 Dipendenze
|
341
|
+
|
342
|
+
- `openai` - Client OpenAI (compatibile con OpenRouter)
|
343
|
+
- `dotenv` - Gestione variabili d'ambiente
|
344
|
+
- `zod` - Validazione schema e type safety
|
345
|
+
- `@openai/agents` - Interfaccia standard per agenti AI
|
346
|
+
|
347
|
+
## 🌐 API Supportate
|
348
|
+
|
349
|
+
### OpenRouter (Raccomandato)
|
350
|
+
- **URL**: `https://openrouter.ai/api/v1`
|
351
|
+
- **Modelli**: 50+ modelli disponibili
|
352
|
+
- **Costi**: Ridotti fino al 50%
|
353
|
+
- **Setup**: Una sola API key
|
354
|
+
|
355
|
+
### OpenAI (Supporto diretto)
|
356
|
+
- **URL**: `https://api.openai.com/v1`
|
357
|
+
- **Modelli**: GPT-3.5, GPT-4 serie
|
358
|
+
- **Costi**: Listino OpenAI standard
|
359
|
+
|
360
|
+
### Altri Provider
|
361
|
+
Il toolkit è compatibile con qualsiasi API che segue il formato OpenAI.
|
362
|
+
|
363
|
+
## 🤝 Contribuire
|
364
|
+
|
365
|
+
1. Fork del repository
|
366
|
+
2. Crea un branch per la tua feature
|
367
|
+
3. Commit delle modifiche
|
368
|
+
4. Push al branch
|
369
|
+
5. Apri una Pull Request
|
370
|
+
|
371
|
+
## 📄 Licenza
|
372
|
+
|
373
|
+
ISC License - vedi file LICENSE per dettagli.
|
package/package.json
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
{
|
2
|
+
"name": "@lvx74/openrrouter-ai-agent",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "A powerful AI agent toolkit compatible with @openai/agents for building conversational AI with tool calling support using OpenRouter",
|
5
|
+
"type": "module",
|
6
|
+
"main": "src/index.js",
|
7
|
+
"exports": {
|
8
|
+
".": {
|
9
|
+
"import": "./src/index.js",
|
10
|
+
"types": "./src/index.d.ts"
|
11
|
+
},
|
12
|
+
"./Agent": {
|
13
|
+
"import": "./src/Agent.js",
|
14
|
+
"types": "./src/Agent.d.ts"
|
15
|
+
},
|
16
|
+
"./Tool": {
|
17
|
+
"import": "./src/Tool.js",
|
18
|
+
"types": "./src/Tool.d.ts"
|
19
|
+
}
|
20
|
+
},
|
21
|
+
"files": [
|
22
|
+
"src/",
|
23
|
+
"README.md",
|
24
|
+
"LICENSE"
|
25
|
+
],
|
26
|
+
"scripts": {
|
27
|
+
"start": "node example/index.js",
|
28
|
+
"dev": "node example/index.js",
|
29
|
+
"example": "node example/index.js",
|
30
|
+
"example:basic": "node example/basic.js",
|
31
|
+
"server": "node example/server.js",
|
32
|
+
"server:dev": "node --watch example/server.js",
|
33
|
+
"test": "node example/test.js",
|
34
|
+
"build": "echo \"No build step needed for pure ESM package\"",
|
35
|
+
"prepublishOnly": "npm test"
|
36
|
+
},
|
37
|
+
"keywords": [
|
38
|
+
"ai",
|
39
|
+
"agent",
|
40
|
+
"openai",
|
41
|
+
"openwebui",
|
42
|
+
"webserver",
|
43
|
+
"chatbot",
|
44
|
+
"tools",
|
45
|
+
"conversational-ai",
|
46
|
+
"llm",
|
47
|
+
"assistant",
|
48
|
+
"chat",
|
49
|
+
"openrouter"
|
50
|
+
],
|
51
|
+
"author": {
|
52
|
+
"name": "Luca Saggese",
|
53
|
+
"email": "luca.saggese@gmail.com",
|
54
|
+
"url": "https://github.com/luca-saggese"
|
55
|
+
},
|
56
|
+
"license": "MIT",
|
57
|
+
"repository": {
|
58
|
+
"type": "git",
|
59
|
+
"url": "git+https://github.com/luca-saggese/ai-agent-toolkit.git"
|
60
|
+
},
|
61
|
+
"bugs": {
|
62
|
+
"url": "https://github.com/luca-saggese/ai-agent-toolkit/issues"
|
63
|
+
},
|
64
|
+
"homepage": "https://github.com/luca-saggese/ai-agent-toolkit#readme",
|
65
|
+
"engines": {
|
66
|
+
"node": ">=16.0.0"
|
67
|
+
},
|
68
|
+
"peerDependencies": {
|
69
|
+
"dotenv": "^16.0.0 || ^17.0.0"
|
70
|
+
},
|
71
|
+
"dependencies": {
|
72
|
+
"openai": "^5.10.2",
|
73
|
+
"zod": "^3.25.67",
|
74
|
+
"express": "^4.18.2",
|
75
|
+
"cors": "^2.8.5",
|
76
|
+
"express-rate-limit": "^7.1.5"
|
77
|
+
},
|
78
|
+
"devDependencies": {
|
79
|
+
"@openai/agents": "^0.0.13",
|
80
|
+
"dotenv": "^17.2.1"
|
81
|
+
}
|
82
|
+
}
|
package/src/Agent.js
ADDED
@@ -0,0 +1,495 @@
|
|
1
|
+
import 'dotenv/config'
|
2
|
+
import OpenAI from 'openai'
|
3
|
+
import { EventEmitter } from 'events'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Classe Agent compatibile con @openai/agents per gestire conversazioni con AI e tool calls
|
7
|
+
* Supporta eventi per streaming di tutti i messaggi (user, assistant, tool, tool_calls)
|
8
|
+
*/
|
9
|
+
export class Agent extends EventEmitter {
|
10
|
+
constructor(options = {}) {
|
11
|
+
super() // Inizializza EventEmitter
|
12
|
+
|
13
|
+
// Supporto per sia il formato nuovo che quello vecchio
|
14
|
+
if (typeof options === 'string') {
|
15
|
+
// Formato vecchio: Agent(apiKey, model, systemPrompt, tools)
|
16
|
+
this.openai = new OpenAI({
|
17
|
+
apiKey: arguments[0],
|
18
|
+
baseURL: 'https://openrouter.ai/api/v1',
|
19
|
+
})
|
20
|
+
this.model = arguments[1]
|
21
|
+
this.systemPrompt = arguments[2] || ''
|
22
|
+
this.tools = arguments[3] || []
|
23
|
+
} else {
|
24
|
+
// Formato nuovo: Agent({ model, tools, instructions, apiKey, ... })
|
25
|
+
this.openai = new OpenAI({
|
26
|
+
apiKey: options.apiKey || process.env.OPENROUTER_API_KEY,
|
27
|
+
baseURL: options.baseURL || 'https://openrouter.ai/api/v1',
|
28
|
+
})
|
29
|
+
this.model = options.model || 'qwen/qwen3-coder:free'
|
30
|
+
this.systemPrompt = (options.instructions || options.systemPrompt || '') + `\n\nALWAYS CALL ONLY 1 tool at a time.\n`
|
31
|
+
this.tools = options.tools || []
|
32
|
+
}
|
33
|
+
|
34
|
+
this.messages = options.messages || []
|
35
|
+
|
36
|
+
// Mappa dei tools per accesso rapido
|
37
|
+
this.toolMap = new Map()
|
38
|
+
this.tools.forEach(tool => {
|
39
|
+
this.toolMap.set(tool.name, tool)
|
40
|
+
})
|
41
|
+
|
42
|
+
// Inizializza con il system prompt se fornito
|
43
|
+
if (this.systemPrompt) {
|
44
|
+
this.messages.push({ role: 'system', content: this.systemPrompt })
|
45
|
+
}
|
46
|
+
|
47
|
+
// Configurazioni aggiuntive
|
48
|
+
this.maxIterations = options.maxIterations || 10
|
49
|
+
this.temperature = options.temperature || 0.7
|
50
|
+
this.debug = options.debug || true
|
51
|
+
this.verbose = options.verbose !== undefined ? options.verbose : true // Default attivo per mostrare thoughts
|
52
|
+
}
|
53
|
+
|
54
|
+
/**
|
55
|
+
* Emette un evento per il messaggio specificato
|
56
|
+
*/
|
57
|
+
_emitMessage(message, eventType = 'message') {
|
58
|
+
this.emit(eventType, message)
|
59
|
+
this.emit('message', { ...message, eventType })
|
60
|
+
}
|
61
|
+
|
62
|
+
/**
|
63
|
+
* Aggiunge un messaggio utente alla conversazione
|
64
|
+
*/
|
65
|
+
addMessage(content, role = 'user') {
|
66
|
+
const message = { role, content }
|
67
|
+
this.messages.push(message)
|
68
|
+
|
69
|
+
// Emetti evento per il messaggio utente
|
70
|
+
this._emitMessage(message, 'user_message')
|
71
|
+
}
|
72
|
+
|
73
|
+
/**
|
74
|
+
* Alias per compatibilità
|
75
|
+
*/
|
76
|
+
addUserMessage(content) {
|
77
|
+
this.addMessage(content, 'user')
|
78
|
+
}
|
79
|
+
|
80
|
+
/**
|
81
|
+
* Ottiene le definizioni dei tools per OpenAI
|
82
|
+
*/
|
83
|
+
getToolDefinitions() {
|
84
|
+
return this.tools.map(tool => tool.getDefinition())
|
85
|
+
}
|
86
|
+
|
87
|
+
/**
|
88
|
+
* Esegue una singola iterazione della conversazione
|
89
|
+
*/
|
90
|
+
async step() {
|
91
|
+
const toolDefinitions = this.getToolDefinitions()
|
92
|
+
|
93
|
+
const res = await this.openai.chat.completions.create({
|
94
|
+
model: this.model,
|
95
|
+
messages: this.messages,
|
96
|
+
tools: toolDefinitions.length > 0 ? toolDefinitions : undefined,
|
97
|
+
tool_choice: toolDefinitions.length > 0 ? 'auto' : undefined,
|
98
|
+
temperature: this.temperature
|
99
|
+
})
|
100
|
+
|
101
|
+
const msg = res.choices[0].message
|
102
|
+
|
103
|
+
if (this.debug) {
|
104
|
+
console.log('🤖 Risposta del modello:', JSON.stringify(res, null, 2))
|
105
|
+
}
|
106
|
+
|
107
|
+
if (!msg.tool_calls) {
|
108
|
+
// Risposta finale
|
109
|
+
this.messages.push(msg)
|
110
|
+
|
111
|
+
// Emetti evento per la risposta assistant
|
112
|
+
this._emitMessage(msg, 'assistant_message')
|
113
|
+
|
114
|
+
// Log del pensiero finale dell'AI (solo se verbose)
|
115
|
+
if (this.verbose) {
|
116
|
+
console.log('💭 Thought (Pensiero finale):')
|
117
|
+
console.log(` "${msg.content}"`)
|
118
|
+
}
|
119
|
+
|
120
|
+
return {
|
121
|
+
type: 'response',
|
122
|
+
content: msg.content,
|
123
|
+
role: 'assistant'
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
// Log del reasoning dell'AI prima di chiamare i tools (solo se verbose)
|
128
|
+
if (this.verbose) {
|
129
|
+
if (msg.content) {
|
130
|
+
console.log('🧠 AI Reasoning (Ragionamento):')
|
131
|
+
console.log(` "${msg.content}"`)
|
132
|
+
}
|
133
|
+
|
134
|
+
console.log('🛠 Tool calls da eseguire:')
|
135
|
+
for (const call of msg.tool_calls) {
|
136
|
+
console.log(`🔧 Lanciando tool: ${call.function.name}`)
|
137
|
+
console.log(` 📋 Parametri: ${call.function.arguments}`)
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
// Aggiungi il messaggio assistant con le tool_calls
|
142
|
+
const assistantMessage = {
|
143
|
+
role: 'assistant',
|
144
|
+
content: null,
|
145
|
+
tool_calls: msg.tool_calls
|
146
|
+
}
|
147
|
+
this.messages.push(assistantMessage)
|
148
|
+
|
149
|
+
// Emetti evento per il messaggio assistant con tool_calls
|
150
|
+
this._emitMessage(assistantMessage, 'assistant_tool_calls')
|
151
|
+
|
152
|
+
// Esegui ogni tool call
|
153
|
+
const toolResults = []
|
154
|
+
for (const toolCall of msg.tool_calls) {
|
155
|
+
const { name, arguments: argsStr } = toolCall.function
|
156
|
+
|
157
|
+
try {
|
158
|
+
const args = JSON.parse(argsStr)
|
159
|
+
const tool = this.toolMap.get(name)
|
160
|
+
|
161
|
+
if (!tool) {
|
162
|
+
const errorMsg = `Tool '${name}' non trovato`
|
163
|
+
console.log(`⚠️ Tool Error: ${errorMsg}`)
|
164
|
+
|
165
|
+
const toolMessage = {
|
166
|
+
role: 'tool',
|
167
|
+
tool_call_id: toolCall.id,
|
168
|
+
name,
|
169
|
+
content: errorMsg
|
170
|
+
}
|
171
|
+
this.messages.push(toolMessage)
|
172
|
+
|
173
|
+
// Emetti evento per il messaggio tool
|
174
|
+
this._emitMessage(toolMessage, 'tool_message')
|
175
|
+
|
176
|
+
toolResults.push({ name, error: errorMsg })
|
177
|
+
continue
|
178
|
+
}
|
179
|
+
|
180
|
+
console.log(`⚡ Eseguendo tool: ${name}...`)
|
181
|
+
const startTime = Date.now()
|
182
|
+
const result = await tool.execute(args)
|
183
|
+
|
184
|
+
if (this.verbose) {
|
185
|
+
console.log(`📋 Observation (Osservazione da ${name}):`)
|
186
|
+
console.log(` "${result}"`)
|
187
|
+
}
|
188
|
+
toolCall.done = true;
|
189
|
+
toolCall.result = result;
|
190
|
+
toolCall.execution_time = Date.now() - startTime;
|
191
|
+
|
192
|
+
const toolMessage = {
|
193
|
+
role: 'tool',
|
194
|
+
tool_call_id: toolCall.id,
|
195
|
+
tool_calls: msg.tool_calls,
|
196
|
+
name,
|
197
|
+
content: typeof result === 'string' ? result : JSON.stringify(result)
|
198
|
+
|
199
|
+
}
|
200
|
+
this.messages.push(toolMessage)
|
201
|
+
|
202
|
+
// Emetti evento per il messaggio tool
|
203
|
+
this._emitMessage(toolMessage, 'tool_message')
|
204
|
+
|
205
|
+
toolResults.push({ name, result })
|
206
|
+
|
207
|
+
} catch (error) {
|
208
|
+
const errorMsg = `Errore nell'esecuzione di ${name}: ${error.message}`
|
209
|
+
console.log(`❌ Tool Error: ${errorMsg}`)
|
210
|
+
|
211
|
+
const toolMessage = {
|
212
|
+
role: 'tool',
|
213
|
+
tool_call_id: toolCall.id,
|
214
|
+
name,
|
215
|
+
content: errorMsg
|
216
|
+
}
|
217
|
+
this.messages.push(toolMessage)
|
218
|
+
|
219
|
+
// Emetti evento per il messaggio tool di errore
|
220
|
+
this._emitMessage(toolMessage, 'tool_message')
|
221
|
+
|
222
|
+
toolResults.push({ name, error: errorMsg })
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
return {
|
227
|
+
type: 'tool_calls',
|
228
|
+
tool_calls: msg.tool_calls,
|
229
|
+
results: toolResults
|
230
|
+
}
|
231
|
+
}
|
232
|
+
|
233
|
+
/**
|
234
|
+
* Versione streamable di run() che emette eventi per ogni messaggio
|
235
|
+
* Usa questa versione quando vuoi ricevere eventi in tempo reale
|
236
|
+
*/
|
237
|
+
async runStream(userMessage) {
|
238
|
+
if (this.verbose) {
|
239
|
+
console.log('🚀 Iniziando elaborazione in streaming...')
|
240
|
+
console.log(`📝 User Input: "${userMessage}"`)
|
241
|
+
console.log('─'.repeat(60))
|
242
|
+
}
|
243
|
+
|
244
|
+
// Emetti evento di inizio
|
245
|
+
this.emit('start', { userMessage })
|
246
|
+
|
247
|
+
this.addUserMessage(userMessage)
|
248
|
+
|
249
|
+
let iterations = 0
|
250
|
+
while (iterations < this.maxIterations) {
|
251
|
+
iterations++
|
252
|
+
if (this.verbose) {
|
253
|
+
console.log(`\n🔄 Iterazione ${iterations}:`)
|
254
|
+
}
|
255
|
+
|
256
|
+
// Emetti evento di iterazione
|
257
|
+
this.emit('iteration', { iteration: iterations })
|
258
|
+
|
259
|
+
const result = await this.step()
|
260
|
+
|
261
|
+
if (result.type === 'response') {
|
262
|
+
if (this.verbose) {
|
263
|
+
console.log('─'.repeat(60))
|
264
|
+
console.log(`✅ Elaborazione completata in ${iterations} iterazione${iterations > 1 ? 'i' : ''}`)
|
265
|
+
}
|
266
|
+
|
267
|
+
// Emetti evento di completamento
|
268
|
+
this.emit('complete', {
|
269
|
+
content: result.content,
|
270
|
+
role: result.role,
|
271
|
+
iterations,
|
272
|
+
messages: this.getHistory()
|
273
|
+
})
|
274
|
+
|
275
|
+
return {
|
276
|
+
content: result.content,
|
277
|
+
role: result.role,
|
278
|
+
iterations,
|
279
|
+
messages: this.getHistory()
|
280
|
+
}
|
281
|
+
}
|
282
|
+
|
283
|
+
// Se ci sono stati tool calls, continua il loop
|
284
|
+
if (result.type === 'tool_calls' && this.verbose) {
|
285
|
+
console.log('↻ Continuando con la prossima iterazione...')
|
286
|
+
}
|
287
|
+
}
|
288
|
+
|
289
|
+
const error = new Error(`Raggiunto il limite massimo di iterazioni (${this.maxIterations})`)
|
290
|
+
this.emit('error', error)
|
291
|
+
throw error
|
292
|
+
}
|
293
|
+
|
294
|
+
/**
|
295
|
+
* Processa un messaggio utente completo con tutti i tool calls necessari
|
296
|
+
*/
|
297
|
+
async run(userMessage) {
|
298
|
+
if (this.verbose) {
|
299
|
+
console.log('🚀 Iniziando elaborazione...')
|
300
|
+
console.log(`📝 User Input: "${userMessage}"`)
|
301
|
+
console.log('─'.repeat(60))
|
302
|
+
}
|
303
|
+
|
304
|
+
this.addUserMessage(userMessage)
|
305
|
+
|
306
|
+
let iterations = 0
|
307
|
+
while (iterations < this.maxIterations) {
|
308
|
+
iterations++
|
309
|
+
if (this.verbose) {
|
310
|
+
console.log(`\n🔄 Iterazione ${iterations}:`)
|
311
|
+
}
|
312
|
+
|
313
|
+
const result = await this.step()
|
314
|
+
|
315
|
+
if (result.type === 'response') {
|
316
|
+
if (this.verbose) {
|
317
|
+
console.log('─'.repeat(60))
|
318
|
+
console.log(`✅ Elaborazione completata in ${iterations} iterazione${iterations > 1 ? 'i' : ''}`)
|
319
|
+
}
|
320
|
+
|
321
|
+
return {
|
322
|
+
content: result.content,
|
323
|
+
role: result.role,
|
324
|
+
iterations,
|
325
|
+
messages: this.getHistory()
|
326
|
+
}
|
327
|
+
}
|
328
|
+
|
329
|
+
// Se ci sono stati tool calls, continua il loop
|
330
|
+
if (result.type === 'tool_calls' && this.verbose) {
|
331
|
+
console.log('↻ Continuando con la prossima iterazione...')
|
332
|
+
}
|
333
|
+
}
|
334
|
+
|
335
|
+
throw new Error(`Raggiunto il limite massimo di iterazioni (${this.maxIterations})`)
|
336
|
+
}
|
337
|
+
|
338
|
+
/**
|
339
|
+
* Alias per compatibilità con il codice esistente
|
340
|
+
*/
|
341
|
+
async processMessage(userMessage) {
|
342
|
+
const result = await this.run(userMessage)
|
343
|
+
return result.content
|
344
|
+
}
|
345
|
+
|
346
|
+
/**
|
347
|
+
* Resetta la conversazione mantenendo solo il system prompt
|
348
|
+
*/
|
349
|
+
reset() {
|
350
|
+
this.messages = []
|
351
|
+
if (this.systemPrompt) {
|
352
|
+
this.messages.push({ role: 'system', content: this.systemPrompt })
|
353
|
+
}
|
354
|
+
}
|
355
|
+
|
356
|
+
/**
|
357
|
+
* Ottiene la cronologia dei messaggi
|
358
|
+
*/
|
359
|
+
getHistory() {
|
360
|
+
return [...this.messages]
|
361
|
+
}
|
362
|
+
|
363
|
+
/**
|
364
|
+
* Imposta la cronologia dei messaggi
|
365
|
+
*/
|
366
|
+
setHistory(messages) {
|
367
|
+
if (!Array.isArray(messages)) {
|
368
|
+
throw new Error('La cronologia deve essere un array di messaggi')
|
369
|
+
}
|
370
|
+
|
371
|
+
// Validazione dei messaggi
|
372
|
+
for (const msg of messages) {
|
373
|
+
if (!msg.role || !['system', 'user', 'assistant', 'tool'].includes(msg.role)) {
|
374
|
+
throw new Error(`Ruolo del messaggio non valido: ${msg.role}`)
|
375
|
+
}
|
376
|
+
if (msg.content === undefined && msg.role !== 'assistant') {
|
377
|
+
throw new Error('Il messaggio deve avere un contenuto')
|
378
|
+
}
|
379
|
+
}
|
380
|
+
|
381
|
+
this.messages = [...messages]
|
382
|
+
|
383
|
+
// Ricostruisci la mappa dei tools se ci sono tool calls nella cronologia
|
384
|
+
this.toolMap.clear()
|
385
|
+
this.tools.forEach(tool => {
|
386
|
+
this.toolMap.set(tool.name, tool)
|
387
|
+
})
|
388
|
+
}
|
389
|
+
|
390
|
+
/**
|
391
|
+
* Aggiunge messaggi alla cronologia esistente
|
392
|
+
*/
|
393
|
+
appendToHistory(messages) {
|
394
|
+
if (!Array.isArray(messages)) {
|
395
|
+
throw new Error('I messaggi devono essere un array')
|
396
|
+
}
|
397
|
+
|
398
|
+
for (const msg of messages) {
|
399
|
+
if (!msg.role || !['system', 'user', 'assistant', 'tool'].includes(msg.role)) {
|
400
|
+
throw new Error(`Ruolo del messaggio non valido: ${msg.role}`)
|
401
|
+
}
|
402
|
+
}
|
403
|
+
|
404
|
+
this.messages.push(...messages)
|
405
|
+
}
|
406
|
+
|
407
|
+
/**
|
408
|
+
* Ottiene una versione leggibile della cronologia
|
409
|
+
*/
|
410
|
+
getReadableHistory() {
|
411
|
+
return this.messages
|
412
|
+
.filter(msg => msg.role !== 'tool') // Filtra i messaggi dei tools per leggibilità
|
413
|
+
.map(msg => {
|
414
|
+
const role = msg.role === 'user' ? '👤 Utente' :
|
415
|
+
msg.role === 'assistant' ? '🤖 Assistant' :
|
416
|
+
msg.role === 'system' ? '⚙️ System' : msg.role
|
417
|
+
return `${role}: ${msg.content || '[Tool calls]'}`
|
418
|
+
})
|
419
|
+
.join('\n\n')
|
420
|
+
}
|
421
|
+
|
422
|
+
/**
|
423
|
+
* Ottiene statistiche sulla cronologia
|
424
|
+
*/
|
425
|
+
getHistoryStats() {
|
426
|
+
const stats = {
|
427
|
+
total: this.messages.length,
|
428
|
+
user: 0,
|
429
|
+
assistant: 0,
|
430
|
+
system: 0,
|
431
|
+
tool: 0,
|
432
|
+
toolCalls: 0
|
433
|
+
}
|
434
|
+
|
435
|
+
this.messages.forEach(msg => {
|
436
|
+
stats[msg.role] = (stats[msg.role] || 0) + 1
|
437
|
+
if (msg.tool_calls) {
|
438
|
+
stats.toolCalls += msg.tool_calls.length
|
439
|
+
}
|
440
|
+
})
|
441
|
+
|
442
|
+
return stats
|
443
|
+
}
|
444
|
+
|
445
|
+
/**
|
446
|
+
* Ottiene l'ultimo messaggio
|
447
|
+
*/
|
448
|
+
getLastMessage() {
|
449
|
+
return this.messages[this.messages.length - 1] || null
|
450
|
+
}
|
451
|
+
|
452
|
+
/**
|
453
|
+
* Aggiunge un tool alla lista
|
454
|
+
*/
|
455
|
+
addTool(tool) {
|
456
|
+
this.tools.push(tool)
|
457
|
+
this.toolMap.set(tool.name, tool)
|
458
|
+
}
|
459
|
+
|
460
|
+
/**
|
461
|
+
* Rimuove un tool dalla lista
|
462
|
+
*/
|
463
|
+
removeTool(toolName) {
|
464
|
+
this.tools = this.tools.filter(tool => tool.name !== toolName)
|
465
|
+
this.toolMap.delete(toolName)
|
466
|
+
}
|
467
|
+
|
468
|
+
/**
|
469
|
+
* Ottiene la lista dei tools disponibili
|
470
|
+
*/
|
471
|
+
getTools() {
|
472
|
+
return [...this.tools]
|
473
|
+
}
|
474
|
+
|
475
|
+
/**
|
476
|
+
* Imposta la modalità debug
|
477
|
+
*/
|
478
|
+
setDebug(debug) {
|
479
|
+
this.debug = debug
|
480
|
+
}
|
481
|
+
|
482
|
+
/**
|
483
|
+
* Imposta la modalità verbose (mostra thoughts e observations)
|
484
|
+
*/
|
485
|
+
setVerbose(verbose) {
|
486
|
+
this.verbose = verbose
|
487
|
+
}
|
488
|
+
|
489
|
+
/**
|
490
|
+
* Ottiene lo stato della modalità verbose
|
491
|
+
*/
|
492
|
+
isVerbose() {
|
493
|
+
return this.verbose
|
494
|
+
}
|
495
|
+
}
|
package/src/Tool.js
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
import { z } from 'zod'
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Classe base per definire un tool compatibile con @openai/agents
|
5
|
+
*/
|
6
|
+
export class Tool {
|
7
|
+
constructor(options) {
|
8
|
+
this.name = options.name
|
9
|
+
this.description = options.description
|
10
|
+
this.parameters = options.parameters
|
11
|
+
this.handler = options.handler
|
12
|
+
|
13
|
+
// Supporto per schema Zod (opzionale)
|
14
|
+
if (options.schema) {
|
15
|
+
this.schema = options.schema
|
16
|
+
this.parameters = this.zodToJsonSchema(options.schema)
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
/**
|
21
|
+
* Converte uno schema Zod in JSON Schema per OpenAI
|
22
|
+
*/
|
23
|
+
zodToJsonSchema(schema) {
|
24
|
+
// Implementazione semplificata per gli schemi di base
|
25
|
+
if (schema._def.typeName === 'ZodObject') {
|
26
|
+
const properties = {}
|
27
|
+
const required = []
|
28
|
+
|
29
|
+
for (const [key, value] of Object.entries(schema.shape)) {
|
30
|
+
if (value._def.typeName === 'ZodString') {
|
31
|
+
properties[key] = {
|
32
|
+
type: 'string',
|
33
|
+
description: value._def.description || `${key} parameter`
|
34
|
+
}
|
35
|
+
} else if (value._def.typeName === 'ZodNumber') {
|
36
|
+
properties[key] = {
|
37
|
+
type: 'number',
|
38
|
+
description: value._def.description || `${key} parameter`
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
if (!value.isOptional()) {
|
43
|
+
required.push(key)
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
return {
|
48
|
+
type: 'object',
|
49
|
+
properties,
|
50
|
+
required
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
return this.parameters
|
55
|
+
}
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Restituisce la definizione del tool per OpenAI (compatibile con @openai/agents)
|
59
|
+
*/
|
60
|
+
getDefinition() {
|
61
|
+
return {
|
62
|
+
type: 'function',
|
63
|
+
function: {
|
64
|
+
name: this.name,
|
65
|
+
description: this.description,
|
66
|
+
parameters: this.parameters
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* Esegue il tool con gli argomenti forniti
|
73
|
+
*/
|
74
|
+
async execute(args) {
|
75
|
+
// Validazione con schema Zod se disponibile
|
76
|
+
if (this.schema) {
|
77
|
+
try {
|
78
|
+
const validatedArgs = this.schema.parse(args)
|
79
|
+
return await this.handler(validatedArgs)
|
80
|
+
} catch (error) {
|
81
|
+
throw new Error(`Validation error: ${error.message}`)
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
return await this.handler(args)
|
86
|
+
}
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Alias per compatibilità con @openai/agents
|
90
|
+
*/
|
91
|
+
async run(args) {
|
92
|
+
return await this.execute(args)
|
93
|
+
}
|
94
|
+
}
|
package/src/cli.js
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
/**
|
2
|
+
* Utility functions for AI Agent Toolkit
|
3
|
+
*/
|
4
|
+
|
5
|
+
import readline from 'readline'
|
6
|
+
import fs from 'fs'
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Creates a complete chat interface with readline
|
10
|
+
*/
|
11
|
+
export function createChatInterface(agent, options = {}) {
|
12
|
+
const {
|
13
|
+
prompt = '💬 Tu: ',
|
14
|
+
welcomeMessage = '🤖 Assistant: Ciao! Come posso aiutarti oggi?',
|
15
|
+
exitCommands = ['/exit', '/quit'],
|
16
|
+
showHelp = true
|
17
|
+
} = options
|
18
|
+
|
19
|
+
const rl = readline.createInterface({
|
20
|
+
input: process.stdin,
|
21
|
+
output: process.stdout,
|
22
|
+
prompt: `\n${prompt}`
|
23
|
+
})
|
24
|
+
|
25
|
+
// Special commands handler
|
26
|
+
function handleSpecialCommands(input) {
|
27
|
+
const command = input.toLowerCase().trim()
|
28
|
+
|
29
|
+
if (exitCommands.includes(command)) {
|
30
|
+
console.log('\n👋 Arrivederci!')
|
31
|
+
rl.close()
|
32
|
+
return true
|
33
|
+
}
|
34
|
+
|
35
|
+
switch (command) {
|
36
|
+
case '/reset':
|
37
|
+
agent.reset()
|
38
|
+
console.log('\n🔄 Conversazione resettata!')
|
39
|
+
return true
|
40
|
+
|
41
|
+
case '/history':
|
42
|
+
console.log('\n📚 Cronologia:')
|
43
|
+
console.log(agent.getReadableHistory?.() || JSON.stringify(agent.getHistory(), null, 2))
|
44
|
+
return true
|
45
|
+
|
46
|
+
case '/tools':
|
47
|
+
console.log('\n🛠 Tools disponibili:')
|
48
|
+
agent.getTools().forEach(tool => {
|
49
|
+
console.log(` • ${tool.name}: ${tool.description}`)
|
50
|
+
})
|
51
|
+
return true
|
52
|
+
|
53
|
+
case '/verbose on':
|
54
|
+
if (agent.setVerbose) {
|
55
|
+
agent.setVerbose(true)
|
56
|
+
console.log('\n📢 Modalità verbose attivata')
|
57
|
+
}
|
58
|
+
return true
|
59
|
+
|
60
|
+
case '/verbose off':
|
61
|
+
if (agent.setVerbose) {
|
62
|
+
agent.setVerbose(false)
|
63
|
+
console.log('\n🔇 Modalità verbose disattivata')
|
64
|
+
}
|
65
|
+
return true
|
66
|
+
|
67
|
+
case '/help':
|
68
|
+
showHelpMessage()
|
69
|
+
return true
|
70
|
+
|
71
|
+
default:
|
72
|
+
return false
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
function showHelpMessage() {
|
77
|
+
console.log('\n📖 Comandi disponibili:')
|
78
|
+
console.log(` • ${exitCommands.join('/')} - Esci dalla chat`)
|
79
|
+
console.log(' • /reset - Resetta la conversazione')
|
80
|
+
console.log(' • /history - Mostra cronologia')
|
81
|
+
console.log(' • /tools - Lista dei tools disponibili')
|
82
|
+
console.log(' • /verbose on/off - Attiva/disattiva modalità verbose')
|
83
|
+
console.log(' • /help - Mostra questo aiuto')
|
84
|
+
}
|
85
|
+
|
86
|
+
// Main input handler
|
87
|
+
async function handleInput(input) {
|
88
|
+
if (handleSpecialCommands(input)) {
|
89
|
+
return
|
90
|
+
}
|
91
|
+
|
92
|
+
if (input.trim() === '') {
|
93
|
+
return
|
94
|
+
}
|
95
|
+
|
96
|
+
try {
|
97
|
+
const result = await agent.run(input)
|
98
|
+
console.log(`\n🤖 Assistant: ${result.content}`)
|
99
|
+
} catch (error) {
|
100
|
+
console.error('\n❌ Errore:', error.message)
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
// Event listeners
|
105
|
+
rl.on('line', async (input) => {
|
106
|
+
await handleInput(input)
|
107
|
+
rl.prompt()
|
108
|
+
})
|
109
|
+
|
110
|
+
rl.on('close', () => {
|
111
|
+
console.log('\n👋 Sessione terminata!')
|
112
|
+
process.exit(0)
|
113
|
+
})
|
114
|
+
|
115
|
+
// Start the chat
|
116
|
+
console.log(welcomeMessage)
|
117
|
+
if (showHelp) {
|
118
|
+
showHelpMessage()
|
119
|
+
}
|
120
|
+
rl.prompt()
|
121
|
+
|
122
|
+
return {
|
123
|
+
rl,
|
124
|
+
close: () => rl.close(),
|
125
|
+
handleInput
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
/**
|
130
|
+
* Save conversation history to file
|
131
|
+
*/
|
132
|
+
export function saveHistory(agent, filename) {
|
133
|
+
if (!filename) {
|
134
|
+
filename = `chat_history_${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.json`
|
135
|
+
}
|
136
|
+
|
137
|
+
const history = agent.getHistory()
|
138
|
+
fs.writeFileSync(filename, JSON.stringify(history, null, 2))
|
139
|
+
return filename
|
140
|
+
}
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Load conversation history from file
|
144
|
+
*/
|
145
|
+
export function loadHistory(agent, filename) {
|
146
|
+
if (!fs.existsSync(filename)) {
|
147
|
+
throw new Error(`File not found: ${filename}`)
|
148
|
+
}
|
149
|
+
|
150
|
+
const historyData = fs.readFileSync(filename, 'utf8')
|
151
|
+
const history = JSON.parse(historyData)
|
152
|
+
|
153
|
+
agent.setHistory(history)
|
154
|
+
return history
|
155
|
+
}
|
package/src/index.js
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
/**
|
2
|
+
* AI Agent Toolkit - Main Entry Point
|
3
|
+
*
|
4
|
+
* A powerful AI agent toolkit compatible with @openai/agents
|
5
|
+
* for building conversational AI with tool calling support.
|
6
|
+
*
|
7
|
+
* @example
|
8
|
+
* ```javascript
|
9
|
+
* import { Agent, Tool } from '@yourname/ai-agent-toolkit'
|
10
|
+
*
|
11
|
+
* // Create a simple tool
|
12
|
+
* const weatherTool = new Tool({
|
13
|
+
* name: 'get_weather',
|
14
|
+
* description: 'Get weather information for a city',
|
15
|
+
* schema: z.object({
|
16
|
+
* city: z.string().describe('City name')
|
17
|
+
* }),
|
18
|
+
* handler: async ({ city }) => `Weather in ${city}: 25°C, sunny`
|
19
|
+
* })
|
20
|
+
*
|
21
|
+
* // Create an agent
|
22
|
+
* const agent = new Agent({
|
23
|
+
* model: 'gpt-4',
|
24
|
+
* apiKey: process.env.OPENAI_API_KEY,
|
25
|
+
* instructions: 'You are a helpful weather assistant.',
|
26
|
+
* tools: [weatherTool]
|
27
|
+
* })
|
28
|
+
*
|
29
|
+
* // Use the agent
|
30
|
+
* const response = await agent.run('What\'s the weather in Rome?')
|
31
|
+
* console.log(response.content)
|
32
|
+
* ```
|
33
|
+
*/
|
34
|
+
|
35
|
+
// Core exports
|
36
|
+
export { Agent } from './Agent.js'
|
37
|
+
export { Tool } from './Tool.js'
|
38
|
+
|
39
|
+
// Utility functions
|
40
|
+
export { createChatInterface } from './cli.js'
|
41
|
+
|
42
|
+
/**
|
43
|
+
* Quick setup function for common use cases
|
44
|
+
*/
|
45
|
+
export function createAgent(options) {
|
46
|
+
return new Agent(options)
|
47
|
+
}
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Quick tool creation function
|
51
|
+
*/
|
52
|
+
export function createTool(options) {
|
53
|
+
return new Tool(options)
|
54
|
+
}
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Version information
|
58
|
+
*/
|
59
|
+
export const version = '1.0.0'
|