my-ai-chat-framework 2.0.0 → 2.7.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/README.md +242 -45
- package/dist/my-ai-chat-framework.browser.es.js +1048 -184
- package/dist/my-ai-chat-framework.browser.es.js.map +1 -1
- package/dist/my-ai-chat-framework.browser.umd.js +1056 -188
- package/dist/my-ai-chat-framework.browser.umd.js.map +1 -1
- package/dist/my-ai-chat-framework.node.cjs.js +1056 -188
- package/dist/my-ai-chat-framework.node.cjs.js.map +1 -1
- package/package.json +7 -7
- package/src/adapters/openai.js +214 -64
- package/src/core/ChatService.js +298 -110
- package/src/core/Errors.js +60 -0
- package/src/core/EventEmitter.js +19 -0
- package/src/core/MessageStore.js +39 -0
- package/src/core/SystemPromptStore.js +118 -0
- package/src/index.js +10 -14
- package/src/plugins/model-registry.js +187 -0
- package/src/plugins/tool-calling.js +172 -78
- package/src/utils/MessageFormatter.js +204 -0
- package/src/utils/typeCheck.js +12 -0
- package/src/utils/url.js +18 -0
- package/.env +0 -15
- package/test.js +0 -107
package/README.md
CHANGED
|
@@ -1,19 +1,62 @@
|
|
|
1
1
|
|
|
2
|
-
**⚠️ Note**: This project is created for **learning purposes** and is **AI‑generated**. It is not intended for production use. No backward compatibility is guaranteed. Use at your own risk.
|
|
3
|
-
|
|
4
2
|
# 🤖 My AI Chat Framework
|
|
5
3
|
|
|
6
4
|
A lightweight, modular AI chat framework with plugin system, unified message format, and tool calling support.
|
|
7
5
|
|
|
6
|
+
> **⚠️ Note**: This project is created for **learning purposes** and is **AI‑generated**. It is not intended for production use. No backward compatibility is guaranteed. Use at your own risk.
|
|
7
|
+
|
|
8
8
|
## ✨ Features
|
|
9
9
|
|
|
10
|
-
- **Lightweight Core** –
|
|
11
|
-
- **Plugin System** – Add features
|
|
10
|
+
- **Lightweight Core** – ~300 lines, easy to understand and extend.
|
|
11
|
+
- **Plugin System** – Add features (tool calling, reasoning, custom adapters) without touching the core.
|
|
12
12
|
- **Unified Message Format** – Consistent data structure across all components.
|
|
13
|
-
- **Multi‑Environment** – Builds ES module, UMD, and CommonJS
|
|
13
|
+
- **Multi‑Environment** – Builds ES module, UMD, and CommonJS for browser & Node.js.
|
|
14
14
|
- **No External Dependencies** – Uses native `fetch` (Node 18+ & modern browsers).
|
|
15
15
|
- **Tool Calling** – Built‑in plugin to handle function calls from AI models.
|
|
16
16
|
- **Streaming** – Full support for real‑time responses.
|
|
17
|
+
- **Flexible Configuration** – Supports both flat and nested `modelParams` structure.
|
|
18
|
+
- **Event‑Driven** – Built‑in EventEmitter for `message`, `sending`, `error`, `stream-progress` events.
|
|
19
|
+
- **Custom Error Classes** – `APIError`, `NetworkError`, `ConfigurationError`, `ParsingError` for fine‑grained error handling.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 🧱 Architecture
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
src/
|
|
27
|
+
├── index.js # Public entry point (re-exports)
|
|
28
|
+
├── core/
|
|
29
|
+
│ ├── ChatService.js # Main service: config, send/stream, plugin hosting
|
|
30
|
+
│ ├── MessageStore.js # In-memory message list with CRUD helpers
|
|
31
|
+
│ ├── EventEmitter.js # Minimal pub/sub (on/off/emit)
|
|
32
|
+
│ └── Errors.js # Custom error classes
|
|
33
|
+
├── adapters/
|
|
34
|
+
│ └── openai.js # OpenAI‑compatible API adapter (plugin pattern)
|
|
35
|
+
├── plugins/
|
|
36
|
+
│ └── tool-calling.js # Tool calling plugin (auto‑detect & loop)
|
|
37
|
+
└── utils/
|
|
38
|
+
├── typeCheck.js # Type checking helpers
|
|
39
|
+
└── url.js # URL joining utility
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Data flow**:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
User calls chat.send(input)
|
|
46
|
+
→ ChatService._request()
|
|
47
|
+
→ emits 'sending'
|
|
48
|
+
→ MessageStore.add(userMsg)
|
|
49
|
+
→ adapter.buildRequest(messages, config) ← formats request body
|
|
50
|
+
→ adapter.send(requestBody, config) ← HTTP call (fetch)
|
|
51
|
+
→ adapter.parseResponse(response) ← normalize response
|
|
52
|
+
→ MessageStore.add(assistantMsg)
|
|
53
|
+
→ emits 'message'
|
|
54
|
+
→ returns assistantMsg
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
With `toolCallingPlugin`, the flow loops: response → detect tool_calls → execute tools → add tool results → sendExisting → repeat (max 5 iterations).
|
|
58
|
+
|
|
59
|
+
---
|
|
17
60
|
|
|
18
61
|
## 📦 Installation
|
|
19
62
|
|
|
@@ -21,72 +64,220 @@ A lightweight, modular AI chat framework with plugin system, unified message for
|
|
|
21
64
|
npm install my-ai-chat-framework
|
|
22
65
|
```
|
|
23
66
|
|
|
67
|
+
---
|
|
68
|
+
|
|
24
69
|
## 🚀 Quick Start
|
|
25
70
|
|
|
71
|
+
### Basic Usage (Flat Configuration)
|
|
72
|
+
|
|
26
73
|
```javascript
|
|
27
74
|
import { ChatService, openaiAdapter, toolCallingPlugin } from 'my-ai-chat-framework';
|
|
28
75
|
|
|
29
76
|
const chat = new ChatService({
|
|
30
77
|
apiKey: 'your-api-key',
|
|
78
|
+
baseUrl: 'https://api.deepseek.com', // optional, defaults to OpenAI
|
|
31
79
|
model: 'deepseek-chat',
|
|
32
|
-
|
|
80
|
+
temperature: 0.7,
|
|
81
|
+
maxTokens: 2000
|
|
33
82
|
});
|
|
34
83
|
|
|
35
84
|
chat.use(openaiAdapter);
|
|
36
85
|
chat.use(toolCallingPlugin);
|
|
37
86
|
|
|
38
|
-
chat.
|
|
39
|
-
|
|
87
|
+
chat.on('message', msg => console.log(msg.content));
|
|
88
|
+
await chat.send('Hello!');
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Using `modelParams` (Recommended for Many Parameters)
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
const chat = new ChatService({
|
|
95
|
+
apiKey: 'your-api-key',
|
|
96
|
+
baseUrl: 'https://api.deepseek.com',
|
|
97
|
+
model: 'deepseek-chat', // still at top level for convenience
|
|
98
|
+
modelParams: { // optional parameters grouped
|
|
99
|
+
temperature: 0.8,
|
|
100
|
+
maxTokens: 1500,
|
|
101
|
+
reasoningEffort: 'medium' // for deepseek-reasoner
|
|
102
|
+
}
|
|
40
103
|
});
|
|
104
|
+
```
|
|
41
105
|
|
|
42
|
-
|
|
106
|
+
### Registering a Tool
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
chat.registerTool('get_weather', 'Get current weather for a city',
|
|
110
|
+
async (args) => {
|
|
111
|
+
// args = { city: 'Beijing' }
|
|
112
|
+
return `Weather in ${args.city}: 22°C, sunny`;
|
|
113
|
+
},
|
|
114
|
+
{ // parameter schema (optional but recommended)
|
|
115
|
+
city: { type: 'string', description: 'City name', required: true }
|
|
116
|
+
}
|
|
117
|
+
);
|
|
43
118
|
|
|
44
|
-
await chat.send('
|
|
119
|
+
await chat.send('What\'s the weather in Beijing?');
|
|
120
|
+
// → AI calls get_weather, framework executes it, AI responds with weather info
|
|
45
121
|
```
|
|
46
122
|
|
|
123
|
+
---
|
|
124
|
+
|
|
47
125
|
## 🔌 Plugins & Adapters
|
|
48
126
|
|
|
49
|
-
|
|
50
|
-
|
|
127
|
+
### openaiAdapter
|
|
128
|
+
|
|
129
|
+
Converts internal messages to OpenAI‑compatible format. Supports:
|
|
130
|
+
- `apiUrl` – full URL (highest priority)
|
|
131
|
+
- `baseUrl` + `path` – base domain + API path
|
|
132
|
+
- Defaults to `https://api.openai.com/v1/chat/completions`
|
|
133
|
+
|
|
134
|
+
| Config field | Type | Default | Description |
|
|
135
|
+
|-------------|------|---------|-------------|
|
|
136
|
+
| `apiKey` | string | **required** | Bearer token for Authorization header |
|
|
137
|
+
| `apiUrl` | string | – | Full request URL (overrides baseUrl+path) |
|
|
138
|
+
| `baseUrl` | string | `https://api.openai.com` | API base domain |
|
|
139
|
+
| `path` | string | `/v1/chat/completions` | API endpoint path |
|
|
140
|
+
|
|
141
|
+
### toolCallingPlugin
|
|
142
|
+
|
|
143
|
+
Detects `tool_calls` in assistant responses, executes registered tools, feeds results back, and continues the conversation (up to `maxIterations` = 5).
|
|
144
|
+
|
|
145
|
+
- `chat.registerTool(name, description, executor, parameters?)` – register a tool
|
|
146
|
+
- Automatically injects `tool` role messages into the conversation
|
|
147
|
+
- Recovers from tool execution errors gracefully (logs error, returns error message to model)
|
|
148
|
+
|
|
149
|
+
---
|
|
51
150
|
|
|
52
|
-
|
|
151
|
+
## 📡 Events (EventEmitter)
|
|
53
152
|
|
|
54
|
-
|
|
153
|
+
`ChatService` extends `EventEmitter`. Subscribe with `chat.on(event, handler)`:
|
|
55
154
|
|
|
56
|
-
|
|
155
|
+
| Event | Payload | When |
|
|
156
|
+
|-------|---------|------|
|
|
157
|
+
| `sending` | `{ addUser, userInput, timestamp }` | Before each request |
|
|
158
|
+
| `message` | `{ role, content, ... }` | Full assistant message received |
|
|
159
|
+
| `stream-progress` | chunk object | Each streaming chunk arrives |
|
|
160
|
+
| `error` | `{ error, timestamp }` | Any error during request |
|
|
57
161
|
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
162
|
+
```javascript
|
|
163
|
+
chat.on('sending', ({ userInput }) => console.log('Sending:', userInput));
|
|
164
|
+
chat.on('message', msg => console.log('Got:', msg.content));
|
|
165
|
+
chat.on('error', ({ error }) => console.error('Error:', error.message));
|
|
166
|
+
|
|
167
|
+
// on() returns an unsubscribe function
|
|
168
|
+
const unsubscribe = chat.on('message', handler);
|
|
169
|
+
unsubscribe(); // stop listening
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 🧩 ChatService API
|
|
175
|
+
|
|
176
|
+
| Method | Returns | Description |
|
|
177
|
+
|--------|---------|-------------|
|
|
178
|
+
| `chat.send(userInput)` | `Promise<Message>` | Send a message, get reply (non‑streaming) |
|
|
179
|
+
| `chat.stream(userInput, onProgress, onDone)` | `Promise<Message>` | Send a message, get streaming reply |
|
|
180
|
+
| `chat.sendExisting()` | `Promise<Message>` | Re‑send current messages without adding user input |
|
|
181
|
+
| `chat.sendExistingStream(onProgress, onDone)` | `Promise<Message>` | Same as above, streaming |
|
|
182
|
+
| `chat.use(plugin)` | `this` | Install a plugin/adapter |
|
|
183
|
+
| `chat.setAdapter(adapter)` | `void` | Manually set the adapter |
|
|
184
|
+
| `chat.on(event, handler)` | `unsubscribe function` | Subscribe to events |
|
|
185
|
+
| `chat.registerTool(name, desc, fn, params?)` | `this` | Register a tool (requires toolCallingPlugin) |
|
|
186
|
+
| `chat.messages` | `MessageStore` | Access the message store directly |
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## 🗄️ MessageStore API
|
|
191
|
+
|
|
192
|
+
| Method | Description |
|
|
193
|
+
|--------|-------------|
|
|
194
|
+
| `add(message)` | Add a raw message object |
|
|
195
|
+
| `addUser(content, meta?)` | Add a user message |
|
|
196
|
+
| `addAssistant(content, meta?)` | Add an assistant message |
|
|
197
|
+
| `addSystem(content, meta?)` | Add a system message |
|
|
198
|
+
| `addTool(content, toolCallId, meta?)` | Add a tool result message |
|
|
199
|
+
| `getAll()` | Return a shallow copy of all messages |
|
|
200
|
+
| `getLast()` | Return the last message (or null) |
|
|
201
|
+
| `clear()` | Remove all messages |
|
|
202
|
+
| `undoToLastAssistant()` | Remove messages after the last assistant message |
|
|
203
|
+
|
|
204
|
+
**Message format**:
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
{
|
|
208
|
+
id: string, // auto‑generated if not provided
|
|
209
|
+
role: 'user' | 'assistant' | 'system' | 'tool',
|
|
210
|
+
content: string,
|
|
211
|
+
toolCalls?: Array, // assistant messages with tool calls
|
|
212
|
+
toolCallId?: string, // tool messages
|
|
213
|
+
reasoningContent?: string, // deepseek-reasoner
|
|
214
|
+
timestamp?: number,
|
|
215
|
+
metadata?: any
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## ❌ Error Handling
|
|
222
|
+
|
|
223
|
+
The framework throws typed errors for different failure modes:
|
|
224
|
+
|
|
225
|
+
| Error Class | `.name` | When |
|
|
226
|
+
|------------|---------|------|
|
|
227
|
+
| `APIError` | `'APIError'` | Non‑2xx HTTP responses (401, 429, 500, etc.) |
|
|
228
|
+
| `NetworkError` | `'NetworkError'` | `fetch` failures, connection timeouts |
|
|
229
|
+
| `ConfigurationError` | `'ConfigurationError'` | Missing required config |
|
|
230
|
+
| `ParsingError` | `'ParsingError'` | Malformed API response |
|
|
231
|
+
|
|
232
|
+
```javascript
|
|
233
|
+
import { APIError, NetworkError, ConfigurationError, ParsingError } from 'my-ai-chat-framework';
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
await chat.send('Hello');
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (error instanceof APIError) {
|
|
239
|
+
console.error(`API ${error.statusCode}: ${error.message}`);
|
|
240
|
+
} else if (error instanceof NetworkError) {
|
|
241
|
+
console.error('Network issue:', error.message);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
66
244
|
```
|
|
67
245
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 📚 Configuration Reference
|
|
249
|
+
|
|
250
|
+
`new ChatService(config)` accepts:
|
|
251
|
+
|
|
252
|
+
| Option | Type | Default | Description |
|
|
253
|
+
|--------|------|---------|-------------|
|
|
254
|
+
| `apiKey` | string | **required** | Your API key |
|
|
255
|
+
| `baseUrl` | string | `'https://api.openai.com'` | API base URL (used with `path`) |
|
|
256
|
+
| `path` | string | `'/v1/chat/completions'` | API path (used with `baseUrl`) |
|
|
257
|
+
| `apiUrl` | string | – | Full API URL (overrides `baseUrl`+`path`) |
|
|
258
|
+
| `model` | string | **required** | Model name (e.g., `deepseek-chat`) |
|
|
259
|
+
| `modelParams` | object | `{}` | Grouped model parameters (see below) |
|
|
260
|
+
| `temperature` | number | `0.7` | Sampling temperature (0–2) |
|
|
261
|
+
| `maxTokens` | number | `2000` | Max tokens to generate |
|
|
262
|
+
| `reasoningEffort` | string | – | For `deepseek-reasoner`: `'low'`, `'medium'`, `'high'` |
|
|
263
|
+
|
|
264
|
+
> Both flat and `modelParams` styles work. `modelParams` takes precedence over top‑level values.
|
|
72
265
|
|
|
73
|
-
|
|
266
|
+
---
|
|
74
267
|
|
|
75
|
-
|
|
76
|
-
- `addUser(content, metadata)` – Convenience method.
|
|
77
|
-
- `addAssistant(content, metadata)`
|
|
78
|
-
- `addSystem(content, metadata)`
|
|
79
|
-
- `addTool(content, toolCallId, metadata)`
|
|
80
|
-
- `getAll()` – Get all messages.
|
|
81
|
-
- `undoToLastAssistant()` – Rollback to the last assistant message.
|
|
268
|
+
## 🧪 Testing
|
|
82
269
|
|
|
83
|
-
|
|
270
|
+
```bash
|
|
271
|
+
# 1. Create a .env file with your API key
|
|
272
|
+
echo "DEEPSEEK_API_KEY=sk-xxxxx" > .env
|
|
273
|
+
|
|
274
|
+
# 2. Run the test
|
|
275
|
+
npm test
|
|
276
|
+
```
|
|
84
277
|
|
|
85
|
-
|
|
86
|
-
- `off(event, handler)` – Unsubscribe.
|
|
87
|
-
- `emit(event, data)` – Emit an event.
|
|
278
|
+
The test script (`test.js`) sends a simple message and logs the response.
|
|
88
279
|
|
|
89
|
-
|
|
280
|
+
---
|
|
90
281
|
|
|
91
282
|
## 🛠️ Development
|
|
92
283
|
|
|
@@ -94,14 +285,20 @@ Events: `sending`, `message`, `stream-progress`, `error`, etc.
|
|
|
94
285
|
git clone https://github.com/your-username/my-ai-chat-framework.git
|
|
95
286
|
cd my-ai-chat-framework
|
|
96
287
|
npm install
|
|
97
|
-
|
|
98
|
-
|
|
288
|
+
|
|
289
|
+
# Dev mode (watching)
|
|
290
|
+
npm run dev
|
|
291
|
+
|
|
292
|
+
# Build the library (ES + UMD + CJS)
|
|
293
|
+
npm run build
|
|
294
|
+
|
|
295
|
+
# Run tests
|
|
296
|
+
npm test
|
|
99
297
|
```
|
|
100
298
|
|
|
299
|
+
---
|
|
300
|
+
|
|
101
301
|
## 📄 License
|
|
102
302
|
|
|
103
303
|
MIT
|
|
104
304
|
|
|
105
|
-
## 🤝 Contributing
|
|
106
|
-
|
|
107
|
-
Contributions are welcome! Please open an issue or submit a pull request.
|