@baishuyun/chat-backend 0.0.3 → 0.0.6
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 +24 -0
- package/config/index.ts +8 -10
- package/dist/config/index.js +7 -10
- package/dist/src/app/main.js +2 -2
- package/dist/src/config/hono.config.js +11 -11
- package/dist/src/controllers/form/build/build.controller.js +3 -2
- package/dist/src/controllers/form/fill/batch-fill.controller.js +139 -0
- package/dist/src/controllers/form/fill/createBatchFillingTransformStream.js +124 -0
- package/dist/src/controllers/form/fill/createFieldsFillingResultTransformStream.js +24 -17
- package/dist/src/controllers/form/fill/fill.controller.js +29 -7
- package/dist/src/controllers/form/fill/model.js +12 -1
- package/dist/src/controllers/form/fill/utils.js +40 -13
- package/ecosystem.config.cjs +23 -0
- package/package.json +8 -5
- package/src/app/main.ts +2 -2
- package/src/config/hono.config.ts +12 -12
- package/src/controllers/form/build/build.controller.ts +3 -2
- package/src/controllers/form/build/model.ts +1 -1
- package/src/controllers/form/fill/batch-fill.controller.ts +202 -0
- package/src/controllers/form/fill/createBatchFillingTransformStream.ts +156 -0
- package/src/controllers/form/fill/createFieldsFillingResultTransformStream.ts +29 -20
- package/src/controllers/form/fill/fill.controller.ts +32 -8
- package/src/controllers/form/fill/model.ts +15 -1
- package/src/controllers/form/fill/utils.ts +51 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @baishuyun/chat-backend
|
|
2
2
|
|
|
3
|
+
## 0.0.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @baishuyun/coze-provider@0.0.7
|
|
9
|
+
- @baishuyun/types@1.0.7
|
|
10
|
+
|
|
11
|
+
## 0.0.5
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies
|
|
16
|
+
- @baishuyun/coze-provider@0.0.6
|
|
17
|
+
- @baishuyun/types@1.0.6
|
|
18
|
+
|
|
19
|
+
## 0.0.4
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
- @baishuyun/coze-provider@0.0.5
|
|
25
|
+
- @baishuyun/types@1.0.5
|
|
26
|
+
|
|
3
27
|
## 0.0.3
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
package/config/index.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import path from
|
|
2
|
-
import dotenv from
|
|
3
|
-
import { fileURLToPath } from
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
4
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
});
|
|
14
|
-
}
|
|
8
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
9
|
+
|
|
10
|
+
dotenv.config({
|
|
11
|
+
path: path.resolve(__dirname, isProd ? '../../.env' : '../.env'),
|
|
12
|
+
});
|
package/dist/config/index.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import path from
|
|
2
|
-
import dotenv from
|
|
3
|
-
import { fileURLToPath } from
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
4
|
const __filename = fileURLToPath(import.meta.url);
|
|
5
5
|
const __dirname = path.dirname(__filename);
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
path: path.resolve(__dirname, "../.env"),
|
|
11
|
-
});
|
|
12
|
-
}
|
|
6
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
7
|
+
dotenv.config({
|
|
8
|
+
path: path.resolve(__dirname, isProd ? '../../.env' : '../.env'),
|
|
9
|
+
});
|
package/dist/src/app/main.js
CHANGED
|
@@ -6,8 +6,8 @@ import app from '../config/hono.config.js';
|
|
|
6
6
|
// 子路由
|
|
7
7
|
import { createFormRouter } from '../routes/form/form.route.js';
|
|
8
8
|
// 挂载子路由
|
|
9
|
-
app.route('api/form', createFormRouter());
|
|
9
|
+
app.route('web/api/form', createFormRouter());
|
|
10
10
|
// add test route
|
|
11
|
-
app.get('/api/test', (c) => {
|
|
11
|
+
app.get('web/api/test', (c) => {
|
|
12
12
|
return c.json({ message: 'Test route is working!' });
|
|
13
13
|
});
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { Hono } from
|
|
2
|
-
import { serve } from
|
|
3
|
-
import config from
|
|
4
|
-
import { cors } from
|
|
5
|
-
import { logMiddleware } from
|
|
6
|
-
import { logger } from
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { serve } from '@hono/node-server';
|
|
3
|
+
import config from 'config';
|
|
4
|
+
import { cors } from 'hono/cors';
|
|
5
|
+
import { logMiddleware } from '../logger/log-middleware.js';
|
|
6
|
+
import { logger } from '../logger/index.js';
|
|
7
7
|
/**
|
|
8
8
|
* 公共配置层,跨域、日志等中间件配置
|
|
9
9
|
*/
|
|
10
10
|
const app = new Hono();
|
|
11
11
|
// Enable CORS for all routes starting with /api/
|
|
12
|
-
app.use(
|
|
12
|
+
app.use('web/api/*', cors());
|
|
13
13
|
// Logging middleware
|
|
14
14
|
app.use(logMiddleware);
|
|
15
15
|
// 全局错误处理中间件(捕获所有路由/中间件异常)
|
|
@@ -20,14 +20,14 @@ app.onError((err, c) => {
|
|
|
20
20
|
method: c.req.method,
|
|
21
21
|
path: c.req.path,
|
|
22
22
|
status: 500, // 默认 500 错误
|
|
23
|
-
},
|
|
23
|
+
}, 'Request failed with unhandled error'); // 错误描述
|
|
24
24
|
// 返回统一的 JSON 错误响应
|
|
25
|
-
return c.json({ message:
|
|
25
|
+
return c.json({ message: 'Internal Server Error' }, 500);
|
|
26
26
|
});
|
|
27
27
|
serve({
|
|
28
28
|
fetch: app.fetch,
|
|
29
|
-
hostname: config.get(
|
|
30
|
-
port: config.get(
|
|
29
|
+
hostname: config.get('app.host') || '',
|
|
30
|
+
port: config.get('app.port') || 3001,
|
|
31
31
|
}, (info) => {
|
|
32
32
|
logger.info(`Server is running on http://localhost:${info.port}`);
|
|
33
33
|
});
|
|
@@ -19,14 +19,15 @@ export const buildForm = async (c) => {
|
|
|
19
19
|
const isBuildStage = requestBody.stage === "build";
|
|
20
20
|
const stream = streamText({
|
|
21
21
|
model: createModel([
|
|
22
|
-
createFieldsJsonTransformStream(isBuildStage),
|
|
23
|
-
new SuggestionTransformStream(isBuildStage),
|
|
22
|
+
() => createFieldsJsonTransformStream(isBuildStage),
|
|
23
|
+
() => new SuggestionTransformStream(isBuildStage),
|
|
24
24
|
]),
|
|
25
25
|
messages: convertToModelMessages(requestBody.messages),
|
|
26
26
|
includeRawChunks: true,
|
|
27
27
|
headers: {
|
|
28
28
|
"x-user-stage": requestBody.stage,
|
|
29
29
|
"x-user-id": Date.now().toString(),
|
|
30
|
+
"x-user-token": c.req.header("authorization") || "",
|
|
30
31
|
},
|
|
31
32
|
});
|
|
32
33
|
return stream.toUIMessageStreamResponse();
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {} from 'hono';
|
|
2
|
+
import { convertToModelMessages, streamText, createUIMessageStream, createUIMessageStreamResponse, generateId, } from 'ai';
|
|
3
|
+
import { logger } from '../../../logger/index.js';
|
|
4
|
+
import { createBatchFillingModel } from './model.js';
|
|
5
|
+
import { mode2part } from './utils.js';
|
|
6
|
+
const getModelMessagesFromUserMessages = ({ continueMessageId, currentMsg, formStructure, mode, messages, }) => {
|
|
7
|
+
// init fill
|
|
8
|
+
if (continueMessageId && currentMsg) {
|
|
9
|
+
return convertToModelMessages([
|
|
10
|
+
{
|
|
11
|
+
role: 'user',
|
|
12
|
+
parts: [
|
|
13
|
+
{
|
|
14
|
+
type: 'text',
|
|
15
|
+
text: `formStructure: ${JSON.stringify(formStructure)}`,
|
|
16
|
+
},
|
|
17
|
+
mode2part(mode),
|
|
18
|
+
{
|
|
19
|
+
type: 'text',
|
|
20
|
+
text: currentMsg,
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
]);
|
|
25
|
+
}
|
|
26
|
+
let lastUserMsg = messages.findLast((m) => m.role === 'user') || {
|
|
27
|
+
id: generateId(),
|
|
28
|
+
role: 'user',
|
|
29
|
+
parts: [],
|
|
30
|
+
};
|
|
31
|
+
lastUserMsg.parts.push({
|
|
32
|
+
type: 'text',
|
|
33
|
+
text: `formStructure: ${JSON.stringify(formStructure)}`,
|
|
34
|
+
});
|
|
35
|
+
lastUserMsg.parts.push(mode2part(mode));
|
|
36
|
+
return convertToModelMessages([lastUserMsg]);
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* 表单填写
|
|
40
|
+
* @param c
|
|
41
|
+
* @returns
|
|
42
|
+
*/
|
|
43
|
+
export const batchFillForm = async (c) => {
|
|
44
|
+
let requestBody;
|
|
45
|
+
try {
|
|
46
|
+
const json = await c.req.json();
|
|
47
|
+
requestBody = json;
|
|
48
|
+
}
|
|
49
|
+
catch (_) {
|
|
50
|
+
return c.json({ error: 'Invalid JSON' }, 400);
|
|
51
|
+
}
|
|
52
|
+
const formStructure = requestBody.formStructure;
|
|
53
|
+
const mode = requestBody.mode;
|
|
54
|
+
const continueMessageId = requestBody.continueMessageId; // 新增:继续生成的消息 ID
|
|
55
|
+
const userMessages = requestBody.messages;
|
|
56
|
+
// clear empty user text part message
|
|
57
|
+
const messages = userMessages.filter((msg) => {
|
|
58
|
+
if (msg.role !== 'user') {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
const firstPart = msg.parts[0];
|
|
62
|
+
if (!firstPart) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if (firstPart.type === 'text' && firstPart.text.trim() === '') {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
});
|
|
70
|
+
// 如果是继续生成模式
|
|
71
|
+
if (continueMessageId) {
|
|
72
|
+
return handleContinueGeneration(c, {
|
|
73
|
+
messages,
|
|
74
|
+
formStructure,
|
|
75
|
+
continueMessageId,
|
|
76
|
+
msg: requestBody.msg,
|
|
77
|
+
mode,
|
|
78
|
+
stage: requestBody.stage,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const modelMessages = getModelMessagesFromUserMessages({
|
|
82
|
+
messages,
|
|
83
|
+
mode,
|
|
84
|
+
formStructure,
|
|
85
|
+
});
|
|
86
|
+
// 正常创建新流
|
|
87
|
+
const stream = streamText({
|
|
88
|
+
model: createBatchFillingModel(),
|
|
89
|
+
messages: modelMessages,
|
|
90
|
+
includeRawChunks: true,
|
|
91
|
+
headers: {
|
|
92
|
+
'x-user-stage': requestBody.stage,
|
|
93
|
+
'x-user-token': c.req.header('authorization') || '',
|
|
94
|
+
'x-user-var': JSON.stringify({
|
|
95
|
+
mode: requestBody.mode,
|
|
96
|
+
}),
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
return stream.toUIMessageStreamResponse({
|
|
100
|
+
originalMessages: messages, // 建议添加,便于消息 ID 管理
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* 处理继续生成(扩展同一消息)
|
|
105
|
+
*/
|
|
106
|
+
async function handleContinueGeneration(c, { messages, continueMessageId, mode, msg, formStructure, stage, }) {
|
|
107
|
+
const stream = createUIMessageStream({
|
|
108
|
+
execute: async ({ writer }) => {
|
|
109
|
+
writer.write({
|
|
110
|
+
type: 'start',
|
|
111
|
+
messageId: continueMessageId,
|
|
112
|
+
});
|
|
113
|
+
// 构建模型消息
|
|
114
|
+
const modelMessages = getModelMessagesFromUserMessages({
|
|
115
|
+
messages,
|
|
116
|
+
continueMessageId,
|
|
117
|
+
currentMsg: msg,
|
|
118
|
+
mode,
|
|
119
|
+
formStructure,
|
|
120
|
+
});
|
|
121
|
+
logger.debug(`continue modelMessages ${JSON.stringify(modelMessages)}`);
|
|
122
|
+
const result = streamText({
|
|
123
|
+
model: createBatchFillingModel(),
|
|
124
|
+
messages: modelMessages,
|
|
125
|
+
includeRawChunks: true,
|
|
126
|
+
headers: {
|
|
127
|
+
'x-user-stage': stage,
|
|
128
|
+
'x-user-var': JSON.stringify({
|
|
129
|
+
mode,
|
|
130
|
+
}),
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
writer.merge(result.toUIMessageStream({
|
|
134
|
+
sendStart: false,
|
|
135
|
+
}));
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
return createUIMessageStreamResponse({ stream });
|
|
139
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {} from '@ai-sdk/provider';
|
|
2
|
+
import { JSONParser } from '@streamparser/json';
|
|
3
|
+
import { createTextInfoEnqueuer } from '@baishuyun/coze-provider';
|
|
4
|
+
import { getLastSubFormField } from './utils.js';
|
|
5
|
+
import { fillingChunkGuard as chunkGuard } from './utils.js';
|
|
6
|
+
import { logger } from '../../../logger/index.js';
|
|
7
|
+
export const createBatchFillingResultTransformer = (enableJsonParser) => {
|
|
8
|
+
let parser;
|
|
9
|
+
let id;
|
|
10
|
+
// 解析完成标记:用于等待解析器内部队列处理完毕
|
|
11
|
+
let parseCompleted;
|
|
12
|
+
let resolveParseCompleted;
|
|
13
|
+
const transformer = {
|
|
14
|
+
flush: async (controller) => {
|
|
15
|
+
try {
|
|
16
|
+
if (parser) {
|
|
17
|
+
await parseCompleted;
|
|
18
|
+
logger.debug('stop parser');
|
|
19
|
+
}
|
|
20
|
+
controller.terminate(); // 信号通知下游可读流已关闭
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
controller.error('stop error' + e);
|
|
24
|
+
// controller.terminate(); // 信号通知下游可读流已关闭
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
transform: (chunk, controller) => {
|
|
28
|
+
if (!parser) {
|
|
29
|
+
logger.warn('json parser not init');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const enqueueTextDelta = createTextInfoEnqueuer(controller);
|
|
33
|
+
try {
|
|
34
|
+
if (chunkGuard(chunk)) {
|
|
35
|
+
if ('id' in chunk && 'delta' in chunk) {
|
|
36
|
+
id = chunk.id;
|
|
37
|
+
const regExp = /`?`?`?/;
|
|
38
|
+
let delta = chunk.delta.replace(regExp, '');
|
|
39
|
+
parser.write(delta.replace('json', ''));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// logger.debug(`bypass chunk in batch filling transformer: ${JSON.stringify(chunk)}`);
|
|
44
|
+
controller.enqueue(chunk);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
logger.debug('exception in transform while processing filling chunk');
|
|
49
|
+
controller.error(e);
|
|
50
|
+
enqueueTextDelta('error', {
|
|
51
|
+
type: 'agent-error',
|
|
52
|
+
error: '解析异常,请刷新重试',
|
|
53
|
+
});
|
|
54
|
+
resolveParseCompleted?.();
|
|
55
|
+
controller.terminate(); // 信号通知下游可读流已关闭
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
start: (controller) => {
|
|
59
|
+
parser = new JSONParser({
|
|
60
|
+
paths: ['$.*', '$.*.value.*.*'],
|
|
61
|
+
});
|
|
62
|
+
parseCompleted = new Promise((resolve) => {
|
|
63
|
+
resolveParseCompleted = resolve;
|
|
64
|
+
});
|
|
65
|
+
const enqueueTextDelta = enableJsonParser
|
|
66
|
+
? createTextInfoEnqueuer(controller)
|
|
67
|
+
: (content) => { };
|
|
68
|
+
parser.onValue = (parsedInfo) => {
|
|
69
|
+
const value = parsedInfo.value;
|
|
70
|
+
if (value.error && value.errorMessage === 'timeout') {
|
|
71
|
+
enqueueTextDelta(JSON.stringify(value), {
|
|
72
|
+
type: 'agent-error',
|
|
73
|
+
error: '操作超时,请刷新重试',
|
|
74
|
+
});
|
|
75
|
+
resolveParseCompleted?.();
|
|
76
|
+
controller.terminate(); // 信号通知下游可读流已关闭
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const subFormField = getLastSubFormField(parsedInfo);
|
|
80
|
+
if (subFormField) {
|
|
81
|
+
// enqueueTextDelta(
|
|
82
|
+
// JSON.stringify(value),
|
|
83
|
+
// {
|
|
84
|
+
// type: 'mcp-fields-json',
|
|
85
|
+
// field: subFormField.value,
|
|
86
|
+
// },
|
|
87
|
+
// id,
|
|
88
|
+
// true
|
|
89
|
+
// );
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (value.fieldType === 'subform') {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// logger.debug(`id in onValue: ${id}`);
|
|
96
|
+
enqueueTextDelta(`${JSON.stringify(value)},`, {
|
|
97
|
+
type: 'mcp-fields-json',
|
|
98
|
+
field: value,
|
|
99
|
+
}, id, true);
|
|
100
|
+
};
|
|
101
|
+
let errorLogged = false;
|
|
102
|
+
parser.onError = (err) => {
|
|
103
|
+
if (!errorLogged) {
|
|
104
|
+
console.error('JsonWidgetStream: JSON Parsing Error:', err);
|
|
105
|
+
errorLogged = true;
|
|
106
|
+
}
|
|
107
|
+
enqueueTextDelta('error', {
|
|
108
|
+
type: 'agent-error',
|
|
109
|
+
error: '操作超时,请刷新重试',
|
|
110
|
+
});
|
|
111
|
+
resolveParseCompleted?.();
|
|
112
|
+
controller.terminate(); // 信号通知下游可读流已关闭
|
|
113
|
+
};
|
|
114
|
+
parser.onEnd = () => {
|
|
115
|
+
enqueueTextDelta(' ', JSON.stringify({
|
|
116
|
+
appendConfirm: 'save-fields',
|
|
117
|
+
}));
|
|
118
|
+
logger.debug('mark as end');
|
|
119
|
+
resolveParseCompleted?.();
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
return new TransformStream(transformer);
|
|
124
|
+
};
|
|
@@ -2,6 +2,7 @@ import {} from '@ai-sdk/provider';
|
|
|
2
2
|
import { JSONParser } from '@streamparser/json';
|
|
3
3
|
import { createTextInfoEnqueuer } from '@baishuyun/coze-provider';
|
|
4
4
|
import { getLastSubFormField } from './utils.js';
|
|
5
|
+
import { fillingChunkGuard as chunkGuard } from './utils.js';
|
|
5
6
|
import { logger } from '../../../logger/index.js';
|
|
6
7
|
export const createFieldsFillingResultTransformer = (enableJsonParser) => {
|
|
7
8
|
let parser;
|
|
@@ -9,22 +10,12 @@ export const createFieldsFillingResultTransformer = (enableJsonParser) => {
|
|
|
9
10
|
// 解析完成标记:用于等待解析器内部队列处理完毕
|
|
10
11
|
let parseCompleted;
|
|
11
12
|
let resolveParseCompleted;
|
|
12
|
-
const chunkGuard = (chunk) => {
|
|
13
|
-
if (!enableJsonParser) {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
if (chunk.type !== 'text-delta' || !('delta' in chunk)) {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
return true;
|
|
20
|
-
};
|
|
21
13
|
const transformer = {
|
|
22
14
|
flush: async (controller) => {
|
|
23
15
|
try {
|
|
24
16
|
if (parser) {
|
|
25
|
-
await parseCompleted;
|
|
17
|
+
await parseCompleted;
|
|
26
18
|
logger.debug('stop parser');
|
|
27
|
-
// parser.end(); // 此时调用 end() 不会出现未完成 Token 异常
|
|
28
19
|
}
|
|
29
20
|
controller.terminate(); // 信号通知下游可读流已关闭
|
|
30
21
|
}
|
|
@@ -38,6 +29,7 @@ export const createFieldsFillingResultTransformer = (enableJsonParser) => {
|
|
|
38
29
|
logger.warn('json parser not init');
|
|
39
30
|
return;
|
|
40
31
|
}
|
|
32
|
+
const enqueueTextDelta = createTextInfoEnqueuer(controller);
|
|
41
33
|
try {
|
|
42
34
|
if (chunkGuard(chunk)) {
|
|
43
35
|
if ('id' in chunk && 'delta' in chunk) {
|
|
@@ -52,7 +44,14 @@ export const createFieldsFillingResultTransformer = (enableJsonParser) => {
|
|
|
52
44
|
}
|
|
53
45
|
}
|
|
54
46
|
catch (e) {
|
|
47
|
+
logger.debug('exception in transform while processing filling chunk');
|
|
55
48
|
controller.error(e);
|
|
49
|
+
enqueueTextDelta('error', {
|
|
50
|
+
type: 'agent-error',
|
|
51
|
+
error: '解析异常,请刷新重试',
|
|
52
|
+
});
|
|
53
|
+
resolveParseCompleted?.();
|
|
54
|
+
controller.terminate(); // 信号通知下游可读流已关闭
|
|
56
55
|
}
|
|
57
56
|
},
|
|
58
57
|
start: (controller) => {
|
|
@@ -67,6 +66,15 @@ export const createFieldsFillingResultTransformer = (enableJsonParser) => {
|
|
|
67
66
|
: (content) => { };
|
|
68
67
|
parser.onValue = (parsedInfo) => {
|
|
69
68
|
const value = parsedInfo.value;
|
|
69
|
+
if (value.error && value.errorMessage === 'timeout') {
|
|
70
|
+
enqueueTextDelta(JSON.stringify(value), {
|
|
71
|
+
type: 'agent-error',
|
|
72
|
+
error: '操作超时,请刷新重试',
|
|
73
|
+
});
|
|
74
|
+
resolveParseCompleted?.();
|
|
75
|
+
controller.terminate(); // 信号通知下游可读流已关闭
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
70
78
|
const subFormField = getLastSubFormField(parsedInfo);
|
|
71
79
|
if (subFormField) {
|
|
72
80
|
enqueueTextDelta(JSON.stringify(value), {
|
|
@@ -85,16 +93,15 @@ export const createFieldsFillingResultTransformer = (enableJsonParser) => {
|
|
|
85
93
|
};
|
|
86
94
|
let errorLogged = false;
|
|
87
95
|
parser.onError = (err) => {
|
|
88
|
-
// controller.enqueue({
|
|
89
|
-
// type: "error",
|
|
90
|
-
// error: "JsonWidgetStream: JSON Parsing Error:" + err.message,
|
|
91
|
-
// });
|
|
92
96
|
if (!errorLogged) {
|
|
93
97
|
console.error('JsonWidgetStream: JSON Parsing Error:', err);
|
|
94
98
|
errorLogged = true;
|
|
95
99
|
}
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
enqueueTextDelta('error', {
|
|
101
|
+
type: 'agent-error',
|
|
102
|
+
error: '操作超时,请刷新重试',
|
|
103
|
+
});
|
|
104
|
+
resolveParseCompleted?.();
|
|
98
105
|
controller.terminate(); // 信号通知下游可读流已关闭
|
|
99
106
|
};
|
|
100
107
|
parser.onEnd = () => {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {} from 'hono';
|
|
2
|
-
import { convertToModelMessages, streamText } from 'ai';
|
|
2
|
+
import { convertToModelMessages, streamText, generateId } from 'ai';
|
|
3
3
|
import { logger } from '../../../logger/index.js';
|
|
4
4
|
import { createFillingModel } from './model.js';
|
|
5
|
+
import { mode2part } from './utils.js';
|
|
6
|
+
import { batchFillForm } from './batch-fill.controller.js';
|
|
5
7
|
/**
|
|
6
8
|
* 表单填写
|
|
7
9
|
* @param c
|
|
@@ -17,12 +19,26 @@ export const fillForm = async (c) => {
|
|
|
17
19
|
return c.json({ error: 'Invalid JSON' }, 400);
|
|
18
20
|
}
|
|
19
21
|
const formStructure = requestBody.formStructure;
|
|
22
|
+
const mode = requestBody.mode;
|
|
23
|
+
const num = requestBody.count;
|
|
24
|
+
const messages = requestBody.messages;
|
|
25
|
+
// 如果是批量填写,调用批量填写接口
|
|
26
|
+
if (mode === 'batch') {
|
|
27
|
+
return batchFillForm(c);
|
|
28
|
+
}
|
|
20
29
|
if (formStructure) {
|
|
30
|
+
const extraPart = mode2part(mode, num);
|
|
21
31
|
try {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
messages.push({
|
|
33
|
+
id: generateId(),
|
|
34
|
+
role: 'user',
|
|
35
|
+
parts: [
|
|
36
|
+
{
|
|
37
|
+
type: 'text',
|
|
38
|
+
text: `formStructure: ${JSON.stringify(formStructure)}`,
|
|
39
|
+
},
|
|
40
|
+
extraPart,
|
|
41
|
+
],
|
|
26
42
|
});
|
|
27
43
|
}
|
|
28
44
|
catch (e) {
|
|
@@ -31,11 +47,17 @@ export const fillForm = async (c) => {
|
|
|
31
47
|
}
|
|
32
48
|
const stream = streamText({
|
|
33
49
|
model: createFillingModel(),
|
|
34
|
-
messages: convertToModelMessages(
|
|
50
|
+
messages: convertToModelMessages(messages),
|
|
35
51
|
includeRawChunks: true,
|
|
36
52
|
headers: {
|
|
37
53
|
'x-user-stage': requestBody.stage,
|
|
54
|
+
'x-user-var': JSON.stringify({
|
|
55
|
+
mode: requestBody.mode,
|
|
56
|
+
}),
|
|
57
|
+
'x-user-token': c.req.header('authorization') || '',
|
|
38
58
|
},
|
|
39
59
|
});
|
|
40
|
-
return stream.toUIMessageStreamResponse(
|
|
60
|
+
return stream.toUIMessageStreamResponse({
|
|
61
|
+
originalMessages: messages, // 建议添加,便于消息 ID 管理
|
|
62
|
+
});
|
|
41
63
|
};
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import { createCoze } from '@baishuyun/coze-provider';
|
|
2
2
|
import { createFieldsFillingResultTransformer } from './createFieldsFillingResultTransformStream.js';
|
|
3
3
|
import config from 'config';
|
|
4
|
+
import { createBatchFillingResultTransformer } from './createBatchFillingTransformStream.js';
|
|
4
5
|
export const createFillingModel = (enableParser = true) => {
|
|
5
6
|
const aiFormFilling = createCoze({
|
|
6
7
|
apiKey: config.get('agent.form.fill.apiKey'),
|
|
7
8
|
baseURL: config.get('agent.form.fill.baseUrl'),
|
|
8
9
|
botId: config.get('agent.form.fill.botId'),
|
|
9
|
-
extraStreamTransformers: [createFieldsFillingResultTransformer(enableParser)],
|
|
10
|
+
extraStreamTransformers: [() => createFieldsFillingResultTransformer(enableParser)],
|
|
11
|
+
});
|
|
12
|
+
const formFillingModel = aiFormFilling.chat('chat');
|
|
13
|
+
return formFillingModel;
|
|
14
|
+
};
|
|
15
|
+
export const createBatchFillingModel = (enableParser = true) => {
|
|
16
|
+
const aiFormFilling = createCoze({
|
|
17
|
+
apiKey: config.get('agent.form.fill.apiKey'),
|
|
18
|
+
baseURL: config.get('agent.form.fill.baseUrl'),
|
|
19
|
+
botId: config.get('agent.form.fill.botId'),
|
|
20
|
+
extraStreamTransformers: [() => createBatchFillingResultTransformer(enableParser)],
|
|
10
21
|
});
|
|
11
22
|
const formFillingModel = aiFormFilling.chat('chat');
|
|
12
23
|
return formFillingModel;
|