Skip to content

Commit

Permalink
feat: system plugin (#2024)
Browse files Browse the repository at this point in the history
  • Loading branch information
c121914yu authored Jul 11, 2024
1 parent dd2a9bd commit e80dcb8
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 69 deletions.
2 changes: 1 addition & 1 deletion packages/global/core/plugin/type.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export type PluginItemSchema = {
export type PluginTemplateType = PluginRuntimeType & {
author?: string;
id: string;
source: `${PluginSourceEnum}`;
source: PluginSourceEnum;
templateType: FlowNodeTemplateType['templateType'];
intro: string;
version: string;
Expand Down
3 changes: 2 additions & 1 deletion packages/global/core/workflow/runtime/type.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,12 @@ export type RuntimeNodeItemType = {
};

export type PluginRuntimeType = {
id: string;
teamId?: string;
name: string;
avatar: string;
showStatus?: boolean;
isTool?: boolean;
currentCost?: number;
nodes: StoreNodeItemType[];
edges: StoreEdgeItemType[];
};
Expand Down
12 changes: 12 additions & 0 deletions packages/global/core/workflow/type/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,29 @@ export type WorkflowTemplateType = {

workflow: WorkflowTemplateBasicType;
};

// template market
export type TemplateMarketItemType = WorkflowTemplateType & {
tags?: { id: string; label: string }[];
};

// system plugin
export type SystemPluginTemplateItemType = WorkflowTemplateType & {
templateType: FlowNodeTemplateTypeEnum;
isTool?: boolean;

// commercial plugin config
originCost: number; // n points/one time
currentCost: number;

isActive?: boolean;
inputConfig?: {
// Render config input form. Find the corresponding node and replace the variable directly
key: string;
label: string;
description: string;
value?: any;
}[];

workflow: WorkflowTemplateBasicType;
};
90 changes: 64 additions & 26 deletions packages/plugins/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,39 @@ import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constant
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { SystemPluginResponseType } from './type';
import { NodeTemplateListItemType } from '@fastgpt/global/core/workflow/type/node';
import { isProduction } from '../service/common/system/constants';
import { FastGPTProUrl, isProduction } from '../service/common/system/constants';
import { GET, POST } from '@fastgpt/service/common/api/plusRequest';
import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';

let list = ['getTime', 'fetchUrl', 'mathExprVal'];

/* Get plugins */
export const getCommunityPlugins = () => {
if (isProduction && global.communitySystemPlugins) return global.communitySystemPlugins;

global.communitySystemPlugins = list.map((name) => ({
return list.map<SystemPluginTemplateItemType>((name) => ({
...require(`./src/${name}/template.json`),
id: `${PluginSourceEnum.community}-${name}`
id: `${PluginSourceEnum.community}-${name}`,
isActive: true
}));

return global.communitySystemPlugins;
};
const getCommercialPlugins = () => {
return GET<SystemPluginTemplateItemType[]>('/core/app/plugin/getSystemPlugins');
};
export const getSystemPluginTemplates = async () => {
if (global.systemPlugins) return global.systemPlugins;

export const getCommunityPluginsTemplateList = () => {
return getCommunityPlugins().map<NodeTemplateListItemType>((plugin) => ({
id: plugin.id,
templateType: plugin.templateType ?? FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.pluginModule,
avatar: plugin.avatar,
name: plugin.name,
intro: plugin.intro,
isTool: plugin.isTool
}));
try {
global.systemPlugins = [];
global.systemPlugins = FastGPTProUrl ? await getCommercialPlugins() : getCommunityPlugins();

return global.systemPlugins;
} catch (error) {
//@ts-ignore
global.systemPlugins = undefined;
return Promise.reject(error);
}
};

export const getCommunityCb = async () => {
if (isProduction && global.communitySystemPluginCb) return global.communitySystemPluginCb;

// Do not modify the following code
const loadModule = async (name: string) => {
const module = await import(`./src/${name}/index`);
Expand All @@ -46,12 +49,47 @@ export const getCommunityCb = async () => {
}))
);

global.communitySystemPluginCb = result.reduce<
Record<string, (e: any) => SystemPluginResponseType>
>((acc, { name, cb }) => {
acc[name] = cb;
return acc;
}, {});
return result.reduce<Record<string, (e: any) => SystemPluginResponseType>>(
(acc, { name, cb }) => {
acc[name] = cb;
return acc;
},
{}
);
};
const getCommercialCb = async () => {
const plugins = await getSystemPluginTemplates();
const result = plugins.map((plugin) => {
const name = plugin.id.split('-')[1];

return {
name,
cb: (e: any) =>
POST<Record<string, any>>('/core/app/plugin/run', {
pluginName: name,
data: e
})
};
});

return result.reduce<Record<string, (e: any) => SystemPluginResponseType>>(
(acc, { name, cb }) => {
acc[name] = cb;
return acc;
},
{}
);
};
export const getSystemPluginCb = async () => {
if (isProduction && global.systemPluginCb) return global.systemPluginCb;

return global.communitySystemPluginCb;
try {
global.systemPluginCb = {};
global.systemPluginCb = FastGPTProUrl ? await getCommercialCb() : await getCommunityCb();
return global.systemPluginCb;
} catch (error) {
//@ts-ignore
global.systemPluginCb = undefined;
return Promise.reject(error);
}
};
6 changes: 4 additions & 2 deletions packages/plugins/type.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { PluginTemplateType } from '@fastgpt/global/core/plugin/type.d';
import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';

export type SystemPluginResponseType = Promise<Record<string, any>>;

declare global {
var communitySystemPlugins: SystemPluginTemplateItemType[];
var communitySystemPluginCb: Record<string, (e: any) => SystemPluginResponseType>;
var systemPlugins: SystemPluginTemplateItemType[];
var systemPluginCb: Record<string, (e: any) => SystemPluginResponseType>;
}
22 changes: 11 additions & 11 deletions packages/service/core/app/plugin/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { getNanoid } from '@fastgpt/global/common/string/tools';
import { cloneDeep } from 'lodash';
import { MongoApp } from '../schema';
import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
import { getCommunityPlugins } from '@fastgpt/plugins/register';
import { getSystemPluginTemplates } from '@fastgpt/plugins/register';

/*
plugin id rule:
Expand All @@ -28,7 +28,7 @@ export async function splitCombinePluginId(id: string) {
};
}

const [source, pluginId] = id.split('-') as [`${PluginSourceEnum}`, string];
const [source, pluginId] = id.split('-') as [PluginSourceEnum, string];
if (!source || !pluginId) return Promise.reject('pluginId not found');

return { source, pluginId: id };
Expand All @@ -39,14 +39,6 @@ const getPluginTemplateById = async (
): Promise<SystemPluginTemplateItemType & { teamId?: string }> => {
const { source, pluginId } = await splitCombinePluginId(id);

if (source === PluginSourceEnum.community) {
const item = [...global.communityPlugins, ...getCommunityPlugins()].find(
(plugin) => plugin.id === pluginId
);
if (!item) return Promise.reject('plugin not found');

return cloneDeep(item);
}
if (source === PluginSourceEnum.personal) {
const item = await MongoApp.findById(id).lean();
if (!item) return Promise.reject('plugin not found');
Expand All @@ -68,8 +60,14 @@ const getPluginTemplateById = async (
originCost: 0,
currentCost: 0
};
} else {
const item = [...global.communityPlugins, ...(await getSystemPluginTemplates())].find(
(plugin) => plugin.id === pluginId
);
if (!item) return Promise.reject('plugin not found');

return cloneDeep(item);
}
return Promise.reject('plugin not found');
};

/* format plugin modules to plugin preview module */
Expand Down Expand Up @@ -98,10 +96,12 @@ export async function getPluginRuntimeById(id: string): Promise<PluginRuntimeTyp
const plugin = await getPluginTemplateById(id);

return {
id: plugin.id,
teamId: plugin.teamId,
name: plugin.name,
avatar: plugin.avatar,
showStatus: plugin.showStatus,
currentCost: plugin.currentCost,
nodes: plugin.workflow.nodes,
edges: plugin.workflow.edges
};
Expand Down
21 changes: 21 additions & 0 deletions packages/service/core/app/plugin/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { PluginRuntimeType } from '@fastgpt/global/core/workflow/runtime/type';
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
import { splitCombinePluginId } from './controller';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';

/*
1. Commercial plugin: n points per times
2. Other plugin: sum of children points
*/
export const computedPluginUsage = async (
plugin: PluginRuntimeType,
childrenUsage: ChatNodeUsageType[]
) => {
const { source } = await splitCombinePluginId(plugin.id);

if (source === PluginSourceEnum.commercial) {
return plugin.currentCost ?? 0;
}

return childrenUsage.reduce((sum, item) => sum + (item.totalPoints || 0), 0);
};
27 changes: 13 additions & 14 deletions packages/service/core/workflow/dispatch/plugin/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/
import { dispatchWorkFlow } from '../index';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { getPluginRuntimeById, splitCombinePluginId } from '../../../app/plugin/controller';
import { getPluginRuntimeById } from '../../../app/plugin/controller';
import {
getDefaultEntryNodeIds,
initWorkflowEdgeStatus,
storeNodes2RuntimeNodes
} from '@fastgpt/global/core/workflow/runtime/utils';
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
import { updateToolInputValue } from '../agent/runTool/utils';
import { authAppByTmbId } from '../../../../support/permission/app/auth';
import { authPluginByTmbId } from '../../../../support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
import { computedPluginUsage } from '../../../app/plugin/utils';

type RunPluginProps = ModuleDispatchProps<{
[key: string]: any;
Expand All @@ -33,14 +33,12 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
}

// auth plugin
const { source } = await splitCombinePluginId(pluginId);
if (source === PluginSourceEnum.personal) {
await authAppByTmbId({
appId: pluginId,
tmbId: workflowApp.tmbId,
per: ReadPermissionVal
});
}
await authPluginByTmbId({
appId: pluginId,
tmbId: workflowApp.tmbId,
per: ReadPermissionVal
});

const plugin = await getPluginRuntimeById(pluginId);

// concat dynamic inputs
Expand Down Expand Up @@ -78,12 +76,14 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
output.moduleLogo = plugin.avatar;
}

const usagePoints = await computedPluginUsage(plugin, flowUsages);

return {
assistantResponses,
// responseData, // debug
[DispatchNodeResponseKeyEnum.nodeResponse]: {
moduleLogo: plugin.avatar,
totalPoints: flowResponses.reduce((sum, item) => sum + (item.totalPoints || 0), 0),
totalPoints: usagePoints,
pluginOutput: output?.pluginOutput,
pluginDetail:
mode === 'test' && plugin.teamId === teamId
Expand All @@ -96,8 +96,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
{
moduleName: plugin.name,
totalPoints: flowUsages.reduce((sum, item) => sum + (item.totalPoints || 0), 0),
model: plugin.name,
totalPoints: usagePoints,
tokens: 0
}
],
Expand Down
4 changes: 2 additions & 2 deletions packages/service/core/workflow/dispatch/tools/http468.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/ty
import { getErrText } from '@fastgpt/global/common/error/utils';
import { responseWrite } from '../../../../common/response';
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
import { getCommunityCb } from '@fastgpt/plugins/register';
import { getSystemPluginCb } from '@fastgpt/plugins/register';

type PropsArrType = {
key: string;
Expand Down Expand Up @@ -121,7 +121,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H

try {
const { formatResponse, rawResponse } = await (async () => {
const communityPluginCb = await getCommunityCb();
const communityPluginCb = await getSystemPluginCb();
if (communityPluginCb[httpReqUrl]) {
const pluginResult = await communityPluginCb[httpReqUrl](requestBody);
return {
Expand Down
21 changes: 21 additions & 0 deletions packages/service/support/permission/app/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,27 @@ import { AuthResponseType } from '../type/auth.d';
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
import { AppFolderTypeList } from '@fastgpt/global/core/app/constants';
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { splitCombinePluginId } from '../../../core/app/plugin/controller';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';

export const authPluginByTmbId = async ({
tmbId,
appId,
per
}: {
tmbId: string;
appId: string;
per: PermissionValueType;
}) => {
const { source } = await splitCombinePluginId(appId);
if (source === PluginSourceEnum.personal) {
await authAppByTmbId({
appId,
tmbId,
per
});
}
};

export const authAppByTmbId = async ({
tmbId,
Expand Down
22 changes: 22 additions & 0 deletions packages/web/components/common/Avatar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { Image } from '@chakra-ui/react';
import type { ImageProps } from '@chakra-ui/react';
import { LOGO_ICON } from '@fastgpt/global/common/system/constants';

const Avatar = ({ w = '30px', src, ...props }: ImageProps) => {
return (
<Image
fallbackSrc={LOGO_ICON}
fallbackStrategy={'onError'}
// borderRadius={'md'}
objectFit={'contain'}
alt=""
w={w}
h={w}
src={src || LOGO_ICON}
{...props}
/>
);
};

export default Avatar;
Loading

0 comments on commit e80dcb8

Please sign in to comment.