diff --git a/packages/plugins/@nocobase/plugin-ai/package.json b/packages/plugins/@nocobase/plugin-ai/package.json index 18619e07cd..88489deace 100644 --- a/packages/plugins/@nocobase/plugin-ai/package.json +++ b/packages/plugins/@nocobase/plugin-ai/package.json @@ -14,15 +14,15 @@ }, "devDependencies": { "@ant-design/x": "^1.1.0", - "@langchain/core": "^0.3.55", - "@langchain/deepseek": "^0.0.1", - "@langchain/openai": "^0.4.3", + "@langchain/core": "^0.3.61", + "@langchain/deepseek": "^0.0.2", + "@langchain/openai": "^0.5.16", "nodejs-snowflake": "^2.0.1", "use-context-selector": "^2.0.0", "react-markdown": "^10.1.0", "react-syntax-highlighter": "^15.6.1", - "@langchain/google-genai": "^0.2.5", - "@langchain/anthropic": "^0.3.20", + "@langchain/google-genai": "^0.2.14", + "@langchain/anthropic": "^0.3.23", "echarts": "^5.5.0", "echarts-for-react": "3.0.2", "rehype-raw": "^7.0.0", diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/AddContextButton.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/AddContextButton.tsx index 7b1c19dbda..ff3a2e04c3 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/AddContextButton.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/AddContextButton.tsx @@ -7,13 +7,14 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ +// @ts-nocheck import React, { useMemo } from 'react'; import { Button, Dropdown } from 'antd'; import { useT } from '../locale'; import { AppstoreAddOutlined } from '@ant-design/icons'; import { Schema } from '@formily/react'; import { usePlugin } from '@nocobase/client'; -import PluginAIClient from '../'; +import PluginAIClient from '..'; import { ContextItem } from './types'; export const AddContextButton: React.FC<{ diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/SenderHeader.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/SenderHeader.tsx index f99116a7f5..fa60989b73 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/SenderHeader.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/SenderHeader.tsx @@ -17,7 +17,6 @@ import { UserAddOutlined, CloseCircleOutlined } from '@ant-design/icons'; import { AIEmployeeListItem } from '../AIEmployeeListItem'; import { avatars } from '../avatars'; import { ProfileCard } from '../ProfileCard'; -import { AddContextButton } from './AddContextButton'; import { AttachmentsHeader } from './AttachmentsHeader'; import { ContextItemsHeader } from './ContextItemsHeader'; diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/data-modeling/Diagram.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/data-modeling/Diagram.tsx index aa09e302ed..1fd32caf1d 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/data-modeling/Diagram.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/data-modeling/Diagram.tsx @@ -55,19 +55,22 @@ const layoutElements = async (nodes: Node[], edges: Edge[]) => { edges: edges, }; - return elk - .layout(graph) - .then((layoutedGraph) => ({ - nodes: layoutedGraph.children.map((node) => ({ - ...node, - // React Flow expects a position property on the node instead of `x` - // and `y` fields. - position: { x: node.x, y: node.y }, - })), + return ( + elk + // @ts-ignore + .layout(graph) + .then((layoutedGraph) => ({ + nodes: layoutedGraph.children.map((node) => ({ + ...node, + // React Flow expects a position property on the node instead of `x` + // and `y` fields. + position: { x: node.x, y: node.y }, + })), - edges: layoutedGraph.edges, - })) - .catch(console.error); + edges: layoutedGraph.edges, + })) + .catch(console.error) + ); }; const getDiagramData = (collections: any[]) => { @@ -226,6 +229,7 @@ export const Diagram: React.FC<{ useEffect(() => { getDiagramData(collections) + // @ts-ignore .then(({ nodes, edges }) => { setNodes(nodes); setEdges(edges); diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/data-modeling/types.ts b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/data-modeling/types.ts deleted file mode 100644 index 7d0b3348b8..0000000000 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/data-modeling/types.ts +++ /dev/null @@ -1,259 +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. - */ - -export interface CollectionOptions extends Omit { - /** The unique identifier of the collection, must be unique across the database */ - name: string; - /** The display title of the collection, used for UI presentation */ - title?: string; - /** The description of the collection */ - description?: string; - /** Whether this collection is a through table for many-to-many relationships */ - isThrough?: boolean; - /** The target key(s) used for filtering operations, can be a single key or array of keys */ - filterTargetKey?: string | string[]; - /** Array of field definitions for the collection */ - fields?: FieldOptions[]; - /** - * Whether to automatically generate an 'id' field - * @default true - */ - autoGenId?: boolean; - /** - * Whether to automatically generate a 'createdAt' timestamp field - * @default true - */ - createdAt?: boolean; - /** Whether to automatically generate an 'updatedAt' timestamp field - * @default true - */ - updatedAt?: boolean; - /** - * Whether to automatically generate a 'createdById' field for record ownership - * @default false - */ - createdBy?: boolean; - /** - * Whether to automatically generate an 'updatedById' field for tracking updates - * @default false - */ - updatedBy?: boolean; - /** The template identifier used to create this collection */ - template: 'general' | 'tree' | 'file' | 'calendar' | 'expression'; - /** The field name used for tree structure functionality */ - tree?: 'adjacencyList'; -} - -export type FieldOptions = - | BaseFieldOptions - | StringFieldOptions - | IntegerFieldOptions - | FloatFieldOptions - | DecimalFieldOptions - | DoubleFieldOptions - | JsonFieldOptions - | JsonbFieldOptions - | BooleanFieldOptions - | RadioFieldOptions - | TextFieldOptions - | TimeFieldOptions - | DateFieldOptions - | DatetimeTzFieldOptions - | DatetimeNoTzFieldOptions - | DateOnlyFieldOptions - | UnixTimestampFieldOptions - | UidFieldOptions - | UUIDFieldOptions - | NanoidFieldOptions - | PasswordFieldOptions - | BelongsToFieldOptions - | HasOneFieldOptions - | HasManyFieldOptions - | BelongsToManyFieldOptions; - -/** - * Base options for all field types - * Provides common properties that are available to all field configurations - */ -export interface BaseFieldOptions { - /** The name of the field, used as the column name in the database */ - name: string; - /** The title of the field, used for display in the UI */ - title: string; - /** The description of the field */ - description?: string; - /** Whether the field should be hidden from API responses and UI */ - hidden?: boolean; - /** Required. The user interface component type for this field */ - interface: - | 'id' - | 'input' - | 'integer' - | 'checkbox' - | 'checkboxGroup' - | 'color' - | 'createdAt' - | 'updatedAt' - | 'createdBy' - | 'updatedBy' - | 'date' - | 'datetime' - | 'datetimeNoTz' - | 'email' - | 'icon' - | 'json' - | 'markdown' - | 'multipleSelect' - | 'nanoid' - | 'number' - | 'password' - | 'percent' - | 'phone' - | 'radioGroup' - | 'richText' - | 'select' - | 'textarea' - | 'time' - | 'unixTimestamp' - | 'url' - | 'uuid' - | 'm2m' - | 'm2o' - | 'o2m' - | 'o2o'; - /** enumeration options for the field, used for select/radio/checkbox interfaces */ - enum?: { - label: string; - value: string | number | boolean; - }[]; - /** Additional properties for extensibility */ - [key: string]: any; -} -/** - * Base options for column-based field types - * Extends BaseFieldOptions and includes Sequelize column-specific options - * Excludes the 'type' property as it's handled by the specific field implementations - */ -export interface BaseColumnFieldOptions extends BaseFieldOptions, Omit { - /** The Sequelize data type for the column */ - dataType?: DataType; - - /** Index configuration for the column, can be boolean or detailed index options */ - index?: boolean | ModelIndexesOptions; -} - -export interface StringFieldOptions extends BaseColumnFieldOptions { - type: 'string'; - length?: number; - trim?: boolean; -} -export interface IntegerFieldOptions extends BaseColumnFieldOptions { - type: 'integer'; -} -export interface FloatFieldOptions extends BaseColumnFieldOptions { - type: 'float'; -} -export interface DecimalFieldOptions extends BaseColumnFieldOptions { - type: 'decimal'; - precision: number; - scale: number; -} -export interface DoubleFieldOptions extends BaseColumnFieldOptions { - type: 'double'; -} -export interface JsonFieldOptions extends BaseColumnFieldOptions { - type: 'json'; -} -export interface JsonbFieldOptions extends BaseColumnFieldOptions { - type: 'jsonb'; -} -export interface BooleanFieldOptions extends BaseColumnFieldOptions { - type: 'boolean'; -} -export interface RadioFieldOptions extends BaseColumnFieldOptions { - type: 'radio'; -} -export interface TextFieldOptions extends BaseColumnFieldOptions { - type: 'text'; - length?: 'tiny' | 'medium' | 'long'; - trim?: boolean; -} -export interface TimeFieldOptions extends BaseColumnFieldOptions { - type: 'time'; -} -export interface DateFieldOptions extends BaseColumnFieldOptions { - type: 'date'; -} -export interface DatetimeTzFieldOptions extends BaseColumnFieldOptions { - type: 'datetimeTz'; -} -export interface DatetimeNoTzFieldOptions extends BaseColumnFieldOptions { - type: 'datetimeNoTz'; -} -export interface DateOnlyFieldOptions extends BaseColumnFieldOptions { - type: 'dateOnly'; -} -export interface UnixTimestampFieldOptions extends BaseColumnFieldOptions { - type: 'unixTimestamp'; -} -export interface UidFieldOptions extends BaseColumnFieldOptions { - type: 'uid'; - prefix?: string; - pattern?: string; -} -export interface UUIDFieldOptions extends BaseColumnFieldOptions { - type: 'uuid'; - autoFill?: boolean; -} -export interface NanoidFieldOptions extends BaseColumnFieldOptions { - type: 'nanoid'; - size?: number; - customAlphabet?: string; - autoFill?: boolean; -} -export interface PasswordFieldOptions extends BaseColumnFieldOptions { - type: 'password'; - /** - * @default 64 - */ - length?: number; - /** - * @default 8 - */ - randomBytesSize?: number; -} -export interface BelongsToFieldOptions extends BaseRelationFieldOptions, SequelizeBelongsToOptions { - type: 'belongsTo'; - foreignKey: string; - target: string; - targetKey: string; -} -export interface HasOneFieldOptions extends BaseRelationFieldOptions, SequelizeHasOneOptions { - type: 'hasOne'; - sourceKey: string; - target: string; - foreignKey: string; -} -export interface HasManyFieldOptions extends MultipleRelationFieldOptions, SequelizeHasManyOptions { - type: 'hasMany'; - sourceKey: string; - target: string; - foreignKey: string; -} -export interface BelongsToManyFieldOptions - extends MultipleRelationFieldOptions, - Omit { - type: 'belongsToMany'; - target: string; - through: string; - sourceKey: string; - foreignKey: string; - otherKey: string; - targetKey: string; -} 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 d2e7ba0431..d25a94068e 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 @@ -120,7 +120,7 @@ export type ActionOptions = { export type WorkContextOptions = { name?: string; - menu: { + menu?: { icon?: React.ReactNode; label?: React.ReactNode; Component?: ComponentType<{ diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/llm-providers/google-genai/MessageRenderer.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/llm-providers/google-genai/MessageRenderer.tsx index e6c37cbc58..d1056fe181 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/llm-providers/google-genai/MessageRenderer.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client/llm-providers/google-genai/MessageRenderer.tsx @@ -8,7 +8,7 @@ */ import React from 'react'; -import { Markdown } from '../../ai-employees/chatbox/Markdown'; +import { Markdown } from '../../ai-employees/chatbox/markdown/Markdown'; import { ToolCard } from '../../ai-employees/chatbox/ToolCard'; export const MessageRenderer: React.FC<{ @@ -41,7 +41,15 @@ export const MessageRenderer: React.FC<{ gap: 16, }} > - {typeof content === 'string' && } + {typeof content === 'string' && ( + + )} {msg.tool_calls?.length && } ); diff --git a/packages/plugins/@nocobase/plugin-ai/src/server/ai-employees/built-in/data-modeling/actions.ts b/packages/plugins/@nocobase/plugin-ai/src/server/ai-employees/built-in/data-modeling/actions.ts index 835c9e410d..50df14afa5 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/server/ai-employees/built-in/data-modeling/actions.ts +++ b/packages/plugins/@nocobase/plugin-ai/src/server/ai-employees/built-in/data-modeling/actions.ts @@ -7,4 +7,114 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -export const defineCollections = () => {}; +import { Context, Next } from '@nocobase/actions'; +import _ from 'lodash'; + +export const defineCollections = async (ctx: Context, next: Next) => { + const { collections } = ctx.action.params.values || {}; + const id = { + name: 'id', + type: 'bigInt', + autoIncrement: true, + primaryKey: true, + allowNull: false, + uiSchema: { + type: 'number', + title: '{{t("ID")}}', + 'x-component': 'InputNumber', + 'x-read-pretty': true, + }, + interface: 'integer', + }; + const createdAt = { + name: 'createdAt', + interface: 'createdAt', + type: 'date', + field: 'createdAt', + uiSchema: { + type: 'datetime', + title: '{{t("Created at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }; + const updatedAt = { + type: 'date', + field: 'updatedAt', + name: 'updatedAt', + interface: 'updatedAt', + uiSchema: { + type: 'datetime', + title: '{{t("Last updated at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }; + for (const options of collections) { + if (options.name === 'users') { + continue; + } + if (options.autoGenId !== false && !options.isThrough) { + options.autoGenId = false; + options.fields.unshift(id); + } + if (options.createdAt !== false) { + options.fields.push(createdAt); + } + if (options.updatedAt !== false) { + options.fields.push(updatedAt); + } + const primaryKey = options.fields.find((field: any) => field.primaryKey); + if (!options.filterTargetKey) { + options.filterTargetKey = primaryKey?.name || 'id'; + } + // omit defaultValue + options.fields = options.fields.map((field: any) => { + return _.omit(field, ['defaultValue']); + }); + const transaction = await ctx.app.db.sequelize.transaction(); + try { + const repo = ctx.db.getRepository('collections'); + const collection = await repo.findOne({ + filter: { + name: options.name, + }, + transaction, + }); + if (!collection) { + const collection = await repo.create({ + values: options, + transaction, + context: ctx, + }); + // await collection.load({ transaction }); + await transaction.commit(); + continue; + } + await repo.update({ + filterByTk: collection.name, + values: { + ...options, + fields: options.fields?.map((f: any) => { + delete f.key; + return f; + }), + }, + updateAssociationValues: ['fields'], + transaction, + }); + await collection.loadFields({ + transaction, + }); + await collection.load({ transaction, resetFields: true }); + await transaction.commit(); + } catch (e) { + await transaction.rollback(); + throw e; + } + } + + await next(); +}; diff --git a/packages/plugins/@nocobase/plugin-ai/src/server/manager/ai-manager.ts b/packages/plugins/@nocobase/plugin-ai/src/server/manager/ai-manager.ts index aa6c3fcc1c..685ed57c52 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/server/manager/ai-manager.ts +++ b/packages/plugins/@nocobase/plugin-ai/src/server/manager/ai-manager.ts @@ -81,7 +81,13 @@ export class AIManager { const processSchema = (schema: any) => { if (!schema) return undefined; - return schema instanceof ZodObject && raw ? zodToJsonSchema(schema) : schema; + try { + // Use type assertion to break the recursive type checking + return (schema as any) instanceof ZodObject && raw ? zodToJsonSchema(schema as any) : schema; + } catch (error) { + // Fallback if zodToJsonSchema fails + return schema; + } }; if (tool.type === 'group' && child) {