feat(h5): web 联网能力、实时查询路由与 session Finish 对齐
- 新增 web 能力并挂载 platform/web(web_search/fetch_url) - 实时查询强制 web skill,router fallback 与 await session Finish - Session Broker 覆盖率/指标、stream replay 与相关单测/E2E 脚本 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -144,6 +144,31 @@ function isToolOnlyAssistantMessage(message: Message) {
|
||||
);
|
||||
}
|
||||
|
||||
function TypingBubble() {
|
||||
return (
|
||||
<div className="msg-bubble msg-bubble-assistant msg-typing">
|
||||
<span className="typing-dots">
|
||||
<span />
|
||||
<span />
|
||||
<span />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function shouldShowEndTypingIndicator(messages: Message[], streaming: boolean) {
|
||||
if (!streaming) return false;
|
||||
const lastUserIndex = messages.findLastIndex((message) => message.role === 'user');
|
||||
if (lastUserIndex < 0) return true;
|
||||
const assistantReply = messages
|
||||
.slice(lastUserIndex + 1)
|
||||
.find((message) => message.role === 'assistant');
|
||||
if (!assistantReply) return true;
|
||||
if (getDisplayText(assistantReply).trim() || getThinking(assistantReply)) return false;
|
||||
if (hasToolActivity(assistantReply)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function ToolBadge({ message, active }: { message: Message; active: boolean }) {
|
||||
if (message.role !== 'assistant') return null;
|
||||
const tools = message.content.filter((c) => c.type === 'toolRequest' || c.type === 'toolResponse');
|
||||
@@ -265,6 +290,7 @@ function MessageRow({
|
||||
sessionId,
|
||||
compact = false,
|
||||
activeToolMessage = false,
|
||||
showInlineTyping = false,
|
||||
}: {
|
||||
message: Message;
|
||||
avatarUrl: string | null;
|
||||
@@ -279,6 +305,7 @@ function MessageRow({
|
||||
sessionId?: string;
|
||||
compact?: boolean;
|
||||
activeToolMessage?: boolean;
|
||||
showInlineTyping?: boolean;
|
||||
}) {
|
||||
const [actionsOpen, setActionsOpen] = useState(false);
|
||||
const rawText = getDisplayText(message);
|
||||
@@ -295,7 +322,11 @@ function MessageRow({
|
||||
<div className={`msg-row msg-row-assistant`}>
|
||||
<TKMindAvatar />
|
||||
<div className="msg-content">
|
||||
<ToolBadge message={message} active={activeToolMessage} />
|
||||
{showInlineTyping ? (
|
||||
<TypingBubble />
|
||||
) : (
|
||||
<ToolBadge message={message} active={activeToolMessage} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -471,6 +502,20 @@ export function MessageList({
|
||||
const visibleMessages = renderableMessages.filter(
|
||||
(message) => !isToolOnlyAssistantMessage(message) || message === activeToolMessage,
|
||||
);
|
||||
const showEndTyping = shouldShowEndTypingIndicator(renderableMessages, streaming);
|
||||
const lastUserIndex = renderableMessages.findLastIndex((message) => message.role === 'user');
|
||||
const inlineTypingMessage =
|
||||
lastUserIndex >= 0
|
||||
? renderableMessages
|
||||
.slice(lastUserIndex + 1)
|
||||
.find(
|
||||
(message) =>
|
||||
message.role === 'assistant' &&
|
||||
!getDisplayText(message).trim() &&
|
||||
!getThinking(message) &&
|
||||
!hasToolActivity(message),
|
||||
) ?? null
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="message-list">
|
||||
@@ -499,21 +544,16 @@ export function MessageList({
|
||||
publishUsername={publishUsername}
|
||||
compact={compact}
|
||||
activeToolMessage={message === activeToolMessage}
|
||||
showInlineTyping={showEndTyping && message === inlineTypingMessage}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{streaming && (
|
||||
<div className="msg-row msg-row-assistant">
|
||||
{showEndTyping && !inlineTypingMessage && (
|
||||
<div className="msg-row msg-row-assistant" role="status" aria-live="polite" aria-label="助手正在回复">
|
||||
<TKMindAvatar />
|
||||
<div className="msg-content">
|
||||
<div className="msg-bubble msg-bubble-assistant msg-typing">
|
||||
<span className="typing-dots">
|
||||
<span />
|
||||
<span />
|
||||
<span />
|
||||
</span>
|
||||
</div>
|
||||
<TypingBubble />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user