st-comp 0.0.269 → 0.0.271
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/es/VarietyAiHelper.cjs +4 -4
- package/es/VarietyAiHelper.js +222 -222
- package/es/VarietySearch.cjs +18 -18
- package/es/VarietySearch.js +1826 -1821
- package/es/aiTools-4412c56c.cjs +2 -0
- package/es/aiTools-e6e4e996.js +111 -0
- package/es/aiTools.js +102 -275
- package/es/style.css +1 -1
- package/lib/aiTools.js +102 -275
- package/lib/bundle.js +1 -1
- package/lib/bundle.umd.cjs +199 -201
- package/lib/{index-8ba1607b.js → index-15c834c8.js} +17152 -17185
- package/lib/{python-16c6e601.js → python-dcd40798.js} +1 -1
- package/lib/style.css +1 -1
- package/package.json +1 -1
- package/packages/VarietyAiHelper/index.vue +7 -53
- package/packages/VarietySearch/components/FactorScreen/index.vue +38 -25
- package/public/aiTools.js +102 -275
- package/src/App.vue +3 -3
- package/src/pages/VarietySearch/index.vue +264 -82
- package/es/aiTools-275c9799.cjs +0 -4
- package/es/aiTools-9c59a74a.js +0 -149
package/package.json
CHANGED
|
@@ -209,7 +209,6 @@ const sendMessage = async () => {
|
|
|
209
209
|
scrollToBottom();
|
|
210
210
|
return;
|
|
211
211
|
}
|
|
212
|
-
|
|
213
212
|
if (type === "node") {
|
|
214
213
|
if (!firstNodeReceived) {
|
|
215
214
|
firstNodeReceived = true;
|
|
@@ -229,7 +228,6 @@ const sendMessage = async () => {
|
|
|
229
228
|
scrollToBottom();
|
|
230
229
|
return;
|
|
231
230
|
}
|
|
232
|
-
|
|
233
231
|
if (type === "message") {
|
|
234
232
|
fullResponse = data;
|
|
235
233
|
const lastMessage = messageList.value[messageList.value.length - 1];
|
|
@@ -238,7 +236,6 @@ const sendMessage = async () => {
|
|
|
238
236
|
scrollToBottom();
|
|
239
237
|
}
|
|
240
238
|
}
|
|
241
|
-
|
|
242
239
|
if (type === "finish") {
|
|
243
240
|
isSending.value = false;
|
|
244
241
|
showFinalResult.value = true;
|
|
@@ -404,12 +401,14 @@ defineExpose({
|
|
|
404
401
|
<div
|
|
405
402
|
v-if="isJSONSerializable(message.content)"
|
|
406
403
|
class="message-json"
|
|
404
|
+
:class="{ 'result-animation': index === messageList.length - 1 && showFinalResult }"
|
|
407
405
|
v-html="renderJSONContent(formatJSONContent(message.content))"
|
|
408
406
|
></div>
|
|
409
407
|
|
|
410
408
|
<div
|
|
411
409
|
v-else-if="message.content"
|
|
412
410
|
class="message-text"
|
|
411
|
+
:class="{ 'result-animation': index === messageList.length - 1 && showFinalResult }"
|
|
413
412
|
>
|
|
414
413
|
{{ message.content }}
|
|
415
414
|
</div>
|
|
@@ -539,22 +538,8 @@ defineExpose({
|
|
|
539
538
|
|
|
540
539
|
.message-item {
|
|
541
540
|
display: flex;
|
|
542
|
-
animation: fadeInUp 0.3s ease-out;
|
|
543
541
|
margin-bottom: 20px;
|
|
544
542
|
|
|
545
|
-
&:nth-child(1) {
|
|
546
|
-
animation-delay: 0.05s;
|
|
547
|
-
}
|
|
548
|
-
&:nth-child(2) {
|
|
549
|
-
animation-delay: 0.1s;
|
|
550
|
-
}
|
|
551
|
-
&:nth-child(3) {
|
|
552
|
-
animation-delay: 0.15s;
|
|
553
|
-
}
|
|
554
|
-
&:nth-child(4) {
|
|
555
|
-
animation-delay: 0.2s;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
543
|
.avatar {
|
|
559
544
|
flex-shrink: 0;
|
|
560
545
|
:deep(.el-avatar) {
|
|
@@ -633,7 +618,6 @@ defineExpose({
|
|
|
633
618
|
.node-status-icon {
|
|
634
619
|
position: relative;
|
|
635
620
|
z-index: 1;
|
|
636
|
-
background: white;
|
|
637
621
|
border-radius: 50%;
|
|
638
622
|
width: 20px;
|
|
639
623
|
height: 20px;
|
|
@@ -720,18 +704,12 @@ defineExpose({
|
|
|
720
704
|
border-radius: 18px 18px 18px 4px;
|
|
721
705
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
722
706
|
border: 1px solid rgba(102, 126, 234, 0.2);
|
|
723
|
-
background: white;
|
|
724
707
|
transition: all 0.3s ease;
|
|
725
708
|
}
|
|
726
709
|
|
|
727
|
-
.message-text {
|
|
728
|
-
animation: messageAppear 0.3s ease-out;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
710
|
.message-json {
|
|
732
711
|
width: 100%;
|
|
733
712
|
overflow: hidden;
|
|
734
|
-
animation: resultAppear 0.5s cubic-bezier(0.2, 0.9, 0.4, 1.1);
|
|
735
713
|
|
|
736
714
|
:deep(.parsed-conditions) {
|
|
737
715
|
margin: 0;
|
|
@@ -742,16 +720,10 @@ defineExpose({
|
|
|
742
720
|
color: #f56c6c;
|
|
743
721
|
margin-bottom: 8px;
|
|
744
722
|
padding: 8px 12px;
|
|
745
|
-
background: linear-gradient(135deg, #
|
|
723
|
+
background: linear-gradient(135deg, #ffebeb 0%, #ffe0e0 100%);
|
|
746
724
|
border-radius: 12px;
|
|
747
725
|
font-weight: 500;
|
|
748
726
|
transition: all 0.2s ease;
|
|
749
|
-
|
|
750
|
-
&:hover {
|
|
751
|
-
transform: translateX(4px);
|
|
752
|
-
background: linear-gradient(135deg, #ffebeb 0%, #ffe0e0 100%);
|
|
753
|
-
}
|
|
754
|
-
|
|
755
727
|
&:last-child {
|
|
756
728
|
margin-bottom: 0;
|
|
757
729
|
}
|
|
@@ -759,6 +731,10 @@ defineExpose({
|
|
|
759
731
|
}
|
|
760
732
|
}
|
|
761
733
|
|
|
734
|
+
.result-animation {
|
|
735
|
+
animation: resultAppear 0.5s cubic-bezier(0.2, 0.9, 0.4, 1.1);
|
|
736
|
+
}
|
|
737
|
+
|
|
762
738
|
.message-createTime {
|
|
763
739
|
font-size: 11px;
|
|
764
740
|
color: #9ca3af;
|
|
@@ -875,17 +851,6 @@ defineExpose({
|
|
|
875
851
|
}
|
|
876
852
|
}
|
|
877
853
|
|
|
878
|
-
@keyframes fadeInUp {
|
|
879
|
-
from {
|
|
880
|
-
opacity: 0;
|
|
881
|
-
transform: translateY(10px);
|
|
882
|
-
}
|
|
883
|
-
to {
|
|
884
|
-
opacity: 1;
|
|
885
|
-
transform: translateY(0);
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
|
|
889
854
|
@keyframes slideIn {
|
|
890
855
|
from {
|
|
891
856
|
opacity: 0;
|
|
@@ -915,17 +880,6 @@ defineExpose({
|
|
|
915
880
|
}
|
|
916
881
|
}
|
|
917
882
|
|
|
918
|
-
@keyframes messageAppear {
|
|
919
|
-
from {
|
|
920
|
-
opacity: 0;
|
|
921
|
-
transform: translateY(8px);
|
|
922
|
-
}
|
|
923
|
-
to {
|
|
924
|
-
opacity: 1;
|
|
925
|
-
transform: translateY(0);
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
|
|
929
883
|
@keyframes resultAppear {
|
|
930
884
|
0% {
|
|
931
885
|
opacity: 0;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
<!-- 因子筛选组件 -->
|
|
2
2
|
<script setup name="FactorScreen">
|
|
3
3
|
import { nextTick, ref, watch, inject, reactive } from "vue";
|
|
4
|
+
import { getToken } from "st-func";
|
|
4
5
|
import { Close, Plus, CircleCloseFilled, InfoFilled, Document } from "@element-plus/icons-vue";
|
|
5
6
|
import { handleVerifyScore, extractConditionDetails, extractVariables } from "./tools.js";
|
|
6
|
-
import {
|
|
7
|
+
import { sendToNodeFactorAiTest } from "../../../../public/aiTools";
|
|
7
8
|
import FactorDescription from "./FactorDescription.vue";
|
|
8
9
|
import MonacoEditor from "../../../MonacoEditor/index.vue";
|
|
9
10
|
|
|
10
|
-
const { request } = inject("stConfig"); // 组件库全局配置
|
|
11
|
+
const { request, env } = inject("stConfig"); // 组件库全局配置
|
|
11
12
|
|
|
12
13
|
const emit = defineEmits(["actionState"]);
|
|
13
14
|
const props = defineProps({
|
|
@@ -41,6 +42,7 @@ const factorType = ref("脚本");
|
|
|
41
42
|
const scriptTestLoading = ref(false);
|
|
42
43
|
const scriptCopyLoading = ref(false);
|
|
43
44
|
const scriptAILoading = ref(false);
|
|
45
|
+
const scriptAIButtonLoading = ref(false);
|
|
44
46
|
const testVariety = ref("");
|
|
45
47
|
const testVarietyVisible = ref(false);
|
|
46
48
|
const scriptTestLogVisible = ref(false);
|
|
@@ -392,29 +394,37 @@ const handleScriptTest = async (action) => {
|
|
|
392
394
|
// 测试: AI分析
|
|
393
395
|
const handleTestAi = () => {
|
|
394
396
|
emit("actionState", "因子筛选", "AI分析");
|
|
395
|
-
const appId = "87be17ecc84d4d2ea3108d7155bec2ac";
|
|
396
|
-
const apiKey = "sk-d995eb26a4334bdeb2ccb4cbfaf51de8";
|
|
397
397
|
scriptAILoading.value = true;
|
|
398
|
+
scriptAIButtonLoading.value = true;
|
|
398
399
|
scriptTestResult.ai = "";
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
400
|
+
try {
|
|
401
|
+
sendToNodeFactorAiTest({
|
|
402
|
+
baseUrl: env.VITE_BASE_URL,
|
|
403
|
+
token: getToken(),
|
|
404
|
+
value: scriptTestResult.code,
|
|
405
|
+
callback: (type, data) => {
|
|
406
|
+
if (type === "error") {
|
|
407
|
+
scriptAILoading.value = false;
|
|
408
|
+
scriptAIButtonLoading.value = false;
|
|
409
|
+
ElMessage.error("AI分析失败: " + data);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
if (type === "message") {
|
|
413
|
+
scriptAILoading.value = false;
|
|
414
|
+
scriptTestResult.ai += data;
|
|
415
|
+
}
|
|
416
|
+
if (type === "finish") {
|
|
417
|
+
scriptAILoading.value = false;
|
|
418
|
+
scriptAIButtonLoading.value = false;
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
} catch (error) {
|
|
423
|
+
ElMessage.error(`AI响应异常: ${error}`);
|
|
424
|
+
scriptTestResult.ai = error;
|
|
425
|
+
scriptAILoading.value = false;
|
|
426
|
+
scriptAIButtonLoading.value = false;
|
|
427
|
+
}
|
|
418
428
|
};
|
|
419
429
|
|
|
420
430
|
// 监控: 窗口开关
|
|
@@ -1006,7 +1016,7 @@ watch(
|
|
|
1006
1016
|
<div class="dialog-footer">
|
|
1007
1017
|
<el-button
|
|
1008
1018
|
type="primary"
|
|
1009
|
-
:loading="
|
|
1019
|
+
:loading="scriptAIButtonLoading"
|
|
1010
1020
|
@click="handleTestAi"
|
|
1011
1021
|
>
|
|
1012
1022
|
AI分析
|
|
@@ -1137,8 +1147,11 @@ watch(
|
|
|
1137
1147
|
word-wrap: break-word;
|
|
1138
1148
|
}
|
|
1139
1149
|
.ai {
|
|
1140
|
-
flex:
|
|
1150
|
+
flex: 1;
|
|
1141
1151
|
background-color: white;
|
|
1152
|
+
:deep(.vuepress-markdown-body) {
|
|
1153
|
+
padding: 0;
|
|
1154
|
+
}
|
|
1142
1155
|
}
|
|
1143
1156
|
}
|
|
1144
1157
|
}
|
package/public/aiTools.js
CHANGED
|
@@ -1,307 +1,93 @@
|
|
|
1
|
-
// 辅助函数:处理缓冲区剩余数据
|
|
2
|
-
const tryProcessBuffer = (buffer, callback) => {
|
|
3
|
-
const lines = buffer.split("\n");
|
|
4
|
-
let currentEvent = null;
|
|
5
|
-
|
|
6
|
-
for (const line of lines) {
|
|
7
|
-
if (line.startsWith("event:")) {
|
|
8
|
-
currentEvent = line.substring(6).trim();
|
|
9
|
-
continue;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
if (line.startsWith("data:")) {
|
|
13
|
-
const dataStr = line.substring(5).trim();
|
|
14
|
-
if (dataStr && dataStr !== "[DONE]") {
|
|
15
|
-
try {
|
|
16
|
-
const parsed = JSON.parse(dataStr);
|
|
17
|
-
|
|
18
|
-
// 错误响应
|
|
19
|
-
if (currentEvent === "error" || parsed.code || parsed.message) {
|
|
20
|
-
const errorMsg = parsed.message || parsed.code || "未知错误";
|
|
21
|
-
callback("error", `百炼服务端错误: ${errorMsg}`);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// 正常响应
|
|
26
|
-
const text = parsed?.output?.text;
|
|
27
|
-
if (text && callback) callback("message", text);
|
|
28
|
-
} catch (e) {}
|
|
29
|
-
}
|
|
30
|
-
currentEvent = null;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
1
|
/**
|
|
36
|
-
* @description:
|
|
2
|
+
* @description: 通用 SSE 流式请求
|
|
3
|
+
* @param {Object} params
|
|
4
|
+
* @param {string} params.baseUrl - 服务地址
|
|
5
|
+
* @param {string} params.path - 接口路径
|
|
6
|
+
* @param {string} params.token - 认证token
|
|
7
|
+
* @param {string} params.value - 查询内容
|
|
8
|
+
* @param {string} params.origin - 来源标识,默认 "web"
|
|
9
|
+
* @param {Function} params.getSSE - 回调函数 (type, str) => void
|
|
10
|
+
* type: "finish" || "error" || "message"
|
|
37
11
|
*/
|
|
38
|
-
|
|
39
|
-
export const sendToBaiLianAppNonStreaming = async ({ appId, apiKey, value }) => {
|
|
12
|
+
const sendToNodeSSE = async ({ baseUrl, path, token, value, origin = "web", getSSE }) => {
|
|
40
13
|
try {
|
|
41
|
-
|
|
14
|
+
// 创建请求
|
|
15
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
42
16
|
method: "POST",
|
|
43
17
|
headers: {
|
|
44
|
-
Authorization: `Bearer ${apiKey}`,
|
|
45
18
|
"Content-Type": "application/json",
|
|
19
|
+
token,
|
|
46
20
|
},
|
|
47
21
|
body: JSON.stringify({
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
},
|
|
52
|
-
}),
|
|
53
|
-
});
|
|
54
|
-
if (!response.ok) {
|
|
55
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
56
|
-
}
|
|
57
|
-
const data = await response.json();
|
|
58
|
-
const outputText = data?.output?.text;
|
|
59
|
-
return outputText;
|
|
60
|
-
} catch (error) {
|
|
61
|
-
if (error.name === "AbortError") {
|
|
62
|
-
throw new Error("请求超时");
|
|
63
|
-
}
|
|
64
|
-
throw error;
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
// 百炼应用(流式)
|
|
68
|
-
export const sendToBaiLianAppStreaming = async ({ appId, apiKey, value, callback }) => {
|
|
69
|
-
try {
|
|
70
|
-
const response = await fetch(`https://dashscope.aliyuncs.com/api/v1/apps/${appId}/completion`, {
|
|
71
|
-
method: "POST",
|
|
72
|
-
body: JSON.stringify({
|
|
73
|
-
input: { prompt: value },
|
|
74
|
-
parameters: { incremental_output: "true" },
|
|
75
|
-
debug: {},
|
|
22
|
+
query: value,
|
|
23
|
+
origin,
|
|
24
|
+
stream: true,
|
|
76
25
|
}),
|
|
77
|
-
headers: {
|
|
78
|
-
Authorization: `Bearer ${apiKey}`,
|
|
79
|
-
"Content-Type": "application/json",
|
|
80
|
-
"X-DashScope-SSE": "enable",
|
|
81
|
-
},
|
|
82
26
|
});
|
|
83
|
-
|
|
84
27
|
if (!response.ok) {
|
|
85
|
-
|
|
28
|
+
getSSE("error", `HTTP ${response.status}: ${response.statusText}`);
|
|
86
29
|
return;
|
|
87
30
|
}
|
|
88
31
|
|
|
32
|
+
// 创建reader读取器和decoder解码器
|
|
89
33
|
const reader = response.body.getReader();
|
|
90
34
|
const decoder = new TextDecoder();
|
|
91
|
-
let buffer = "";
|
|
92
|
-
let currentEvent = null; // 记录当前 event 类型
|
|
93
|
-
|
|
94
|
-
while (true) {
|
|
95
|
-
const { done, value } = await reader.read();
|
|
96
|
-
if (done) {
|
|
97
|
-
// 处理最后可能遗留的数据
|
|
98
|
-
if (buffer.trim()) {
|
|
99
|
-
tryProcessBuffer(buffer, callback);
|
|
100
|
-
}
|
|
101
|
-
callback("finish", "");
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
buffer += decoder.decode(value, { stream: true });
|
|
106
35
|
|
|
107
|
-
|
|
108
|
-
while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
|
|
109
|
-
const line = buffer.substring(0, newlineIndex).trim();
|
|
110
|
-
buffer = buffer.substring(newlineIndex + 1);
|
|
111
|
-
|
|
112
|
-
// 处理 event: 行(错误类型)
|
|
113
|
-
if (line.startsWith("event:")) {
|
|
114
|
-
currentEvent = line.substring(6).trim();
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// 处理 data: 行
|
|
119
|
-
if (line.startsWith("data:")) {
|
|
120
|
-
const dataStr = line.substring(5).trim();
|
|
121
|
-
|
|
122
|
-
// 检查是否是错误响应
|
|
123
|
-
if (currentEvent === "error" || (dataStr && dataStr.startsWith("{"))) {
|
|
124
|
-
try {
|
|
125
|
-
const parsed = JSON.parse(dataStr);
|
|
126
|
-
|
|
127
|
-
// 处理错误响应
|
|
128
|
-
if (parsed.code || parsed.message || currentEvent === "error") {
|
|
129
|
-
const errorMsg = parsed.message || parsed.code || "未知错误";
|
|
130
|
-
callback("error", `百炼服务端错误: ${errorMsg}`);
|
|
131
|
-
return; // 终止流式处理
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// 正常响应:提取 text
|
|
135
|
-
const text = parsed?.output?.text;
|
|
136
|
-
if (text && callback) {
|
|
137
|
-
callback("message", text);
|
|
138
|
-
}
|
|
139
|
-
} catch (e) {
|
|
140
|
-
console.debug("JSON 解析失败:", dataStr);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// 重置 event 类型
|
|
145
|
-
currentEvent = null;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
} catch (error) {
|
|
150
|
-
console.error("流式请求失败:", error);
|
|
151
|
-
callback("error", error.message);
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
// 百炼workflow(流式)
|
|
155
|
-
export const sendToBaiLianWorkflowStreaming = async ({ mode, appId, apiKey, token, value, callback }) => {
|
|
156
|
-
try {
|
|
157
|
-
const response = await fetch(`https://dashscope.aliyuncs.com/api/v1/apps/${appId}/completion`, {
|
|
158
|
-
method: "POST",
|
|
159
|
-
body: JSON.stringify({
|
|
160
|
-
input: { prompt: value, biz_params: { token, mode, origin: "web" } },
|
|
161
|
-
parameters: { incremental_output: "true", flow_stream_mode: "message_format_plus" },
|
|
162
|
-
debug: {},
|
|
163
|
-
}),
|
|
164
|
-
headers: {
|
|
165
|
-
Authorization: `Bearer ${apiKey}`,
|
|
166
|
-
"Content-Type": "application/json",
|
|
167
|
-
"X-DashScope-SSE": "enable",
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
if (!response.ok) {
|
|
172
|
-
callback("error", `HTTP ${response.status}: ${response.statusText}`);
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const reader = response.body.getReader();
|
|
177
|
-
const decoder = new TextDecoder();
|
|
36
|
+
// 缓冲区数据
|
|
178
37
|
let buffer = "";
|
|
179
|
-
let
|
|
180
|
-
// 记录已出现的节点(去重用)
|
|
181
|
-
const nodeMap = {};
|
|
38
|
+
let newlineIndex;
|
|
182
39
|
|
|
183
40
|
while (true) {
|
|
184
41
|
const { done, value } = await reader.read();
|
|
42
|
+
// 判断SSE是否传输结束
|
|
185
43
|
if (done) {
|
|
186
|
-
|
|
187
|
-
callback("message", finalResult);
|
|
188
|
-
}
|
|
189
|
-
callback("finish", "");
|
|
44
|
+
getSSE("finish");
|
|
190
45
|
break;
|
|
191
46
|
}
|
|
192
|
-
|
|
47
|
+
// 缓冲区填入数据
|
|
193
48
|
buffer += decoder.decode(value, { stream: true });
|
|
194
49
|
let newlineIndex;
|
|
50
|
+
// 从缓冲区中逐条提取完整的消息行
|
|
195
51
|
while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
|
|
52
|
+
// 提取一行
|
|
196
53
|
const line = buffer.substring(0, newlineIndex).trim();
|
|
54
|
+
// 移除已提取的部分
|
|
197
55
|
buffer = buffer.substring(newlineIndex + 1);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const dataStr = line.substring(
|
|
56
|
+
if (line.startsWith("data: ")) {
|
|
57
|
+
// 切割掉前面的data: , 总计6个字符, 并且去除空格
|
|
58
|
+
const dataStr = line.substring(6).trim();
|
|
59
|
+
// 过滤空数据, 和结束标记
|
|
201
60
|
if (!dataStr || dataStr === "[DONE]") continue;
|
|
202
|
-
|
|
203
|
-
try {
|
|
204
|
-
const parsed = JSON.parse(dataStr);
|
|
205
|
-
const output = parsed?.output;
|
|
206
|
-
const wfMsg = output?.workflow_message;
|
|
207
|
-
|
|
208
|
-
// 提取节点信息
|
|
209
|
-
if (wfMsg?.node_name && wfMsg?.node_type) {
|
|
210
|
-
const nodeName = wfMsg.node_name;
|
|
211
|
-
const nodeStatus = wfMsg.node_status; // "executing" | "success"
|
|
212
|
-
const isCompleted = wfMsg.node_is_completed;
|
|
213
|
-
|
|
214
|
-
// 去重:同一节点只推送状态变化
|
|
215
|
-
const prevStatus = nodeMap[nodeName];
|
|
216
|
-
if (prevStatus !== nodeStatus || (isCompleted && prevStatus !== "success")) {
|
|
217
|
-
nodeMap[nodeName] = isCompleted ? "success" : nodeStatus;
|
|
218
|
-
callback("node", {
|
|
219
|
-
name: nodeName,
|
|
220
|
-
status: isCompleted ? "success" : "executing",
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// 最终结果
|
|
226
|
-
if (output?.finish_reason === "stop" && output?.text) {
|
|
227
|
-
finalResult = output.text;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// 错误
|
|
231
|
-
if (output?.code || output?.message) {
|
|
232
|
-
callback("error", output.message || output.code || "未知错误");
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
} catch (e) {
|
|
236
|
-
// 忽略非 JSON 行
|
|
237
|
-
}
|
|
61
|
+
getSSE("message", dataStr);
|
|
238
62
|
}
|
|
239
63
|
}
|
|
240
64
|
}
|
|
241
65
|
} catch (error) {
|
|
242
|
-
console.error("
|
|
243
|
-
|
|
66
|
+
console.error("SSE请求失败:", error);
|
|
67
|
+
getSSE("error", error.message || "sendToNodeSSE检测到异常");
|
|
244
68
|
}
|
|
245
69
|
};
|
|
246
70
|
|
|
247
|
-
|
|
248
|
-
* @description: Node+OpenAi
|
|
249
|
-
*/
|
|
250
|
-
// 品种池AI助手(流式) - 品种池AI助手
|
|
71
|
+
// 品种池AI查询助手
|
|
251
72
|
export const sendToNodeVarietyAiHelper = async ({ baseUrl, token, value, callback }) => {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}),
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
if (!response.ok) {
|
|
267
|
-
callback("error", `HTTP ${response.status}: ${response.statusText}`);
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const reader = response.body.getReader();
|
|
272
|
-
const decoder = new TextDecoder();
|
|
273
|
-
let buffer = "";
|
|
274
|
-
let finalResult = "";
|
|
275
|
-
const nodeMap = {};
|
|
276
|
-
|
|
277
|
-
while (true) {
|
|
278
|
-
const { done, value } = await reader.read();
|
|
279
|
-
if (done) {
|
|
280
|
-
if (finalResult) {
|
|
281
|
-
callback("message", finalResult);
|
|
282
|
-
}
|
|
283
|
-
callback("finish", "");
|
|
284
|
-
break;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
buffer += decoder.decode(value, { stream: true });
|
|
288
|
-
let newlineIndex;
|
|
289
|
-
while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
|
|
290
|
-
const line = buffer.substring(0, newlineIndex).trim();
|
|
291
|
-
buffer = buffer.substring(newlineIndex + 1);
|
|
292
|
-
|
|
293
|
-
if (line.startsWith("data: ")) {
|
|
294
|
-
const dataStr = line.substring(6).trim();
|
|
295
|
-
if (!dataStr || dataStr === "[DONE]") continue;
|
|
296
|
-
|
|
73
|
+
// 工作流节点
|
|
74
|
+
const nodeMap = {};
|
|
75
|
+
const params = {
|
|
76
|
+
baseUrl,
|
|
77
|
+
path: "/middleLayer/ai/varietyAiHelper",
|
|
78
|
+
token,
|
|
79
|
+
value,
|
|
80
|
+
origin: "web",
|
|
81
|
+
getSSE: (type, str) => {
|
|
82
|
+
switch (type) {
|
|
83
|
+
case "message": {
|
|
297
84
|
try {
|
|
298
|
-
const data = JSON.parse(
|
|
299
|
-
|
|
300
|
-
// 节点状态更新
|
|
85
|
+
const data = JSON.parse(str);
|
|
86
|
+
// 节点状态
|
|
301
87
|
if (data.node_name && data.node_status) {
|
|
302
88
|
const nodeName = data.node_name;
|
|
303
89
|
const nodeStatus = data.node_status === "success" ? "success" : "executing";
|
|
304
|
-
|
|
90
|
+
// 节点状态发生改变, 推送节点状态变更
|
|
305
91
|
if (nodeMap[nodeName] !== nodeStatus) {
|
|
306
92
|
nodeMap[nodeName] = nodeStatus;
|
|
307
93
|
callback("node", {
|
|
@@ -310,26 +96,67 @@ export const sendToNodeVarietyAiHelper = async ({ baseUrl, token, value, callbac
|
|
|
310
96
|
});
|
|
311
97
|
}
|
|
312
98
|
}
|
|
313
|
-
|
|
314
|
-
// 最终结果
|
|
99
|
+
// 结果输出
|
|
315
100
|
if (data.output?.finish_reason === "stop" && data.output?.text) {
|
|
316
|
-
|
|
317
|
-
callback("message", finalResult);
|
|
101
|
+
callback("message", data.output.text);
|
|
318
102
|
}
|
|
319
|
-
|
|
320
103
|
// 错误处理
|
|
321
104
|
if (data.error) {
|
|
322
105
|
callback("error", data.error);
|
|
323
|
-
return;
|
|
324
106
|
}
|
|
325
|
-
} catch (
|
|
326
|
-
//
|
|
107
|
+
} catch (error) {
|
|
108
|
+
// 忽略非序列化消息
|
|
327
109
|
}
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case "finish": {
|
|
113
|
+
callback("finish");
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case "error": {
|
|
117
|
+
callback("error", str);
|
|
118
|
+
break;
|
|
328
119
|
}
|
|
329
120
|
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
return sendToNodeSSE(params);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// 因子脚本测试助手
|
|
127
|
+
export const sendToNodeFactorAiTest = async ({ baseUrl, token, value, callback }) => {
|
|
128
|
+
const params = {
|
|
129
|
+
baseUrl,
|
|
130
|
+
path: "/middleLayer/ai/factorAiTest",
|
|
131
|
+
token,
|
|
132
|
+
value,
|
|
133
|
+
origin: "web",
|
|
134
|
+
getSSE: (type, str) => {
|
|
135
|
+
switch (type) {
|
|
136
|
+
case "message": {
|
|
137
|
+
try {
|
|
138
|
+
const data = JSON.parse(str);
|
|
139
|
+
// 结果输出
|
|
140
|
+
if (data.output) {
|
|
141
|
+
callback("message", data.output?.content);
|
|
142
|
+
}
|
|
143
|
+
// 错误处理
|
|
144
|
+
if (data.error) {
|
|
145
|
+
callback("error", data.error);
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {}
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case "finish": {
|
|
151
|
+
callback("finish");
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
case "error": {
|
|
155
|
+
callback("error", str);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
return sendToNodeSSE(params);
|
|
335
162
|
};
|