u
This commit is contained in:
@@ -10,6 +10,17 @@ import SearchRoundedIcon from '@mui/icons-material/SearchRounded';
|
||||
import ComputerRoundedIcon from '@mui/icons-material/ComputerRounded';
|
||||
import ImageSearchRoundedIcon from '@mui/icons-material/ImageSearchRounded';
|
||||
import CompareRoundedIcon from '@mui/icons-material/CompareRounded';
|
||||
import SupportIcon from '@mui/icons-material/Support';
|
||||
import RecordVoiceOverIcon from '@mui/icons-material/RecordVoiceOver';
|
||||
import PhonelinkIcon from '@mui/icons-material/Phonelink';
|
||||
import SaveAsIcon from '@mui/icons-material/SaveAs';
|
||||
import DescriptionIcon from '@mui/icons-material/Description';
|
||||
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import GoogleIcon from '@mui/icons-material/Google';
|
||||
import WebIcon from '@mui/icons-material/Web';
|
||||
import EscalatorWarningIcon from '@mui/icons-material/EscalatorWarning';
|
||||
import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf';
|
||||
import HealingMarkdown from './HealingMarkdown';
|
||||
import {
|
||||
CHROMA,
|
||||
@@ -23,6 +34,17 @@ const TOOL_ICON_MAP = {
|
||||
javascript_execute: ComputerRoundedIcon,
|
||||
search_images: ImageSearchRoundedIcon,
|
||||
analyze_image: CompareRoundedIcon,
|
||||
apisuggestionsorissues: SupportIcon,
|
||||
ask_user: RecordVoiceOverIcon,
|
||||
dashback_request: PhonelinkIcon,
|
||||
patch: SaveAsIcon,
|
||||
read_file: DescriptionIcon,
|
||||
reset_vfs: DeleteForeverIcon,
|
||||
rm: DeleteIcon,
|
||||
web_search: GoogleIcon,
|
||||
url_get: WebIcon,
|
||||
sub_agent: EscalatorWarningIcon,
|
||||
pdf_read: PictureAsPdfIcon,
|
||||
};
|
||||
|
||||
function formatMessageTime(isoString) {
|
||||
@@ -173,27 +195,108 @@ function HandshakeIconView({ state }) {
|
||||
);
|
||||
}
|
||||
|
||||
function extractReasoningText(msg, payload) {
|
||||
if (payload && typeof payload === 'object') {
|
||||
if (payload.plain !== undefined) {
|
||||
if (typeof payload.plain === 'string' && payload.plain.trim()) {
|
||||
return payload.plain.trim();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
if (payload.encrypted) {
|
||||
return '';
|
||||
}
|
||||
const candidate = payload.text || payload.content || payload.thinking || payload.reasoning;
|
||||
if (candidate && typeof candidate === 'string') {
|
||||
const trimmed = candidate.trim();
|
||||
if (trimmed.startsWith('{') && (trimmed.includes('"encrypted"') || trimmed.includes('"plain"'))) {
|
||||
try {
|
||||
const inner = JSON.parse(trimmed);
|
||||
if (inner.plain && typeof inner.plain === 'string' && inner.plain.trim()) {
|
||||
return inner.plain.trim();
|
||||
}
|
||||
} catch {}
|
||||
return '';
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof msg?.message === 'string') {
|
||||
const raw = msg.message.trim();
|
||||
if (raw.startsWith('{') && (raw.includes('"encrypted"') || raw.includes('"plain"'))) {
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
if (parsed.plain && typeof parsed.plain === 'string' && parsed.plain.trim()) {
|
||||
return parsed.plain.trim();
|
||||
}
|
||||
} catch {}
|
||||
return '';
|
||||
}
|
||||
if (!raw.startsWith('{')) {
|
||||
return raw;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
class ReasoningTooltipContent extends PureComponent {
|
||||
boxRef = createRef();
|
||||
|
||||
scrollToBottom = () => {
|
||||
const el = this.boxRef.current;
|
||||
if (el) {
|
||||
el.scrollTop = el.scrollHeight;
|
||||
requestAnimationFrame(() => {
|
||||
if (this.boxRef.current) {
|
||||
this.boxRef.current.scrollTop = this.boxRef.current.scrollHeight;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.text !== prevProps.text) {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { text } = this.props;
|
||||
return (
|
||||
<Box
|
||||
ref={this.boxRef}
|
||||
sx={{
|
||||
p: 0.5,
|
||||
maxWidth: 440,
|
||||
maxHeight: 280,
|
||||
overflowY: 'auto',
|
||||
whiteSpace: 'pre-wrap',
|
||||
fontSize: '0.75rem',
|
||||
lineHeight: 1.45,
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function BrainIconView({ reasoning }) {
|
||||
const tooltipContent = reasoning?.text?.trim() || '';
|
||||
if (!tooltipContent) return null;
|
||||
if (tooltipContent.startsWith('{') && (tooltipContent.includes('"encrypted"') || tooltipContent.includes('"plain"'))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ClickTooltip
|
||||
title={
|
||||
<Box
|
||||
sx={{
|
||||
p: 0.5,
|
||||
maxWidth: 440,
|
||||
maxHeight: 280,
|
||||
overflowY: 'auto',
|
||||
whiteSpace: 'pre-wrap',
|
||||
fontSize: '0.75rem',
|
||||
lineHeight: 1.45,
|
||||
}}
|
||||
>
|
||||
{tooltipContent}
|
||||
</Box>
|
||||
}
|
||||
title={<ReasoningTooltipContent text={tooltipContent} />}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
@@ -216,10 +319,11 @@ function BrainIconView({ reasoning }) {
|
||||
}
|
||||
|
||||
function WorkIconView({ tool }) {
|
||||
if (!tool || !tool.name) return null;
|
||||
const isGreen = tool?.state === 'green';
|
||||
const ToolNameIcon = tool?.name ? TOOL_ICON_MAP[tool.name] : null;
|
||||
const prefix = ToolNameIcon ? tool?.name : null;
|
||||
const tooltipText = formatToolTooltip(tool?.content, tool?.result, tool?.parameters, prefix);
|
||||
const tooltipText = formatToolTooltip(tool?.content, tool?.result, tool?.parameters || tool?.arguments, prefix);
|
||||
|
||||
return (
|
||||
<ClickTooltip
|
||||
@@ -424,6 +528,19 @@ export function normalizeChatMessages(rawMessages = []) {
|
||||
};
|
||||
|
||||
let turnSeq = 0;
|
||||
const findExistingTool = (toolCallId) => {
|
||||
if (!toolCallId) return null;
|
||||
if (currentTurn?.tools) {
|
||||
const t = currentTurn.tools.find((tool) => tool.id === toolCallId);
|
||||
if (t) return t;
|
||||
}
|
||||
for (let i = currentAssistantTurns.length - 1; i >= 0; i--) {
|
||||
const t = currentAssistantTurns[i].tools?.find((tool) => tool.id === toolCallId);
|
||||
if (t) return t;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const getOrCreateTurn = (msg) => {
|
||||
const shouldStartNew =
|
||||
!currentTurn ||
|
||||
@@ -481,29 +598,39 @@ export function normalizeChatMessages(rawMessages = []) {
|
||||
}
|
||||
|
||||
if (msg.role === 'reasoning') {
|
||||
const turn = getOrCreateTurn(msg);
|
||||
const text =
|
||||
payload.plain ||
|
||||
payload.text ||
|
||||
payload.content ||
|
||||
payload.thinking ||
|
||||
payload.reasoning ||
|
||||
(typeof msg.message === 'string' ? msg.message : '');
|
||||
turn.reasonings.push({
|
||||
id: msg.id || `r-${turn.reasonings.length}`,
|
||||
text,
|
||||
sealed: true,
|
||||
});
|
||||
const text = extractReasoningText(msg, payload);
|
||||
if (text) {
|
||||
const turn = getOrCreateTurn(msg);
|
||||
turn.reasonings.push({
|
||||
id: msg.id || `r-${turn.reasonings.length}`,
|
||||
text,
|
||||
sealed: true,
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (msg.role === 'tool') {
|
||||
const existingTool = payload.tool_call_id ? findExistingTool(payload.tool_call_id) : null;
|
||||
const isAskUser = payload.name === 'ask_user' || existingTool?.name === 'ask_user';
|
||||
|
||||
if (!isAskUser && existingTool) {
|
||||
existingTool.result = payload.result !== undefined ? payload.result : (payload.content !== undefined ? payload.content : '');
|
||||
if (payload.parameters) {
|
||||
existingTool.parameters = payload.parameters;
|
||||
}
|
||||
if (payload.name && !existingTool.name) {
|
||||
existingTool.name = payload.name;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const turn = getOrCreateTurn(msg);
|
||||
turn.tools.push({
|
||||
id: payload.tool_call_id || msg.id,
|
||||
state: 'green',
|
||||
name: payload.name || '',
|
||||
content: payload.content !== undefined ? payload.content : (payload.parameters || ''),
|
||||
name: isAskUser ? 'ask_user' : (payload.name || ''),
|
||||
content: payload.content !== undefined ? payload.content : (payload.answer || payload.parameters || ''),
|
||||
parameters: payload.parameters,
|
||||
result: payload.result,
|
||||
});
|
||||
@@ -526,13 +653,22 @@ export function normalizeChatMessages(rawMessages = []) {
|
||||
try {
|
||||
args = typeof tc.function?.arguments === 'string' ? JSON.parse(tc.function.arguments) : (tc.function?.arguments || {});
|
||||
} catch {}
|
||||
turn.tools.push({
|
||||
id: tc.id || `tc-${Date.now()}`,
|
||||
state: 'green',
|
||||
name: tc.function?.name || 'tool',
|
||||
content: JSON.stringify(args, null, 2),
|
||||
result: null,
|
||||
});
|
||||
|
||||
const isAskUser = tc.function?.name === 'ask_user';
|
||||
const existing = !isAskUser && tc.id ? findExistingTool(tc.id) : null;
|
||||
if (existing) {
|
||||
existing.parameters = tc.function?.arguments || args;
|
||||
if (tc.function?.name && !existing.name) existing.name = tc.function.name;
|
||||
} else {
|
||||
turn.tools.push({
|
||||
id: tc.id || `tc-${Date.now()}`,
|
||||
state: 'green',
|
||||
name: tc.function?.name || 'tool',
|
||||
parameters: tc.function?.arguments || args,
|
||||
content: null,
|
||||
result: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user