Merge branch 'gzy' into 'master'
feat: iframe增加智能体显示 See merge request knowledge-base-bj/deerflow-server!157
This commit is contained in:
commit
d8af6c8f48
3 files changed
+107
-56
No files matched your search
@@ -20,6 +20,7 @@ import {
|
|||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { PromptInputButton } from "@/components/ai-elements/prompt-input";
|
import { PromptInputButton } from "@/components/ai-elements/prompt-input";
|
||||||
import { AgentManageDialog } from "@/components/workspace/agents/agent-manage-dialog";
|
import { AgentManageDialog } from "@/components/workspace/agents/agent-manage-dialog";
|
||||||
|
import { Tooltip } from "@/components/workspace/tooltip";
|
||||||
import { useAgentSelector, useAgentsPaginated } from "@/core/agents";
|
import { useAgentSelector, useAgentsPaginated } from "@/core/agents";
|
||||||
import type { Agent } from "@/core/agents";
|
import type { Agent } from "@/core/agents";
|
||||||
import { getStoredAuth } from "@/core/auth";
|
import { getStoredAuth } from "@/core/auth";
|
||||||
@@ -86,6 +87,7 @@ export function AgentSelector({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const triggerLabel = selectedAgent ? desensitize(selectedAgent.name) : "选择 Agent";
|
const triggerLabel = selectedAgent ? desensitize(selectedAgent.name) : "选择 Agent";
|
||||||
|
const iconTooltipLabel = selectedAgent ? triggerLabel : "未选择";
|
||||||
const iconMode = triggerVariant === "icon";
|
const iconMode = triggerVariant === "icon";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -93,38 +95,40 @@ export function AgentSelector({
|
|||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
{iconMode ? (
|
{iconMode ? (
|
||||||
<Button
|
<Tooltip content={iconTooltipLabel}>
|
||||||
type="button"
|
<Button
|
||||||
variant="ghost"
|
type="button"
|
||||||
size="icon"
|
variant="ghost"
|
||||||
disabled={disabled}
|
size="icon"
|
||||||
aria-label={selectedAgent ? `已选择智能体:${triggerLabel}` : "选择智能体"}
|
disabled={disabled}
|
||||||
className={cn(
|
aria-label={selectedAgent ? `已选择智能体:${triggerLabel}` : "选择智能体"}
|
||||||
"relative size-9 rounded-full text-muted-foreground shadow-none hover:bg-muted/60 hover:text-foreground",
|
className={cn(
|
||||||
selectedAgent && "text-amber-600 hover:text-amber-700",
|
"relative size-9 rounded-full text-muted-foreground shadow-none hover:bg-muted/60 hover:text-foreground",
|
||||||
selectedAgent && iconActiveClassName,
|
selectedAgent && "text-amber-600 hover:text-amber-700",
|
||||||
iconButtonClassName,
|
selectedAgent && iconActiveClassName,
|
||||||
)}
|
iconButtonClassName,
|
||||||
>
|
)}
|
||||||
<BotIcon className="size-4" />
|
>
|
||||||
{selectedAgent && (
|
<BotIcon className="size-4" />
|
||||||
<span
|
{selectedAgent && (
|
||||||
role="button"
|
<span
|
||||||
tabIndex={0}
|
role="button"
|
||||||
aria-label="清除已选 agent"
|
tabIndex={0}
|
||||||
className="absolute -right-1 -top-1 inline-flex size-4 items-center justify-center rounded-full border bg-background text-foreground shadow-sm hover:bg-muted"
|
aria-label="清除已选 agent"
|
||||||
onClick={handleClear}
|
className="absolute -right-1 -top-1 inline-flex size-4 items-center justify-center rounded-full border bg-background text-foreground shadow-sm hover:bg-muted"
|
||||||
onKeyDown={(e) => {
|
onClick={handleClear}
|
||||||
if (e.key === "Enter" || e.key === " ") {
|
onKeyDown={(e) => {
|
||||||
e.preventDefault();
|
if (e.key === "Enter" || e.key === " ") {
|
||||||
onAgentChange(null);
|
e.preventDefault();
|
||||||
}
|
onAgentChange(null);
|
||||||
}}
|
}
|
||||||
>
|
}}
|
||||||
<XIcon className="size-2.5" />
|
>
|
||||||
</span>
|
<XIcon className="size-2.5" />
|
||||||
)}
|
</span>
|
||||||
</Button>
|
)}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<PromptInputButton
|
<PromptInputButton
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -166,3 +166,25 @@ test("mapped domain overrides the user's manual selection for that incoming mess
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("unmapped incoming url resets auto-selected agent back to default when there is no manual selection", () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
resolveIncomingIframeSelection({
|
||||||
|
pageUrl: "https://another-unknown.example.com/article",
|
||||||
|
mappings: [
|
||||||
|
{
|
||||||
|
id: "m1",
|
||||||
|
domain: "foo.example.com",
|
||||||
|
agentId: "agent-1",
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
availableAgentIds: ["agent-1", "agent-2"],
|
||||||
|
manualSelectedAgentId: null,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
selectedAgentId: null,
|
||||||
|
forceDefaultAssistant: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -83,6 +83,13 @@ type BrainyChatInterruptMessage = {
|
|||||||
};
|
};
|
||||||
requestId?: string;
|
requestId?: string;
|
||||||
};
|
};
|
||||||
|
type BrainyUrlContextMessage = {
|
||||||
|
source?: string;
|
||||||
|
type?: string;
|
||||||
|
payload?: {
|
||||||
|
url?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
type PendingIframeSubmit = {
|
type PendingIframeSubmit = {
|
||||||
message: PromptInputMessage;
|
message: PromptInputMessage;
|
||||||
externalContext?: Record<string, unknown>;
|
externalContext?: Record<string, unknown>;
|
||||||
@@ -429,25 +436,10 @@ export default function IframeChatPage() {
|
|||||||
[thread],
|
[thread],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
const applyIncomingUrlSelection = useCallback(
|
||||||
const handleMessage = (
|
(pageUrl: string | null | undefined) => {
|
||||||
event: MessageEvent<BrainyChatSubmitMessage | BrainyChatInterruptMessage>,
|
|
||||||
) => {
|
|
||||||
const data = event.data;
|
|
||||||
if (data?.source !== "brainy-extension") return;
|
|
||||||
if (data?.type === "brainy:chat-interrupt") {
|
|
||||||
void handleInterrupt(data as BrainyChatInterruptMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (data?.type !== "brainy:chat-submit") return;
|
|
||||||
const submitMessage = data as BrainyChatSubmitMessage;
|
|
||||||
const { submitText, files, iframeContext, additionalKwargs } =
|
|
||||||
parseBrainyChatSubmitMessage(submitMessage);
|
|
||||||
|
|
||||||
if (!submitText && files.length === 0) return;
|
|
||||||
|
|
||||||
const nextSelection = resolveIncomingIframeSelection({
|
const nextSelection = resolveIncomingIframeSelection({
|
||||||
pageUrl: submitMessage.payload?.pageContext?.url,
|
pageUrl,
|
||||||
mappings: pluginManagementSettings?.domainAgentMappings ?? [],
|
mappings: pluginManagementSettings?.domainAgentMappings ?? [],
|
||||||
availableAgentIds: selectorLoading
|
availableAgentIds: selectorLoading
|
||||||
? undefined
|
? undefined
|
||||||
@@ -456,6 +448,40 @@ export default function IframeChatPage() {
|
|||||||
});
|
});
|
||||||
setSelectedAgentId(nextSelection.selectedAgentId);
|
setSelectedAgentId(nextSelection.selectedAgentId);
|
||||||
setForceDefaultAssistant(nextSelection.forceDefaultAssistant);
|
setForceDefaultAssistant(nextSelection.forceDefaultAssistant);
|
||||||
|
return nextSelection;
|
||||||
|
},
|
||||||
|
[
|
||||||
|
manualSelectedAgentId,
|
||||||
|
pluginManagementSettings?.domainAgentMappings,
|
||||||
|
selectorAgents,
|
||||||
|
selectorLoading,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleMessage = (
|
||||||
|
event: MessageEvent<
|
||||||
|
BrainyChatSubmitMessage | BrainyChatInterruptMessage | BrainyUrlContextMessage
|
||||||
|
>,
|
||||||
|
) => {
|
||||||
|
const data = event.data;
|
||||||
|
if (data?.source !== "brainy-extension") return;
|
||||||
|
if (data?.type === "brainy:chat-interrupt") {
|
||||||
|
void handleInterrupt(data as BrainyChatInterruptMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data?.type === "brainy:context-url") {
|
||||||
|
applyIncomingUrlSelection(
|
||||||
|
(data as BrainyUrlContextMessage).payload?.url ?? null,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data?.type !== "brainy:chat-submit") return;
|
||||||
|
const submitMessage = data as BrainyChatSubmitMessage;
|
||||||
|
const { submitText, files, iframeContext, additionalKwargs } =
|
||||||
|
parseBrainyChatSubmitMessage(submitMessage);
|
||||||
|
|
||||||
|
if (!submitText && files.length === 0) return;
|
||||||
|
|
||||||
console.log("收到插件消息", {
|
console.log("收到插件消息", {
|
||||||
text: submitMessage.payload?.text,
|
text: submitMessage.payload?.text,
|
||||||
@@ -473,8 +499,8 @@ export default function IframeChatPage() {
|
|||||||
message: { text: submitText, files },
|
message: { text: submitText, files },
|
||||||
externalContext: iframeContext,
|
externalContext: iframeContext,
|
||||||
options: additionalKwargs ? { additionalKwargs } : undefined,
|
options: additionalKwargs ? { additionalKwargs } : undefined,
|
||||||
targetSelectedAgentId: nextSelection.selectedAgentId,
|
targetSelectedAgentId: selectedAgentId,
|
||||||
targetForceDefaultAssistant: nextSelection.forceDefaultAssistant,
|
targetForceDefaultAssistant: forceDefaultAssistant,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -483,11 +509,10 @@ export default function IframeChatPage() {
|
|||||||
window.removeEventListener("message", handleMessage);
|
window.removeEventListener("message", handleMessage);
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
|
applyIncomingUrlSelection,
|
||||||
|
forceDefaultAssistant,
|
||||||
handleInterrupt,
|
handleInterrupt,
|
||||||
manualSelectedAgentId,
|
selectedAgentId,
|
||||||
pluginManagementSettings?.domainAgentMappings,
|
|
||||||
selectorAgents,
|
|
||||||
selectorLoading,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const isLandingState = isNewThread && !hasStartedConversation;
|
const isLandingState = isNewThread && !hasStartedConversation;
|
||||||
|
|||||||
Reference in new issue
Block a user