Merge branch '2.0-ai' into 2.0

This commit is contained in:
xilesun 2025-06-30 21:54:36 +08:00
commit 40b386eb7c
9 changed files with 152 additions and 283 deletions

View File

@ -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",

View File

@ -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<{

View File

@ -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';

View File

@ -55,7 +55,9 @@ const layoutElements = async (nodes: Node[], edges: Edge[]) => {
edges: edges,
};
return elk
return (
elk
// @ts-ignore
.layout(graph)
.then((layoutedGraph) => ({
nodes: layoutedGraph.children.map((node) => ({
@ -67,7 +69,8 @@ const layoutElements = async (nodes: Node[], edges: Edge[]) => {
edges: layoutedGraph.edges,
}))
.catch(console.error);
.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);

View File

@ -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<SequelizeModelOptions, 'name' | 'hooks'> {
/** 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<ModelAttributeColumnOptions, 'type'> {
/** 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<SequelizeBelongsToManyOptions, 'through'> {
type: 'belongsToMany';
target: string;
through: string;
sourceKey: string;
foreignKey: string;
otherKey: string;
targetKey: string;
}

View File

@ -120,7 +120,7 @@ export type ActionOptions = {
export type WorkContextOptions = {
name?: string;
menu: {
menu?: {
icon?: React.ReactNode;
label?: React.ReactNode;
Component?: ComponentType<{

View File

@ -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' && <Markdown markdown={content} />}
{typeof content === 'string' && (
<Markdown
message={{
...msg,
// @ts-ignore
content,
}}
/>
)}
{msg.tool_calls?.length && <ToolCard tools={msg.tool_calls} messageId={msg.messageId} />}
</div>
);

View File

@ -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();
};

View File

@ -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) {