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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "st-comp",
3
3
  "public": true,
4
- "version": "0.0.269",
4
+ "version": "0.0.271",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
@@ -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, #fff5f5 0%, #ffebeb 100%);
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 { sendToBaiLianAppStreaming } from "../../../../public/aiTools";
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
- let fullResponse = "";
400
- sendToBaiLianAppStreaming({
401
- appId,
402
- apiKey,
403
- value: scriptTestResult.code,
404
- callback: (type, data) => {
405
- // 百炼应用错误
406
- if (type === "error") return;
407
- // 流式输出(正常)
408
- else if (type === "message") {
409
- fullResponse += data;
410
- scriptTestResult.ai = fullResponse;
411
- }
412
- // 流式输出(完毕)
413
- else if (type === "finish") {
414
- scriptAILoading.value = false;
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="scriptAILoading"
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: 2;
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: 百炼dashscope直连
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
- const response = await fetch(`https://dashscope.aliyuncs.com/api/v1/apps/${appId}/completion`, {
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
- input: { prompt: value },
49
- parameters: {
50
- incremental_output: false, // 非流式
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
- callback("error", `HTTP ${response.status}: ${response.statusText}`);
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
- let newlineIndex;
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 finalResult = "";
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
- if (finalResult) {
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
- if (line.startsWith("data:")) {
200
- const dataStr = line.substring(5).trim();
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("工作流请求失败:", error);
243
- callback("error", error.message);
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
- try {
253
- const response = await fetch(`${baseUrl}/middleLayer/ai/varietyAiHelper`, {
254
- method: "POST",
255
- headers: {
256
- "Content-Type": "application/json",
257
- token: token,
258
- },
259
- body: JSON.stringify({
260
- query: value,
261
- origin: "web",
262
- stream: true,
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(dataStr);
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
- finalResult = data.output.text;
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 (e) {
326
- // 忽略非 JSON 行
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
- } catch (error) {
332
- console.error("Node服务请求失败:", error);
333
- callback("error", error.message);
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
  };