v0.1.2
parent
3cb25ba795
commit
a2ac5c4fb5
|
|
@ -1,3 +1,4 @@
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -147,14 +147,30 @@ export function useBotsSync() {
|
||||||
if (data.type === 'AGENT_STATE') {
|
if (data.type === 'AGENT_STATE') {
|
||||||
const state = String(payload.state || data.state || 'INFO');
|
const state = String(payload.state || data.state || 'INFO');
|
||||||
const messageRaw = String(payload.action_msg || payload.msg || data.action_msg || data.msg || '');
|
const messageRaw = String(payload.action_msg || payload.msg || data.action_msg || data.msg || '');
|
||||||
const message = summarizeProgressText(messageRaw, isZh);
|
const normalizedState = normalizeState(state);
|
||||||
|
const fullMessage = normalizeAssistantMessageText(messageRaw);
|
||||||
|
const message = fullMessage || summarizeProgressText(messageRaw, isZh) || t.stateUpdated;
|
||||||
updateBotState(bot.id, state, message);
|
updateBotState(bot.id, state, message);
|
||||||
addBotEvent(bot.id, {
|
addBotEvent(bot.id, {
|
||||||
state: normalizeState(state),
|
state: normalizedState,
|
||||||
text: message || t.stateUpdated,
|
text: message || t.stateUpdated,
|
||||||
ts: Date.now(),
|
ts: Date.now(),
|
||||||
channel: sourceChannel || undefined,
|
channel: sourceChannel || undefined,
|
||||||
});
|
});
|
||||||
|
if (isDashboardChannel && fullMessage && (normalizedState === 'THINKING' || normalizedState === 'TOOL_CALL')) {
|
||||||
|
const chatText = normalizedState === 'TOOL_CALL' ? `${isZh ? '工具调用' : 'Tool Call'}\n${fullMessage}` : fullMessage;
|
||||||
|
const now = Date.now();
|
||||||
|
const prev = lastProgressRef.current[bot.id];
|
||||||
|
if (!prev || prev.text !== chatText || now - prev.ts > 1200) {
|
||||||
|
addBotMessage(bot.id, {
|
||||||
|
role: 'assistant',
|
||||||
|
text: chatText,
|
||||||
|
ts: now,
|
||||||
|
kind: 'progress',
|
||||||
|
});
|
||||||
|
lastProgressRef.current[bot.id] = { text: chatText, ts: now };
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.type === 'ASSISTANT_MESSAGE') {
|
if (data.type === 'ASSISTANT_MESSAGE') {
|
||||||
|
|
@ -178,10 +194,10 @@ export function useBotsSync() {
|
||||||
if (isProgress) {
|
if (isProgress) {
|
||||||
const state = normalizeBusState(isTool);
|
const state = normalizeBusState(isTool);
|
||||||
const progressText = summarizeProgressText(content, isZh);
|
const progressText = summarizeProgressText(content, isZh);
|
||||||
updateBotState(bot.id, state, progressText);
|
|
||||||
addBotEvent(bot.id, { state, text: progressText || t.progress, ts: Date.now(), channel: sourceChannel || undefined });
|
|
||||||
if (isDashboardChannel) {
|
|
||||||
const fullProgress = content || progressText || (isZh ? '处理中...' : 'Processing...');
|
const fullProgress = content || progressText || (isZh ? '处理中...' : 'Processing...');
|
||||||
|
updateBotState(bot.id, state, fullProgress);
|
||||||
|
addBotEvent(bot.id, { state, text: fullProgress || t.progress, ts: Date.now(), channel: sourceChannel || undefined });
|
||||||
|
if (isDashboardChannel) {
|
||||||
const chatText = isTool ? `${isZh ? '工具调用' : 'Tool Call'}\n${fullProgress}` : fullProgress;
|
const chatText = isTool ? `${isZh ? '工具调用' : 'Tool Call'}\n${fullProgress}` : fullProgress;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const prev = lastProgressRef.current[bot.id];
|
const prev = lastProgressRef.current[bot.id];
|
||||||
|
|
|
||||||
|
|
@ -1074,6 +1074,26 @@
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ops-runtime-action {
|
||||||
|
min-width: 0;
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ops-runtime-action-text {
|
||||||
|
display: block;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
line-height: 1.54;
|
||||||
|
max-height: 82px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ops-runtime-action-text.expanded {
|
||||||
|
max-height: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
.ops-preview {
|
.ops-preview {
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|
|
||||||
|
|
@ -391,6 +391,7 @@ export function BotDashboardModule({
|
||||||
const [compactPanelTab, setCompactPanelTab] = useState<CompactPanelTab>('chat');
|
const [compactPanelTab, setCompactPanelTab] = useState<CompactPanelTab>('chat');
|
||||||
const [isCompactMobile, setIsCompactMobile] = useState(false);
|
const [isCompactMobile, setIsCompactMobile] = useState(false);
|
||||||
const [expandedProgressByKey, setExpandedProgressByKey] = useState<Record<string, boolean>>({});
|
const [expandedProgressByKey, setExpandedProgressByKey] = useState<Record<string, boolean>>({});
|
||||||
|
const [runtimeActionExpanded, setRuntimeActionExpanded] = useState(false);
|
||||||
const runtimeMenuRef = useRef<HTMLDivElement | null>(null);
|
const runtimeMenuRef = useRef<HTMLDivElement | null>(null);
|
||||||
const openWorkspacePathFromChat = (path: string) => {
|
const openWorkspacePathFromChat = (path: string) => {
|
||||||
const normalized = String(path || '').trim();
|
const normalized = String(path || '').trim();
|
||||||
|
|
@ -607,12 +608,23 @@ export function BotDashboardModule({
|
||||||
return 'IDLE';
|
return 'IDLE';
|
||||||
}, [selectedBot, isThinking, latestEvent]);
|
}, [selectedBot, isThinking, latestEvent]);
|
||||||
const runtimeAction = useMemo(() => {
|
const runtimeAction = useMemo(() => {
|
||||||
const action = summarizeProgressText(selectedBot?.last_action || '', isZh);
|
const action = normalizeAssistantMessageText(selectedBot?.last_action || '').trim();
|
||||||
if (action && action !== t.processing) return action;
|
if (action) return action;
|
||||||
const eventText = summarizeProgressText(latestEvent?.text || '', isZh);
|
const eventText = normalizeAssistantMessageText(latestEvent?.text || '').trim();
|
||||||
if (eventText && eventText !== t.processing) return eventText;
|
if (eventText) return eventText;
|
||||||
return '-';
|
return '-';
|
||||||
}, [selectedBot, latestEvent, isZh, t.processing]);
|
}, [selectedBot, latestEvent]);
|
||||||
|
const runtimeActionSummary = useMemo(() => {
|
||||||
|
const full = String(runtimeAction || '').trim();
|
||||||
|
if (!full || full === '-') return '-';
|
||||||
|
return summarizeProgressText(full, isZh);
|
||||||
|
}, [runtimeAction, isZh]);
|
||||||
|
const runtimeActionHasMore = useMemo(() => {
|
||||||
|
const full = String(runtimeAction || '').trim();
|
||||||
|
const summary = String(runtimeActionSummary || '').trim();
|
||||||
|
return Boolean(full && full !== '-' && summary && full !== summary);
|
||||||
|
}, [runtimeAction, runtimeActionSummary]);
|
||||||
|
const runtimeActionDisplay = runtimeActionExpanded || !runtimeActionHasMore ? runtimeAction : runtimeActionSummary;
|
||||||
|
|
||||||
const shouldCollapseProgress = (text: string) => {
|
const shouldCollapseProgress = (text: string) => {
|
||||||
const normalized = String(text || '').trim();
|
const normalized = String(text || '').trim();
|
||||||
|
|
@ -626,8 +638,12 @@ export function BotDashboardModule({
|
||||||
conversation.map((item, idx) => {
|
conversation.map((item, idx) => {
|
||||||
const itemKey = `${item.ts}-${idx}`;
|
const itemKey = `${item.ts}-${idx}`;
|
||||||
const isProgressBubble = item.role !== 'user' && (item.kind || 'final') === 'progress';
|
const isProgressBubble = item.role !== 'user' && (item.kind || 'final') === 'progress';
|
||||||
const collapsible = isProgressBubble && shouldCollapseProgress(item.text);
|
const fullText = String(item.text || '');
|
||||||
|
const summaryText = isProgressBubble ? summarizeProgressText(fullText, isZh) : fullText;
|
||||||
|
const hasSummary = isProgressBubble && summaryText.trim().length > 0 && summaryText.trim() !== fullText.trim();
|
||||||
|
const collapsible = isProgressBubble && (hasSummary || shouldCollapseProgress(fullText));
|
||||||
const expanded = Boolean(expandedProgressByKey[itemKey]);
|
const expanded = Boolean(expandedProgressByKey[itemKey]);
|
||||||
|
const displayText = isProgressBubble && !expanded ? summaryText : fullText;
|
||||||
return (
|
return (
|
||||||
<div key={itemKey} className={`ops-chat-row ${item.role === 'user' ? 'is-user' : 'is-assistant'}`}>
|
<div key={itemKey} className={`ops-chat-row ${item.role === 'user' ? 'is-user' : 'is-assistant'}`}>
|
||||||
<div className={`ops-chat-item ${item.role === 'user' ? 'is-user' : 'is-assistant'}`}>
|
<div className={`ops-chat-item ${item.role === 'user' ? 'is-user' : 'is-assistant'}`}>
|
||||||
|
|
@ -648,7 +664,7 @@ export function BotDashboardModule({
|
||||||
<div className="whitespace-pre-wrap">{normalizeUserMessageText(item.text)}</div>
|
<div className="whitespace-pre-wrap">{normalizeUserMessageText(item.text)}</div>
|
||||||
) : (
|
) : (
|
||||||
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
||||||
{decorateWorkspacePathsForMarkdown(item.text)}
|
{decorateWorkspacePathsForMarkdown(displayText)}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
)
|
)
|
||||||
) : null}
|
) : null}
|
||||||
|
|
@ -736,6 +752,7 @@ export function BotDashboardModule({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setExpandedProgressByKey({});
|
setExpandedProgressByKey({});
|
||||||
|
setRuntimeActionExpanded(false);
|
||||||
}, [selectedBotId]);
|
}, [selectedBotId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -1932,7 +1949,20 @@ export function BotDashboardModule({
|
||||||
<>
|
<>
|
||||||
<div className="ops-runtime-row"><span>{t.container}</span><strong className="mono">{selectedBot.docker_status}</strong></div>
|
<div className="ops-runtime-row"><span>{t.container}</span><strong className="mono">{selectedBot.docker_status}</strong></div>
|
||||||
<div className="ops-runtime-row"><span>{t.current}</span><strong className="mono">{displayState}</strong></div>
|
<div className="ops-runtime-row"><span>{t.current}</span><strong className="mono">{displayState}</strong></div>
|
||||||
<div className="ops-runtime-row"><span>{t.lastAction}</span><strong>{runtimeAction}</strong></div>
|
<div className="ops-runtime-row">
|
||||||
|
<span>{t.lastAction}</span>
|
||||||
|
<div className="ops-runtime-action">
|
||||||
|
<strong className={`ops-runtime-action-text ${runtimeActionExpanded ? 'expanded' : ''}`}>{runtimeActionDisplay}</strong>
|
||||||
|
{runtimeActionHasMore ? (
|
||||||
|
<button
|
||||||
|
className="ops-chat-more-btn"
|
||||||
|
onClick={() => setRuntimeActionExpanded((prev) => !prev)}
|
||||||
|
>
|
||||||
|
{runtimeActionExpanded ? (isZh ? '收起' : 'Less') : (isZh ? '更多' : 'More')}
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="ops-runtime-row"><span>Provider</span><strong className="mono">{selectedBot.llm_provider || '-'}</strong></div>
|
<div className="ops-runtime-row"><span>Provider</span><strong className="mono">{selectedBot.llm_provider || '-'}</strong></div>
|
||||||
<div className="ops-runtime-row"><span>Model</span><strong className="mono">{selectedBot.llm_model || '-'}</strong></div>
|
<div className="ops-runtime-row"><span>Model</span><strong className="mono">{selectedBot.llm_model || '-'}</strong></div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue