myagent-ai 1.6.5 → 1.6.7
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.
|
Binary file
|
package/agents/base.py
CHANGED
|
@@ -145,6 +145,7 @@ class BaseAgent(ABC):
|
|
|
145
145
|
request_kwargs.update(kwargs)
|
|
146
146
|
|
|
147
147
|
full_text = ""
|
|
148
|
+
full_reasoning = "" # Track reasoning tokens (for o1/o3/DeepSeek-R1 etc.)
|
|
148
149
|
tool_calls_acc: Dict[int, Dict] = {} # index -> {id, name, arguments_str}
|
|
149
150
|
finish_reason = ""
|
|
150
151
|
|
|
@@ -166,6 +167,12 @@ class BaseAgent(ABC):
|
|
|
166
167
|
else:
|
|
167
168
|
await _write_sse({"type": "text_delta", "content": delta_text})
|
|
168
169
|
|
|
170
|
+
async def _emit_reasoning_delta(delta_text: str):
|
|
171
|
+
"""处理推理 token:发送 reasoning_delta SSE 事件"""
|
|
172
|
+
nonlocal full_reasoning
|
|
173
|
+
full_reasoning += delta_text
|
|
174
|
+
await _write_sse({"type": "reasoning_delta", "content": delta_text})
|
|
175
|
+
|
|
169
176
|
try:
|
|
170
177
|
if self.llm.provider in self.llm._OPENAI_COMPATIBLE_PROVIDERS or self.llm.provider == "zhipu":
|
|
171
178
|
# 使用异步客户端流式
|
|
@@ -190,6 +197,11 @@ class BaseAgent(ABC):
|
|
|
190
197
|
if delta.content:
|
|
191
198
|
await _emit_text_delta(delta.content)
|
|
192
199
|
|
|
200
|
+
# Handle reasoning_content (OpenAI o1/o3, DeepSeek-R1, Qwen-QwQ etc.)
|
|
201
|
+
reasoning_content = getattr(delta, 'reasoning_content', None) or getattr(delta, 'reasoning', None)
|
|
202
|
+
if reasoning_content:
|
|
203
|
+
await _emit_reasoning_delta(reasoning_content)
|
|
204
|
+
|
|
193
205
|
# Handle tool_call deltas (accumulate)
|
|
194
206
|
if hasattr(delta, 'tool_calls') and delta.tool_calls:
|
|
195
207
|
for tc_delta in delta.tool_calls:
|
|
@@ -252,8 +264,17 @@ class BaseAgent(ABC):
|
|
|
252
264
|
if event is None:
|
|
253
265
|
break
|
|
254
266
|
if event.type == "content_block_delta":
|
|
255
|
-
|
|
267
|
+
# Handle text content
|
|
268
|
+
if hasattr(event.delta, "text") and event.delta.text:
|
|
256
269
|
await _emit_text_delta(event.delta.text)
|
|
270
|
+
# Handle extended thinking (Anthropic reasoning)
|
|
271
|
+
if hasattr(event.delta, "thinking") and event.delta.thinking:
|
|
272
|
+
await _emit_reasoning_delta(event.delta.thinking)
|
|
273
|
+
# Handle thinking delta via type
|
|
274
|
+
if hasattr(event.delta, "type") and event.delta.type == "thinking_delta":
|
|
275
|
+
thinking_text = getattr(event.delta, "thinking", "")
|
|
276
|
+
if thinking_text:
|
|
277
|
+
await _emit_reasoning_delta(thinking_text)
|
|
257
278
|
elif event.type == "message_stop":
|
|
258
279
|
finish_reason = "stop"
|
|
259
280
|
|
|
@@ -292,9 +313,14 @@ class BaseAgent(ABC):
|
|
|
292
313
|
break
|
|
293
314
|
try:
|
|
294
315
|
data = json.loads(line.decode('utf-8') if isinstance(line, bytes) else line)
|
|
295
|
-
|
|
316
|
+
message = data.get("message", {})
|
|
317
|
+
content = message.get("content", "")
|
|
296
318
|
if content:
|
|
297
319
|
await _emit_text_delta(content)
|
|
320
|
+
# Handle Ollama thinking/reasoning (e.g., DeepSeek-R1 via Ollama)
|
|
321
|
+
thinking = message.get("thinking", "")
|
|
322
|
+
if thinking:
|
|
323
|
+
await _emit_reasoning_delta(thinking)
|
|
298
324
|
if data.get("done"):
|
|
299
325
|
finish_reason = "stop"
|
|
300
326
|
# Record usage from Ollama
|
package/package.json
CHANGED
|
Binary file
|
package/web/api_server.py
CHANGED
|
@@ -592,7 +592,16 @@ class ApiServer:
|
|
|
592
592
|
agent_path=agent_path, agent_system_prompt=agent_system_prompt,
|
|
593
593
|
chat_mode=chat_mode, stream_response=response,
|
|
594
594
|
)
|
|
595
|
+
elif self.core.main_agent and self.core.llm:
|
|
596
|
+
# model_chain is empty (no model_id configured), but LLM is available
|
|
597
|
+
# Use _stream_process_message for true token-by-token streaming
|
|
598
|
+
full_response = await self._stream_process_message(
|
|
599
|
+
clean_message, session_id, response,
|
|
600
|
+
agent_path=agent_path, agent_system_prompt=agent_system_prompt,
|
|
601
|
+
chat_mode=chat_mode,
|
|
602
|
+
)
|
|
595
603
|
else:
|
|
604
|
+
# No LLM at all — non-streaming fallback
|
|
596
605
|
full_response = await self.core.process_message(clean_message, session_id)
|
|
597
606
|
await response.write(("data: " + json.dumps({"type": "text", "content": full_response}) + "\n\n").encode())
|
|
598
607
|
|
|
@@ -640,6 +649,12 @@ class ApiServer:
|
|
|
640
649
|
agent_path=agent_path, agent_system_prompt=agent_system_prompt_q,
|
|
641
650
|
chat_mode=chat_mode, stream_response=response,
|
|
642
651
|
)
|
|
652
|
+
elif self.core.main_agent and self.core.llm:
|
|
653
|
+
full_response = await self._stream_process_message(
|
|
654
|
+
clean_message_q, session_id, response,
|
|
655
|
+
agent_path=agent_path, agent_system_prompt=agent_system_prompt_q,
|
|
656
|
+
chat_mode=chat_mode,
|
|
657
|
+
)
|
|
643
658
|
else:
|
|
644
659
|
full_response = await self.core.process_message(clean_message_q, session_id)
|
|
645
660
|
await response.write(("data: " + json.dumps({"type": "text", "content": full_response}) + "\n\n").encode())
|
|
@@ -3870,6 +3885,7 @@ class ApiServer:
|
|
|
3870
3885
|
# 获取agent的显示信息
|
|
3871
3886
|
avatar = "🤖"
|
|
3872
3887
|
display_name = agent_path
|
|
3888
|
+
agent_color = "var(--accent)"
|
|
3873
3889
|
if agent_cfg:
|
|
3874
3890
|
avatar = agent_cfg.get("avatar_emoji", "🤖") or "🤖"
|
|
3875
3891
|
display_name = agent_cfg.get("name", agent_path)
|
|
@@ -3879,6 +3895,7 @@ class ApiServer:
|
|
|
3879
3895
|
"agent_path": agent_path,
|
|
3880
3896
|
"name": display_name,
|
|
3881
3897
|
"avatar": avatar,
|
|
3898
|
+
"agent_color": agent_color,
|
|
3882
3899
|
"response": response,
|
|
3883
3900
|
}
|
|
3884
3901
|
except Exception as e:
|
package/web/ui/chat.html
CHANGED
|
@@ -3351,6 +3351,17 @@ function renderMessages() {
|
|
|
3351
3351
|
<div class="thought-content">${renderMarkdown(msg.thought)}</div>
|
|
3352
3352
|
</details>`;
|
|
3353
3353
|
})() : '';
|
|
3354
|
+
const reasoningHtml = msg.reasoning ? (() => {
|
|
3355
|
+
const isStreaming = !!msg.streaming;
|
|
3356
|
+
return `<details class="thought-block ${isStreaming ? 'streaming' : ''}" ${isStreaming ? 'open' : ''}>
|
|
3357
|
+
<summary>
|
|
3358
|
+
<span class="thought-icon">💡</span>
|
|
3359
|
+
<span class="thought-label">模型推理过程</span>
|
|
3360
|
+
${isStreaming ? '<span class="thought-badge">推理中...</span>' : '<span class="thought-badge">已完成</span>'}
|
|
3361
|
+
</summary>
|
|
3362
|
+
<div class="thought-content">${renderMarkdown(msg.reasoning)}</div>
|
|
3363
|
+
</details>`;
|
|
3364
|
+
})() : '';
|
|
3354
3365
|
const actionBtns = (!isUser && msg.content) ? `
|
|
3355
3366
|
<div class="msg-actions">
|
|
3356
3367
|
<button class="msg-action-btn" onclick="copyMessage(${i})" title="复制">
|
|
@@ -3376,6 +3387,7 @@ function renderMessages() {
|
|
|
3376
3387
|
<div class="message-row ${msg.role}">
|
|
3377
3388
|
<div class="message-avatar">${avatar}</div>
|
|
3378
3389
|
<div style="flex:1;min-width:0">
|
|
3390
|
+
${reasoningHtml}
|
|
3379
3391
|
${thoughtHtml}
|
|
3380
3392
|
${content || streamingIndicator ? `<div class="message-bubble">${content}${ttsIndicator}</div>` : ''}
|
|
3381
3393
|
${streamingIndicator}
|
|
@@ -3393,6 +3405,165 @@ function renderMessages() {
|
|
|
3393
3405
|
scrollToBottom();
|
|
3394
3406
|
}
|
|
3395
3407
|
|
|
3408
|
+
// ── Incremental Streaming Update (avoid full DOM rebuild on every token) ──
|
|
3409
|
+
function updateStreamingMessage(msgIdx) {
|
|
3410
|
+
// Only update the last streaming message bubble without rebuilding the entire DOM
|
|
3411
|
+
const msg = state.messages[msgIdx];
|
|
3412
|
+
if (!msg || msg.role !== 'assistant') return;
|
|
3413
|
+
|
|
3414
|
+
const container = document.getElementById('messagesInner');
|
|
3415
|
+
if (!container) return;
|
|
3416
|
+
|
|
3417
|
+
// Find or create the streaming message row
|
|
3418
|
+
const rows = container.querySelectorAll('.message-row.assistant');
|
|
3419
|
+
let targetRow = null;
|
|
3420
|
+
// Count assistant rows to find the right one
|
|
3421
|
+
let assistantCount = 0;
|
|
3422
|
+
for (const row of rows) {
|
|
3423
|
+
assistantCount++;
|
|
3424
|
+
if (assistantCount === msgIdx + 1) {
|
|
3425
|
+
targetRow = row;
|
|
3426
|
+
break;
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
|
|
3430
|
+
// Fallback: if we can't find by count, use last assistant row
|
|
3431
|
+
if (!targetRow && rows.length > 0) {
|
|
3432
|
+
targetRow = rows[rows.length - 1];
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3435
|
+
if (!targetRow) {
|
|
3436
|
+
// Message row doesn't exist yet, fall back to full render
|
|
3437
|
+
renderMessages();
|
|
3438
|
+
return;
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3441
|
+
const contentArea = targetRow.querySelector(':scope > div');
|
|
3442
|
+
if (!contentArea) return;
|
|
3443
|
+
|
|
3444
|
+
// Update reasoning block (model inference/reasoning - e.g. o1, DeepSeek-R1)
|
|
3445
|
+
let reasoningBlock = contentArea.querySelector('.thought-block .thought-label');
|
|
3446
|
+
// Find reasoning block by looking for "模型推理过程" label
|
|
3447
|
+
let reasoningDetails = null;
|
|
3448
|
+
const allThoughtBlocks = contentArea.querySelectorAll('.thought-block');
|
|
3449
|
+
for (const tb of allThoughtBlocks) {
|
|
3450
|
+
const label = tb.querySelector('.thought-label');
|
|
3451
|
+
if (label && label.textContent.includes('模型推理过程')) {
|
|
3452
|
+
reasoningDetails = tb;
|
|
3453
|
+
break;
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
if (msg.reasoning) {
|
|
3457
|
+
const reasoningHtml = `<details class="thought-block ${msg.streaming ? 'streaming' : ''}" ${msg.streaming ? 'open' : ''}>
|
|
3458
|
+
<summary>
|
|
3459
|
+
<span class="thought-icon">💡</span>
|
|
3460
|
+
<span class="thought-label">模型推理过程</span>
|
|
3461
|
+
${msg.streaming ? '<span class="thought-badge">推理中...</span>' : '<span class="thought-badge">已完成</span>'}
|
|
3462
|
+
</summary>
|
|
3463
|
+
<div class="thought-content">${renderMarkdown(msg.reasoning)}</div>
|
|
3464
|
+
</details>`;
|
|
3465
|
+
if (reasoningDetails) {
|
|
3466
|
+
reasoningDetails.outerHTML = reasoningHtml;
|
|
3467
|
+
} else {
|
|
3468
|
+
contentArea.insertAdjacentHTML('afterbegin', reasoningHtml);
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3472
|
+
// Update thought block (agent thinking)
|
|
3473
|
+
let thoughtBlock = null;
|
|
3474
|
+
const updatedBlocks = contentArea.querySelectorAll('.thought-block');
|
|
3475
|
+
for (const tb of updatedBlocks) {
|
|
3476
|
+
const label = tb.querySelector('.thought-label');
|
|
3477
|
+
if (label && label.textContent.includes('Agent 思考过程')) {
|
|
3478
|
+
thoughtBlock = tb;
|
|
3479
|
+
break;
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
if (msg.thought) {
|
|
3483
|
+
const thoughtHtml = `<details class="thought-block ${msg.streaming ? 'streaming' : ''}" ${msg.streaming ? 'open' : ''}>
|
|
3484
|
+
<summary>
|
|
3485
|
+
<span class="thought-icon">🧠</span>
|
|
3486
|
+
<span class="thought-label">Agent 思考过程</span>
|
|
3487
|
+
${msg.streaming ? '<span class="thought-badge">思考中...</span>' : '<span class="thought-badge">已完成</span>'}
|
|
3488
|
+
</summary>
|
|
3489
|
+
<div class="thought-content">${renderMarkdown(msg.thought)}</div>
|
|
3490
|
+
</details>`;
|
|
3491
|
+
if (thoughtBlock) {
|
|
3492
|
+
thoughtBlock.outerHTML = thoughtHtml;
|
|
3493
|
+
} else {
|
|
3494
|
+
contentArea.insertAdjacentHTML('afterbegin', thoughtHtml);
|
|
3495
|
+
}
|
|
3496
|
+
} else if (thoughtBlock && !msg.thought) {
|
|
3497
|
+
thoughtBlock.remove();
|
|
3498
|
+
}
|
|
3499
|
+
|
|
3500
|
+
// Update message bubble content
|
|
3501
|
+
const bubble = contentArea.querySelector('.message-bubble');
|
|
3502
|
+
const content = msg.content ? renderMarkdown(msg.content) : '';
|
|
3503
|
+
const streamingIndicator = msg.streaming && !msg.content && !msg.thought ? `
|
|
3504
|
+
<div class="streaming-indicator">
|
|
3505
|
+
<div class="spinner"></div>
|
|
3506
|
+
<div class="streaming-dots">
|
|
3507
|
+
<span class="dot"></span><span class="dot"></span><span class="dot"></span>
|
|
3508
|
+
</div>
|
|
3509
|
+
<span style="font-weight:500">Agent 正在思考...</span>
|
|
3510
|
+
</div>` : '';
|
|
3511
|
+
|
|
3512
|
+
if (bubble) {
|
|
3513
|
+
if (content) {
|
|
3514
|
+
bubble.innerHTML = content;
|
|
3515
|
+
bubble.style.display = '';
|
|
3516
|
+
} else {
|
|
3517
|
+
bubble.style.display = 'none';
|
|
3518
|
+
}
|
|
3519
|
+
} else if (content) {
|
|
3520
|
+
contentArea.insertAdjacentHTML('afterbegin', `<div class="message-bubble">${content}</div>`);
|
|
3521
|
+
}
|
|
3522
|
+
|
|
3523
|
+
// Update streaming indicator
|
|
3524
|
+
let indicator = contentArea.querySelector('.streaming-indicator');
|
|
3525
|
+
if (streamingIndicator) {
|
|
3526
|
+
if (!indicator) {
|
|
3527
|
+
contentArea.insertAdjacentHTML('beforeend', streamingIndicator);
|
|
3528
|
+
}
|
|
3529
|
+
} else if (indicator) {
|
|
3530
|
+
indicator.remove();
|
|
3531
|
+
}
|
|
3532
|
+
|
|
3533
|
+
// Update exec events
|
|
3534
|
+
const existingPanel = contentArea.querySelector('.exec-events-panel');
|
|
3535
|
+
if (msg.exec_events && msg.exec_events.length > 0) {
|
|
3536
|
+
const execHtml = renderExecEvents(msg.exec_events, msgIdx);
|
|
3537
|
+
if (existingPanel) {
|
|
3538
|
+
existingPanel.outerHTML = execHtml;
|
|
3539
|
+
} else {
|
|
3540
|
+
contentArea.insertAdjacentHTML('beforeend', execHtml);
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
|
|
3544
|
+
// Auto-scroll
|
|
3545
|
+
scrollToBottom();
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
// Throttle streaming updates (max ~20fps to avoid excessive reflows)
|
|
3549
|
+
const _streamThrottle = { last: 0, pending: false, idx: -1 };
|
|
3550
|
+
function throttledStreamUpdate(msgIdx) {
|
|
3551
|
+
_streamThrottle.idx = msgIdx;
|
|
3552
|
+
if (_streamThrottle.pending) return;
|
|
3553
|
+
const now = performance.now();
|
|
3554
|
+
if (now - _streamThrottle.last < 50) {
|
|
3555
|
+
_streamThrottle.pending = true;
|
|
3556
|
+
requestAnimationFrame(() => {
|
|
3557
|
+
_streamThrottle.pending = false;
|
|
3558
|
+
_streamThrottle.last = performance.now();
|
|
3559
|
+
updateStreamingMessage(_streamThrottle.idx);
|
|
3560
|
+
});
|
|
3561
|
+
return;
|
|
3562
|
+
}
|
|
3563
|
+
_streamThrottle.last = now;
|
|
3564
|
+
updateStreamingMessage(msgIdx);
|
|
3565
|
+
}
|
|
3566
|
+
|
|
3396
3567
|
// ── Execution Events Rendering ──
|
|
3397
3568
|
function renderExecEvents(events, msgIndex) {
|
|
3398
3569
|
// 过滤出有意义的事件(仅展示调用/结果对,跳过中间状态)
|
|
@@ -3789,6 +3960,8 @@ async function sendMessage() {
|
|
|
3789
3960
|
|
|
3790
3961
|
if (evt.type === 'session') {
|
|
3791
3962
|
sessionIdReceived = evt.session_id;
|
|
3963
|
+
// Sync the actual session ID (backend may prefix with agent_path)
|
|
3964
|
+
state.activeSessionId = evt.session_id;
|
|
3792
3965
|
} else if (evt.type === 'text') {
|
|
3793
3966
|
fullResponse = evt.content;
|
|
3794
3967
|
state.messages[msgIdx].content = evt.content;
|
|
@@ -3797,12 +3970,12 @@ async function sendMessage() {
|
|
|
3797
3970
|
// Incremental streaming token
|
|
3798
3971
|
fullResponse += evt.content;
|
|
3799
3972
|
state.messages[msgIdx].content = fullResponse;
|
|
3800
|
-
|
|
3973
|
+
throttledStreamUpdate(msgIdx);
|
|
3801
3974
|
} else if (evt.type === 'thought_delta') {
|
|
3802
3975
|
// Agent 思考过程增量文本(流式推送,单独显示)
|
|
3803
3976
|
fullThought += evt.content;
|
|
3804
3977
|
state.messages[msgIdx].thought = fullThought;
|
|
3805
|
-
|
|
3978
|
+
throttledStreamUpdate(msgIdx);
|
|
3806
3979
|
} else if (evt.type === 'thought') {
|
|
3807
3980
|
// Agent 思考过程文本
|
|
3808
3981
|
if (fullThought.trim() === '') {
|
|
@@ -3811,7 +3984,7 @@ async function sendMessage() {
|
|
|
3811
3984
|
fullThought += '\n\n' + evt.content;
|
|
3812
3985
|
}
|
|
3813
3986
|
state.messages[msgIdx].thought = fullThought;
|
|
3814
|
-
|
|
3987
|
+
throttledStreamUpdate(msgIdx);
|
|
3815
3988
|
} else if (evt.type === 'queue_start') {
|
|
3816
3989
|
// New message starting from queue
|
|
3817
3990
|
if (state.messages[msgIdx]) {
|
|
@@ -3829,13 +4002,13 @@ async function sendMessage() {
|
|
|
3829
4002
|
// Clear intermediate text from previous agent loop iterations
|
|
3830
4003
|
fullResponse = '';
|
|
3831
4004
|
state.messages[msgIdx].content = '';
|
|
3832
|
-
|
|
4005
|
+
throttledStreamUpdate(msgIdx);
|
|
3833
4006
|
} else if (evt.type === 'exec_event') {
|
|
3834
4007
|
// Real-time execution event (tool call, code exec, skill result, etc.)
|
|
3835
4008
|
execEventsReceived.push(evt.data);
|
|
3836
4009
|
// 立即更新消息的 exec_events 并渲染
|
|
3837
4010
|
state.messages[msgIdx].exec_events = [...execEventsReceived];
|
|
3838
|
-
|
|
4011
|
+
throttledStreamUpdate(msgIdx);
|
|
3839
4012
|
} else if (evt.type === 'task_plan_updated') {
|
|
3840
4013
|
// 任务计划已更新,刷新侧边栏任务列表
|
|
3841
4014
|
loadTaskPlan();
|
|
@@ -3845,6 +4018,15 @@ async function sendMessage() {
|
|
|
3845
4018
|
execEventsReceived = evt.exec_events;
|
|
3846
4019
|
state.messages[msgIdx].exec_events = [...execEventsReceived];
|
|
3847
4020
|
}
|
|
4021
|
+
} else if (evt.type === 'reasoning_delta') {
|
|
4022
|
+
// 模型推理过程增量文本(OpenAI o1/o3/DeepSeek-R1 等推理模型)
|
|
4023
|
+
if (!state.messages[msgIdx].reasoning) state.messages[msgIdx].reasoning = '';
|
|
4024
|
+
state.messages[msgIdx].reasoning += evt.content;
|
|
4025
|
+
throttledStreamUpdate(msgIdx);
|
|
4026
|
+
} else if (evt.type === 'reasoning') {
|
|
4027
|
+
// 模型推理完整文本
|
|
4028
|
+
state.messages[msgIdx].reasoning = evt.content;
|
|
4029
|
+
throttledStreamUpdate(msgIdx);
|
|
3848
4030
|
} else if (evt.type === 'error') {
|
|
3849
4031
|
fullResponse = '❌ ' + evt.error;
|
|
3850
4032
|
state.messages[msgIdx].content = fullResponse;
|
|
@@ -4770,7 +4952,22 @@ async function selectGroup(gid) {
|
|
|
4770
4952
|
|
|
4771
4953
|
// Load messages
|
|
4772
4954
|
var msgsData = await getGroupMessages(gid);
|
|
4773
|
-
|
|
4955
|
+
// Normalize backend message format: map sender->role, sender_name->agent_name, sender_avatar->agent_emoji
|
|
4956
|
+
groupMessages = (msgsData || []).map(function(m) {
|
|
4957
|
+
if (m.sender) {
|
|
4958
|
+
return {
|
|
4959
|
+
role: m.sender === 'user' ? 'user' : 'assistant',
|
|
4960
|
+
agent: m.agent_path || '',
|
|
4961
|
+
agent_name: m.sender_name || '',
|
|
4962
|
+
agent_emoji: m.sender_avatar || '🤖',
|
|
4963
|
+
agent_color: m.agent_color || '',
|
|
4964
|
+
content: m.content || '',
|
|
4965
|
+
time: m.timestamp ? new Date(m.timestamp * 1000).toISOString() : (m.time || ''),
|
|
4966
|
+
type: m.msg_type === 'system' ? 'system' : '',
|
|
4967
|
+
};
|
|
4968
|
+
}
|
|
4969
|
+
return m;
|
|
4970
|
+
});
|
|
4774
4971
|
renderGroupMessages();
|
|
4775
4972
|
} catch (e) {
|
|
4776
4973
|
toast('加载群聊失败: ' + e.message, 'error');
|
|
@@ -4847,8 +5044,9 @@ function renderGroupMessages() {
|
|
|
4847
5044
|
// Multiple agent responses (broadcast response)
|
|
4848
5045
|
for (var j = 0; j < msg.responses.length; j++) {
|
|
4849
5046
|
var r = msg.responses[j];
|
|
4850
|
-
|
|
4851
|
-
var
|
|
5047
|
+
// Backend returns "name" and "avatar" fields, with "agent_name"/"agent_emoji" as fallback
|
|
5048
|
+
var rName = r.name || r.agent_name || r.agent || 'Agent';
|
|
5049
|
+
var rEmoji = r.avatar || r.agent_emoji || '🤖';
|
|
4852
5050
|
var rColor = r.agent_color || 'var(--accent)';
|
|
4853
5051
|
html += '<div class="group-msg-row">'
|
|
4854
5052
|
+ '<div class="group-msg-avatar" style="background:' + rColor + ';color:#fff">' + rEmoji + '</div>'
|