@chatbi-v/mocks 1.0.1
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 +119 -0
- package/dist/adapter.d.ts +48 -0
- package/dist/adapter.js +260 -0
- package/dist/generator.d.ts +68 -0
- package/dist/generator.js +149 -0
- package/dist/index.cjs +1071 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.js +86 -0
- package/dist/index.mjs +1040 -0
- package/dist/interceptor.d.ts +10 -0
- package/dist/interceptor.js +67 -0
- package/dist/strategies.d.ts +20 -0
- package/dist/strategies.js +127 -0
- package/dist/types.d.ts +75 -0
- package/dist/types.js +7 -0
- package/dist/utils.d.ts +20 -0
- package/dist/utils.js +103 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# @chatbi/mocks
|
|
2
|
+
|
|
3
|
+
ChatBI Mock 数据模块,提供灵活的 JSON 和 SSE (Server-Sent Events) 数据模拟功能。
|
|
4
|
+
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- **多种响应策略**:支持 `json` (普通接口)、`sse` (流式接口)、`sse-page` (带分页的流式接口)。
|
|
8
|
+
- **Mock.js 深度集成**:完全支持 Mock.js 的模板语法(如 `'data|8-12'` 生成数组)。
|
|
9
|
+
- **动态模板插值**:支持在 Mock 模板中使用 `{{$query.param}}` 引用请求参数。
|
|
10
|
+
- **自动扁平化**:SSE 事件流自动扁平化处理,支持批量生成事件。
|
|
11
|
+
|
|
12
|
+
## 快速开始
|
|
13
|
+
|
|
14
|
+
### 1. 定义 Mock Schema
|
|
15
|
+
|
|
16
|
+
在 `api/modules/chat.mock.ts` 或其他 mock 文件中定义接口模拟规则:
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { SUCCESS_CODE } from '@chatbi/core';
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
// 普通 JSON 接口
|
|
23
|
+
getConversationList: {
|
|
24
|
+
type: 'json',
|
|
25
|
+
delay: 200,
|
|
26
|
+
responseSchema: {
|
|
27
|
+
code: SUCCESS_CODE,
|
|
28
|
+
msg: 'success',
|
|
29
|
+
'data|10': [{ id: '@increment', title: '@ctitle' }]
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// SSE 流式接口
|
|
34
|
+
chat: {
|
|
35
|
+
type: 'sse',
|
|
36
|
+
responseSchema: {
|
|
37
|
+
'data|3-5': [{
|
|
38
|
+
event: 'data',
|
|
39
|
+
data: { content: '@cparagraph', role: 'assistant' },
|
|
40
|
+
delay: '@increment(100)'
|
|
41
|
+
}]
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// SSE 分页接口 (适用于历史记录等场景)
|
|
46
|
+
getHistory: {
|
|
47
|
+
type: 'sse-page',
|
|
48
|
+
responseSchema: {
|
|
49
|
+
'data|8-12': [{ // 随机生成 8-12 条数据
|
|
50
|
+
event: 'data',
|
|
51
|
+
data: {
|
|
52
|
+
id: '@guid',
|
|
53
|
+
content: '@csentence',
|
|
54
|
+
role: '@pick(["user", "assistant"])'
|
|
55
|
+
},
|
|
56
|
+
delay: 50
|
|
57
|
+
}]
|
|
58
|
+
},
|
|
59
|
+
// 分页元数据事件(自动计算并在最后推送)
|
|
60
|
+
pageEvent: {
|
|
61
|
+
event: 'page',
|
|
62
|
+
data: {
|
|
63
|
+
pageNo: '{{$query.pageNo}}', // 引用请求参数
|
|
64
|
+
pageSize: 20,
|
|
65
|
+
total: 100,
|
|
66
|
+
hasNext: true
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 核心概念
|
|
74
|
+
|
|
75
|
+
### 策略类型 (type)
|
|
76
|
+
|
|
77
|
+
| 类型 | 描述 |
|
|
78
|
+
|Data|说明|
|
|
79
|
+
|---|---|
|
|
80
|
+
| `json` | 默认类型。返回标准的 JSON 对象。 |
|
|
81
|
+
| `sse` | 返回 `Content-Type: text/event-stream`。将 `responseSchema` 生成的数组扁平化后,按 `delay` 排序依次推送。 |
|
|
82
|
+
| `sse-page` | 基于 `sse`,但在流的末尾会自动追加一个 `page` 事件,用于传递分页元数据。需要配合 `pageEvent` 字段使用。 |
|
|
83
|
+
|
|
84
|
+
### 模板增强功能
|
|
85
|
+
|
|
86
|
+
#### 1. 数组范围生成
|
|
87
|
+
支持 Mock.js 的 Key 规则生成指定数量的事件。在 SSE 场景下,生成的数组会被自动展开(flatten),确保每个数组元素作为一个独立的 SSE 事件发送。
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
{
|
|
91
|
+
'data|8-12': [ // 随机生成 8 到 12 个事件
|
|
92
|
+
{ event: 'data', ... }
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### 2. 请求参数引用与计算
|
|
98
|
+
在字符串值中可以使用 `{{$query.xxx}}` 语法引用 URL 查询参数。支持简单的 JavaScript 表达式计算。
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
{
|
|
102
|
+
// 假设请求 URL 为 ?pageNo=2
|
|
103
|
+
pageNo: '{{$query.pageNo}}', // 结果: 2 (自动转换类型)
|
|
104
|
+
offset: '{{$query.pageNo * 10}}', // 结果: 20
|
|
105
|
+
desc: '当前是第 {{$query.pageNo}} 页' // 结果: "当前是第 2 页"
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### SSE 数据结构约定
|
|
110
|
+
|
|
111
|
+
为了配合前端的 `StreamStrategy`,SSE 事件通常遵循以下结构:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
interface MockEvent {
|
|
115
|
+
event: string; // 事件类型:data, log, error, page, todos
|
|
116
|
+
data: any; // 事件载荷
|
|
117
|
+
delay?: number; // 延迟毫秒数(相对于流开始时间)
|
|
118
|
+
}
|
|
119
|
+
```
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ApiAdapter, ApiEndpointConfig, ApiRequestConfig, StreamCallbacks } from '@chatbi-v/core';
|
|
2
|
+
import { MockGeneratorStrategy } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* 基于 Mock.js 的请求适配器
|
|
5
|
+
* @description 根据 API 配置中的 responseSchema 自动生成 Mock 数据,支持普通 JSON 和流式 SSE 响应
|
|
6
|
+
*/
|
|
7
|
+
export declare class MockAdapter implements ApiAdapter {
|
|
8
|
+
private delay;
|
|
9
|
+
private static strategies;
|
|
10
|
+
/**
|
|
11
|
+
* 构造函数
|
|
12
|
+
* @param delay 全局模拟延迟时间(毫秒),默认 300ms
|
|
13
|
+
*/
|
|
14
|
+
constructor(delay?: number);
|
|
15
|
+
/**
|
|
16
|
+
* 注册自定义 Mock 生成策略
|
|
17
|
+
* @param key 策略唯一标识符 (e.g., 'chat_stream', 'history_stream')
|
|
18
|
+
* @param strategy 实现了 MockGeneratorStrategy 接口的策略实例
|
|
19
|
+
*/
|
|
20
|
+
static registerStrategy(key: string, strategy: MockGeneratorStrategy): void;
|
|
21
|
+
/**
|
|
22
|
+
* 处理普通 HTTP 请求(非流式)
|
|
23
|
+
* @param config 请求配置对象
|
|
24
|
+
* @param endpointConfig API 端点配置,包含 responseSchema
|
|
25
|
+
* @returns Promise 返回模拟的响应数据
|
|
26
|
+
*/
|
|
27
|
+
request<T = any>(config: ApiRequestConfig, endpointConfig?: ApiEndpointConfig): Promise<T>;
|
|
28
|
+
/**
|
|
29
|
+
* 处理流式请求 (SSE)
|
|
30
|
+
* @param config 请求配置对象
|
|
31
|
+
* @param callbacks 流式回调函数集合 (onMessage, onFinish, onError)
|
|
32
|
+
* @param endpointConfig API 端点配置,包含 responseSchema
|
|
33
|
+
*/
|
|
34
|
+
stream(config: ApiRequestConfig, callbacks: StreamCallbacks, endpointConfig?: ApiEndpointConfig): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* 判断是否为遗留的 schema 格式
|
|
37
|
+
*/
|
|
38
|
+
private isLegacySchema;
|
|
39
|
+
/**
|
|
40
|
+
* 判断是否为新版配置格式
|
|
41
|
+
* @description 检查配置对象中是否包含有效的 type 字段
|
|
42
|
+
*/
|
|
43
|
+
private isNewConfig;
|
|
44
|
+
/**
|
|
45
|
+
* 生成遗留的高级 schema 数据块
|
|
46
|
+
*/
|
|
47
|
+
private generateAdvancedChunks;
|
|
48
|
+
}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { createLogger, dateUtils, } from '@chatbi-v/core';
|
|
2
|
+
import Mock from 'mockjs';
|
|
3
|
+
import { ChatStreamStrategy, HistoryStreamStrategy, StrategyFactory } from './strategies';
|
|
4
|
+
import { sleep } from './utils';
|
|
5
|
+
const logger = createLogger('MockAdapter');
|
|
6
|
+
/**
|
|
7
|
+
* 基于 Mock.js 的请求适配器
|
|
8
|
+
* @description 根据 API 配置中的 responseSchema 自动生成 Mock 数据,支持普通 JSON 和流式 SSE 响应
|
|
9
|
+
*/
|
|
10
|
+
export class MockAdapter {
|
|
11
|
+
delay;
|
|
12
|
+
// 遗留策略(为了兼容旧版代码)
|
|
13
|
+
static strategies = {
|
|
14
|
+
chat_stream: new ChatStreamStrategy(),
|
|
15
|
+
history_stream: new HistoryStreamStrategy(),
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* 构造函数
|
|
19
|
+
* @param delay 全局模拟延迟时间(毫秒),默认 300ms
|
|
20
|
+
*/
|
|
21
|
+
constructor(delay = 300) {
|
|
22
|
+
this.delay = delay;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 注册自定义 Mock 生成策略
|
|
26
|
+
* @param key 策略唯一标识符 (e.g., 'chat_stream', 'history_stream')
|
|
27
|
+
* @param strategy 实现了 MockGeneratorStrategy 接口的策略实例
|
|
28
|
+
*/
|
|
29
|
+
static registerStrategy(key, strategy) {
|
|
30
|
+
this.strategies[key] = strategy;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 处理普通 HTTP 请求(非流式)
|
|
34
|
+
* @param config 请求配置对象
|
|
35
|
+
* @param endpointConfig API 端点配置,包含 responseSchema
|
|
36
|
+
* @returns Promise 返回模拟的响应数据
|
|
37
|
+
*/
|
|
38
|
+
async request(config, endpointConfig) {
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
setTimeout(() => {
|
|
41
|
+
if (!endpointConfig || !endpointConfig.responseSchema) {
|
|
42
|
+
logger.warn(`未找到响应架构配置: ${config.url}`);
|
|
43
|
+
resolve({});
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
let schema = endpointConfig.responseSchema;
|
|
47
|
+
let mockData;
|
|
48
|
+
// 支持函数式 schema,允许根据请求参数动态生成 schema
|
|
49
|
+
if (typeof schema === 'function') {
|
|
50
|
+
schema = schema(config);
|
|
51
|
+
}
|
|
52
|
+
// 构造有效配置对象,合并 endpointConfig 顶层属性
|
|
53
|
+
const effectiveConfig = {
|
|
54
|
+
type: endpointConfig.type,
|
|
55
|
+
pageEvent: endpointConfig.pageEvent,
|
|
56
|
+
responseSchema: schema,
|
|
57
|
+
...(typeof schema === 'object' ? schema : {}),
|
|
58
|
+
};
|
|
59
|
+
// 1. 字符串 Schema:直接作为结果返回(极少情况)
|
|
60
|
+
if (typeof schema === 'string') {
|
|
61
|
+
mockData = schema;
|
|
62
|
+
}
|
|
63
|
+
// 2. 遗留的高级 Schema (通过 _type 字段识别)
|
|
64
|
+
else if (this.isLegacySchema(schema)) {
|
|
65
|
+
const strategy = MockAdapter.strategies[schema._type];
|
|
66
|
+
if (strategy) {
|
|
67
|
+
const chunks = strategy.generate(schema);
|
|
68
|
+
// 将流式块合并为单个字符串,并清理 SSE 格式标记
|
|
69
|
+
mockData = chunks
|
|
70
|
+
.join('')
|
|
71
|
+
.replace(/event: data\ndata: /g, '')
|
|
72
|
+
.replace(/\n\n/g, '');
|
|
73
|
+
try {
|
|
74
|
+
mockData = JSON.parse(mockData);
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
// 忽略解析错误,保持原始数据格式
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
logger.warn(`未找到对应的策略类型: ${schema._type}`);
|
|
82
|
+
mockData = {};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// 3. 新版 Schema (通过 type: 'json' | 'sse' | 'sse-page' 识别)
|
|
86
|
+
else if (this.isNewConfig(effectiveConfig)) {
|
|
87
|
+
const type = effectiveConfig.type || 'json';
|
|
88
|
+
const strategy = StrategyFactory.getStrategy(type);
|
|
89
|
+
// 对于 request() 调用,我们只处理 'json' 类型
|
|
90
|
+
if (type === 'json') {
|
|
91
|
+
mockData = strategy.process(effectiveConfig, config.params || config.data);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
logger.warn(`在同步请求中调用了流式接口: ${config.url}`);
|
|
95
|
+
mockData = {};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// 4. 默认 MockJS 回退:直接使用 Mock.mock 生成
|
|
99
|
+
else {
|
|
100
|
+
mockData = Mock.mock(schema);
|
|
101
|
+
}
|
|
102
|
+
logger.info(`Request: ${config.method} ${config.url}`, config.data || config.params);
|
|
103
|
+
logger.info(`Response:`, mockData);
|
|
104
|
+
resolve(mockData);
|
|
105
|
+
}, this.delay);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 处理流式请求 (SSE)
|
|
110
|
+
* @param config 请求配置对象
|
|
111
|
+
* @param callbacks 流式回调函数集合 (onMessage, onFinish, onError)
|
|
112
|
+
* @param endpointConfig API 端点配置,包含 responseSchema
|
|
113
|
+
*/
|
|
114
|
+
async stream(config, callbacks, endpointConfig) {
|
|
115
|
+
const { onMessage, onFinish, onError } = callbacks;
|
|
116
|
+
const signal = config.signal;
|
|
117
|
+
return new Promise((resolve) => {
|
|
118
|
+
// 如果请求已被取消,直接结束
|
|
119
|
+
if (signal && signal.aborted) {
|
|
120
|
+
if (onFinish)
|
|
121
|
+
onFinish();
|
|
122
|
+
resolve();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
setTimeout(async () => {
|
|
126
|
+
try {
|
|
127
|
+
if (!endpointConfig || !endpointConfig.responseSchema) {
|
|
128
|
+
logger.warn(`未找到流式响应架构: ${config.url}`);
|
|
129
|
+
if (onFinish)
|
|
130
|
+
onFinish();
|
|
131
|
+
resolve();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const requestStart = dateUtils.now();
|
|
135
|
+
logger.info(`[SSE Start] Request: ${config.method} ${config.url}`, {
|
|
136
|
+
params: config.data || config.params,
|
|
137
|
+
time: dateUtils.dayjs().toISOString(),
|
|
138
|
+
});
|
|
139
|
+
let schema = endpointConfig.responseSchema;
|
|
140
|
+
// 支持函数式 schema
|
|
141
|
+
if (typeof schema === 'function') {
|
|
142
|
+
schema = schema(config);
|
|
143
|
+
}
|
|
144
|
+
// 构造有效配置对象,合并 endpointConfig 顶层属性
|
|
145
|
+
const effectiveConfig = {
|
|
146
|
+
type: endpointConfig.type,
|
|
147
|
+
pageEvent: endpointConfig.pageEvent,
|
|
148
|
+
responseSchema: schema,
|
|
149
|
+
...(typeof schema === 'object' ? schema : {}),
|
|
150
|
+
};
|
|
151
|
+
// 1. 新版 Schema 策略 (sse, sse-page)
|
|
152
|
+
if (this.isNewConfig(effectiveConfig)) {
|
|
153
|
+
const type = effectiveConfig.type || 'sse';
|
|
154
|
+
const strategy = StrategyFactory.getStrategy(type);
|
|
155
|
+
// 策略处理返回 MockEvent 数组
|
|
156
|
+
const events = strategy.process(effectiveConfig, config.params || config.data);
|
|
157
|
+
if (Array.isArray(events)) {
|
|
158
|
+
const startTime = dateUtils.now();
|
|
159
|
+
let eventCount = 0;
|
|
160
|
+
logger.info(`[SSE Processing] Generated ${events.length} events for ${type} strategy`);
|
|
161
|
+
for (const event of events) {
|
|
162
|
+
if (signal && signal.aborted) {
|
|
163
|
+
logger.info(`[SSE Abort] Stream aborted by user after ${dateUtils.now() - requestStart}ms`);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
// 计算延迟
|
|
167
|
+
const delay = typeof event.delay === 'number'
|
|
168
|
+
? event.delay
|
|
169
|
+
: parseInt(String(event.delay || 0));
|
|
170
|
+
const elapsed = dateUtils.now() - startTime;
|
|
171
|
+
const remaining = Math.max(0, delay - elapsed);
|
|
172
|
+
if (remaining > 0) {
|
|
173
|
+
await sleep(remaining);
|
|
174
|
+
}
|
|
175
|
+
if (signal && signal.aborted)
|
|
176
|
+
break;
|
|
177
|
+
// 构造 SSE 格式数据块
|
|
178
|
+
const chunk = `event: ${event.event}\ndata: ${JSON.stringify(event.data)}\n\n`;
|
|
179
|
+
// 记录关键事件日志
|
|
180
|
+
if (['error', 'todos', 'page'].includes(event.event)) {
|
|
181
|
+
logger.info(`[SSE Event] Emitting special event: ${event.event}`, event.data);
|
|
182
|
+
}
|
|
183
|
+
else if (eventCount === 0 || eventCount === events.length - 1) {
|
|
184
|
+
logger.info(`[SSE Event] Emitting ${eventCount === 0 ? 'first' : 'last'} data event`, { preview: JSON.stringify(event.data).slice(0, 50) + '...' });
|
|
185
|
+
}
|
|
186
|
+
if (onMessage)
|
|
187
|
+
onMessage(chunk);
|
|
188
|
+
eventCount++;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// 2. 遗留策略处理
|
|
193
|
+
else {
|
|
194
|
+
let chunks = [];
|
|
195
|
+
if (typeof schema === 'string') {
|
|
196
|
+
chunks = schema.split('\n\n').map((chunk) => chunk + '\n\n');
|
|
197
|
+
}
|
|
198
|
+
else if (this.isLegacySchema(schema)) {
|
|
199
|
+
chunks = this.generateAdvancedChunks(schema);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
const mockData = Mock.mock(schema);
|
|
203
|
+
chunks = [`data: ${JSON.stringify(mockData)}\n\n`];
|
|
204
|
+
}
|
|
205
|
+
for (const chunk of chunks) {
|
|
206
|
+
if (signal && signal.aborted) {
|
|
207
|
+
logger.info('[SSE Abort] Stream aborted by user');
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
if (chunk.trim()) {
|
|
211
|
+
if (onMessage)
|
|
212
|
+
onMessage(chunk);
|
|
213
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (!signal || !signal.aborted) {
|
|
218
|
+
logger.info(`[SSE Complete] Stream finished successfully in ${dateUtils.now() - requestStart}ms`);
|
|
219
|
+
if (onFinish)
|
|
220
|
+
onFinish();
|
|
221
|
+
}
|
|
222
|
+
resolve();
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
logger.error(`[SSE Error] Stream processing failed`, error);
|
|
226
|
+
if (onError)
|
|
227
|
+
onError(error);
|
|
228
|
+
resolve();
|
|
229
|
+
}
|
|
230
|
+
}, this.delay);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* 判断是否为遗留的 schema 格式
|
|
235
|
+
*/
|
|
236
|
+
isLegacySchema(schema) {
|
|
237
|
+
return schema && typeof schema === 'object' && '_type' in schema;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* 判断是否为新版配置格式
|
|
241
|
+
* @description 检查配置对象中是否包含有效的 type 字段
|
|
242
|
+
*/
|
|
243
|
+
isNewConfig(config) {
|
|
244
|
+
return (config &&
|
|
245
|
+
typeof config === 'object' &&
|
|
246
|
+
(['json', 'sse', 'sse-page'].includes(config.type) || 'responseSchema' in config) // 备用检查,如果 schema 内部定义了结构
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* 生成遗留的高级 schema 数据块
|
|
251
|
+
*/
|
|
252
|
+
generateAdvancedChunks(schema) {
|
|
253
|
+
const strategy = MockAdapter.strategies[schema._type];
|
|
254
|
+
if (strategy) {
|
|
255
|
+
return strategy.generate(schema);
|
|
256
|
+
}
|
|
257
|
+
logger.warn(`未找到对应的策略类型: ${schema._type}`);
|
|
258
|
+
return [];
|
|
259
|
+
}
|
|
260
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock 响应生成器
|
|
3
|
+
* @description 用于动态生成流式响应数据,支持阶段流转、日志插入和内容分块
|
|
4
|
+
*/
|
|
5
|
+
export declare class MockResponseGenerator {
|
|
6
|
+
private events;
|
|
7
|
+
private sessionId;
|
|
8
|
+
private agentName;
|
|
9
|
+
private currentTodos;
|
|
10
|
+
constructor(agentName?: string, sessionId?: string);
|
|
11
|
+
/**
|
|
12
|
+
* 生成历史消息流
|
|
13
|
+
*/
|
|
14
|
+
emitHistory(history: {
|
|
15
|
+
role: string;
|
|
16
|
+
content: string;
|
|
17
|
+
createTime: string;
|
|
18
|
+
todos?: any[];
|
|
19
|
+
}[]): this;
|
|
20
|
+
/**
|
|
21
|
+
* 初始化执行计划
|
|
22
|
+
* @param steps 计划步骤列表
|
|
23
|
+
*/
|
|
24
|
+
initPlan(steps: string[]): this;
|
|
25
|
+
/**
|
|
26
|
+
* 更新执行计划状态
|
|
27
|
+
* @param activeIndex 当前正在进行的步骤索引
|
|
28
|
+
*/
|
|
29
|
+
updatePlanStatus(activeIndex: number): this;
|
|
30
|
+
/**
|
|
31
|
+
* 标记所有计划为完成
|
|
32
|
+
*/
|
|
33
|
+
completePlan(): this;
|
|
34
|
+
/**
|
|
35
|
+
* 添加日志
|
|
36
|
+
* @param content 日志内容
|
|
37
|
+
* @param type 日志类型
|
|
38
|
+
* @param agentName 可选的 Agent 名称
|
|
39
|
+
*/
|
|
40
|
+
addLog(content: string, type?: 'info' | 'warning' | 'error', agentName?: string): this;
|
|
41
|
+
/**
|
|
42
|
+
* 添加系统错误事件
|
|
43
|
+
* @param errorCode 错误码
|
|
44
|
+
* @param errorMessage 错误信息
|
|
45
|
+
*/
|
|
46
|
+
addError(errorCode: string, errorMessage: string): this;
|
|
47
|
+
/**
|
|
48
|
+
* 添加流式内容块
|
|
49
|
+
* @param content 完整内容
|
|
50
|
+
* @param chunkSize 分块大小
|
|
51
|
+
*/
|
|
52
|
+
streamContent(content: string, chunkSize?: number): this;
|
|
53
|
+
/**
|
|
54
|
+
* 结束流
|
|
55
|
+
*/
|
|
56
|
+
finish(): this;
|
|
57
|
+
/**
|
|
58
|
+
* 生成 A2UI 响应
|
|
59
|
+
* @param component A2UI 组件配置对象
|
|
60
|
+
*/
|
|
61
|
+
emitA2UI(component: Record<string, any>): this;
|
|
62
|
+
/**
|
|
63
|
+
* 生成最终的响应字符串
|
|
64
|
+
*/
|
|
65
|
+
toString(): string;
|
|
66
|
+
private pushTodos;
|
|
67
|
+
private pushEvent;
|
|
68
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { dateUtils } from '@chatbi-v/core';
|
|
2
|
+
/**
|
|
3
|
+
* Mock 响应生成器
|
|
4
|
+
* @description 用于动态生成流式响应数据,支持阶段流转、日志插入和内容分块
|
|
5
|
+
*/
|
|
6
|
+
export class MockResponseGenerator {
|
|
7
|
+
events = [];
|
|
8
|
+
sessionId;
|
|
9
|
+
agentName;
|
|
10
|
+
currentTodos = [];
|
|
11
|
+
constructor(agentName = 'Assistant', sessionId) {
|
|
12
|
+
this.sessionId = sessionId || ('conv_' + dateUtils.now());
|
|
13
|
+
this.agentName = agentName;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 生成历史消息流
|
|
17
|
+
*/
|
|
18
|
+
emitHistory(history) {
|
|
19
|
+
history.forEach(msg => {
|
|
20
|
+
const data = {
|
|
21
|
+
content: msg.content,
|
|
22
|
+
sessionId: this.sessionId,
|
|
23
|
+
role: msg.role,
|
|
24
|
+
completed: true,
|
|
25
|
+
agentName: this.agentName,
|
|
26
|
+
createTime: msg.createTime
|
|
27
|
+
};
|
|
28
|
+
this.pushEvent('data', data);
|
|
29
|
+
if (msg.todos) {
|
|
30
|
+
this.pushEvent('todos', { items: msg.todos });
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 初始化执行计划
|
|
37
|
+
* @param steps 计划步骤列表
|
|
38
|
+
*/
|
|
39
|
+
initPlan(steps) {
|
|
40
|
+
this.currentTodos = steps.map(step => ({
|
|
41
|
+
content: step,
|
|
42
|
+
status: 'PENDING'
|
|
43
|
+
}));
|
|
44
|
+
this.pushTodos();
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 更新执行计划状态
|
|
49
|
+
* @param activeIndex 当前正在进行的步骤索引
|
|
50
|
+
*/
|
|
51
|
+
updatePlanStatus(activeIndex) {
|
|
52
|
+
this.currentTodos = this.currentTodos.map((todo, index) => {
|
|
53
|
+
if (index < activeIndex) {
|
|
54
|
+
return { ...todo, status: 'COMPLETED' };
|
|
55
|
+
}
|
|
56
|
+
else if (index === activeIndex) {
|
|
57
|
+
return { ...todo, status: 'IN_PROGRESS' };
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
return { ...todo, status: 'PENDING' };
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
this.pushTodos();
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 标记所有计划为完成
|
|
68
|
+
*/
|
|
69
|
+
completePlan() {
|
|
70
|
+
this.currentTodos = this.currentTodos.map(todo => ({ ...todo, status: 'COMPLETED' }));
|
|
71
|
+
this.pushTodos();
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 添加日志
|
|
76
|
+
* @param content 日志内容
|
|
77
|
+
* @param type 日志类型
|
|
78
|
+
* @param agentName 可选的 Agent 名称
|
|
79
|
+
*/
|
|
80
|
+
addLog(content, type = 'info', agentName) {
|
|
81
|
+
this.pushEvent('log', {
|
|
82
|
+
type,
|
|
83
|
+
content,
|
|
84
|
+
agentName: agentName || this.agentName
|
|
85
|
+
});
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 添加系统错误事件
|
|
90
|
+
* @param errorCode 错误码
|
|
91
|
+
* @param errorMessage 错误信息
|
|
92
|
+
*/
|
|
93
|
+
addError(errorCode, errorMessage) {
|
|
94
|
+
this.pushEvent('error', {
|
|
95
|
+
errorCode,
|
|
96
|
+
errorMessage
|
|
97
|
+
});
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 添加流式内容块
|
|
102
|
+
* @param content 完整内容
|
|
103
|
+
* @param chunkSize 分块大小
|
|
104
|
+
*/
|
|
105
|
+
streamContent(content, chunkSize = 20) {
|
|
106
|
+
for (let i = 0; i < content.length; i += chunkSize) {
|
|
107
|
+
const chunk = content.slice(i, i + chunkSize);
|
|
108
|
+
this.pushEvent('data', {
|
|
109
|
+
content: chunk,
|
|
110
|
+
sessionId: this.sessionId,
|
|
111
|
+
completed: false,
|
|
112
|
+
agentName: this.agentName
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 结束流
|
|
119
|
+
*/
|
|
120
|
+
finish() {
|
|
121
|
+
this.pushEvent('data', {
|
|
122
|
+
content: '',
|
|
123
|
+
sessionId: this.sessionId,
|
|
124
|
+
completed: true,
|
|
125
|
+
agentName: this.agentName
|
|
126
|
+
});
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 生成 A2UI 响应
|
|
131
|
+
* @param component A2UI 组件配置对象
|
|
132
|
+
*/
|
|
133
|
+
emitA2UI(component) {
|
|
134
|
+
const content = JSON.stringify(component, null, 2);
|
|
135
|
+
return this.streamContent(content);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* 生成最终的响应字符串
|
|
139
|
+
*/
|
|
140
|
+
toString() {
|
|
141
|
+
return this.events.join('\n');
|
|
142
|
+
}
|
|
143
|
+
pushTodos() {
|
|
144
|
+
this.pushEvent('todos', { items: this.currentTodos });
|
|
145
|
+
}
|
|
146
|
+
pushEvent(type, data) {
|
|
147
|
+
this.events.push(`event: ${type}\ndata: ${JSON.stringify(data)}\n`);
|
|
148
|
+
}
|
|
149
|
+
}
|