diff --git a/packages/core/flow-engine/src/components/subModel/AddSubModelButton.tsx b/packages/core/flow-engine/src/components/subModel/AddSubModelButton.tsx
index 87221bb14ce..9d0de01765e 100644
--- a/packages/core/flow-engine/src/components/subModel/AddSubModelButton.tsx
+++ b/packages/core/flow-engine/src/components/subModel/AddSubModelButton.tsx
@@ -23,7 +23,7 @@ import { buildSubModelGroups, buildSubModelItems } from './utils';
export interface SubModelItem {
key?: string;
- label?: string;
+ label?: string | React.ReactNode;
type?: 'group' | 'divider';
disabled?: boolean;
icon?: React.ReactNode;
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 b27376a9286..ed6f0ca4d7b 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
@@ -7,18 +7,18 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
-import React, { useMemo } from 'react';
-import { FloatButton, Avatar, Dropdown } from 'antd';
+import React, { useMemo, useState } from 'react';
+import { FloatButton, Avatar, Dropdown, Button, Divider } from 'antd';
import icon from '../icon.png';
import { css } from '@emotion/css';
import { useAIEmployeesContext } from '../AIEmployeesProvider';
import { AIEmployeeListItem } from '../AIEmployeeListItem';
import { useAISelectionContext } from '../selector/AISelectorProvider';
-import { PauseCircleFilled } from '@ant-design/icons';
+import { PauseCircleFilled, RightOutlined, LeftOutlined } from '@ant-design/icons';
import { useToken } from '@nocobase/client';
-import { FlowModelRenderer, useFlowEngine } from '@nocobase/flow-engine';
import { useChatBoxStore } from './stores/chat-box';
import { useChatBoxActions } from './hooks/useChatBoxActions';
+import { ShortcutList } from '../shortcuts/ShortcutList';
export const ChatButton: React.FC = () => {
const { aiEmployees } = useAIEmployeesContext();
@@ -30,13 +30,6 @@ export const ChatButton: React.FC = () => {
const { stopSelect, selectable } = useAISelectionContext();
const { token } = useToken();
- // const flowEngine = useFlowEngine();
- //
- // const model = useMemo(() => {
- // return flowEngine.createModel({
- // use: 'AIEmployeeShortcutListModel',
- // });
- // }, [flowEngine]);
const items = useMemo(() => {
return aiEmployees?.map((employee) => ({
@@ -58,36 +51,39 @@ export const ChatButton: React.FC = () => {
return (
- {/*
*/}
+
{!selectable ? (
- }
+ {
setOpen(!open);
}}
/>
) : (
-
{
+ const app = useApp();
const t = useT();
+ const open = useChatBoxStore.use.open();
const setOpen = useChatBoxStore.use.setOpen();
const setSenderValue = useChatBoxStore.use.setSenderValue();
const setTaskVariables = useChatBoxStore.use.setTaskVariables();
@@ -119,7 +122,7 @@ export const useChatBoxActions = () => {
const triggerTask = useCallback(
async (options: TriggerTaskOptions) => {
- const { aiEmployee, tasks, variables, localVariables } = options;
+ const { aiEmployee, tasks } = options;
updateRole(aiEmployee);
if (!open) {
setOpen(true);
@@ -147,11 +150,7 @@ export const useChatBoxActions = () => {
if (tasks.length === 1) {
setMessages(msgs);
const task = tasks[0];
- const { userMessage, systemMessage, attachments, workContext } = await parseTask(
- task,
- variables,
- localVariables,
- );
+ const { userMessage, systemMessage, attachments, workContext } = await parseTask(app, task);
if (userMessage && userMessage.type === 'text') {
setSenderValue(userMessage.content);
} else {
diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/utils.ts b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/utils.ts
index e7369cde461..2e40293e8e5 100644
--- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/utils.ts
+++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/utils.ts
@@ -7,8 +7,9 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
-import { useLocalVariables, useVariables } from '@nocobase/client';
+import { Application, useLocalVariables, useVariables } from '@nocobase/client';
import { ContextItem } from '../types';
+import PluginAIClient from '../..';
async function replaceVariables(template, variables, localVariables = {}) {
const regex = /\{\{\s*(.*?)\s*\}\}/g;
@@ -53,6 +54,7 @@ async function replaceVariables(template, variables, localVariables = {}) {
}
export const parseTask = async (
+ app: Application,
task: {
message: {
user?: string;
@@ -61,26 +63,23 @@ export const parseTask = async (
workContext?: ContextItem[];
};
},
- variables: Record,
- localVariables?: Record,
) => {
let userMessage: any;
const { message } = task;
if (message?.user) {
- const content = await replaceVariables(message.user, variables, localVariables);
userMessage = {
type: 'text',
- content,
+ content: message.user,
};
}
let systemMessage: string;
if (message?.system) {
- systemMessage = await replaceVariables(message.system, variables, localVariables);
+ systemMessage = message.system;
}
const attachments = [];
if (message?.attachments?.length) {
for (const attachment of message.attachments) {
- const obj = await variables?.parseVariable(attachment, localVariables).then(({ value }) => value);
+ const obj = attachment;
if (!obj) {
continue;
}
@@ -97,7 +96,27 @@ export const parseTask = async (
}
}
}
- return { userMessage, systemMessage, attachments, workContext: message.workContext };
+ const workContext = [];
+ if (message.workContext) {
+ const plugin = app.pm.get('ai') as PluginAIClient;
+ for (const context of message.workContext) {
+ if (context.content) {
+ workContext.push(context);
+ continue;
+ }
+ const contextOptions = plugin.aiManager.workContext.get(context.type);
+ if (!(contextOptions && contextOptions.getContent)) {
+ workContext.push(context);
+ continue;
+ }
+ const content = contextOptions.getContent(app, context);
+ workContext.push({
+ ...context,
+ content,
+ });
+ }
+ }
+ return { userMessage, systemMessage, attachments, workContext };
};
const publicPath = window['__nocobase_dev_public_path__'] || window['__nocobase_public_path__'] || '/';
diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/data-modeling.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/data-modeling.tsx
deleted file mode 100644
index 807e052cf5c..00000000000
--- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/data-modeling.tsx
+++ /dev/null
@@ -1,131 +0,0 @@
-/**
- * This file is part of the NocoBase (R) project.
- * Copyright (c) 2020-2024 NocoBase Co., Ltd.
- * Authors: NocoBase Team.
- *
- * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
- * For more information, please refer to: https://www.nocobase.com/agreement.
- */
-
-import React from 'react';
-import { WorkContextOptions } from '../types';
-import { BuildOutlined, StarOutlined, SelectOutlined, DatabaseOutlined } from '@ant-design/icons';
-import { tval } from '@nocobase/utils/client';
-// @ts-ignore
-import pkg from '../../../../package.json';
-import { useT } from '../../locale';
-import { useAISelectionContext } from '../selector/AISelectorProvider';
-import { useTranslation } from 'react-i18next';
-import { Schema } from '@formily/react';
-import { Checkbox } from 'antd';
-
-export const CollectionDefinitionsContext: WorkContextOptions = {
- menu: {
- icon: ,
- label: tval('Modern pages', { ns: pkg.name }),
- },
- children: {
- // form: {
- // menu: {
- // icon: ,
- // Component: ({ onAdd }) => {
- // const t = useT();
- // const { t: collectionT } = useTranslation('lm-collections');
- // const { startSelect, ctx } = useAISelectionContext();
- // return (
- // {
- // startSelect('form', {
- // onSelect: ({ uid, fieldSchema, collection }) => {
- // if (!uid) {
- // return;
- // }
- // let title = '';
- // if (collection) {
- // title = Schema.compile(collection.title, { t: collectionT });
- // }
- // onAdd({
- // uid,
- // title,
- // content: JSON.stringify(fieldSchema.toJSON()),
- // });
- // },
- // });
- // }}
- // >
- // {t('Select form')}
- //
- // );
- // },
- // },
- // tag: {
- // Component: ({ item }) => {
- // const t = useT();
- // return (
- // <>
- // {t('Form')} ({item.title})
- // >
- // );
- // },
- // },
- // actions: [
- // {
- // responseType: 'json',
- // Component: ({ item, message, value }) => {
- // const t = useT();
- // const { ctx } = useAISelectionContext();
- //
- // return (
- // }
- // variant="link"
- // color="primary"
- // onClick={() => {
- // const content = value || (message.content as unknown as string);
- // const data = content.replace('```json', '').replace('```', '');
- // const json = JSON.parse(data);
- // const form = ctx[item.uid]?.form;
- // if (form) {
- // form.setValues(json);
- // }
- // }}
- // >
- // {t('Fill out form')} ({item.title})
- //
- // );
- // },
- // },
- // ],
- // },
- users: {
- menu: {
- Component: ({ onAdd }) => {
- const t = useT();
- const { t: collectionT } = useTranslation('lm-collections');
- const { startSelect } = useAISelectionContext();
- return {t('Users #f61f6cda073')};
- },
- },
- tag: {
- Component: ({ item }) => {
- return (
- <>
- {item.title}
- >
- );
- },
- },
- },
- roles: {
- menu: {
- Component: ({ onAdd }) => {
- const t = useT();
- const { startSelect } = useAISelectionContext();
-
- return {t('Roles #4cb2eb66a99')};
- },
- },
- },
- },
-};
diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/flow-models.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/flow-models.tsx
new file mode 100644
index 00000000000..8bc89b579fb
--- /dev/null
+++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/flow-models.tsx
@@ -0,0 +1,71 @@
+/**
+ * This file is part of the NocoBase (R) project.
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
+ * Authors: NocoBase Team.
+ *
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
+ * For more information, please refer to: https://www.nocobase.com/agreement.
+ */
+
+import React from 'react';
+import { WorkContextOptions } from '../types';
+import { BuildOutlined, StarOutlined } from '@ant-design/icons';
+import { tval } from '@nocobase/utils/client';
+// @ts-ignore
+import pkg from '../../../../package.json';
+import { useFlowEngine } from '@nocobase/flow-engine';
+import _ from 'lodash';
+
+function parseModel(node: any) {
+ if (!node || typeof node !== 'object' || !node.uid || !node.use) return null;
+
+ const parsed = {
+ uid: node.uid,
+ use: node.use,
+ props: node.props || {},
+ decoratorProps: node.decoratorProps || {},
+ stepParams: node.stepParams || {},
+ collectionField: node.collectionField
+ ? _.pick(node.collectionField, ['dataSourceKey', 'enum', 'name', 'type', 'title', 'uiSchema'])
+ : {},
+ children: {},
+ };
+
+ if (node.subModels && typeof node.subModels === 'object') {
+ for (const key in node.subModels) {
+ const group = node.subModels[key];
+ if (Array.isArray(group)) {
+ parsed.children[key] = group.map((sub) => parseModel(sub)).filter(Boolean);
+ } else if (typeof group === 'object') {
+ const child = parseModel(group);
+ if (child) {
+ parsed.children[key] = [child];
+ }
+ }
+ }
+ }
+
+ return parsed;
+}
+
+export const FlowModelsContext: WorkContextOptions = {
+ name: 'flow-model',
+ tag: {
+ Component: ({ item }) => {
+ const flowEngine = useFlowEngine();
+ const model = flowEngine.getModel(item.uid);
+ return (
+ <>
+ {model.title}
+ >
+ );
+ },
+ },
+ getContent: (app, { uid }) => {
+ const model = app.flowEngine.getModel(uid);
+ if (!model) {
+ return '';
+ }
+ return JSON.stringify(parseModel(model));
+ },
+};
diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutListModel.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutListModel.tsx
index ed8da402772..cdb6e181043 100644
--- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutListModel.tsx
+++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutListModel.tsx
@@ -18,24 +18,13 @@ type AIEmployeeShortcutListModelStructure = {
};
export class AIEmployeeShortcutListModel extends FlowModel {
- // onInit() {
- // this.addSubModel('shortcuts', {
- // uid: 'data-modeling',
- // use: 'AIEmployeeShortcutModel',
- // props: {
- // avatar: 'notion-3-male',
- // },
- // });
- // }
- //
- // render() {
- // return (
- //
- // {this.mapSubModels('shortcuts', (shortcut) => {
- // console.log(shortcut.props);
- // return ;
- // })}
- //
- // );
- // }
+ render() {
+ return (
+
+ {this.mapSubModels('shortcuts', (shortcut) => {
+ return ;
+ })}
+
+ );
+ }
}
diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutModel.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutModel.tsx
index 367330e12d2..9c9b775662e 100644
--- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutModel.tsx
+++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutModel.tsx
@@ -7,22 +7,67 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
-import React from 'react';
-import { Avatar, FloatButton } from 'antd';
+import React, { useMemo, useState } from 'react';
+import { Avatar, Button, Spin, Popover } from 'antd';
import { FlowModel } from '@nocobase/flow-engine';
import { avatars } from '../../avatars';
+import { AIEmployee, TriggerTaskOptions } from '../../types';
+import { useAIEmployeesContext } from '../../AIEmployeesProvider';
+import { useChatBoxActions } from '../../chatbox/hooks/useChatBoxActions';
+import { ProfileCard } from '../../ProfileCard';
+
+const Shortcut: React.FC = ({ aiEmployee: { username }, tasks }) => {
+ const [focus, setFocus] = useState(false);
+
+ const {
+ aiEmployeesMap,
+ service: { loading },
+ } = useAIEmployeesContext();
+ const aiEmployee = aiEmployeesMap[username];
+
+ const { triggerTask } = useChatBoxActions();
+
+ const currentAvatar = useMemo(() => {
+ const avatar = aiEmployee?.avatar;
+ if (!avatar) {
+ return null;
+ }
+ if (focus) {
+ return avatars(avatar, {
+ gesture: ['waveLongArm'],
+ gestureProbability: 100,
+ translateX: -15,
+ });
+ }
+ return avatars(avatar);
+ }, [aiEmployee, focus]);
+
+ return (
+
+ } placement="topLeft">
+ setFocus(true)}
+ onMouseLeave={() => setFocus(false)}
+ onClick={() => {
+ triggerTask({ aiEmployee, tasks });
+ }}
+ />
+
+
+ );
+};
export class AIEmployeeShortcutModel extends FlowModel {
+ public declare props: TriggerTaskOptions;
+
render() {
- // const { avatar } = this.props;
- // console.log(this.props, this.stepParams, this.getProps());
- // return (
- // }
- // style={{
- // insetInlineEnd: '94px',
- // }}
- // />
- // );
+ return ;
}
}
diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/form-filler/tools/index.ts b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/form-filler/tools/index.ts
new file mode 100644
index 00000000000..761eb59a6b2
--- /dev/null
+++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/form-filler/tools/index.ts
@@ -0,0 +1,28 @@
+/**
+ * This file is part of the NocoBase (R) project.
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
+ * Authors: NocoBase Team.
+ *
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
+ * For more information, please refer to: https://www.nocobase.com/agreement.
+ */
+
+import { FlowEngine } from '@nocobase/flow-engine';
+import { ToolOptions } from '../../../manager/ai-manager';
+import { FormModel } from '@nocobase/client';
+
+export const formFillerTool: [string, string, ToolOptions] = [
+ 'frontend',
+ 'formFiller',
+ {
+ invoke: (ctx, params) => {
+ const { form: uid, data } = params;
+ if (!uid || !data) {
+ return;
+ }
+ const flowEngine = ctx.flowEngine as FlowEngine;
+ const model = flowEngine.getModel(uid) as FormModel;
+ model?.form?.setValues(data);
+ },
+ },
+];
diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/icon.png b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/icon.png
index d40cf8c92e5..1273b3ab8a1 100644
Binary files a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/icon.png and b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/icon.png differ
diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/selector/AISelectorProvider.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/selector/AISelectorProvider.tsx
index 0ee6e55f6d4..56f618a0250 100644
--- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/selector/AISelectorProvider.tsx
+++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/selector/AISelectorProvider.tsx
@@ -9,6 +9,7 @@
import React, { createContext, useCallback, useContext, useState } from 'react';
import { Selector } from '../types';
+import { useFlowEngine } from '@nocobase/flow-engine';
export const AISelectionContext = createContext<{
selectable: string;
@@ -32,9 +33,12 @@ export const AISelectionProvider: React.FC<{
}> = (props) => {
const [selectable, setSelectable] = useState('');
const [selector, setSelector] = useState(null);
+ const flowEngine = useFlowEngine();
const [ctx, setCtx] = useState<{
[key: string]: Record;
- }>({});
+ }>({
+ flowEngine: flowEngine,
+ });
const startSelect = (selectType: string, selector?: Selector) => {
if (selector) {
diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/ShortcutList.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/ShortcutList.tsx
new file mode 100644
index 00000000000..5bf2c8016ca
--- /dev/null
+++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/ShortcutList.tsx
@@ -0,0 +1,106 @@
+/**
+ * This file is part of the NocoBase (R) project.
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
+ * Authors: NocoBase Team.
+ *
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
+ * For more information, please refer to: https://www.nocobase.com/agreement.
+ */
+
+import { Button, Divider } from 'antd';
+import { LeftOutlined, RightOutlined, PlusOutlined } from '@ant-design/icons';
+import { AddSubModelButton, FlowModelRenderer, useFlowEngine } from '@nocobase/flow-engine';
+import React, { useMemo, useState } from 'react';
+import { useShortcuts } from './useShortcuts';
+import { useDesignable, useToken } from '@nocobase/client';
+import { useAIEmployeesContext } from '../AIEmployeesProvider';
+import { AIEmployeeListItem } from '../AIEmployeeListItem';
+
+export const ShortcutList: React.FC = () => {
+ const flowEngine = useFlowEngine();
+ const { designable } = useDesignable();
+ const { shortcuts, uid, builtIn } = useShortcuts();
+ const { token } = useToken();
+ const designMode = designable && !builtIn;
+
+ const {
+ aiEmployees,
+ service: { loading },
+ } = useAIEmployeesContext();
+
+ const [folded, setFolded] = useState(false);
+
+ const model = useMemo(() => {
+ return flowEngine.createModel({
+ uid: `ai-shortcuts-${uid}`,
+ use: 'AIEmployeeShortcutListModel',
+ subModels: {
+ shortcuts,
+ },
+ });
+ }, [flowEngine, shortcuts]);
+
+ return (
+ <>
+ {shortcuts.length > 0 && (
+ <>
+ : }
+ style={{
+ height: '52px',
+ width: '12px',
+ fontSize: token.fontSizeSM,
+ }}
+ onClick={() => setFolded(!folded)}
+ />
+ {!folded && }
+ >
+ )}
+ {!builtIn && (
+ {
+ return loading
+ ? []
+ : aiEmployees.map((aiEmployee) => ({
+ key: aiEmployee.username,
+ label: ,
+ createModelOptions: {
+ use: 'AIEmployeeShortcutModel',
+ props: {
+ aiEmployee: {
+ username: 'form_assistant',
+ },
+ },
+ },
+ }));
+ }}
+ >
+ }
+ variant="dashed"
+ color="default"
+ style={{
+ width: '48px',
+ height: '48px',
+ color: 'var(--colorSettings)',
+ borderColor: 'var(--colorSettings)',
+ background: 'transparent',
+ }}
+ />
+
+ )}
+ {(shortcuts.length || designMode) && (
+
+ )}
+ >
+ );
+};
diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/useShortcuts.ts b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/useShortcuts.ts
new file mode 100644
index 00000000000..51731ecaa86
--- /dev/null
+++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/useShortcuts.ts
@@ -0,0 +1,66 @@
+/**
+ * This file is part of the NocoBase (R) project.
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
+ * Authors: NocoBase Team.
+ *
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
+ * For more information, please refer to: https://www.nocobase.com/agreement.
+ */
+
+import { useParams, useLocation } from 'react-router-dom';
+
+export const useShortcuts = () => {
+ const { name } = useParams();
+ const { pathname } = useLocation();
+ if (pathname === '/admin/settings/data-source-manager/main/collections') {
+ return {
+ uid: 'data-modeling',
+ builtIn: true,
+ shortcuts: [
+ {
+ uid: 'orin',
+ use: 'AIEmployeeShortcutModel',
+ props: {
+ aiEmployee: {
+ username: 'orin',
+ },
+ },
+ },
+ ],
+ };
+ }
+ if (name === '0krytmcef9g') {
+ return {
+ uid: name,
+ shortcuts: [
+ {
+ uid: 'form_assistant',
+ use: 'AIEmployeeShortcutModel',
+ props: {
+ aiEmployee: {
+ username: 'form_assistant',
+ },
+ tasks: [
+ {
+ message: {
+ workContext: [
+ {
+ type: 'flow-model',
+ uid: 'e3b85ce6e70',
+ title: 'Form(Add new): Users',
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ ],
+ };
+ }
+ return {
+ uid: name,
+ builtIn: true,
+ shortcuts: [],
+ };
+};
diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/types.ts b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/types.ts
index 4a61de9f77e..ef73e723e9a 100644
--- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/types.ts
+++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/types.ts
@@ -8,6 +8,7 @@
*/
import type { BubbleProps } from '@ant-design/x';
+import { Application } from '@nocobase/client';
import { ComponentType } from 'react';
export type Selector = {
@@ -37,7 +38,7 @@ export type ContextItem = {
type: string;
uid: string;
title: string;
- content: string;
+ content?: string;
};
export type ToolCall = {
@@ -112,9 +113,7 @@ export type Task = {
export type TriggerTaskOptions = {
aiEmployee?: AIEmployee;
- tasks: Task[];
- variables?: Record;
- localVariables?: Record;
+ tasks?: Task[];
};
export type Tool = {
@@ -163,4 +162,5 @@ export type WorkContextOptions = {
};
actions?: ActionOptions[];
children?: Record>;
+ getContent?: (app: Application, item: ContextItem) => string;
};
diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/index.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/index.tsx
index de83ded3ebe..4bed5959e2d 100644
--- a/packages/plugins/@nocobase/plugin-ai/src/client/index.tsx
+++ b/packages/plugins/@nocobase/plugin-ai/src/client/index.tsx
@@ -22,6 +22,8 @@ import { PermissionsTab } from './ai-employees/permissions/PermissionsTab';
import { anthropicProviderOptions } from './llm-providers/anthropic';
import { AIEmployeeShortcutListModel, AIEmployeeShortcutModel } from './ai-employees/flow/models';
import { defineCollectionsTool } from './ai-employees/data-modeling/tools';
+import { FlowModelsContext } from './ai-employees/context/flow-models';
+import { formFillerTool } from './ai-employees/form-filler/tools';
const { AIEmployeesProvider } = lazy(() => import('./ai-employees/AIEmployeesProvider'), 'AIEmployeesProvider');
const { Employees } = lazy(() => import('./ai-employees/manager/Employees'), 'Employees');
const { LLMServices } = lazy(() => import('./llm-services/LLMServices'), 'LLMServices');
@@ -108,22 +110,9 @@ export class PluginAIClient extends Plugin {
Component: StructuredOutputSettings,
});
- // this.aiManager.registerWorkContext('classic-pages', ClassicPagesContext);
- // this.aiManager.registerWorkContext('collection-definitions', CollectionDefinitionsContext);
+ this.aiManager.registerWorkContext('flow-model', FlowModelsContext);
this.aiManager.registerTool(...defineCollectionsTool);
- this.aiManager.registerTool('frontEnd', 'formFiller', {
- invoke: (ctx, params) => {
- const { form: uid, data } = params;
- if (!uid || !data) {
- return;
- }
- const form = ctx[uid]?.form;
- if (!form) {
- return;
- }
- form.setValues(data);
- },
- });
+ this.aiManager.registerTool(...formFillerTool);
}
setupWorkflow() {
diff --git a/packages/plugins/@nocobase/plugin-data-source-manager/src/client/component/MainDataSourceManager/Configuration/schemas/collections.ts b/packages/plugins/@nocobase/plugin-data-source-manager/src/client/component/MainDataSourceManager/Configuration/schemas/collections.ts
index 27e5f453115..78b21cf68ac 100644
--- a/packages/plugins/@nocobase/plugin-data-source-manager/src/client/component/MainDataSourceManager/Configuration/schemas/collections.ts
+++ b/packages/plugins/@nocobase/plugin-data-source-manager/src/client/component/MainDataSourceManager/Configuration/schemas/collections.ts
@@ -176,82 +176,6 @@ export const collectionTableSchema: ISchema = {
type: 'primary',
},
},
- aiEmployee: {
- type: 'void',
- 'x-component': 'AIEmployeeButton',
- 'x-toolbar': 'ActionSchemaToolbar',
- 'x-settings': 'aiEmployees:button',
- 'x-component-props': {
- username: 'orin',
- tasks: [
- {
- message: {
- workContext: [],
- attachments: [],
- system: `## Task: Create new schema from scenario
-
-The user will describe a business scenario. Your task is to define the full schema from scratch.
-
-- Confirm all entities and required fields
-- Clarify the relationships between entities
-- Normalize the structure
-- Output a complete block
-
-Avoid defining system fields already provided by templates.`,
- user: 'Start from a business scenario and generate normalized collections',
- },
- title: 'Start from a business scenario and generate normalized collections',
- autoSend: true,
- },
- {
- message: {
- workContext: [],
- attachments: [],
- user: 'Add or adjust fields and relationships in your current database',
- system: `## Task: Modify or expand existing collections
-
-The user provides part of the data model and wants to modify it.
-
-- Ask what changes they want: add tables, add fields, update relations, rename, etc.
-- Only return the changed parts – do not repeat existing schema
-- Wrap new/updated collections in tags
-- Do not remove or rename unless explicitly instructed
-
-Example: Add a new relation to "projects" and a new table:
-
-
-[
- {
- name: "projects",
- fields: [
- {
- name: "ownerId",
- title: "Owner",
- type: "belongsTo",
- interface: "m2o",
- target: "users",
- foreignKey: "ownerId",
- targetKey: "id"
- }
- ]
- },
- {
- name: "project_tags",
- title: "Project Tags",
- template: "general",
- fields: [
- { name: "name", title: "Name", type: "string", interface: "input" }
- ]
- }
-]
-`,
- },
- title: 'Add or adjust fields and relationships in your current database',
- autoSend: true,
- },
- ],
- },
- },
},
},
[uid()]: {