my-ai-chat-framework 2.7.0 → 4.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/CHANGELOG.md +117 -0
- package/LICENSE +20 -20
- package/README.md +103 -16
- package/README_ZH.md +392 -0
- package/dist/my-ai-chat-framework.browser.es.js +1238 -421
- package/dist/my-ai-chat-framework.browser.es.js.map +1 -1
- package/dist/my-ai-chat-framework.browser.umd.js +1245 -420
- package/dist/my-ai-chat-framework.browser.umd.js.map +1 -1
- package/dist/my-ai-chat-framework.node.cjs.js +1245 -420
- package/dist/my-ai-chat-framework.node.cjs.js.map +1 -1
- package/docs/DEVELOPER.md +569 -0
- package/docs/README.md +33 -0
- package/package.json +31 -8
- package/src/adapters/openai.js +124 -23
- package/src/core/ChatService.js +596 -133
- package/src/core/Errors.js +72 -59
- package/src/core/MessageStore.js +27 -0
- package/src/core/Pipeline.js +48 -0
- package/src/core/SystemPromptStore.js +120 -118
- package/src/index.js +7 -4
- package/src/plugins/continuation.js +156 -0
- package/src/plugins/model-registry.js +236 -187
- package/src/plugins/tool-calling.js +245 -187
- package/src/utils/MessageFormatter.js +296 -204
- package/src/utils/typeCheck.js +11 -11
- package/src/utils/url.js +17 -17
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [4.0.0] - 2026-09
|
|
6
|
+
|
|
7
|
+
> 主题:**把"玩法"从核心搬出去,并给核心补上"出口"**。
|
|
8
|
+
> 一句话:核心只保留"怎么合并、怎么发、怎么映射字段";"续写 / 临时消息"变成可选的 continuation 插件。
|
|
9
|
+
|
|
10
|
+
### Added(清债 · A 组)
|
|
11
|
+
- **A1 删旧通道**:`_hooks` / `beforeRequest` 钩子 / `_processResponse` 全部移除(改用 `pipe` 车间 / `replaceStage`);`beforeSend` 步骤改空实现
|
|
12
|
+
- **A2 可扩展配置白名单**:`registerConfigKeys(keys)` / `unregisterConfigKeys` / `configKeys`
|
|
13
|
+
—— 插件自带配置(如 continuation 的 `autoContinue`)可运行时修改,**不必回头改核心**
|
|
14
|
+
- **A3 错误类型统一**:新增 `ValidationError extends ConfigurationError`,15 处裸 `Error` 全部类型化
|
|
15
|
+
- **A5 流式增量**:`onProgress(snap, delta)` —— `snap` 全量、`delta` 本次新增
|
|
16
|
+
- **A6 SSE 健壮化**:支持 `\r\n`、`data:`(无空格)、多行 `data` 拼接
|
|
17
|
+
|
|
18
|
+
### Changed(架构 · B 组)
|
|
19
|
+
- **B1 每请求独立中断**:`abort()` 中断全部 / `abort(id)` 精确中断 / `activeRequests`;多请求互不干扰(旧版共享单个 controller,会中断错的那个)
|
|
20
|
+
- **B3 消息存储可注入**:`new ChatService({ store })`(为将来的对话树/自定义存储留口)
|
|
21
|
+
- **B4 插件命名空间**:插件 API 挂到 `chat.plugins.<插件名>`,不再直接挂 `chat`(撞名从根上消失);三个插件都提供 `uninstall`
|
|
22
|
+
- **A4 工具去限制 / 参数不默认**(BREAKING):
|
|
23
|
+
- 工具循环**默认不限轮数**(旧版硬编码 5 轮);`timeout` 只在显式传入时生效,并修掉超时泄漏(clearTimeout + AbortController 通知工具)
|
|
24
|
+
- `registerTool` 生成标准 JSON Schema(必填项进顶层 `required`,properties 不再残留)
|
|
25
|
+
- `temperature` / `maxTokens` **不再有默认值** —— 不传就不放进请求体(由 API 用自己的默认)
|
|
26
|
+
|
|
27
|
+
### Docs
|
|
28
|
+
- **docs/API-STABILITY.md**:稳定性契约(Stable 清单 / 四个协议 / 事件 payload 形状 / 兼容纪律 / v3→v4 迁移记录)
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- **出口 API**:`chat.replaceStage(name, fn)` / `restoreStage` / `unstage` / `restage` / `getStage` / `internalStages`
|
|
32
|
+
—— 5 个内部步骤(prepareInput / beforeSend / autoContinue / buildRequest / send)可替换、可关闭、可包装
|
|
33
|
+
- **消息过滤器注册点**:`MessageFormatter.registerFilter(name, fn, { priority })` / `unregisterFilter` / `listFilters`
|
|
34
|
+
—— 决定"哪些消息进入请求"的规则变成可插拔链(同名覆盖,返回旧函数)
|
|
35
|
+
- **continuation 插件**(`src/plugins/continuation.js`,随框架发布,非独立包):
|
|
36
|
+
- 续写检测(`replaceStage('autoContinue', …)`)、ephemeral 过滤规则、`chat.continueLast` / `continueLastStream`
|
|
37
|
+
- `createContinuationPlugin({ autoContinue = false })` + `plugin.uninstall(chat)` 完整还原
|
|
38
|
+
- 导出 `ephemeralFilterRule` / `prepareContinue`(只要规则不要插件时可用)
|
|
39
|
+
- `sendExisting(params, { mergeToEntry })` / `sendExistingStream(params, { mergeToEntry }, onProgress, onDone)`
|
|
40
|
+
—— "把结果并回指定消息"成为公开能力(兼容旧签名)
|
|
41
|
+
|
|
42
|
+
### Changed(BREAKING)
|
|
43
|
+
- **`new ChatService()` 不再自带续写与临时消息**:核心删除了 `continueLast` / `continueLastStream` / `_prepareContinue`,
|
|
44
|
+
`autoContinue` 步骤改为空实现,`MessageFormatter` 不再内置 ephemeral 过滤规则
|
|
45
|
+
→ 需要这些能力的:`chat.use(createContinuationPlugin({ autoContinue: true }))`
|
|
46
|
+
- `config.ephemeralContinue` 移除(`updateConfig` 白名单同步收窄);改用插件选项 `autoContinue`
|
|
47
|
+
- 消息"保留/丢弃"规则改为过滤器实现(行为与 v3.x 一致:ephemeral 底部连续、system 分流、空内容清理)
|
|
48
|
+
|
|
49
|
+
### 迁移(从 3.x 升到 4.0)
|
|
50
|
+
```js
|
|
51
|
+
// 旧:什么都不用做,续写开箱可用
|
|
52
|
+
// 新:一行装配
|
|
53
|
+
import { createContinuationPlugin } from 'my-ai-chat-framework';
|
|
54
|
+
chat.use(createContinuationPlugin({ autoContinue: true }));
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## [3.0.0] - 2026-08-?
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
- **公开管道 API**:`chat.pipe({ name, phase?, run(ctx) })` / `chat.unpipe(name)` / `chat.pipelineStages`
|
|
61
|
+
- phase `beforeSend`(默认):内部 beforeRequest 之后、构建请求体之前
|
|
62
|
+
- phase `afterSend`:发送完成、结果落位之后(可改 ctx.result)
|
|
63
|
+
- 不注册任何车间 = 行为与 2.8.x 完全一致
|
|
64
|
+
- **协议固化**:`assertAdapter` 校验 buildRequest/send/stream/parseResponse,缺方法抛 ConfigurationError 并列出方法名;JSDoc @typedef ChatAdapter / PipelineContext / PipeStage
|
|
65
|
+
- **插件车间化**:tool-calling 经 afterSend 车间、model-registry 经 beforeSend 车间,不再占用 `_processResponse` 单座槽位
|
|
66
|
+
- 新增测试 `tests/pipeline.test.js`(8 例:pipe 顺序/冲突/unpipe/改ctx/SSE 冒烟/工具循环端到端)
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
- `ChatService._request` 固定线拆为内部管道:prepareInput → beforeSend → autoContinue → buildRequest → send(行为等价)
|
|
70
|
+
- `_hooks` beforeRequest 保留为兼容桥;`_processResponse` 保留为旧通道(新代码请用 afterSend 车间)
|
|
71
|
+
- 版本号 2.8.0 → 3.0.0(新增公共 API 面)
|
|
72
|
+
|
|
73
|
+
### Known delta(流式 tool-calling 时序)
|
|
74
|
+
- 流式工具调用时,初始回复的 `message` 事件在工具循环前发出(旧版循环后发)。非流式时序与旧版一致。
|
|
75
|
+
|
|
76
|
+
## [2.8.0] - 2026-08-08
|
|
77
|
+
|
|
78
|
+
### Added
|
|
79
|
+
- **配置归属系统**:配置按层归属(运输/请求/会话/插件),谁消费谁声明
|
|
80
|
+
- **工厂函数**:`createOpenAIAdapter(opts)` / `createToolCallingPlugin(opts)` / `createModelRegistryPlugin(opts)`——每个实例自持状态,规避模块级单例互踩陷阱
|
|
81
|
+
- **请求级参数覆盖**:`chat.send(input, params)` / `chat.stream(input, params, onProgress, onDone)` / `sendExisting(params)` / `sendExistingStream(params, ...)`,合并链 = 适配器默认 ← 会话配置 ← 本次覆盖(近者优先)
|
|
82
|
+
- **capabilities 按生效 model 解析**:model-registry 在 beforeRequest 钩子里按"本次请求的 model"(含请求级覆盖)查表
|
|
83
|
+
- `chat.use(plugin, options)`:options 透传 `plugin.install(chat, options)`
|
|
84
|
+
- `new ChatService({ adapter })`:构造时直接注入适配器
|
|
85
|
+
- 工具定义移入插件实例(不再写 `chat.config.tools`),工具超时改走 `createToolCallingPlugin({ timeout })`
|
|
86
|
+
|
|
87
|
+
### Changed
|
|
88
|
+
- `updateConfig` 收窄为会话层字段(model/temperature/maxTokens/modelParams/system/retry/ephemeralContinue),非白名单或非法值抛 `ConfigurationError`(修复绕过校验问题)
|
|
89
|
+
- openaiAdapter 的 transport(apiKey/baseUrl/headers)从实例自持,单例 `openaiAdapter` 保留为平铺兼容路径
|
|
90
|
+
- `buildRequest(messages, config, systemPrompts)` 的 config 现为合并后的请求参数
|
|
91
|
+
|
|
92
|
+
### Fixed
|
|
93
|
+
- TODO.md 高危 #1:插件共享可变状态——工厂化后实例隔离(单例保留为兼容路径)
|
|
94
|
+
- TODO.md 高危 #3:updateConfig 绕过校验——现在白名单 + 校验
|
|
95
|
+
|
|
96
|
+
## [2.7.0] - 2026-05-25
|
|
97
|
+
|
|
98
|
+
### Added
|
|
99
|
+
- **Ephemeral 临时消息**:_ephemeral: true 标记,仅底部连续时参与请求,其余自动过滤
|
|
100
|
+
- **续写转正**:continueLast / continueLastStream 续写 ephemeral 消息后自动转正(delete _ephemeral)
|
|
101
|
+
- **EventEmitter.emitAsync**:异步触发事件,支持 beforeRequest 等钩子
|
|
102
|
+
- **beforeRequest 钩子**:chat._hooks.on('beforeRequest', ...) 在 buildRequest 前修改 messages
|
|
103
|
+
- **_processResponse 钩子**:tool-calling 改为注册此钩子,不再覆盖 send/stream
|
|
104
|
+
|
|
105
|
+
### Changed
|
|
106
|
+
- MessageFormatter 过滤 ephemeral:识别底部连续段,中间 ephemeral 自动跳过
|
|
107
|
+
- ChatService._request 拆为 4 个私有方法(_addUserMessage / _withRetry / _stream / _handleResult)
|
|
108
|
+
- ool-calling.js 改为注册 _processResponse,不再替换 send/stream
|
|
109
|
+
- dapter.stream 不再接收/操作 streamEntry,_complete 由 ChatService 管理
|
|
110
|
+
- openaiAdapter.stream 约减 20 行(删除所有 entry 相关代码)
|
|
111
|
+
|
|
112
|
+
### Fixed
|
|
113
|
+
- retry 循环重复 push 占位消息(已修复:提到循环外)
|
|
114
|
+
- continueLastStream 被续写消息丢失(已修复:独立占位,ChatService 管理)
|
|
115
|
+
- prefix 续写空 content 被过滤(已修复:MessageFormatter 保留 prefix)
|
|
116
|
+
|
|
117
|
+
## [2.6.0] - 2026-05-23
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024 My Name
|
|
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
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 My Name
|
|
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
21
|
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ A lightweight, modular AI chat framework with plugin system, unified message for
|
|
|
16
16
|
- **Streaming** – Full support for real‑time responses.
|
|
17
17
|
- **Flexible Configuration** – Supports both flat and nested `modelParams` structure.
|
|
18
18
|
- **Event‑Driven** – Built‑in EventEmitter for `message`, `sending`, `error`, `stream-progress` events.
|
|
19
|
+
- **Pipeline Stages (v3.0)** – `chat.pipe()` to hook custom logic at `beforeSend` / `afterSend` without touching the core.
|
|
19
20
|
- **Custom Error Classes** – `APIError`, `NetworkError`, `ConfigurationError`, `ParsingError` for fine‑grained error handling.
|
|
20
21
|
|
|
21
22
|
---
|
|
@@ -26,32 +27,35 @@ A lightweight, modular AI chat framework with plugin system, unified message for
|
|
|
26
27
|
src/
|
|
27
28
|
├── index.js # Public entry point (re-exports)
|
|
28
29
|
├── core/
|
|
29
|
-
│ ├── ChatService.js # Main service: config, send/stream, plugin hosting
|
|
30
|
+
│ ├── ChatService.js # Main service: config, pipeline, send/stream, plugin hosting
|
|
31
|
+
│ ├── Pipeline.js # Sequential pipeline (v3.0: ctx flows through stages)
|
|
30
32
|
│ ├── MessageStore.js # In-memory message list with CRUD helpers
|
|
33
|
+
│ ├── SystemPromptStore.js # Toggleable system prompts
|
|
31
34
|
│ ├── EventEmitter.js # Minimal pub/sub (on/off/emit)
|
|
32
35
|
│ └── Errors.js # Custom error classes
|
|
33
36
|
├── adapters/
|
|
34
|
-
│ └── openai.js # OpenAI‑compatible API adapter (
|
|
37
|
+
│ └── openai.js # OpenAI‑compatible API adapter (protocol via assertAdapter)
|
|
35
38
|
├── plugins/
|
|
36
|
-
│
|
|
39
|
+
│ ├── tool-calling.js # Tool calling plugin (afterSend stage, auto‑detect & loop)
|
|
40
|
+
│ └── model-registry.js # Model capability table (beforeSend stage)
|
|
37
41
|
└── utils/
|
|
42
|
+
├── MessageFormatter.js # Message format conversion (registrable)
|
|
38
43
|
├── typeCheck.js # Type checking helpers
|
|
39
44
|
└── url.js # URL joining utility
|
|
45
|
+
tests/ # node:test suites (core / pipeline / integration)
|
|
40
46
|
```
|
|
41
47
|
|
|
42
48
|
**Data flow**:
|
|
43
49
|
|
|
44
50
|
```
|
|
45
51
|
User calls chat.send(input)
|
|
46
|
-
→ ChatService._request()
|
|
47
|
-
→
|
|
48
|
-
→
|
|
49
|
-
→
|
|
50
|
-
→ adapter.send
|
|
51
|
-
→
|
|
52
|
-
→
|
|
53
|
-
→ emits 'message'
|
|
54
|
-
→ returns assistantMsg
|
|
52
|
+
→ ChatService._request() → ctx flows through stages:
|
|
53
|
+
→ prepareInput (add user msg, merge config, emit 'sending')
|
|
54
|
+
→ beforeSend (internal hook + user beforeSend stages)
|
|
55
|
+
→ autoContinue / buildRequest (→ request body)
|
|
56
|
+
→ send (adapter.send / stream + retry + placeholder)
|
|
57
|
+
→ afterSend (user stages + tool-calling loop)
|
|
58
|
+
→ returns result, emitting 'message' along the way
|
|
55
59
|
```
|
|
56
60
|
|
|
57
61
|
With `toolCallingPlugin`, the flow loops: response → detect tool_calls → execute tools → add tool results → sendExisting → repeat (max 5 iterations).
|
|
@@ -88,6 +92,32 @@ chat.on('message', msg => console.log(msg.content));
|
|
|
88
92
|
await chat.send('Hello!');
|
|
89
93
|
```
|
|
90
94
|
|
|
95
|
+
### Factory Mode (Owned Configuration, Recommended)
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
import { ChatService, createOpenAIAdapter, createToolCallingPlugin } from 'my-ai-chat-framework';
|
|
99
|
+
|
|
100
|
+
// transport + request defaults belong to the adapter
|
|
101
|
+
const adapter = createOpenAIAdapter({
|
|
102
|
+
apiKey: 'your-api-key',
|
|
103
|
+
baseUrl: 'https://api.deepseek.com',
|
|
104
|
+
modelParams: { temperature: 0.8, maxTokens: 2000 }
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const chat = new ChatService({
|
|
108
|
+
adapter, // injected at construction (same as chat.use(adapter))
|
|
109
|
+
model: 'deepseek-chat',
|
|
110
|
+
system: 'You are a helpful assistant'
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
chat.use(createToolCallingPlugin({ timeout: 30000 })); // plugin options
|
|
114
|
+
|
|
115
|
+
// per-request override (this request only)
|
|
116
|
+
await chat.send('Hello', { temperature: 0.2 });
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
> ⚠️ The default exports `openaiAdapter` / `toolCallingPlugin` / `modelRegistryPlugin` are module-level singletons — installing one on multiple `ChatService` instances will clobber shared state. Use the factories (`createXxx`) for multiple instances.
|
|
120
|
+
|
|
91
121
|
### Using `modelParams` (Recommended for Many Parameters)
|
|
92
122
|
|
|
93
123
|
```javascript
|
|
@@ -106,7 +136,7 @@ const chat = new ChatService({
|
|
|
106
136
|
### Registering a Tool
|
|
107
137
|
|
|
108
138
|
```javascript
|
|
109
|
-
chat.registerTool('get_weather', 'Get current weather for a city',
|
|
139
|
+
chat.plugins['tool-calling'].registerTool('get_weather', 'Get current weather for a city',
|
|
110
140
|
async (args) => {
|
|
111
141
|
// args = { city: 'Beijing' }
|
|
112
142
|
return `Weather in ${args.city}: 22°C, sunny`;
|
|
@@ -142,12 +172,31 @@ Converts internal messages to OpenAI‑compatible format. Supports:
|
|
|
142
172
|
|
|
143
173
|
Detects `tool_calls` in assistant responses, executes registered tools, feeds results back, and continues the conversation (up to `maxIterations` = 5).
|
|
144
174
|
|
|
145
|
-
- `chat.registerTool(name, description, executor, parameters?)` – register a tool
|
|
175
|
+
- `chat.plugins['tool-calling'].registerTool(name, description, executor, parameters?)` – register a tool
|
|
146
176
|
- Automatically injects `tool` role messages into the conversation
|
|
147
177
|
- Recovers from tool execution errors gracefully (logs error, returns error message to model)
|
|
148
178
|
|
|
149
179
|
---
|
|
150
180
|
|
|
181
|
+
## 🔧 Extending with Pipeline (v3.0)
|
|
182
|
+
|
|
183
|
+
```javascript
|
|
184
|
+
// Inject a role card before every request
|
|
185
|
+
chat.pipe({ name: 'role-card', phase: 'beforeSend', async run(ctx) {
|
|
186
|
+
ctx.messages.add({ role: 'system', content: 'You are a tsundere CEO.' });
|
|
187
|
+
}});
|
|
188
|
+
|
|
189
|
+
// Post-process the reply
|
|
190
|
+
chat.pipe({ name: 'stamp', phase: 'afterSend', run(ctx) {
|
|
191
|
+
if (ctx.result?.content) ctx.result.content += ' — ' + Date.now();
|
|
192
|
+
}});
|
|
193
|
+
|
|
194
|
+
chat.unpipe('role-card'); // remove
|
|
195
|
+
console.log(chat.pipelineStages); // inspect stage order
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
See `docs/DEVELOPER.md` \u201cWrite a Stage\u201d for the full guide.
|
|
199
|
+
|
|
151
200
|
## 📡 Events (EventEmitter)
|
|
152
201
|
|
|
153
202
|
`ChatService` extends `EventEmitter`. Subscribe with `chat.on(event, handler)`:
|
|
@@ -182,7 +231,7 @@ unsubscribe(); // stop listening
|
|
|
182
231
|
| `chat.use(plugin)` | `this` | Install a plugin/adapter |
|
|
183
232
|
| `chat.setAdapter(adapter)` | `void` | Manually set the adapter |
|
|
184
233
|
| `chat.on(event, handler)` | `unsubscribe function` | Subscribe to events |
|
|
185
|
-
| `chat.registerTool(name, desc, fn, params?)` | `this` | Register a tool (requires toolCallingPlugin) |
|
|
234
|
+
| `chat.plugins['tool-calling'].registerTool(name, desc, fn, params?)` | `this` | Register a tool (requires toolCallingPlugin) |
|
|
186
235
|
| `chat.messages` | `MessageStore` | Access the message store directly |
|
|
187
236
|
|
|
188
237
|
---
|
|
@@ -196,10 +245,12 @@ unsubscribe(); // stop listening
|
|
|
196
245
|
| `addAssistant(content, meta?)` | Add an assistant message |
|
|
197
246
|
| `addSystem(content, meta?)` | Add a system message |
|
|
198
247
|
| `addTool(content, toolCallId, meta?)` | Add a tool result message |
|
|
248
|
+
| `addOnceAssistant(content, options?)` | Add a one-shot assistant guide message (defaults to `_ephemeral: true` + `prefix: true`; options: `{ reasoningContent, prefix }`) |
|
|
199
249
|
| `getAll()` | Return a shallow copy of all messages |
|
|
200
250
|
| `getLast()` | Return the last message (or null) |
|
|
201
251
|
| `clear()` | Remove all messages |
|
|
202
252
|
| `undoToLastAssistant()` | Remove messages after the last assistant message |
|
|
253
|
+
| `undoToPreviousUser(id)` | Remove messages from the message with `id` (inclusive) back to, but excluding, the previous user message — useful before resending |
|
|
203
254
|
|
|
204
255
|
**Message format**:
|
|
205
256
|
|
|
@@ -263,6 +314,42 @@ try {
|
|
|
263
314
|
|
|
264
315
|
> Both flat and `modelParams` styles work. `modelParams` takes precedence over top‑level values.
|
|
265
316
|
|
|
317
|
+
### modelParams
|
|
318
|
+
|
|
319
|
+
`modelParams` groups all model-related parameters. Supported fields:
|
|
320
|
+
|
|
321
|
+
| Field | Type | Default | Description |
|
|
322
|
+
|-------|------|---------|-------------|
|
|
323
|
+
| `model` | string | – | Model name (overrides top-level `model`) |
|
|
324
|
+
| `temperature` | number | `0.7` | Sampling temperature (0–2) |
|
|
325
|
+
| `maxTokens` | number | `2000` | Max tokens to generate |
|
|
326
|
+
| `reasoningEffort` | string | – | For `deepseek-reasoner`: `'low'`, `'medium'`, `'high'` |
|
|
327
|
+
| `topP` | number | – | Nucleus sampling (0–1) |
|
|
328
|
+
| `frequencyPenalty` | number | – | Penalize frequent tokens (-2.0–2.0) |
|
|
329
|
+
| `presencePenalty` | number | – | Penalize repeated tokens (-2.0–2.0) |
|
|
330
|
+
| `stop` | string \| string[] | – | Stop sequences |
|
|
331
|
+
| `responseFormat` | object | – | e.g., `{ type: 'json_object' }` |
|
|
332
|
+
| `seed` | number | – | For reproducible results |
|
|
333
|
+
|
|
334
|
+
**Naming convention**: CamelCase fields (`maxTokens`, `topP`) are managed by the adapter (converted to API format). Any unknown field is passed through as‑is—use the provider's native naming (e.g., `snake_case` for OpenAI).
|
|
335
|
+
|
|
336
|
+
```javascript
|
|
337
|
+
const chat = new ChatService({
|
|
338
|
+
apiKey: 'your-api-key',
|
|
339
|
+
baseUrl: 'https://api.deepseek.com',
|
|
340
|
+
model: 'deepseek-chat',
|
|
341
|
+
modelParams: {
|
|
342
|
+
temperature: 0.8,
|
|
343
|
+
maxTokens: 1500,
|
|
344
|
+
topP: 0.9,
|
|
345
|
+
frequencyPenalty: 0.5,
|
|
346
|
+
// unknown fields pass through directly (use API's native names):
|
|
347
|
+
logprobs: true,
|
|
348
|
+
top_logprobs: 5
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
```
|
|
352
|
+
|
|
266
353
|
---
|
|
267
354
|
|
|
268
355
|
## 🧪 Testing
|
|
@@ -275,7 +362,7 @@ echo "DEEPSEEK_API_KEY=sk-xxxxx" > .env
|
|
|
275
362
|
npm test
|
|
276
363
|
```
|
|
277
364
|
|
|
278
|
-
|
|
365
|
+
Unit tests: `npm test` (`tests/core.test.js` + `tests/pipeline.test.js`, no API key needed). Integration test: `npm run test:integration` (`tests/integration.test.js`, requires `DEEPSEEK_API_KEY` in `.env`).
|
|
279
366
|
|
|
280
367
|
---
|
|
281
368
|
|