mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-17 19:44:45 +08:00
merge: upstream/experimental with citations feature
- Merge upstream changes including image search, tooltips, and UI improvements - Keep citations feature with inline hover cards - Resolve conflict in message-list-item.tsx: use upstream img max-width (90%) while preserving citations logic - Maintain file upload improvements with citations support Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -36,6 +36,7 @@ import { cn } from "@/lib/utils";
|
||||
|
||||
import { useArtifacts } from "../artifacts";
|
||||
import { FlipDisplay } from "../flip-display";
|
||||
import { Tooltip } from "../tooltip";
|
||||
|
||||
export function MessageGroup({
|
||||
className,
|
||||
@@ -217,6 +218,48 @@ function ToolCall({
|
||||
)}
|
||||
</ChainOfThoughtStep>
|
||||
);
|
||||
} else if (name === "image_search") {
|
||||
let label: React.ReactNode = t.toolCalls.searchForRelatedImages;
|
||||
if (typeof args.query === "string") {
|
||||
label = t.toolCalls.searchForRelatedImagesFor(args.query);
|
||||
}
|
||||
const results = (
|
||||
result as {
|
||||
results: {
|
||||
source_url: string;
|
||||
thumbnail_url: string;
|
||||
image_url: string;
|
||||
title: string;
|
||||
}[];
|
||||
}
|
||||
)?.results;
|
||||
return (
|
||||
<ChainOfThoughtStep key={id} label={label} icon={SearchIcon}>
|
||||
{Array.isArray(results) && (
|
||||
<ChainOfThoughtSearchResults>
|
||||
{Array.isArray(results) &&
|
||||
results.map((item) => (
|
||||
<Tooltip key={item.image_url} content={item.title}>
|
||||
<a
|
||||
className="size-24 overflow-hidden rounded-lg object-cover"
|
||||
href={item.source_url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<img
|
||||
className="size-24 object-cover"
|
||||
src={item.thumbnail_url}
|
||||
alt={item.title}
|
||||
width={100}
|
||||
height={100}
|
||||
/>
|
||||
</a>
|
||||
</Tooltip>
|
||||
))}
|
||||
</ChainOfThoughtSearchResults>
|
||||
)}
|
||||
</ChainOfThoughtStep>
|
||||
);
|
||||
} else if (name === "web_fetch") {
|
||||
const url = (args as { url: string })?.url;
|
||||
let title = url;
|
||||
|
||||
@@ -243,7 +243,7 @@ function MessageContent_({
|
||||
if (typeof src !== "string") {
|
||||
return (
|
||||
<img
|
||||
className="max-w-full overflow-hidden rounded-lg"
|
||||
className="max-w-[90%] overflow-hidden rounded-lg"
|
||||
src={src}
|
||||
alt={alt}
|
||||
/>
|
||||
@@ -256,7 +256,7 @@ function MessageContent_({
|
||||
return (
|
||||
<a href={url} target="_blank" rel="noopener noreferrer">
|
||||
<img
|
||||
className="max-w-full overflow-hidden rounded-lg"
|
||||
className="max-w-[90%] overflow-hidden rounded-lg"
|
||||
src={url}
|
||||
alt={alt}
|
||||
/>
|
||||
|
||||
@@ -53,6 +53,17 @@ export function MessageList({
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (group.type === "assistant:clarification") {
|
||||
const message = group.messages[0];
|
||||
if (message && hasContent(message)) {
|
||||
return (
|
||||
<MessageResponse key={group.id} rehypePlugins={rehypePlugins}>
|
||||
{extractContentFromMessage(message)}
|
||||
</MessageResponse>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (group.type === "assistant:present-files") {
|
||||
const files: string[] = [];
|
||||
for (const message of group.messages) {
|
||||
|
||||
@@ -106,6 +106,9 @@ export const enUS: Translations = {
|
||||
needYourHelp: "Need your help",
|
||||
useTool: (toolName: string) => `Use "${toolName}" tool`,
|
||||
searchForRelatedInfo: "Search for related information",
|
||||
searchForRelatedImages: "Search for related images",
|
||||
searchForRelatedImagesFor: (query: string) =>
|
||||
`Search for related images for "${query}"`,
|
||||
searchOnWebFor: (query: string) => `Search on the web for "${query}"`,
|
||||
viewWebPage: "View web page",
|
||||
listFolder: "List folder",
|
||||
|
||||
@@ -101,6 +101,8 @@ export interface Translations {
|
||||
needYourHelp: string;
|
||||
useTool: (toolName: string) => string;
|
||||
searchForRelatedInfo: string;
|
||||
searchForRelatedImages: string;
|
||||
searchForRelatedImagesFor: (query: string) => string;
|
||||
searchOnWebFor: (query: string) => string;
|
||||
viewWebPage: string;
|
||||
listFolder: string;
|
||||
|
||||
@@ -104,6 +104,8 @@ export const zhCN: Translations = {
|
||||
needYourHelp: "需要你的协助",
|
||||
useTool: (toolName: string) => `使用 “${toolName}” 工具`,
|
||||
searchForRelatedInfo: "搜索相关信息",
|
||||
searchForRelatedImages: "搜索相关图片",
|
||||
searchForRelatedImagesFor: (query: string) => `搜索相关图片 “${query}”`,
|
||||
searchOnWebFor: (query: string) => `在网络上搜索 “${query}”`,
|
||||
viewWebPage: "查看网页",
|
||||
listFolder: "列出文件夹",
|
||||
|
||||
@@ -14,11 +14,14 @@ interface AssistantMessageGroup extends GenericMessageGroup<"assistant"> {}
|
||||
|
||||
interface AssistantPresentFilesGroup extends GenericMessageGroup<"assistant:present-files"> {}
|
||||
|
||||
interface AssistantClarificationGroup extends GenericMessageGroup<"assistant:clarification"> {}
|
||||
|
||||
type MessageGroup =
|
||||
| HumanMessageGroup
|
||||
| AssistantProcessingGroup
|
||||
| AssistantMessageGroup
|
||||
| AssistantPresentFilesGroup;
|
||||
| AssistantPresentFilesGroup
|
||||
| AssistantClarificationGroup;
|
||||
|
||||
export function groupMessages<T>(
|
||||
messages: Message[],
|
||||
@@ -38,10 +41,28 @@ export function groupMessages<T>(
|
||||
messages: [message],
|
||||
});
|
||||
} else if (message.type === "tool") {
|
||||
if (
|
||||
// Check if this is a clarification tool message
|
||||
if (isClarificationToolMessage(message)) {
|
||||
// Add to processing group if available (to maintain tool call association)
|
||||
if (
|
||||
lastGroup &&
|
||||
lastGroup.type !== "human" &&
|
||||
lastGroup.type !== "assistant" &&
|
||||
lastGroup.type !== "assistant:clarification"
|
||||
) {
|
||||
lastGroup.messages.push(message);
|
||||
}
|
||||
// Also create a separate clarification group for prominent display
|
||||
groups.push({
|
||||
id: message.id,
|
||||
type: "assistant:clarification",
|
||||
messages: [message],
|
||||
});
|
||||
} else if (
|
||||
lastGroup &&
|
||||
lastGroup.type !== "human" &&
|
||||
lastGroup.type !== "assistant"
|
||||
lastGroup.type !== "assistant" &&
|
||||
lastGroup.type !== "assistant:clarification"
|
||||
) {
|
||||
lastGroup.messages.push(message);
|
||||
} else {
|
||||
@@ -186,10 +207,15 @@ export function hasToolCalls(message: Message) {
|
||||
|
||||
export function hasPresentFiles(message: Message) {
|
||||
return (
|
||||
message.type === "ai" && message.tool_calls?.[0]?.name === "present_files"
|
||||
message.type === "ai" &&
|
||||
message.tool_calls?.some((toolCall) => toolCall.name === "present_files")
|
||||
);
|
||||
}
|
||||
|
||||
export function isClarificationToolMessage(message: Message) {
|
||||
return message.type === "tool" && message.name === "ask_clarification";
|
||||
}
|
||||
|
||||
export function extractPresentFilesFromMessage(message: Message) {
|
||||
if (message.type !== "ai" || !hasPresentFiles(message)) {
|
||||
return [];
|
||||
|
||||
Reference in New Issue
Block a user