yiyan-browser-agent 1.10.3 → 1.11.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/package.json +1 -1
- package/src/browser.js +193 -101
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yiyan-browser-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"description": "AI coding agent powered by Yiyan (文心一言) via browser automation (chat.baidu.com) — no API key needed. Performance-optimized. Enhanced with comprehensive security.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
package/src/browser.js
CHANGED
|
@@ -14,12 +14,11 @@ const CrashHandler = require('./stability/crash-handler');
|
|
|
14
14
|
|
|
15
15
|
const SEL = {
|
|
16
16
|
chatInput: [
|
|
17
|
-
'
|
|
18
|
-
'
|
|
19
|
-
'.editable',
|
|
20
|
-
'#chat-input',
|
|
17
|
+
'#chat-textarea',
|
|
18
|
+
'textarea.ci-textarea',
|
|
21
19
|
'textarea[placeholder]',
|
|
22
20
|
'textarea',
|
|
21
|
+
'[role="textbox"]',
|
|
23
22
|
'[contenteditable="true"][role="textbox"]',
|
|
24
23
|
'[contenteditable="true"]',
|
|
25
24
|
'[class*="input-box"]',
|
|
@@ -46,12 +45,16 @@ const SEL = {
|
|
|
46
45
|
'button[aria-label*="停止"]',
|
|
47
46
|
'[aria-label*="stop generating" i]',
|
|
48
47
|
'[data-testid="stop-button"]',
|
|
48
|
+
'[class*="stop-gen"]',
|
|
49
|
+
'[class*="stopGen"]',
|
|
49
50
|
'[class*="stop-btn"]',
|
|
50
51
|
'[class*="stopBtn"]',
|
|
51
52
|
'[class*="abort"]',
|
|
52
53
|
],
|
|
53
54
|
|
|
54
55
|
newChat: [
|
|
56
|
+
'.new-dialog-container-button',
|
|
57
|
+
'[class*="new-dialog-container"]',
|
|
55
58
|
'button[aria-label*="New chat" i]',
|
|
56
59
|
'button[aria-label*="新对话"]',
|
|
57
60
|
'button[aria-label*="New conversation" i]',
|
|
@@ -61,15 +64,19 @@ const SEL = {
|
|
|
61
64
|
'[class*="newChat"]',
|
|
62
65
|
],
|
|
63
66
|
|
|
64
|
-
// Response ready indicators
|
|
67
|
+
// Response ready indicators (chat.baidu.com)
|
|
65
68
|
responseReady: [
|
|
69
|
+
'.ai-entry-block.ai-markdown',
|
|
70
|
+
'.answer-container',
|
|
71
|
+
'.cs-answer-container',
|
|
72
|
+
'.answer-box',
|
|
66
73
|
'[class*="answer"]',
|
|
67
|
-
'[class*="response"]',
|
|
68
74
|
'[class*="markdown"]',
|
|
69
|
-
'.ds-markdown',
|
|
70
75
|
],
|
|
71
76
|
|
|
72
77
|
messageContainer: [
|
|
78
|
+
'#chat-container-main',
|
|
79
|
+
'[class*="chat-container"]',
|
|
73
80
|
'[class*="chat-content"]',
|
|
74
81
|
'[class*="message-list"]',
|
|
75
82
|
'[class*="conversation"]',
|
|
@@ -178,17 +185,22 @@ class YiyanBrowser {
|
|
|
178
185
|
|
|
179
186
|
const needsLogin = await this.page.evaluate(() => {
|
|
180
187
|
const url = window.location.href;
|
|
188
|
+
// URL 中包含登录/认证路径 → 需要登录
|
|
189
|
+
if (url.includes('/auth') || url.includes('/login') || url.includes('/sign')) {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
// 存在密码输入框 → 需要登录
|
|
193
|
+
if (document.querySelector('input[type="password"]')) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
// 页面主体区域被登录界面遮挡(排除侧栏的"请登录"提示)
|
|
181
197
|
const bodyText = document.body?.innerText || '';
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
bodyText.includes('登录') ||
|
|
189
|
-
bodyText.includes('登 录') ||
|
|
190
|
-
!!document.querySelector('input[type="password"]')
|
|
191
|
-
);
|
|
198
|
+
const mainInput = document.querySelector('#chat-textarea, textarea.ci-textarea');
|
|
199
|
+
if (!mainInput) {
|
|
200
|
+
// 输入框不存在可能是需要登录
|
|
201
|
+
return bodyText.includes('Sign in') || bodyText.includes('Log in');
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
192
204
|
});
|
|
193
205
|
|
|
194
206
|
if (needsLogin) {
|
|
@@ -235,18 +247,24 @@ class YiyanBrowser {
|
|
|
235
247
|
// ── Sending Messages (stable: keyboard.type) ────────────────────────────────
|
|
236
248
|
|
|
237
249
|
async sendMessage(text) {
|
|
238
|
-
const { el } = await this._findInput();
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
+
const { el, isTextarea } = await this._findInput();
|
|
251
|
+
|
|
252
|
+
if (isTextarea) {
|
|
253
|
+
// textarea 元素:用 fill() 快速填充
|
|
254
|
+
await el.click({ force: true });
|
|
255
|
+
await this.page.waitForTimeout(100);
|
|
256
|
+
await el.fill('');
|
|
257
|
+
await this.page.waitForTimeout(50);
|
|
258
|
+
await el.fill(text);
|
|
259
|
+
await this.page.waitForTimeout(100);
|
|
260
|
+
} else {
|
|
261
|
+
// contenteditable 元素:用键盘输入
|
|
262
|
+
await el.click({ clickCount: 3, force: true });
|
|
263
|
+
await this.page.waitForTimeout(100);
|
|
264
|
+
await this.page.keyboard.press('Delete');
|
|
265
|
+
await this.page.waitForTimeout(50);
|
|
266
|
+
await this.page.keyboard.type(text, { delay: 10 });
|
|
267
|
+
}
|
|
250
268
|
|
|
251
269
|
// Press Enter to send
|
|
252
270
|
await this.page.keyboard.press('Enter');
|
|
@@ -302,15 +320,28 @@ class YiyanBrowser {
|
|
|
302
320
|
let lastThinkingText = '';
|
|
303
321
|
let stableCount = 0;
|
|
304
322
|
let lastStableTime = Date.now();
|
|
323
|
+
let textChangedAt = Date.now(); // 文本最后变化时间
|
|
305
324
|
let dotCount = 0;
|
|
306
325
|
let hasCompletionMarker = false;
|
|
307
326
|
|
|
308
327
|
while (Date.now() - start < timeout) {
|
|
309
328
|
const text = await this._extractLastMessage();
|
|
310
329
|
|
|
311
|
-
// ── 思考区域内容检测 ──
|
|
330
|
+
// ── 思考区域内容检测 (chat.baidu.com) ──
|
|
312
331
|
const thinkingInfo = await this.page.evaluate(() => {
|
|
313
|
-
|
|
332
|
+
// chat.baidu.com 深度思考区域
|
|
333
|
+
const thinkingSelectors = [
|
|
334
|
+
'[class*="deep-think"]',
|
|
335
|
+
'[class*="deepThink"]',
|
|
336
|
+
'[class*="deep-search"]',
|
|
337
|
+
'[class*="thinking-container"]',
|
|
338
|
+
'[class*="container__SPpah"]', // 旧版兼容
|
|
339
|
+
];
|
|
340
|
+
let thinkingEl = null;
|
|
341
|
+
for (const sel of thinkingSelectors) {
|
|
342
|
+
thinkingEl = document.querySelector(sel);
|
|
343
|
+
if (thinkingEl) break;
|
|
344
|
+
}
|
|
314
345
|
if (!thinkingEl) return { text: '', exists: false };
|
|
315
346
|
|
|
316
347
|
const s = window.getComputedStyle(thinkingEl);
|
|
@@ -320,17 +351,22 @@ class YiyanBrowser {
|
|
|
320
351
|
return { text, exists: true };
|
|
321
352
|
});
|
|
322
353
|
|
|
323
|
-
// ──
|
|
354
|
+
// ── 统一稳定性检测:答案不变即算稳定 ──
|
|
324
355
|
const thinkingStable = thinkingInfo.text === lastThinkingText;
|
|
325
356
|
const answerStable = text === lastText;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
357
|
+
// 如果有思考区域,需要两者都稳定;否则只看答案
|
|
358
|
+
const hasThinking = thinkingInfo.exists && thinkingInfo.text.length > 0;
|
|
359
|
+
const hasContent = text.length > 0;
|
|
360
|
+
const stable = hasThinking
|
|
361
|
+
? (thinkingStable && answerStable && hasContent)
|
|
362
|
+
: (answerStable && hasContent);
|
|
363
|
+
|
|
364
|
+
if (stable) {
|
|
365
|
+
// 稳定且有内容,检查稳定时间
|
|
330
366
|
if (Date.now() - lastStableTime >= stableDelay) {
|
|
331
367
|
stableCount++;
|
|
332
368
|
lastStableTime = Date.now();
|
|
333
|
-
logger.dim(`
|
|
369
|
+
logger.dim(`Stable: ${stableCount}/2 (answer: ${text.length} chars${hasThinking ? ', thinking: ' + thinkingInfo.text.length + ' chars' : ''})`);
|
|
334
370
|
}
|
|
335
371
|
} else {
|
|
336
372
|
// 任一变化,重置
|
|
@@ -338,15 +374,22 @@ class YiyanBrowser {
|
|
|
338
374
|
lastText = text;
|
|
339
375
|
stableCount = 0;
|
|
340
376
|
lastStableTime = Date.now();
|
|
377
|
+
textChangedAt = Date.now();
|
|
341
378
|
}
|
|
342
379
|
|
|
343
|
-
// ── 完成标记检测 ──
|
|
380
|
+
// ── 完成标记检测 (chat.baidu.com) ──
|
|
344
381
|
const detected = await this.page.evaluate(() => {
|
|
345
382
|
const selectors = [
|
|
383
|
+
'.cos-icon.cos-icon-copy',
|
|
384
|
+
'.cos-icon-copy',
|
|
385
|
+
'.feedback-hover-show',
|
|
386
|
+
'[class*="feedback-wrapper"]',
|
|
387
|
+
'.cos-icon.cos-icon-share1',
|
|
388
|
+
'.cos-icon.cos-icon-feedback',
|
|
389
|
+
// 旧版兼容
|
|
346
390
|
'.dialogCardBottom__qoXjps3z',
|
|
347
391
|
'[class*="dialogCardBottom"]',
|
|
348
392
|
'[class*="dialog-bottom"]',
|
|
349
|
-
'[class*="response-footer"]',
|
|
350
393
|
];
|
|
351
394
|
for (const sel of selectors) {
|
|
352
395
|
const el = document.querySelector(sel);
|
|
@@ -363,10 +406,12 @@ class YiyanBrowser {
|
|
|
363
406
|
}
|
|
364
407
|
|
|
365
408
|
// ── 完成判断 ──
|
|
366
|
-
// 条件1:
|
|
367
|
-
// 条件2
|
|
409
|
+
// 条件1: 完成标记出现 + 稳定 2 次
|
|
410
|
+
// 条件2: 稳定 3 次(即使没有完成标记)
|
|
411
|
+
// 条件3(兜底): 文本长时间稳定不变(≥5秒),强制认为完成
|
|
368
412
|
const condition1 = hasCompletionMarker && stableCount >= 2;
|
|
369
413
|
const condition2 = stableCount >= 3;
|
|
414
|
+
const longStable = (Date.now() - textChangedAt) >= 5000 && text.length > 0;
|
|
370
415
|
|
|
371
416
|
if (condition1 || condition2) {
|
|
372
417
|
// 检查 _isGenerating 作为最终确认
|
|
@@ -379,6 +424,13 @@ class YiyanBrowser {
|
|
|
379
424
|
// _isGenerating 说还在生成,重置计数继续等待
|
|
380
425
|
stableCount = 0;
|
|
381
426
|
lastStableTime = Date.now();
|
|
427
|
+
} else if (longStable) {
|
|
428
|
+
// 兜底:文本已经 5 秒没变化了,强制完成
|
|
429
|
+
logger.dim('Long stable fallback — text unchanged for 5s, forcing completion');
|
|
430
|
+
await this.page.waitForTimeout(300);
|
|
431
|
+
logger.clearLine();
|
|
432
|
+
logger.success('Response complete (stable timeout)');
|
|
433
|
+
break;
|
|
382
434
|
}
|
|
383
435
|
|
|
384
436
|
// Progress indicator
|
|
@@ -400,16 +452,16 @@ class YiyanBrowser {
|
|
|
400
452
|
async _getMessageCount() {
|
|
401
453
|
return await this.page.evaluate(() => {
|
|
402
454
|
const candidates = [
|
|
403
|
-
'
|
|
404
|
-
'
|
|
405
|
-
'
|
|
406
|
-
'
|
|
407
|
-
'
|
|
408
|
-
'[
|
|
409
|
-
'[class*="
|
|
410
|
-
'.
|
|
411
|
-
'
|
|
412
|
-
'[class*="
|
|
455
|
+
'.ai-entry-block.ai-markdown',
|
|
456
|
+
'.answer-box',
|
|
457
|
+
'.cs-answer-container',
|
|
458
|
+
'.answer-container',
|
|
459
|
+
'.chat-search-answer-generate-item',
|
|
460
|
+
'[class*="answer-container"]',
|
|
461
|
+
'[class*="answer-box"]',
|
|
462
|
+
'.ai-markdown',
|
|
463
|
+
'.cs-question-bubble',
|
|
464
|
+
'[class*="question-bubble"]',
|
|
413
465
|
];
|
|
414
466
|
for (const sel of candidates) {
|
|
415
467
|
const els = document.querySelectorAll(sel);
|
|
@@ -435,10 +487,15 @@ class YiyanBrowser {
|
|
|
435
487
|
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
436
488
|
const tag = node.tagName.toLowerCase();
|
|
437
489
|
|
|
438
|
-
//
|
|
439
|
-
const cls = node.className
|
|
440
|
-
if (cls.includes('
|
|
441
|
-
|
|
490
|
+
// 排除思考过程区域和 UI 建议区域
|
|
491
|
+
const cls = (typeof node.className === 'string') ? node.className : '';
|
|
492
|
+
if (cls.includes('thinking') || cls.includes('Thinking') ||
|
|
493
|
+
cls.includes('deep-search') || cls.includes('deep_think') ||
|
|
494
|
+
cls.includes('suggestion') || cls.includes('follow-up') ||
|
|
495
|
+
cls.includes('question-container') || cls.includes('question-bubble') ||
|
|
496
|
+
cls.includes('quick-entrance') || cls.includes('feedback-wrapper') ||
|
|
497
|
+
cls.includes('hover-menu') || cls.includes('action-bar')) {
|
|
498
|
+
return; // 跳过思考区域和 UI 元素
|
|
442
499
|
}
|
|
443
500
|
|
|
444
501
|
if (tag === 'pre') {
|
|
@@ -474,27 +531,53 @@ class YiyanBrowser {
|
|
|
474
531
|
return result.trim();
|
|
475
532
|
}
|
|
476
533
|
|
|
477
|
-
// ──
|
|
534
|
+
// ── chat.baidu.com 新选择器 ──
|
|
535
|
+
|
|
536
|
+
// 优先: 获取最后一个回答块的内容
|
|
537
|
+
const answerBlocks = document.querySelectorAll(
|
|
538
|
+
'.ai-entry-block.ai-markdown, .answer-container.cs-enable-selection, .cs-answer-container'
|
|
539
|
+
);
|
|
540
|
+
if (answerBlocks.length > 0) {
|
|
541
|
+
const lastBlock = answerBlocks[answerBlocks.length - 1];
|
|
542
|
+
const text = getFullText(lastBlock);
|
|
543
|
+
if (text.length > 0) return text;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// 回退 1: 通过 answer-box 获取
|
|
547
|
+
const answerBoxes = document.querySelectorAll('.answer-box, .last-answer-box');
|
|
548
|
+
if (answerBoxes.length > 0) {
|
|
549
|
+
const lastBox = answerBoxes[answerBoxes.length - 1];
|
|
550
|
+
const text = getFullText(lastBox);
|
|
551
|
+
if (text.length > 0) return text;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// 回退 2: 旧版 #answer_text_id (兼容)
|
|
478
555
|
const answerEl = document.querySelector('#answer_text_id');
|
|
479
556
|
if (answerEl) {
|
|
480
557
|
return getFullText(answerEl);
|
|
481
558
|
}
|
|
482
559
|
|
|
483
|
-
//
|
|
560
|
+
// 回退 3: 通用 markdown 容器
|
|
561
|
+
const markdowns = document.querySelectorAll('.ai-markdown, [class*="markdown-content"]');
|
|
562
|
+
if (markdowns.length > 0) {
|
|
563
|
+
const lastMd = markdowns[markdowns.length - 1];
|
|
564
|
+
const text = getFullText(lastMd);
|
|
565
|
+
if (text.length > 0) return text;
|
|
566
|
+
}
|
|
567
|
+
|
|
484
568
|
return '';
|
|
485
569
|
});
|
|
486
570
|
}
|
|
487
571
|
|
|
488
572
|
async _isGenerating() {
|
|
489
573
|
return await this.page.evaluate(() => {
|
|
490
|
-
// ── 1.
|
|
574
|
+
// ── 1. 检测停止按钮(最可靠的生成中信号)──
|
|
491
575
|
const stopSelectors = [
|
|
492
576
|
'button[aria-label*="Stop" i]',
|
|
493
577
|
'button[aria-label*="停止"]',
|
|
494
578
|
'[class*="stop-gen"]',
|
|
495
579
|
'[class*="stopGen"]',
|
|
496
|
-
'[class*="
|
|
497
|
-
'[class*=" Generating"]',
|
|
580
|
+
'[class*="stop-btn"]',
|
|
498
581
|
];
|
|
499
582
|
for (const sel of stopSelectors) {
|
|
500
583
|
const el = document.querySelector(sel);
|
|
@@ -504,21 +587,15 @@ class YiyanBrowser {
|
|
|
504
587
|
}
|
|
505
588
|
}
|
|
506
589
|
|
|
507
|
-
// ── 2.
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
'[class*="loading"]',
|
|
511
|
-
'[class*="spinner"]',
|
|
512
|
-
'[class*="blink"]',
|
|
513
|
-
'[class*="cursor"]',
|
|
514
|
-
'[class*="pulsing"]',
|
|
515
|
-
'[class*="thinking"]',
|
|
590
|
+
// ── 2. 检测活跃的 CSS 动画(真正的 loading spinner,不是永久类名)──
|
|
591
|
+
// 只检测 svg 动画元素和明确的 loading 指示器
|
|
592
|
+
const activeAnimSelectors = [
|
|
516
593
|
'svg[class*="loading"]',
|
|
517
594
|
'svg[class*="spinner"]',
|
|
518
|
-
'
|
|
519
|
-
'
|
|
595
|
+
'[class*="loading-indicator"]',
|
|
596
|
+
'[class*="generating-indicator"]',
|
|
520
597
|
];
|
|
521
|
-
for (const sel of
|
|
598
|
+
for (const sel of activeAnimSelectors) {
|
|
522
599
|
const el = document.querySelector(sel);
|
|
523
600
|
if (el) {
|
|
524
601
|
const s = window.getComputedStyle(el);
|
|
@@ -526,30 +603,40 @@ class YiyanBrowser {
|
|
|
526
603
|
}
|
|
527
604
|
}
|
|
528
605
|
|
|
529
|
-
// ── 3.
|
|
606
|
+
// ── 3. 如果有完成标记 → 肯定已完成 ──
|
|
530
607
|
const completionMarkers = [
|
|
531
|
-
'
|
|
532
|
-
'.
|
|
608
|
+
'.cos-icon.cos-icon-copy',
|
|
609
|
+
'.cos-icon-copy',
|
|
610
|
+
'.feedback-hover-show',
|
|
611
|
+
'.cos-icon.cos-icon-share1',
|
|
612
|
+
'.cos-icon.cos-icon-feedback',
|
|
533
613
|
'[class*="copy-btn"]',
|
|
534
|
-
'[class*="copyBtn"]',
|
|
535
614
|
'[aria-label*="Copy" i]',
|
|
536
615
|
'[aria-label*="复制"]',
|
|
537
|
-
'[class*="regenerate"]',
|
|
538
|
-
'[class*="retry"]',
|
|
539
|
-
'[class*="action-btn"]',
|
|
540
616
|
];
|
|
541
|
-
let hasCompletionMarker = false;
|
|
542
617
|
for (const sel of completionMarkers) {
|
|
543
|
-
if (document.querySelector(sel))
|
|
544
|
-
hasCompletionMarker = true;
|
|
545
|
-
break;
|
|
546
|
-
}
|
|
618
|
+
if (document.querySelector(sel)) return false;
|
|
547
619
|
}
|
|
548
620
|
|
|
549
|
-
//
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
621
|
+
// ── 4. 检查是否有光标闪烁(生成中的光标)──
|
|
622
|
+
// 用 getComputedStyle 检测 animation,而不是类名
|
|
623
|
+
const answerArea = document.querySelector(
|
|
624
|
+
'.ai-entry-block.ai-markdown, .answer-container, .cs-answer-container'
|
|
625
|
+
);
|
|
626
|
+
if (answerArea) {
|
|
627
|
+
// 查找最后子元素是否有动画光标
|
|
628
|
+
const lastChild = answerArea.lastElementChild;
|
|
629
|
+
if (lastChild) {
|
|
630
|
+
const s = window.getComputedStyle(lastChild);
|
|
631
|
+
// 如果最后一个元素有活跃的动画 → 还在生成
|
|
632
|
+
if (s.animationName && s.animationName !== 'none' &&
|
|
633
|
+
s.display !== 'none' && s.visibility !== 'hidden') {
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// 有回答内容但没有完成标记 → 可能还在生成
|
|
639
|
+
if (answerArea.innerText && answerArea.innerText.length > 5) {
|
|
553
640
|
return true;
|
|
554
641
|
}
|
|
555
642
|
}
|
|
@@ -568,29 +655,34 @@ class YiyanBrowser {
|
|
|
568
655
|
'正在思考中',
|
|
569
656
|
'正在思考',
|
|
570
657
|
'思考过程',
|
|
571
|
-
'
|
|
572
|
-
'我需要',
|
|
658
|
+
'深度思考',
|
|
573
659
|
'根据搜索结果',
|
|
574
660
|
'参考',
|
|
575
|
-
'picaole需要',
|
|
576
661
|
];
|
|
577
662
|
|
|
578
|
-
// Remove lines that
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
663
|
+
// Remove lines that are standalone thinking/UI markers
|
|
664
|
+
const lines = text.split('\n');
|
|
665
|
+
text = lines.filter(line => {
|
|
666
|
+
const trimmed = line.trim();
|
|
667
|
+
// 移除纯 UI 标记行
|
|
668
|
+
if (thinkingMarkers.some(m => trimmed === m)) return false;
|
|
669
|
+
// 移除建议追问行(新 UI 的 follow-up 按钮)
|
|
670
|
+
if (/^(能否|能再|能帮|可以|请用|用一句|用一段)/.test(trimmed) && trimmed.length < 50) return false;
|
|
671
|
+
return true;
|
|
672
|
+
}).join('\n');
|
|
673
|
+
|
|
674
|
+
// Remove everything before "准备输出结果" (thinking process end marker)
|
|
586
675
|
const outputMarker = '准备输出结果';
|
|
587
676
|
const markerIndex = text.indexOf(outputMarker);
|
|
588
677
|
if (markerIndex !== -1) {
|
|
589
678
|
text = text.slice(markerIndex + outputMarker.length).trim();
|
|
590
679
|
}
|
|
591
680
|
|
|
592
|
-
// Remove everything after regenerate/suggestion markers (
|
|
593
|
-
const cutMarkers = [
|
|
681
|
+
// Remove everything after regenerate/suggestion markers (UI elements)
|
|
682
|
+
const cutMarkers = [
|
|
683
|
+
'重新生成', '重新生成的', '换个回答', '输出更详细的', '再多提供',
|
|
684
|
+
'内容由AI生成', '查看使用规则',
|
|
685
|
+
];
|
|
594
686
|
for (const marker of cutMarkers) {
|
|
595
687
|
const cutIndex = text.indexOf(marker);
|
|
596
688
|
if (cutIndex !== -1) {
|