@userdaoo/iflow-api-bridge 0.1.0 → 0.1.2
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 +108 -135
- package/dist/adapter.d.ts +15 -24
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +107 -45
- package/dist/adapter.js.map +1 -1
- package/dist/openai/transformer.d.ts +6 -31
- package/dist/openai/transformer.d.ts.map +1 -1
- package/dist/openai/transformer.js +28 -36
- package/dist/openai/transformer.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +30 -19
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter.ts +144 -46
- package/src/openai/transformer.ts +33 -37
- package/src/server.ts +46 -21
package/README.md
CHANGED
|
@@ -1,195 +1,168 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @userdaoo/iflow-api-bridge
|
|
2
2
|
|
|
3
|
-
将 iFlow
|
|
3
|
+
iFlow API 桥接服务 - 将 iFlow CLI 封装为 OpenAI 兼容的 API
|
|
4
4
|
|
|
5
5
|
## 特性
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- 📦 **无状态**:不存储会话状态,每次请求独立处理
|
|
7
|
+
- ✅ OpenAI API 兼容 (`/v1/chat/completions`)
|
|
8
|
+
- ✅ 支持流式 (SSE) 和非流式响应
|
|
9
|
+
- ✅ 多模型支持 (GLM、DeepSeek、Kimi、Qwen、MiniMax 等)
|
|
10
|
+
- ✅ 上下文管理 - 超时后仍能保持对话连续性
|
|
11
|
+
- ✅ 支持自定义对话 ID 追踪会话
|
|
13
12
|
|
|
14
13
|
## 安装
|
|
15
14
|
|
|
16
15
|
```bash
|
|
17
|
-
npm install -g @
|
|
16
|
+
npm install -g @userdaoo/iflow-api-bridge
|
|
18
17
|
```
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
### 1. 启动服务
|
|
19
|
+
或使用 npx:
|
|
23
20
|
|
|
24
21
|
```bash
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
npx @userdaoo/iflow-api-bridge --port 8080
|
|
23
|
+
```
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
iflow-api-server --port 3000
|
|
25
|
+
## 使用方法
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
iflow-api-server --model claude
|
|
27
|
+
### 1. 启动服务
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
```bash
|
|
30
|
+
# 使用默认模型 (GLM-4.7)
|
|
31
|
+
iflow-api-server --port 8080
|
|
32
|
+
|
|
33
|
+
# 指定模型
|
|
34
|
+
iflow-api-server --port 8080 --model kimi-k2.5
|
|
36
35
|
```
|
|
37
36
|
|
|
38
|
-
### 2.
|
|
37
|
+
### 2. 配置客户端
|
|
38
|
+
|
|
39
|
+
**Claude Code / OpenCode:**
|
|
39
40
|
|
|
40
41
|
```bash
|
|
41
|
-
# 设置环境变量
|
|
42
42
|
export OPENAI_BASE_URL=http://localhost:8080/v1
|
|
43
|
-
export OPENAI_API_KEY=sk-anything
|
|
44
|
-
|
|
45
|
-
# 启动 Claude Code
|
|
43
|
+
export OPENAI_API_KEY=sk-anything
|
|
46
44
|
claude
|
|
47
|
-
|
|
48
|
-
# 或使用 OpenCode
|
|
49
|
-
opencode
|
|
50
45
|
```
|
|
51
46
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
### 聊天完成
|
|
47
|
+
### 3. API 调用
|
|
55
48
|
|
|
56
49
|
```bash
|
|
50
|
+
# 非流式请求
|
|
57
51
|
curl http://localhost:8080/v1/chat/completions \
|
|
58
52
|
-H "Content-Type: application/json" \
|
|
59
|
-
-H "Authorization: Bearer sk-anything" \
|
|
60
53
|
-d '{
|
|
61
|
-
"model": "
|
|
62
|
-
"messages": [{"role": "user", "content": "
|
|
63
|
-
"stream": false
|
|
54
|
+
"model": "kimi-k2.5",
|
|
55
|
+
"messages": [{"role": "user", "content": "你好"}]
|
|
64
56
|
}'
|
|
65
|
-
```
|
|
66
57
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
```bash
|
|
58
|
+
# 流式请求
|
|
70
59
|
curl http://localhost:8080/v1/chat/completions \
|
|
71
60
|
-H "Content-Type: application/json" \
|
|
72
|
-
-H "Authorization: Bearer sk-anything" \
|
|
73
61
|
-d '{
|
|
74
|
-
"model": "
|
|
75
|
-
"
|
|
76
|
-
"
|
|
62
|
+
"model": "kimi-k2.5",
|
|
63
|
+
"stream": true,
|
|
64
|
+
"messages": [{"role": "user", "content": "你好"}]
|
|
77
65
|
}'
|
|
78
66
|
```
|
|
79
67
|
|
|
80
|
-
|
|
68
|
+
## 支持的模型
|
|
69
|
+
|
|
70
|
+
实际可用模型取决于你的 iFlow CLI 配置:
|
|
71
|
+
|
|
72
|
+
| 模型 ID | 名称 |
|
|
73
|
+
|---------|------|
|
|
74
|
+
| `glm-4.7` | GLM-4.7 (默认) |
|
|
75
|
+
| `iflow-rome-30ba3b` | iFlow-ROME-30BA3B (预览版) |
|
|
76
|
+
| `deepseek-v3.2` | DeepSeek-V3.2 |
|
|
77
|
+
| `glm-5` | GLM-5 |
|
|
78
|
+
| `qwen3-coder-plus` | Qwen3-Coder-Plus |
|
|
79
|
+
| `kimi-k2-thinking` | Kimi-K2-Thinking |
|
|
80
|
+
| `minimax-m2.5` | MiniMax-M2.5 |
|
|
81
|
+
| `kimi-k2.5` | Kimi-K2.5 |
|
|
82
|
+
| `kimi-k2-0905` | Kimi-K2-0905 |
|
|
83
|
+
|
|
84
|
+
获取最新模型列表:
|
|
81
85
|
|
|
82
86
|
```bash
|
|
83
87
|
curl http://localhost:8080/v1/models
|
|
84
88
|
```
|
|
85
89
|
|
|
86
|
-
|
|
90
|
+
## 上下文管理
|
|
91
|
+
|
|
92
|
+
桥接器支持通过 HTTP Header `X-Conversation-Id` 追踪会话:
|
|
87
93
|
|
|
88
94
|
```bash
|
|
89
|
-
|
|
95
|
+
# 第一轮对话
|
|
96
|
+
curl http://localhost:8080/v1/chat/completions \
|
|
97
|
+
-H "Content-Type: application/json" \
|
|
98
|
+
-H "X-Conversation-Id: my-session-123" \
|
|
99
|
+
-d '{"messages": [{"role": "user", "content": "我叫 Alice"}]}'
|
|
100
|
+
|
|
101
|
+
# 第二轮对话(即使第一轮超时,仍能记住上下文)
|
|
102
|
+
curl http://localhost:8080/v1/chat/completions \
|
|
103
|
+
-H "Content-Type: application/json" \
|
|
104
|
+
-H "X-Conversation-Id: my-session-123" \
|
|
105
|
+
-d '{"messages": [{"role": "user", "content": "我叫什么名字?"}]}'
|
|
90
106
|
```
|
|
91
107
|
|
|
92
|
-
|
|
108
|
+
### 上下文特性
|
|
93
109
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
-m, --model <model> 指定使用的模型 (如 claude, gpt-4)
|
|
101
|
-
-c, --config <path> 配置文件路径
|
|
102
|
-
--log-level <level> 日志级别 (DEBUG|INFO|WARN|ERROR) (默认: INFO)
|
|
103
|
-
-V, --version 显示版本号
|
|
104
|
-
--help 显示帮助信息
|
|
105
|
-
```
|
|
110
|
+
- 每个会话独立保存历史消息
|
|
111
|
+
- 最多保留 20 条历史消息(可配置)
|
|
112
|
+
- 24 小时未访问的会话自动清理
|
|
113
|
+
- 超时后重试不会丢失上下文
|
|
114
|
+
|
|
115
|
+
## API 端点
|
|
106
116
|
|
|
107
|
-
|
|
117
|
+
| 端点 | 描述 |
|
|
118
|
+
|------|------|
|
|
119
|
+
| `GET /health` | 健康检查,返回会话统计 |
|
|
120
|
+
| `GET /v1/models` | 获取可用模型列表 |
|
|
121
|
+
| `POST /v1/chat/completions` | 聊天完成 |
|
|
108
122
|
|
|
109
|
-
|
|
123
|
+
## 命令行选项
|
|
110
124
|
|
|
111
|
-
```json
|
|
112
|
-
{
|
|
113
|
-
"port": 8080,
|
|
114
|
-
"host": "0.0.0.0",
|
|
115
|
-
"cors": true,
|
|
116
|
-
"apiKey": "sk-your-secret-key",
|
|
117
|
-
"model": "claude",
|
|
118
|
-
"logLevel": "INFO"
|
|
119
|
-
}
|
|
120
125
|
```
|
|
126
|
+
Options:
|
|
127
|
+
-p, --port <port> 服务端口 (默认: 8080)
|
|
128
|
+
-h, --host <host> 服务主机 (默认: 0.0.0.0)
|
|
129
|
+
--no-cors 禁用 CORS
|
|
130
|
+
-k, --api-key <key> API Key 认证
|
|
131
|
+
-m, --model <model> 默认模型 (默认: glm-4.7)
|
|
132
|
+
-c, --config <path> 配置文件路径
|
|
133
|
+
--log-level <level> 日志级别 (DEBUG|INFO|WARN|ERROR)
|
|
134
|
+
--help 显示帮助
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## 配置优先级
|
|
121
138
|
|
|
122
|
-
|
|
123
|
-
1. CLI 参数
|
|
139
|
+
1. 命令行参数 (最高优先级)
|
|
124
140
|
2. 环境变量
|
|
125
141
|
3. 配置文件
|
|
126
142
|
4. 默认值
|
|
127
143
|
|
|
128
144
|
## 环境变量
|
|
129
145
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
| `IFLOW_API_LOG_LEVEL` | 日志级别 | `INFO` |
|
|
137
|
-
| `IFLOW_API_CORS` | 启用 CORS | `true` |
|
|
138
|
-
|
|
139
|
-
## 模型配置
|
|
140
|
-
|
|
141
|
-
**注意**:`--model` 参数(或 `IFLOW_API_MODEL` 环境变量)主要用于 API 响应标识,实际的模型选择需要在 iFlow CLI 中配置:
|
|
142
|
-
|
|
143
|
-
1. **启动桥接服务时指定模型名称**(用于 API 响应):
|
|
144
|
-
```bash
|
|
145
|
-
iflow-api-server --model claude-3-opus
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
2. **在 iFlow CLI 中配置实际模型**:
|
|
149
|
-
- 运行 `iflow` 进入交互界面
|
|
150
|
-
- 使用 `/model` 或相关命令切换模型
|
|
151
|
-
- 或使用 iFlow 的配置文件指定默认模型
|
|
152
|
-
|
|
153
|
-
3. **客户端请求时指定模型**:
|
|
154
|
-
```bash
|
|
155
|
-
curl http://localhost:8080/v1/chat/completions \
|
|
156
|
-
-H "Content-Type: application/json" \
|
|
157
|
-
-d '{
|
|
158
|
-
"model": "claude-3-opus",
|
|
159
|
-
"messages": [{"role": "user", "content": "Hello!"}]
|
|
160
|
-
}'
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
桥接服务会将模型信息传递给 iFlow,但实际使用的模型取决于 iFlow 的配置。
|
|
164
|
-
|
|
165
|
-
## 与 Claude Code 集成
|
|
166
|
-
|
|
167
|
-
1. 启动 iflow-api-server:
|
|
168
|
-
```bash
|
|
169
|
-
iflow-api-server --port 8080
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
2. 配置 Claude Code:
|
|
173
|
-
```bash
|
|
174
|
-
export OPENAI_BASE_URL=http://localhost:8080/v1
|
|
175
|
-
export OPENAI_API_KEY=sk-iflow
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
3. 启动 Claude Code 并选择 OpenAI 模型:
|
|
179
|
-
```bash
|
|
180
|
-
claude
|
|
181
|
-
# 在 Claude Code 中选择使用 OpenAI 模型
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
## 架构
|
|
185
|
-
|
|
186
|
-
```
|
|
187
|
-
┌─────────────┐ HTTP/OpenAI API ┌─────────────┐ WebSocket ┌─────────┐
|
|
188
|
-
│ Claude Code │ ─────────────────────────> │ iflow-api │ ──────────────────> │ iFlow │
|
|
189
|
-
│ OpenCode │ │ -server │ │ CLI │
|
|
190
|
-
└─────────────┘ └─────────────┘ └─────────┘
|
|
146
|
+
```bash
|
|
147
|
+
export IFLOW_API_PORT=8080
|
|
148
|
+
export IFLOW_API_HOST=0.0.0.0
|
|
149
|
+
export IFLOW_API_KEY=your-secret-key
|
|
150
|
+
export IFLOW_MODEL=glm-4.7
|
|
151
|
+
export IFLOW_LOG_LEVEL=INFO
|
|
191
152
|
```
|
|
192
153
|
|
|
193
|
-
##
|
|
154
|
+
## 注意事项
|
|
155
|
+
|
|
156
|
+
1. **模型选择**: 实际可用模型取决于 iFlow CLI 的配置和 API key
|
|
157
|
+
2. **超时设置**: 默认 5 分钟超时,防止长时间等待
|
|
158
|
+
3. **上下文保持**: 即使客户端断开,服务端仍保留会话上下文
|
|
159
|
+
4. **资源清理**: 定期清理 24 小时未使用的会话
|
|
160
|
+
|
|
161
|
+
## 依赖
|
|
162
|
+
|
|
163
|
+
- Node.js >= 22
|
|
164
|
+
- iFlow CLI (必须已安装并可运行 `iflow` 命令)
|
|
165
|
+
|
|
166
|
+
## License
|
|
194
167
|
|
|
195
|
-
MIT
|
|
168
|
+
MIT
|
package/dist/adapter.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* iFlow SDK 适配器(使用子进程模式)
|
|
3
|
-
* 封装与 iFlow CLI
|
|
3
|
+
* 封装与 iFlow CLI 的通信,支持对话上下文管理
|
|
4
4
|
*/
|
|
5
5
|
export interface IFlowResponse {
|
|
6
6
|
content: string;
|
|
@@ -22,38 +22,29 @@ export interface IFlowAdapterOptions {
|
|
|
22
22
|
apiKey?: string;
|
|
23
23
|
baseUrl?: string;
|
|
24
24
|
timeout?: number;
|
|
25
|
+
maxHistoryLength?: number;
|
|
25
26
|
}
|
|
26
|
-
/**
|
|
27
|
-
* iFlow 适配器 - 使用子进程模式
|
|
28
|
-
*/
|
|
29
27
|
export declare class IFlowAdapter {
|
|
30
28
|
private options;
|
|
31
29
|
private defaultTimeout;
|
|
30
|
+
private conversations;
|
|
31
|
+
private readonly maxHistoryLength;
|
|
32
|
+
private cleanupInterval;
|
|
32
33
|
constructor(options?: IFlowAdapterOptions);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
*/
|
|
40
|
-
sendMessageStream(prompt: string): AsyncGenerator<StreamChunk>;
|
|
41
|
-
/**
|
|
42
|
-
* 解析 iFlow 输出,提取实际回复内容
|
|
43
|
-
*/
|
|
34
|
+
private getConversation;
|
|
35
|
+
private buildPromptWithHistory;
|
|
36
|
+
private saveMessage;
|
|
37
|
+
private cleanupExpiredConversations;
|
|
38
|
+
sendMessage(conversationId: string, systemMessage: string | undefined, userMessage: string): Promise<IFlowResponse>;
|
|
39
|
+
sendMessageStream(conversationId: string, systemMessage: string | undefined, userMessage: string): AsyncGenerator<StreamChunk>;
|
|
44
40
|
private parseResponse;
|
|
45
|
-
/**
|
|
46
|
-
* 连接到 iFlow
|
|
47
|
-
*/
|
|
48
41
|
connect(): Promise<void>;
|
|
49
|
-
/**
|
|
50
|
-
* 断开连接
|
|
51
|
-
*/
|
|
52
42
|
disconnect(): void;
|
|
53
|
-
/**
|
|
54
|
-
* 检查是否已连接
|
|
55
|
-
*/
|
|
56
43
|
isConnected(): boolean;
|
|
44
|
+
getStats(): {
|
|
45
|
+
conversationCount: number;
|
|
46
|
+
totalMessages: number;
|
|
47
|
+
};
|
|
57
48
|
}
|
|
58
49
|
export default IFlowAdapter;
|
|
59
50
|
//# sourceMappingURL=adapter.d.ts.map
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,UAAU,EAAE,UAAU,GAAG,YAAY,GAAG,OAAO,CAAC;CACjD;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,CAAC;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,UAAU,EAAE,UAAU,GAAG,YAAY,GAAG,OAAO,CAAC;CACjD;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,CAAC;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAYD,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,aAAa,CAAwC;IAC7D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,eAAe,CAA+B;gBAE1C,OAAO,GAAE,mBAAwB;IAQ7C,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,sBAAsB;IAwB9B,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,2BAA2B;IAiB7B,WAAW,CACf,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,aAAa,CAAC;IAyDlB,iBAAiB,CACtB,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,WAAW,EAAE,MAAM,GAClB,cAAc,CAAC,WAAW,CAAC;IAkF9B,OAAO,CAAC,aAAa;IAoBf,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B,UAAU,IAAI,IAAI;IASlB,WAAW,IAAI,OAAO;IAItB,QAAQ,IAAI;QAAE,iBAAiB,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE;CAUjE;AAED,eAAe,YAAY,CAAC"}
|
package/dist/adapter.js
CHANGED
|
@@ -1,31 +1,94 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* iFlow SDK 适配器(使用子进程模式)
|
|
4
|
-
* 封装与 iFlow CLI
|
|
4
|
+
* 封装与 iFlow CLI 的通信,支持对话上下文管理
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.IFlowAdapter = void 0;
|
|
8
8
|
const child_process_1 = require("child_process");
|
|
9
|
-
/**
|
|
10
|
-
* iFlow 适配器 - 使用子进程模式
|
|
11
|
-
*/
|
|
12
9
|
class IFlowAdapter {
|
|
13
10
|
options;
|
|
14
|
-
defaultTimeout =
|
|
11
|
+
defaultTimeout = 300000;
|
|
12
|
+
conversations = new Map();
|
|
13
|
+
maxHistoryLength;
|
|
14
|
+
cleanupInterval = null;
|
|
15
15
|
constructor(options = {}) {
|
|
16
16
|
this.options = options;
|
|
17
|
+
this.maxHistoryLength = options.maxHistoryLength || 20;
|
|
18
|
+
this.cleanupInterval = setInterval(() => {
|
|
19
|
+
this.cleanupExpiredConversations();
|
|
20
|
+
}, 30 * 60 * 1000);
|
|
21
|
+
}
|
|
22
|
+
getConversation(conversationId) {
|
|
23
|
+
let conversation = this.conversations.get(conversationId);
|
|
24
|
+
if (!conversation) {
|
|
25
|
+
conversation = {
|
|
26
|
+
messages: [],
|
|
27
|
+
lastAccessTime: Date.now(),
|
|
28
|
+
};
|
|
29
|
+
this.conversations.set(conversationId, conversation);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
conversation.lastAccessTime = Date.now();
|
|
33
|
+
}
|
|
34
|
+
return conversation;
|
|
35
|
+
}
|
|
36
|
+
buildPromptWithHistory(conversationId, systemMessage, userMessage) {
|
|
37
|
+
const conversation = this.getConversation(conversationId);
|
|
38
|
+
const parts = [];
|
|
39
|
+
if (systemMessage) {
|
|
40
|
+
parts.push(`System: ${systemMessage}`);
|
|
41
|
+
}
|
|
42
|
+
for (const msg of conversation.messages) {
|
|
43
|
+
if (msg.role === 'user') {
|
|
44
|
+
parts.push(`User: ${msg.content}`);
|
|
45
|
+
}
|
|
46
|
+
else if (msg.role === 'assistant') {
|
|
47
|
+
parts.push(`Assistant: ${msg.content}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
parts.push(`User: ${userMessage}`);
|
|
51
|
+
return parts.join('\n\n');
|
|
52
|
+
}
|
|
53
|
+
saveMessage(conversationId, role, content) {
|
|
54
|
+
const conversation = this.getConversation(conversationId);
|
|
55
|
+
conversation.messages.push({ role, content });
|
|
56
|
+
if (conversation.messages.length > this.maxHistoryLength) {
|
|
57
|
+
const systemMessages = conversation.messages.filter(m => m.role === 'system');
|
|
58
|
+
const otherMessages = conversation.messages.filter(m => m.role !== 'system');
|
|
59
|
+
const keepCount = this.maxHistoryLength - systemMessages.length;
|
|
60
|
+
conversation.messages = [
|
|
61
|
+
...systemMessages,
|
|
62
|
+
...otherMessages.slice(-keepCount),
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
conversation.lastAccessTime = Date.now();
|
|
66
|
+
}
|
|
67
|
+
cleanupExpiredConversations() {
|
|
68
|
+
const now = Date.now();
|
|
69
|
+
const expireTime = 24 * 60 * 60 * 1000;
|
|
70
|
+
let cleaned = 0;
|
|
71
|
+
for (const [id, conversation] of this.conversations.entries()) {
|
|
72
|
+
if (now - conversation.lastAccessTime > expireTime) {
|
|
73
|
+
this.conversations.delete(id);
|
|
74
|
+
cleaned++;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (cleaned > 0) {
|
|
78
|
+
console.log(`[Adapter] 清理了 ${cleaned} 个过期会话`);
|
|
79
|
+
}
|
|
17
80
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
81
|
+
async sendMessage(conversationId, systemMessage, userMessage) {
|
|
82
|
+
const prompt = this.buildPromptWithHistory(conversationId, systemMessage, userMessage);
|
|
83
|
+
console.log(`[Adapter] 发送消息(非流式)会话: ${conversationId}`);
|
|
84
|
+
console.log(`[Adapter] 历史消息数: ${this.getConversation(conversationId).messages.length}`);
|
|
85
|
+
this.saveMessage(conversationId, 'user', userMessage);
|
|
23
86
|
return new Promise((resolve, reject) => {
|
|
24
87
|
const args = ['-p', prompt];
|
|
25
88
|
if (this.options.model) {
|
|
26
89
|
args.push('-m', this.options.model);
|
|
27
90
|
}
|
|
28
|
-
console.log('[Adapter] 启动 iflow:', args.join(' '));
|
|
91
|
+
console.log('[Adapter] 启动 iflow:', args.slice(0, 3).join(' ') + '...');
|
|
29
92
|
const child = (0, child_process_1.spawn)('iflow', args, {
|
|
30
93
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
31
94
|
});
|
|
@@ -49,10 +112,10 @@ class IFlowAdapter {
|
|
|
49
112
|
clearTimeout(timeout);
|
|
50
113
|
console.log('[Adapter] iFlow 退出,code:', code);
|
|
51
114
|
if (code !== 0 && code !== null) {
|
|
52
|
-
console.error('[Adapter] iFlow stderr:', stderr);
|
|
115
|
+
console.error('[Adapter] iFlow stderr:', stderr.substring(0, 500));
|
|
53
116
|
}
|
|
54
|
-
// 解析响应
|
|
55
117
|
const content = this.parseResponse(stdout);
|
|
118
|
+
this.saveMessage(conversationId, 'assistant', content);
|
|
56
119
|
resolve({
|
|
57
120
|
content,
|
|
58
121
|
stopReason: code === 0 ? 'end_turn' : 'error',
|
|
@@ -60,21 +123,22 @@ class IFlowAdapter {
|
|
|
60
123
|
});
|
|
61
124
|
});
|
|
62
125
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
126
|
+
async *sendMessageStream(conversationId, systemMessage, userMessage) {
|
|
127
|
+
const prompt = this.buildPromptWithHistory(conversationId, systemMessage, userMessage);
|
|
128
|
+
console.log(`[Adapter] 发送消息(流式)会话: ${conversationId}`);
|
|
129
|
+
console.log(`[Adapter] 历史消息数: ${this.getConversation(conversationId).messages.length}`);
|
|
130
|
+
this.saveMessage(conversationId, 'user', userMessage);
|
|
68
131
|
const args = ['-p', prompt];
|
|
69
132
|
if (this.options.model) {
|
|
70
133
|
args.push('-m', this.options.model);
|
|
71
134
|
}
|
|
72
|
-
console.log('[Adapter] 启动 iflow:', args.join(' '));
|
|
135
|
+
console.log('[Adapter] 启动 iflow:', args.slice(0, 3).join(' ') + '...');
|
|
73
136
|
const child = (0, child_process_1.spawn)('iflow', args, {
|
|
74
137
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
75
138
|
});
|
|
76
139
|
let buffer = '';
|
|
77
140
|
let isDone = false;
|
|
141
|
+
let fullContent = '';
|
|
78
142
|
const timeout = setTimeout(() => {
|
|
79
143
|
if (!isDone) {
|
|
80
144
|
child.kill('SIGTERM');
|
|
@@ -87,20 +151,17 @@ class IFlowAdapter {
|
|
|
87
151
|
});
|
|
88
152
|
child.stderr?.on('data', (data) => {
|
|
89
153
|
const msg = data.toString();
|
|
90
|
-
console.log('[Adapter] iFlow stderr:', msg.trim());
|
|
154
|
+
console.log('[Adapter] iFlow stderr:', msg.trim().substring(0, 200));
|
|
91
155
|
});
|
|
92
|
-
// 模拟流式输出 - 逐字符发送
|
|
93
156
|
let lastSentIndex = 0;
|
|
94
157
|
while (!isDone) {
|
|
95
|
-
// 检查进程是否结束
|
|
96
158
|
if (child.exitCode !== null) {
|
|
97
159
|
isDone = true;
|
|
98
160
|
}
|
|
99
|
-
// 发送新内容
|
|
100
161
|
if (buffer.length > lastSentIndex) {
|
|
101
162
|
const newContent = buffer.slice(lastSentIndex);
|
|
102
163
|
lastSentIndex = buffer.length;
|
|
103
|
-
|
|
164
|
+
fullContent += newContent;
|
|
104
165
|
const lines = newContent.split('\n');
|
|
105
166
|
for (const line of lines) {
|
|
106
167
|
if (line.trim()) {
|
|
@@ -116,25 +177,23 @@ class IFlowAdapter {
|
|
|
116
177
|
}
|
|
117
178
|
}
|
|
118
179
|
clearTimeout(timeout);
|
|
119
|
-
// 发送剩余内容
|
|
120
180
|
if (buffer.length > lastSentIndex) {
|
|
181
|
+
const remaining = buffer.slice(lastSentIndex);
|
|
182
|
+
fullContent += remaining;
|
|
121
183
|
yield {
|
|
122
184
|
type: 'content',
|
|
123
|
-
content:
|
|
185
|
+
content: remaining,
|
|
124
186
|
};
|
|
125
187
|
}
|
|
188
|
+
const finalContent = this.parseResponse(fullContent);
|
|
189
|
+
this.saveMessage(conversationId, 'assistant', finalContent);
|
|
126
190
|
yield { type: 'done' };
|
|
127
191
|
}
|
|
128
|
-
/**
|
|
129
|
-
* 解析 iFlow 输出,提取实际回复内容
|
|
130
|
-
*/
|
|
131
192
|
parseResponse(stdout) {
|
|
132
|
-
// 尝试提取 <Execution Info> 之前的内容作为回复
|
|
133
193
|
const executionInfoMatch = stdout.match(/<Execution Info>[\s\S]*$/);
|
|
134
194
|
if (executionInfoMatch) {
|
|
135
195
|
return stdout.substring(0, executionInfoMatch.index).trim();
|
|
136
196
|
}
|
|
137
|
-
// 如果没有 Execution Info,返回全部内容(去掉开头的警告)
|
|
138
197
|
const lines = stdout.split('\n');
|
|
139
198
|
const startIndex = lines.findIndex((line) => !line.includes('DeprecationWarning') &&
|
|
140
199
|
!line.includes('node:') &&
|
|
@@ -144,27 +203,30 @@ class IFlowAdapter {
|
|
|
144
203
|
}
|
|
145
204
|
return stdout.trim();
|
|
146
205
|
}
|
|
147
|
-
/**
|
|
148
|
-
* 连接到 iFlow
|
|
149
|
-
*/
|
|
150
206
|
async connect() {
|
|
151
|
-
|
|
152
|
-
console.log('[Adapter] 子进程模式已就绪');
|
|
207
|
+
console.log('[Adapter] 子进程模式已就绪,支持上下文管理');
|
|
153
208
|
}
|
|
154
|
-
/**
|
|
155
|
-
* 断开连接
|
|
156
|
-
*/
|
|
157
209
|
disconnect() {
|
|
158
|
-
|
|
159
|
-
|
|
210
|
+
console.log('[Adapter] 子进程模式断开,清理资源...');
|
|
211
|
+
if (this.cleanupInterval) {
|
|
212
|
+
clearInterval(this.cleanupInterval);
|
|
213
|
+
this.cleanupInterval = null;
|
|
214
|
+
}
|
|
215
|
+
this.conversations.clear();
|
|
160
216
|
}
|
|
161
|
-
/**
|
|
162
|
-
* 检查是否已连接
|
|
163
|
-
*/
|
|
164
217
|
isConnected() {
|
|
165
|
-
// 子进程模式总是"已连接"
|
|
166
218
|
return true;
|
|
167
219
|
}
|
|
220
|
+
getStats() {
|
|
221
|
+
let totalMessages = 0;
|
|
222
|
+
for (const conv of this.conversations.values()) {
|
|
223
|
+
totalMessages += conv.messages.length;
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
conversationCount: this.conversations.size,
|
|
227
|
+
totalMessages,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
168
230
|
}
|
|
169
231
|
exports.IFlowAdapter = IFlowAdapter;
|
|
170
232
|
exports.default = IFlowAdapter;
|
package/dist/adapter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,iDAAyD;
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|