myagent-ai 1.9.1 → 1.9.3
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/core/llm.py +28 -20
- package/package.json +1 -1
- package/web/ui/chat/chat.js +1 -1
- package/web/ui/chat/flow_engine.js +27 -11
- package/web/ui/chat/middle_chat.html +13 -0
package/core/llm.py
CHANGED
|
@@ -649,28 +649,36 @@ class LLMClient:
|
|
|
649
649
|
logger.error(f"流式 LLM 调用失败: {e}")
|
|
650
650
|
|
|
651
651
|
async def _stream_openai(self, kwargs: dict) -> AsyncGenerator[str, None]:
|
|
652
|
-
"""OpenAI / 兼容接口 (含 Zhipu) 流式调用
|
|
653
|
-
loop = asyncio.get_running_loop()
|
|
654
|
-
|
|
655
|
-
def _create_stream():
|
|
656
|
-
return self._client.chat.completions.create(**kwargs)
|
|
657
|
-
|
|
658
|
-
stream = await loop.run_in_executor(None, _create_stream)
|
|
652
|
+
"""OpenAI / 兼容接口 (含 Zhipu) 流式调用
|
|
659
653
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
654
|
+
支持两种客户端:
|
|
655
|
+
- AsyncOpenAI: 使用 async for 直接异步迭代
|
|
656
|
+
- 同步 OpenAI: 在 executor 中同步迭代
|
|
657
|
+
"""
|
|
658
|
+
# 判断客户端类型,选择合适的流式迭代方式
|
|
659
|
+
is_async = hasattr(self._client, '__aenter__')
|
|
660
|
+
|
|
661
|
+
if is_async:
|
|
662
|
+
# AsyncOpenAI: 直接异步迭代
|
|
663
|
+
stream = await self._client.chat.completions.create(**kwargs)
|
|
664
|
+
async for chunk in stream:
|
|
665
|
+
if chunk.choices and chunk.choices[0].delta.content:
|
|
666
|
+
yield chunk.choices[0].delta.content
|
|
667
|
+
else:
|
|
668
|
+
# 同步 OpenAI: 在 executor 中迭代
|
|
669
|
+
loop = asyncio.get_running_loop()
|
|
666
670
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
671
|
+
def _create_and_iterate():
|
|
672
|
+
stream = self._client.chat.completions.create(**kwargs)
|
|
673
|
+
results = []
|
|
674
|
+
for chunk in stream:
|
|
675
|
+
if chunk.choices and chunk.choices[0].delta.content:
|
|
676
|
+
results.append(chunk.choices[0].delta.content)
|
|
677
|
+
return results
|
|
678
|
+
|
|
679
|
+
chunks = await loop.run_in_executor(None, _create_and_iterate)
|
|
680
|
+
for content in chunks:
|
|
681
|
+
yield content
|
|
674
682
|
|
|
675
683
|
async def _stream_anthropic(
|
|
676
684
|
self, messages: List[Message], kwargs: dict
|
package/package.json
CHANGED
package/web/ui/chat/chat.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
(function() {
|
|
5
5
|
var base = new URL('.', window.location.href).href;
|
|
6
|
-
var v = '?v=
|
|
6
|
+
var v = '?v=6';
|
|
7
7
|
var files = [
|
|
8
8
|
{ url: base + 'left_sessions.html' + v, container: 'leftSessionsContainer' },
|
|
9
9
|
{ url: base + 'middle_chat.html' + v, container: 'middleChatContainer' },
|
|
@@ -620,17 +620,17 @@ function toggleExecEventsPanel(header) {
|
|
|
620
620
|
function renderInlineExecEvent(data, msgIdx) {
|
|
621
621
|
// V2 Tool Event handling (called with full part: {type:'v2_tool', data:{...}})
|
|
622
622
|
if (data.type === 'v2_tool') {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
623
|
+
let inner = data.data;
|
|
624
|
+
let isStart = inner.status === 'running' || inner.type === 'tool_start';
|
|
625
|
+
let iconEmoji = isStart ? '⚡' : (inner.success ? '✅' : '❌');
|
|
626
|
+
let title = inner.title || (inner.tool_name || '工具调用');
|
|
627
|
+
let metaParts = [];
|
|
628
628
|
if (inner.tool_name) metaParts.push(escapeHtml(inner.tool_name));
|
|
629
629
|
if (inner.timeout) metaParts.push('超时 ' + inner.timeout + 's');
|
|
630
630
|
if (inner.callback !== undefined) metaParts.push(inner.callback ? '回调LLM' : '无回调');
|
|
631
|
-
|
|
631
|
+
let metaText = metaParts.join(' · ');
|
|
632
632
|
|
|
633
|
-
|
|
633
|
+
let bodyHtml = '';
|
|
634
634
|
if (inner.type === 'tool_result' && inner.summary) {
|
|
635
635
|
bodyHtml += '<div class="inline-exec-summary">' + escapeHtml(inner.summary) + '</div>';
|
|
636
636
|
}
|
|
@@ -639,7 +639,7 @@ function renderInlineExecEvent(data, msgIdx) {
|
|
|
639
639
|
}
|
|
640
640
|
// Show params for tool_start
|
|
641
641
|
if (inner.type === 'tool_start' && inner.params) {
|
|
642
|
-
|
|
642
|
+
let paramPreview = typeof inner.params === 'string' ? inner.params : JSON.stringify(inner.params);
|
|
643
643
|
if (paramPreview.length > 200) paramPreview = paramPreview.substring(0, 200) + '...';
|
|
644
644
|
bodyHtml += '<div class="inline-exec-code">' + escapeHtml(paramPreview) + '</div>';
|
|
645
645
|
}
|
|
@@ -827,7 +827,8 @@ async function sendMessage() {
|
|
|
827
827
|
// ── 如果正在生成,弹出处理选择框 ──
|
|
828
828
|
if (state.isGenerating) {
|
|
829
829
|
state.tempInputText = text;
|
|
830
|
-
document.getElementById('injectModal')
|
|
830
|
+
var modal = document.getElementById('injectModal');
|
|
831
|
+
if (modal) { modal.style.display = 'flex'; }
|
|
831
832
|
return;
|
|
832
833
|
}
|
|
833
834
|
|
|
@@ -1230,6 +1231,19 @@ function stopGenerating() {
|
|
|
1230
1231
|
if (state.abortController) {
|
|
1231
1232
|
state.abortController.abort();
|
|
1232
1233
|
}
|
|
1234
|
+
// 立即重置 UI 状态(防止 abort 后 finally 未执行导致 UI 卡死)
|
|
1235
|
+
state.isGenerating = false;
|
|
1236
|
+
state.abortController = null;
|
|
1237
|
+
hideTypingIndicator();
|
|
1238
|
+
stopExecTimerPolling();
|
|
1239
|
+
var sendBtn = document.getElementById('sendBtn');
|
|
1240
|
+
var stopBtn = document.getElementById('stopBtn');
|
|
1241
|
+
var input = document.getElementById('userInput');
|
|
1242
|
+
if (sendBtn) { sendBtn.style.display = ''; sendBtn.disabled = !(input && input.value.trim()); }
|
|
1243
|
+
if (stopBtn) stopBtn.style.display = 'none';
|
|
1244
|
+
// 关闭可能打开的 injectModal
|
|
1245
|
+
var injectModal = document.getElementById('injectModal');
|
|
1246
|
+
if (injectModal) injectModal.style.display = 'none';
|
|
1233
1247
|
}
|
|
1234
1248
|
|
|
1235
1249
|
// ══════════════════════════════════════════════════════
|
|
@@ -1237,7 +1251,8 @@ function stopGenerating() {
|
|
|
1237
1251
|
// ══════════════════════════════════════════════════════
|
|
1238
1252
|
|
|
1239
1253
|
function closeInjectModal() {
|
|
1240
|
-
document.getElementById('injectModal')
|
|
1254
|
+
var modal = document.getElementById('injectModal');
|
|
1255
|
+
if (modal) modal.style.display = 'none';
|
|
1241
1256
|
state.tempInputText = '';
|
|
1242
1257
|
}
|
|
1243
1258
|
|
|
@@ -1438,7 +1453,8 @@ function handleKeyDown(e) {
|
|
|
1438
1453
|
if (state.isGenerating) {
|
|
1439
1454
|
// 任务执行中,使用统一的 injectModal 弹出选择对话框
|
|
1440
1455
|
state.tempInputText = text;
|
|
1441
|
-
document.getElementById('injectModal')
|
|
1456
|
+
var modal = document.getElementById('injectModal');
|
|
1457
|
+
if (modal) { modal.style.display = 'flex'; }
|
|
1442
1458
|
} else {
|
|
1443
1459
|
sendMessage();
|
|
1444
1460
|
}
|
|
@@ -142,4 +142,17 @@
|
|
|
142
142
|
<div class="input-hint">MyAgent 可能会出错,请核实重要信息</div>
|
|
143
143
|
</div>
|
|
144
144
|
</div>
|
|
145
|
+
|
|
146
|
+
<!-- Inject Modal (生成中消息注入选择) -->
|
|
147
|
+
<div id="injectModal" class="modal-overlay" style="display:none">
|
|
148
|
+
<div class="modal">
|
|
149
|
+
<h3>任务执行中</h3>
|
|
150
|
+
<p>Agent 正在执行任务,请选择如何处理你的消息:</p>
|
|
151
|
+
<div style="display:flex;flex-direction:column;gap:8px;margin-bottom:0">
|
|
152
|
+
<button class="modal-btn" style="width:100%;justify-content:center;background:var(--accent);color:#fff" onclick="handleInjectChoice('queue')">排队等待</button>
|
|
153
|
+
<button class="modal-btn" style="width:100%;justify-content:center" onclick="handleInjectChoice('inject')">注入当前任务</button>
|
|
154
|
+
<button class="modal-btn" style="width:100%;justify-content:center;color:var(--text2)" onclick="closeInjectModal()">取消</button>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
145
158
|
</div>
|