优化消息的部分call tool显示

This commit is contained in:
xintaofei
2026-03-11 17:14:53 +08:00
parent 4c96037385
commit 346fcd2217
3 changed files with 72 additions and 3 deletions

View File

@@ -22,6 +22,7 @@ import { useTranslations } from "next-intl"
import { isValidElement } from "react"
import { CodeBlock } from "./code-block"
import { MessageResponse } from "./message"
export type ToolProps = ComponentProps<typeof Collapsible>
@@ -315,12 +316,34 @@ function isDuplicateErrorOutput(
export type ToolOutputProps = ComponentProps<"div"> & {
output: ToolPart["output"]
errorText: ToolPart["errorText"]
renderAsMarkdown?: boolean
}
const MD_INDICATORS = [
/^#{1,6}\s/m,
/^\s*[-*+]\s/m,
/^\s*\d+\.\s/m,
/\*\*[^*]+\*\*/,
/\[.+\]\(.+\)/,
/```[\s\S]*?```/,
/^\s*>/m,
/^\|.+\|$/m,
]
function looksLikeMarkdown(text: string): boolean {
let count = 0
for (const re of MD_INDICATORS) {
if (re.test(text)) count++
if (count >= 2) return true
}
return false
}
export const ToolOutput = ({
className,
output,
errorText,
renderAsMarkdown,
...props
}: ToolOutputProps) => {
const t = useTranslations("Folder.chat.tool")
@@ -342,8 +365,18 @@ export const ToolOutput = ({
<CodeBlock code={JSON.stringify(output, null, 2)} language="json" />
)
} else if (typeof output === "string") {
const language = detectOutputLanguage(output)
Output = <CodeBlock code={output} language={language} />
const shouldRenderMd =
renderAsMarkdown ?? (detectOutputLanguage(output) === "log" && looksLikeMarkdown(output))
if (shouldRenderMd) {
Output = (
<div className="prose prose-sm dark:prose-invert max-w-none p-3 text-sm [&_ul]:list-inside [&_ol]:list-inside">
<MessageResponse>{output}</MessageResponse>
</div>
)
} else {
const language = detectOutputLanguage(output)
Output = <CodeBlock code={output} language={language} />
}
}
return (

View File

@@ -871,6 +871,7 @@ function getToolIcon(
if (name === "task") return getTaskToolIcon(input ?? null)
if (name === "taskcreate" || name === "taskupdate" || name === "tasklist")
return <ListTodoIcon className={ICON_CLASS} />
if (name === "agent") return getTaskToolIcon(input ?? null)
if (name === "skill") return <SparklesIcon className={ICON_CLASS} />
if (name === "enterplanmode" || name === "exitplanmode")
return <BrainIcon className={ICON_CLASS} />
@@ -1000,6 +1001,13 @@ function deriveToolTitle(
if (desc) return `${prefix}${ellipsis(desc, 60 - prefix.length)}`
if (subagent) return subagent
}
if (name === "agent") {
const subagent = getField("subagent_type")
const desc = getField("description")
const prefix = subagent ? `${subagent}: ` : ""
if (desc) return `${prefix}${ellipsis(desc, 60 - prefix.length)}`
if (subagent) return subagent
}
if (name === "taskcreate") {
const subj = getField("subject")
if (subj) return `TaskCreate: ${ellipsis(subj, 50)}`
@@ -2156,7 +2164,8 @@ const ToolCallPart = memo(function ToolCallPart({
input={part.input}
/>
)}
{toolNameLower === "task" && part.output ? (
{(toolNameLower === "task" || toolNameLower === "agent") &&
part.output ? (
<div className="text-sm prose prose-sm dark:prose-invert max-w-none [&_ul]:list-inside [&_ol]:list-inside">
<MessageResponse>{part.output}</MessageResponse>
</div>

View File

@@ -32,6 +32,29 @@ const EXACT_TOOL_NAME_ALIASES: Record<string, string> = {
web_search: "websearch",
context7_query_docs: "context7_query-docs",
context7_resolve_library_id: "context7_resolve-library-id",
agent: "agent",
// Gemini CLI
searchtext: "grep",
search_text: "grep",
writefile: "write",
editfile: "edit",
// Codex
update_plan: "task",
request_user_input: "question",
// OpenCode
delegate_task: "task",
call_omo_agent: "agent",
ast_grep_search: "grep",
ast_grep_replace: "edit",
background_task: "task",
background_cancel: "task",
background_output: "task",
slashcommand: "skill",
question: "question",
lsp_diagnostics: "lsp",
lsp_document_symbols: "lsp",
lsp_goto_definition: "lsp",
lsp_servers: "lsp",
execute: "bash",
search: "grep",
fetch: "webfetch",
@@ -65,6 +88,10 @@ function inferFromFreeformName(input: string): string | null {
if (/^glob(?:\b|[_\s:-])/.test(normalized)) return "glob"
if (/^webfetch(?:\b|[_\s:-])/.test(normalized)) return "webfetch"
if (/^websearch(?:\b|[_\s:-])/.test(normalized)) return "websearch"
if (/\bweb[_\s-]?search\b/.test(normalized)) return "websearch"
if (/\bgrep\b/.test(normalized)) return "grep"
if (/\bagent\b/.test(normalized)) return "agent"
if (/\blsp\b/.test(normalized)) return "lsp"
if (/^todowrite(?:\b|[_\s:-])/.test(normalized)) return "todowrite"
if (/^taskupdate(?:\b|[_\s:-])/.test(normalized)) return "taskupdate"
if (/^taskcreate(?:\b|[_\s:-])/.test(normalized)) return "taskcreate"