st-comp 0.0.270 → 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.270",
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;
@@ -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
  };
package/src/App.vue CHANGED
@@ -47,7 +47,7 @@ const route = useRoute();
47
47
  * @description: 组件库Demo本地
48
48
  */
49
49
  // const VITE_RUN_ENV = {
50
- // VITE_PROJECT_NAME: "动量回测",
50
+ // VITE_PROJECT_NAME: "迈幻量化",
51
51
  // VITE_PROJECT_CLIENTID: "7",
52
52
  // VITE_BASE_URL: "http://127.0.0.1:7001",
53
53
  // BASE_URL: "/",
@@ -61,7 +61,7 @@ const route = useRoute();
61
61
  * @description: 组件库Demo测试
62
62
  */
63
63
  const VITE_RUN_ENV = {
64
- VITE_PROJECT_NAME: "动量回测",
64
+ VITE_PROJECT_NAME: "迈幻量化",
65
65
  VITE_PROJECT_CLIENTID: "7",
66
66
  VITE_BASE_URL: "http://192.168.12.38:5173",
67
67
  BASE_URL: "/",
@@ -75,7 +75,7 @@ const VITE_RUN_ENV = {
75
75
  * @description: 组件库Demo线上
76
76
  */
77
77
  // const VITE_RUN_ENV = {
78
- // VITE_PROJECT_NAME: "动量回测",
78
+ // VITE_PROJECT_NAME: "迈幻量化",
79
79
  // VITE_PROJECT_CLIENTID: "7",
80
80
  // VITE_BASE_URL: "//investapi.hzyotoy.com",
81
81
  // BASE_URL: "/",