diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatButton.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatButton.tsx index 3f46330ab7..446ca89db4 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatButton.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatButton.tsx @@ -8,12 +8,13 @@ */ import React, { useMemo } from 'react'; -import { FloatButton, Avatar, Dropdown } from 'antd'; +import { FloatButton, Avatar, Dropdown, Popover } from 'antd'; import icon from '../icon.svg'; import { css } from '@emotion/css'; import { useChatBoxContext } from './ChatBoxContext'; import { useAIEmployeesContext } from '../AIEmployeesProvider'; import { avatars } from '../avatars'; +import { ProfileCard } from '../ProfileCard'; export const ChatButton: React.FC = () => { const { aiEmployees } = useAIEmployeesContext(); @@ -36,13 +37,16 @@ export const ChatButton: React.FC = () => { switchAIEmployee(employee); }} > - + } placement="leftTop"> + + + {employee.nickname} ), diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatMessagesProvider.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatMessagesProvider.tsx index 2d8bafb783..abc472634c 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatMessagesProvider.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatMessagesProvider.tsx @@ -8,7 +8,7 @@ */ import { createContext, useCallback, useContext, useRef } from 'react'; -import { Message, ResendOptions, SendOptions } from '../types'; // 假设有这些类型定义 +import { AIEmployee, Message, ResendOptions, SendOptions } from '../types'; // 假设有这些类型定义 import React, { useState } from 'react'; import { uid } from '@formily/shared'; import { useT } from '../../locale'; @@ -29,6 +29,7 @@ interface ChatMessagesContextValue { ) => Promise; resendMessages: (options: ResendOptions) => void; cancelRequest: () => void; + callTool: (options: { sessionId: string; messageId: string; aiEmployee: AIEmployee }) => void; messagesService: any; lastMessageRef: (node: HTMLElement | null) => void; } @@ -166,7 +167,6 @@ export const ChatMessagesProvider: React.FC<{ children: React.ReactNode }> = ({ sessionId, aiEmployee, messages: sendMsgs, - infoFormValues, onConversationCreate, }: SendOptions & { onConversationCreate?: (sessionId: string) => void; @@ -179,17 +179,6 @@ export const ChatMessagesProvider: React.FC<{ children: React.ReactNode }> = ({ setMessages((prev) => prev.slice(0, -1)); } - if (infoFormValues) { - msgs.push({ - key: uid(), - role: aiEmployee.username, - content: { - type: 'info', - content: infoFormValues, - }, - }); - } - msgs.push( ...sendMsgs.map((msg) => ({ key: uid(), @@ -313,6 +302,31 @@ export const ChatMessagesProvider: React.FC<{ children: React.ReactNode }> = ({ setResponseLoading(false); }, [currentConversation]); + const callTool = useCallback(async ({ sessionId, messageId, aiEmployee }) => { + addMessage({ + key: uid(), + role: aiEmployee.username, + content: { type: 'text', content: '' }, + loading: true, + }); + + try { + const sendRes = await api.request({ + url: 'aiConversations:callTool', + method: 'POST', + headers: { Accept: 'text/event-stream' }, + data: { sessionId, messageId }, + responseType: 'stream', + adapter: 'fetch', + }); + + await processStreamResponse(sendRes.data); + messagesServiceRef.current.run(sessionId); + } catch (err) { + throw err; + } + }, []); + const loadMoreMessages = useCallback(async () => { const messagesService = messagesServiceRef.current; if (messagesService.loading || !messagesService.data?.meta?.hasMore) { @@ -333,6 +347,7 @@ export const ChatMessagesProvider: React.FC<{ children: React.ReactNode }> = ({ sendMessages, resendMessages, cancelRequest, + callTool, messagesService, lastMessageRef, }} diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/Conversations.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/Conversations.tsx index 659380f1f6..848fd387f3 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/Conversations.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/Conversations.tsx @@ -226,7 +226,8 @@ export const Conversations: React.FC = memo(() => { icon: , }, ], - onClick: ({ key }) => { + onClick: ({ key, domEvent }) => { + domEvent.stopPropagation(); switch (key) { case 'delete': modal.confirm({ diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/MessageRenderer.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/MessageRenderer.tsx index 05cd38aa63..60435e110e 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/MessageRenderer.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/MessageRenderer.tsx @@ -16,10 +16,11 @@ import { useT } from '../../locale'; import { useChatMessages } from './ChatMessagesProvider'; import { useChatBoxContext } from './ChatBoxContext'; import { useChatConversations } from './ChatConversationsProvider'; -import { SchemaComponent } from '@nocobase/client'; +import { SchemaComponent, usePlugin } from '@nocobase/client'; import { uid } from '@formily/shared'; import { Markdown } from './Markdown'; import { ToolCard } from './ToolCard'; +import PluginAIClient from '../..'; const MessageWrapper = React.forwardRef< HTMLDivElement, @@ -33,6 +34,29 @@ const MessageWrapper = React.forwardRef< return props.children; }); +const AITextMessageRenderer: React.FC<{ + msg: any; +}> = ({ msg }) => { + const plugin = usePlugin('ai') as PluginAIClient; + const provider = plugin.aiManager.llmProviders.get(msg.metadata?.provider); + if (!provider?.components?.MessageRenderer) { + return ( +
+ {typeof msg.content === 'string' && } + {msg.tool_calls?.length && } +
+ ); + } + const M = provider.components.MessageRenderer; + return ; +}; + const AIMessageRenderer: React.FC<{ msg: any; }> = ({ msg }) => { @@ -58,18 +82,7 @@ const AIMessageRenderer: React.FC<{ }, }} variant="borderless" - content={ -
- {typeof msg.content === 'string' && } - {msg.tool_calls?.length && } -
- } + content={} footer={ + useAddActionProps(field), + }} + /> + + ); +}; + +export const EditParameter: React.FC = () => { + const t = useT(); + const form = useForm(); + const field = form.query('parameters').take() as Field; + const [visible, setVisible] = useState(false); + const index = ArrayItems.useIndex(); + const record = ArrayItems.useRecord(); + + return ( + +