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 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** – Only ~300 lines, easy to understand and extend.
11
- - **Plugin System** – Add features like tool calling, reasoning, or custom adapters without touching the core.
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 formats for browser and Node.js.
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
- apiUrl: 'https://api.deepseek.com/v1/chat/completions'
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.registerTool('greet', 'Say hello', async (args) => {
39
- return `Hello, ${args.name}!`;
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
- chat.on('message', msg => console.log(msg.content));
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('Please greet Alice.');
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
- - **openaiAdapter** – Converts internal messages to OpenAI-compatible format.
50
- - **toolCallingPlugin** – Detects `tool_calls` in responses, executes tools, and continues the conversation.
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
- You can easily create your own adapter for other APIs (Anthropic, Cohere, etc.) or plugins for logging, caching, etc.
151
+ ## 📡 Events (EventEmitter)
53
152
 
54
- ## 📚 API
153
+ `ChatService` extends `EventEmitter`. Subscribe with `chat.on(event, handler)`:
55
154
 
56
- ### ChatService
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
- ```typescript
59
- new ChatService(config: {
60
- apiKey: string;
61
- model?: string;
62
- apiUrl?: string;
63
- temperature?: number;
64
- maxTokens?: number;
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
- - `use(plugin)` – Load a plugin.
69
- - `send(message)` – Send a message and wait for the complete response.
70
- - `stream(message, onProgress, onDone)` – Stream the response.
71
- - `registerTool(name, description, executor)` – Register a tool (only available after loading `toolCallingPlugin`).
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
- ### MessageStore
266
+ ---
74
267
 
75
- - `add(message)` – Add a message (unified format).
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
- ### EventEmitter
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
- - `on(event, handler)` Subscribe to events.
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
- Events: `sending`, `message`, `stream-progress`, `error`, etc.
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
- npm run build # build the library
98
- npm test # run the test script
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.