mirror of
https://github.com/labring/FastGPT.git
synced 2025-12-25 20:02:47 +00:00
4.8-fix (#1305)
Some checks failed
Build FastGPT images in Personal warehouse / build-fastgpt-images (push) Waiting to run
Deploy image by kubeconfig / build-fastgpt-docs-images (push) Has been cancelled
Deploy image to vercel / deploy-production (push) Has been cancelled
Deploy image by kubeconfig / update-docs-image (push) Has been cancelled
Some checks failed
Build FastGPT images in Personal warehouse / build-fastgpt-images (push) Waiting to run
Deploy image by kubeconfig / build-fastgpt-docs-images (push) Has been cancelled
Deploy image to vercel / deploy-production (push) Has been cancelled
Deploy image by kubeconfig / update-docs-image (push) Has been cancelled
* fix if-else find variables (#92) * fix if-else find variables * change workflow output type * fix tooltip style * fix * 4.8 (#93) * api middleware * perf: app version histories * faq * perf: value type show * fix: ts * fix: Run the same node multiple times * feat: auto save workflow * perf: auto save workflow --------- Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
parent
c8412e7dc9
commit
d407e87dd9
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
// Place your FastGPT 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
|
||||
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
|
||||
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
|
||||
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
||||
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
|
||||
// Placeholders with the same ids are connected.
|
||||
// Example:
|
||||
"Next api template": {
|
||||
"scope": "javascript,typescript",
|
||||
"prefix": "nextapi",
|
||||
"body": [
|
||||
"import type { NextApiRequest, NextApiResponse } from 'next';",
|
||||
"import { NextAPI } from '@/service/middle/entry';",
|
||||
"",
|
||||
"type Props = {};",
|
||||
"",
|
||||
"type Response = {};",
|
||||
"",
|
||||
"async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<{}> {",
|
||||
" $1",
|
||||
" return {}",
|
||||
"}",
|
||||
"",
|
||||
"export default NextAPI(handler);"
|
||||
],
|
||||
"description": "FastGPT Next API template"
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,10 @@ images: []
|
|||
|
||||
1. `docker ps -a` 查看所有容器运行状态,检查是否全部 running,如有异常,尝试`docker logs 容器名`查看对应日志。
|
||||
2. 容器都运行正常的,`docker logs 容器名` 查看报错日志
|
||||
3. 无法解决时,可以找找[Issue](https://github.com/labring/FastGPT/issues),或新提 Issue,私有部署错误,务必提供详细的日志,否则很难排查。
|
||||
3. 带有`requestId`的,都是 OneAPI 提示错误,大部分都是因为模型接口报错。
|
||||
4. 无法解决时,可以找找[Issue](https://github.com/labring/FastGPT/issues),或新提 Issue,私有部署错误,务必提供详细的日志,否则很难排查。
|
||||
|
||||
|
||||
|
||||
|
||||
## 二、通用问题
|
||||
|
|
@ -90,4 +93,9 @@ FastGPT 模型配置文件中的 model 必须与 OneAPI 渠道中的模型对应
|
|||
|
||||
OneAPI 的 API Key 配置错误,需要修改`OPENAI_API_KEY`环境变量,并重启容器(先 docker-compose down 然后再 docker-compose up -d 运行一次)。
|
||||
|
||||
可以`exec`进入容器,`env`查看环境变量是否生效。
|
||||
可以`exec`进入容器,`env`查看环境变量是否生效。
|
||||
|
||||
### bad_response_status_code bad response status code 503
|
||||
|
||||
1. 模型服务不可用
|
||||
2. ....
|
||||
|
|
@ -4,6 +4,7 @@ import cronParser from 'cron-parser';
|
|||
export const formatTime2YMDHM = (time?: Date) =>
|
||||
time ? dayjs(time).format('YYYY-MM-DD HH:mm') : '';
|
||||
export const formatTime2YMD = (time?: Date) => (time ? dayjs(time).format('YYYY-MM-DD') : '');
|
||||
export const formatTime2HM = (time: Date = new Date()) => dayjs(time).format('HH:mm');
|
||||
|
||||
/* cron time parse */
|
||||
export const cronParser2Fields = (cronString: string) => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { VariableInputEnum } from '../workflow/constants';
|
|||
import { SelectedDatasetType } from '../workflow/api';
|
||||
import { DatasetSearchModeEnum } from '../dataset/constants';
|
||||
import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d';
|
||||
import { StoreEdgeItemType } from 'core/workflow/type/edge';
|
||||
import { StoreEdgeItemType } from '../workflow/type/edge';
|
||||
|
||||
export interface AppSchema {
|
||||
_id: string;
|
||||
|
|
@ -18,6 +18,7 @@ export interface AppSchema {
|
|||
avatar: string;
|
||||
intro: string;
|
||||
updateTime: number;
|
||||
|
||||
modules: StoreNodeItemType[];
|
||||
edges: StoreEdgeItemType[];
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
import { StoreNodeItemType } from '../workflow/type';
|
||||
import { StoreEdgeItemType } from '../workflow/type/edge';
|
||||
|
||||
export type AppVersionSchemaType = {
|
||||
appId: string;
|
||||
time: Date;
|
||||
nodes: StoreNodeItemType[];
|
||||
edges: StoreEdgeItemType[];
|
||||
};
|
||||
|
|
@ -14,18 +14,21 @@ export enum WorkflowIOValueTypeEnum {
|
|||
string = 'string',
|
||||
number = 'number',
|
||||
boolean = 'boolean',
|
||||
object = 'object',
|
||||
arrayString = 'arrayString',
|
||||
arrayNumber = 'arrayNumber',
|
||||
arrayBoolean = 'arrayBoolean',
|
||||
arrayObject = 'arrayObject',
|
||||
any = 'any',
|
||||
|
||||
chatHistory = 'chatHistory',
|
||||
datasetQuote = 'datasetQuote',
|
||||
|
||||
dynamic = 'dynamic',
|
||||
|
||||
// plugin special type
|
||||
selectApp = 'selectApp',
|
||||
selectDataset = 'selectDataset',
|
||||
|
||||
// tool
|
||||
tools = 'tools'
|
||||
selectDataset = 'selectDataset'
|
||||
}
|
||||
|
||||
/* reg: modulename key */
|
||||
|
|
@ -173,3 +176,5 @@ export enum RuntimeEdgeStatusEnum {
|
|||
'active' = 'active',
|
||||
'skipped' = 'skipped'
|
||||
}
|
||||
|
||||
export const VARIABLE_NODE_ID = 'VARIABLE_NODE_ID';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { FlowNodeTypeEnum } from '../node/constant';
|
|||
import { StoreNodeItemType } from '../type';
|
||||
import { StoreEdgeItemType } from '../type/edge';
|
||||
import { RuntimeEdgeItemType, RuntimeNodeItemType } from './type';
|
||||
import { VARIABLE_NODE_ID } from '../../../../../projects/app/src/web/core/workflow/constants/index';
|
||||
import { VARIABLE_NODE_ID } from '../constants';
|
||||
|
||||
export const initWorkflowEdgeStatus = (edges: StoreEdgeItemType[]): RuntimeEdgeItemType[] => {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -10,16 +10,26 @@ import {
|
|||
NodeOutputKeyEnum,
|
||||
FlowNodeTemplateTypeEnum
|
||||
} from '../../constants';
|
||||
import { Input_Template_Dataset_Quote } from '../input';
|
||||
import { getNanoid } from '../../../../common/string/tools';
|
||||
import { getHandleConfig } from '../utils';
|
||||
import { FlowNodeInputItemType } from '../../type/io.d';
|
||||
|
||||
export const getOneQuoteInputTemplate = (key = getNanoid()): FlowNodeInputItemType => ({
|
||||
...Input_Template_Dataset_Quote,
|
||||
const defaultQuoteKey = 'defaultQuoteKey';
|
||||
|
||||
export const getOneQuoteInputTemplate = ({
|
||||
key = getNanoid(),
|
||||
index
|
||||
}: {
|
||||
key?: string;
|
||||
index: number;
|
||||
}): FlowNodeInputItemType => ({
|
||||
key,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.custom],
|
||||
description: ''
|
||||
renderTypeList: [FlowNodeInputTypeEnum.reference],
|
||||
label: `引用${index}`,
|
||||
debugLabel: '知识库引用',
|
||||
canEdit: key !== defaultQuoteKey,
|
||||
description: '',
|
||||
valueType: WorkflowIOValueTypeEnum.datasetQuote
|
||||
});
|
||||
|
||||
export const DatasetConcatModule: FlowNodeTemplateType = {
|
||||
|
|
@ -37,7 +47,7 @@ export const DatasetConcatModule: FlowNodeTemplateType = {
|
|||
key: NodeInputKeyEnum.datasetMaxTokens,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.custom],
|
||||
label: '最大 Tokens',
|
||||
value: 1500,
|
||||
value: 3000,
|
||||
valueType: WorkflowIOValueTypeEnum.number
|
||||
},
|
||||
{
|
||||
|
|
@ -45,7 +55,7 @@ export const DatasetConcatModule: FlowNodeTemplateType = {
|
|||
renderTypeList: [FlowNodeInputTypeEnum.custom],
|
||||
label: ''
|
||||
},
|
||||
getOneQuoteInputTemplate()
|
||||
getOneQuoteInputTemplate({ key: defaultQuoteKey, index: 1 })
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,19 +1,12 @@
|
|||
import type { NextApiResponse, NextApiHandler, NextApiRequest } from 'next';
|
||||
import type { NextApiResponse, NextApiRequest } from 'next';
|
||||
import NextCors from 'nextjs-cors';
|
||||
|
||||
export function withNextCors(handler: NextApiHandler): NextApiHandler {
|
||||
return async function nextApiHandlerWrappedWithNextCors(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
const methods = ['GET', 'eHEAD', 'PUT', 'PATCH', 'POST', 'DELETE'];
|
||||
const origin = req.headers.origin;
|
||||
await NextCors(req, res, {
|
||||
methods,
|
||||
origin: origin,
|
||||
optionsSuccessStatus: 200
|
||||
});
|
||||
|
||||
return handler(req, res);
|
||||
};
|
||||
export async function withNextCors(req: NextApiRequest, res: NextApiResponse) {
|
||||
const methods = ['GET', 'eHEAD', 'PUT', 'PATCH', 'POST', 'DELETE'];
|
||||
const origin = req.headers.origin;
|
||||
await NextCors(req, res, {
|
||||
methods,
|
||||
origin: origin,
|
||||
optionsSuccessStatus: 200
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,10 @@ export const jsonRes = <T = any>(
|
|||
message?: string;
|
||||
data?: T;
|
||||
error?: any;
|
||||
url?: string;
|
||||
}
|
||||
) => {
|
||||
const { code = 200, message = '', data = null, error } = props || {};
|
||||
const { code = 200, message = '', data = null, error, url } = props || {};
|
||||
|
||||
const errResponseKey = typeof error === 'string' ? error : error?.message;
|
||||
// Specified error
|
||||
|
|
@ -47,7 +48,7 @@ export const jsonRes = <T = any>(
|
|||
msg = error?.error?.message;
|
||||
}
|
||||
|
||||
addLog.error(`response error: ${msg}`, error);
|
||||
addLog.error(`Api response error: ${url}, ${msg}`, error);
|
||||
}
|
||||
|
||||
res.status(code).json({
|
||||
|
|
|
|||
|
|
@ -169,7 +169,16 @@ class PgClass {
|
|||
}
|
||||
async query<T extends QueryResultRow = any>(sql: string) {
|
||||
const pg = await connectPg();
|
||||
return pg.query<T>(sql);
|
||||
const start = Date.now();
|
||||
return pg.query<T>(sql).then((res) => {
|
||||
const time = Date.now() - start;
|
||||
|
||||
if (time > 300) {
|
||||
addLog.warn(`pg query time: ${time}ms, sql: ${sql}`);
|
||||
}
|
||||
|
||||
return res;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
import { AppSchema } from '@fastgpt/global/core/app/type';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { getLLMModel } from '../ai/model';
|
||||
import { MongoAppVersion } from './versionSchema';
|
||||
|
||||
export const beforeUpdateAppFormat = <T extends AppSchema['modules'] | undefined>({
|
||||
nodes
|
||||
}: {
|
||||
nodes: T;
|
||||
}) => {
|
||||
if (nodes) {
|
||||
let maxTokens = 3000;
|
||||
|
||||
nodes.forEach((item) => {
|
||||
if (
|
||||
item.flowNodeType === FlowNodeTypeEnum.chatNode ||
|
||||
item.flowNodeType === FlowNodeTypeEnum.tools
|
||||
) {
|
||||
const model =
|
||||
item.inputs.find((item) => item.key === NodeInputKeyEnum.aiModel)?.value || '';
|
||||
const chatModel = getLLMModel(model);
|
||||
const quoteMaxToken = chatModel.quoteMaxToken || 3000;
|
||||
|
||||
maxTokens = Math.max(maxTokens, quoteMaxToken);
|
||||
}
|
||||
});
|
||||
|
||||
nodes.forEach((item) => {
|
||||
if (item.flowNodeType === FlowNodeTypeEnum.datasetSearchNode) {
|
||||
item.inputs.forEach((input) => {
|
||||
if (input.key === NodeInputKeyEnum.datasetMaxTokens) {
|
||||
const val = input.value as number;
|
||||
if (val > maxTokens) {
|
||||
input.value = maxTokens;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
nodes
|
||||
};
|
||||
};
|
||||
|
||||
export const getAppLatestVersion = async (appId: string, app?: AppSchema) => {
|
||||
const version = await MongoAppVersion.findOne({
|
||||
appId
|
||||
}).sort({
|
||||
time: -1
|
||||
});
|
||||
|
||||
if (version) {
|
||||
return {
|
||||
nodes: version.nodes,
|
||||
edges: version.edges
|
||||
};
|
||||
}
|
||||
return {
|
||||
nodes: app?.modules || [],
|
||||
edges: app?.edges || []
|
||||
};
|
||||
};
|
||||
|
|
@ -8,7 +8,7 @@ import {
|
|||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
|
||||
export const appCollectionName = 'apps';
|
||||
export const AppCollectionName = 'apps';
|
||||
|
||||
const AppSchema = new Schema({
|
||||
teamId: {
|
||||
|
|
@ -46,6 +46,8 @@ const AppSchema = new Schema({
|
|||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
|
||||
// tmp store
|
||||
modules: {
|
||||
type: Array,
|
||||
default: []
|
||||
|
|
@ -92,6 +94,6 @@ try {
|
|||
}
|
||||
|
||||
export const MongoApp: Model<AppType> =
|
||||
models[appCollectionName] || model(appCollectionName, AppSchema);
|
||||
models[AppCollectionName] || model(AppCollectionName, AppSchema);
|
||||
|
||||
MongoApp.syncIndexes();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
import { connectionMongo, type Model } from '../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
|
||||
|
||||
export const AppVersionCollectionName = 'app.versions';
|
||||
|
||||
const AppVersionSchema = new Schema({
|
||||
appId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: AppVersionCollectionName,
|
||||
required: true
|
||||
},
|
||||
time: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
nodes: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
edges: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
AppVersionSchema.index({ appId: 1, time: -1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoAppVersion: Model<AppVersionSchemaType> =
|
||||
models[AppVersionCollectionName] || model(AppVersionCollectionName, AppVersionSchema);
|
||||
|
||||
MongoAppVersion.syncIndexes();
|
||||
|
|
@ -7,7 +7,7 @@ import {
|
|||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import { appCollectionName } from '../app/schema';
|
||||
import { AppCollectionName } from '../app/schema';
|
||||
import { userCollectionName } from '../../support/user/schema';
|
||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
|
|
@ -40,7 +40,7 @@ const ChatItemSchema = new Schema({
|
|||
},
|
||||
appId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: appCollectionName,
|
||||
ref: AppCollectionName,
|
||||
required: true
|
||||
},
|
||||
time: {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import { appCollectionName } from '../app/schema';
|
||||
import { AppCollectionName } from '../app/schema';
|
||||
|
||||
export const chatCollectionName = 'chat';
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ const ChatSchema = new Schema({
|
|||
},
|
||||
appId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: appCollectionName,
|
||||
ref: AppCollectionName,
|
||||
required: true
|
||||
},
|
||||
updateTime: {
|
||||
|
|
|
|||
|
|
@ -220,9 +220,16 @@ export async function dispatchWorkFlow({
|
|||
).then((result) => {
|
||||
const flat = result.flat();
|
||||
if (flat.length === 0) return;
|
||||
// update output
|
||||
|
||||
// Update the node output at the end of the run and get the next nodes
|
||||
const nextNodes = flat.map((item) => nodeOutput(item.node, item.result)).flat();
|
||||
return checkNodeCanRun(nextNodes);
|
||||
|
||||
// Remove repeat nodes(Make sure that the node is only executed once)
|
||||
const filterNextNodes = nextNodes.filter(
|
||||
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
|
||||
);
|
||||
|
||||
return checkNodeCanRun(filterNextNodes);
|
||||
});
|
||||
}
|
||||
// 运行完一轮后,清除连线的状态,避免污染进程
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
} from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
||||
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/type';
|
||||
import { getHandleId } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.condition]: IfElseConditionType;
|
||||
|
|
@ -20,20 +21,21 @@ function checkCondition(condition: VariableConditionEnum, variableValue: any, va
|
|||
[VariableConditionEnum.isNotEmpty]: () => !!variableValue,
|
||||
[VariableConditionEnum.equalTo]: () => variableValue === value,
|
||||
[VariableConditionEnum.notEqual]: () => variableValue !== value,
|
||||
[VariableConditionEnum.greaterThan]: () => variableValue > Number(value),
|
||||
[VariableConditionEnum.lessThan]: () => variableValue < Number(value),
|
||||
[VariableConditionEnum.greaterThanOrEqualTo]: () => variableValue >= Number(value),
|
||||
[VariableConditionEnum.lessThanOrEqualTo]: () => variableValue <= Number(value),
|
||||
[VariableConditionEnum.include]: () => variableValue.includes(value),
|
||||
[VariableConditionEnum.notInclude]: () => !variableValue.includes(value),
|
||||
[VariableConditionEnum.startWith]: () => variableValue.startsWith(value),
|
||||
[VariableConditionEnum.endWith]: () => variableValue.endsWith(value),
|
||||
[VariableConditionEnum.lengthEqualTo]: () => variableValue.length === Number(value),
|
||||
[VariableConditionEnum.lengthNotEqualTo]: () => variableValue.length !== Number(value),
|
||||
[VariableConditionEnum.lengthGreaterThan]: () => variableValue.length > Number(value),
|
||||
[VariableConditionEnum.lengthGreaterThanOrEqualTo]: () => variableValue.length >= Number(value),
|
||||
[VariableConditionEnum.lengthLessThan]: () => variableValue.length < Number(value),
|
||||
[VariableConditionEnum.lengthLessThanOrEqualTo]: () => variableValue.length <= Number(value)
|
||||
[VariableConditionEnum.greaterThan]: () => Number(variableValue) > Number(value),
|
||||
[VariableConditionEnum.lessThan]: () => Number(variableValue) < Number(value),
|
||||
[VariableConditionEnum.greaterThanOrEqualTo]: () => Number(variableValue) >= Number(value),
|
||||
[VariableConditionEnum.lessThanOrEqualTo]: () => Number(variableValue) <= Number(value),
|
||||
[VariableConditionEnum.include]: () => variableValue?.includes(value),
|
||||
[VariableConditionEnum.notInclude]: () => !variableValue?.includes(value),
|
||||
[VariableConditionEnum.startWith]: () => variableValue?.startsWith(value),
|
||||
[VariableConditionEnum.endWith]: () => variableValue?.endsWith(value),
|
||||
[VariableConditionEnum.lengthEqualTo]: () => variableValue?.length === Number(value),
|
||||
[VariableConditionEnum.lengthNotEqualTo]: () => variableValue?.length !== Number(value),
|
||||
[VariableConditionEnum.lengthGreaterThan]: () => variableValue?.length > Number(value),
|
||||
[VariableConditionEnum.lengthGreaterThanOrEqualTo]: () =>
|
||||
variableValue?.length >= Number(value),
|
||||
[VariableConditionEnum.lengthLessThan]: () => variableValue?.length < Number(value),
|
||||
[VariableConditionEnum.lengthLessThanOrEqualTo]: () => variableValue?.length <= Number(value)
|
||||
};
|
||||
|
||||
return (operations[condition] || (() => false))();
|
||||
|
|
@ -43,15 +45,18 @@ export const dispatchIfElse = async (props: Props): Promise<DispatchNodeResultTy
|
|||
const {
|
||||
params,
|
||||
runtimeNodes,
|
||||
variables,
|
||||
node: { nodeId }
|
||||
} = props;
|
||||
const { condition, ifElseList } = params;
|
||||
const listResult = ifElseList.map((item) => {
|
||||
const { variable, condition: variableCondition, value } = item;
|
||||
|
||||
const variableValue = runtimeNodes
|
||||
.find((node) => node.nodeId === variable[0])
|
||||
?.outputs?.find((item) => item.id === variable[1])?.value;
|
||||
const variableValue = getReferenceVariableValue({
|
||||
value: variable,
|
||||
variables,
|
||||
nodes: runtimeNodes
|
||||
});
|
||||
|
||||
return checkCondition(variableCondition as VariableConditionEnum, variableValue, value || '');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import { appCollectionName } from '../../core/app/schema';
|
||||
import { AppCollectionName } from '../../core/app/schema';
|
||||
|
||||
const OutLinkSchema = new Schema({
|
||||
shareId: {
|
||||
|
|
@ -25,7 +25,7 @@ const OutLinkSchema = new Schema({
|
|||
},
|
||||
appId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: appCollectionName,
|
||||
ref: AppCollectionName,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
|
|
|
|||
|
|
@ -30,12 +30,10 @@ export async function authApp({
|
|||
// get app
|
||||
const app = await MongoApp.findOne({ _id: appId, teamId }).lean();
|
||||
if (!app) {
|
||||
return Promise.reject(AppErrEnum.unAuthApp);
|
||||
return Promise.reject(AppErrEnum.unExist);
|
||||
}
|
||||
|
||||
const isOwner =
|
||||
role !== TeamMemberRoleEnum.visitor &&
|
||||
(String(app.tmbId) === tmbId || role === TeamMemberRoleEnum.owner);
|
||||
const isOwner = String(app.tmbId) === tmbId;
|
||||
const canWrite =
|
||||
isOwner ||
|
||||
(app.permission === PermissionTypeEnum.public && role !== TeamMemberRoleEnum.visitor);
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ export const iconPaths = {
|
|||
'common/paramsLight': () => import('./icons/common/paramsLight.svg'),
|
||||
'common/playFill': () => import('./icons/common/playFill.svg'),
|
||||
'common/playLight': () => import('./icons/common/playLight.svg'),
|
||||
'common/publishFill': () => import('./icons/common/publishFill.svg'),
|
||||
'common/questionLight': () => import('./icons/common/questionLight.svg'),
|
||||
'common/refreshLight': () => import('./icons/common/refreshLight.svg'),
|
||||
'common/resultLight': () => import('./icons/common/resultLight.svg'),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
<svg t="1714186779322" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1213"
|
||||
width="128" height="128">
|
||||
<path
|
||||
d="M957.78379975 134.65795581c-2.56331135 83.17185735-20.44423515 163.12467052-50.30317593 240.57510884-35.69958418 92.67796903-87.96862781 174.24030494-161.20251807 241.63355181-5.94396957 5.46839892-7.80783541 11.32361235-7.01830818 19.026796 2.5328431 24.92705029 4.5596486 49.91503711 7.12295994 74.84208611 5.9598665 58.12425835-14.32010468 105.71575589-61.01609761 139.47861502-50.06340414 36.20694738-102.1695094 69.6412786-153.84243498 103.55515469-32.57194606 21.38212813-72.60730269 1.98176462-76.2277327-36.80306673-4.17150847-44.53539301-7.45016355-89.16219079-10.5340864-133.78633995-1.53533793-22.33459502-0.93921859-22.52932723-22.48428636-25.38937392-57.18636407-7.56938794-101.94563325-34.8213021-132.68418171-84.27401429-15.81040175-25.46488176-23.6473811-53.61097493-26.40410034-83.18642866-0.81866989-8.86495278-4.3808133-11.13020574-12.70925895-11.59252939-46.28003447-2.57788397-92.52960201-5.3332789-138.74869996-8.8199132-38.44173211-2.92098321-57.93085045-43.61339568-37.32367695-77.56966271 14.4671474-23.86993165 29.87483771-47.17288918 44.81888136-70.76065808 16.1071371-25.38937393 32.07915418-50.85425569 48.21675953-76.21316007 29.53173846-46.41383019 72.1012638-71.48924752 126.5905208-71.93699984 31.2008721-0.23844748 62.46003078 5.27366671 93.64633157 8.64107663 5.89892998 0.64115892 10.0863341-0.16426396 14.16908774-4.73848387 72.39799786-81.24970362 162.00661664-136.08206117 263.446211-172.76457792 57.33473241-20.74096921 116.33859714-34.28479495 176.89237242-40.54271983 30.51467231-3.17400331 61.23864946-4.32120112 91.72285352 1.34060572 16.61450031 3.08392285 17.46363973 3.77012135 20.02562678 20.05609502C956.4869093 101.71509088 958.51238919 118.07524806 957.78379975 134.65795581L957.78379975 134.65795581zM813.20902898 339.81696881c0.87828208-71.05871775-57.73744384-132.1635715-127.410515-133.309445-72.35428259-1.20680999-135.02361821 51.61331338-136.73779142 134.08439962-1.44525748 68.77756914 60.61338619 131.07466028 129.88374722 131.55155523C752.69897026 472.67866149 812.29895435 413.94371248 813.20902898 339.81696881L813.20902898 339.81696881zM195.18722288 640.09538918c13.69219278 0.17883529 21.29204896 6.24335355 26.19479682 20.29321819 19.25067215 55.04033549 54.28127779 96.80443794 105.06002435 125.11479508 11.20438926 6.24335355 23.0207935 11.30904103 35.22401354 15.48187512 21.93320788 7.50845143 26.93928317 28.96476307 10.66788212 45.47328728-24.10705481 24.43558273-48.46977833 48.60357404-72.56226181 73.02590976-7.97209938 8.07542683-16.74829602 11.90383476-27.95268527 7.71775497-11.38322454-4.2470176-16.88209172-12.72383026-17.46363974-24.55480712-0.35767186-7.49520313-0.17883529-15.03544713-0.35767188-22.55846986-0.31263099-13.81274148-0.44775102-13.91739325-13.11197036-9.71541653-27.08765021 9.01464541-54.11701383 18.10347435-81.20466405 27.10354714-7.9866707 2.66796441-16.00248532 3.77012135-23.81032074-0.67030285-11.44416105-6.51226928-15.76403785-18.14851392-11.160674-32.46729429 8.99874849-27.93811395 18.43200097-55.71196266 27.64005428-83.57456879 3.99267318-12.09856827 3.7396531-12.35158706-9.26766421-12.55956629-7.83830367-0.11922438-15.70442566 0.01457133-23.54140371-0.37224318-11.4136928-0.55107846-19.5341592-6.09498652-23.75070856-16.94170392-4.14104022-10.56455466-1.49029705-19.77260668 6.19831398-27.54997384 25.15092515-25.40526957 50.42240031-50.68999172 75.6620816-76.00518086C182.68726879 642.31560258 188.64713398 639.45423026 195.18722288 640.09538918L195.18722288 640.09538918z"
|
||||
p-id="1214"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -54,21 +54,20 @@ const MultipleRowSelect = ({
|
|||
bg: 'primary.50',
|
||||
color: 'primary.600'
|
||||
}}
|
||||
onClick={() => {
|
||||
const newValue = [...cloneValue];
|
||||
newValue[index] = item.value;
|
||||
setCloneValue(newValue);
|
||||
if (!hasChildren) {
|
||||
onSelect(newValue);
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
{...(item.value === selectedValue
|
||||
? {
|
||||
color: 'primary.600'
|
||||
}
|
||||
: {
|
||||
onClick: () => {
|
||||
const newValue = [...cloneValue];
|
||||
newValue[index] = item.value;
|
||||
setCloneValue(newValue);
|
||||
if (!hasChildren) {
|
||||
onSelect(newValue);
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
})}
|
||||
: {})}
|
||||
>
|
||||
{item.label}
|
||||
</Flex>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const MyTooltip = ({ children, forceShow = false, shouldWrapChildren = true, ...
|
|||
<Tooltip
|
||||
className="tooltip"
|
||||
bg={'white'}
|
||||
arrowShadowColor={' rgba(0,0,0,0.05)'}
|
||||
arrowShadowColor={'rgba(0,0,0,0.05)'}
|
||||
hasArrow
|
||||
arrowSize={12}
|
||||
offset={[-15, 15]}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
import { useTranslation } from 'next-i18next';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useBeforeunload = (props?: { callback?: () => any; tip?: string }) => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const { tip = t('Confirm to leave the page'), callback } = props || {};
|
||||
|
||||
useEffect(() => {
|
||||
const listen =
|
||||
process.env.NODE_ENV !== 'production'
|
||||
? (e: any) => {
|
||||
e.preventDefault();
|
||||
e.returnValue = tip;
|
||||
callback?.();
|
||||
}
|
||||
: () => {};
|
||||
window.addEventListener('beforeunload', listen);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', listen);
|
||||
};
|
||||
}, [tip, callback]);
|
||||
};
|
||||
|
|
@ -59,7 +59,7 @@ export const useConfirm = (props?: {
|
|||
onClose,
|
||||
ConfirmModal: useCallback(
|
||||
({
|
||||
closeText = t('common.Close'),
|
||||
closeText = t('common.Cancel'),
|
||||
confirmText = t('common.Confirm'),
|
||||
isLoading,
|
||||
bg,
|
||||
|
|
|
|||
|
|
@ -356,9 +356,6 @@ importers:
|
|||
'@fortaine/fetch-event-source':
|
||||
specifier: ^3.0.6
|
||||
version: 3.0.6
|
||||
'@node-rs/jieba':
|
||||
specifier: 1.10.0
|
||||
version: 1.10.0
|
||||
'@tanstack/react-query':
|
||||
specifier: ^4.24.10
|
||||
version: 4.24.10(react-dom@18.2.0)(react@18.2.0)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ const nextConfig = {
|
|||
if (isServer) {
|
||||
config.externals.push('isolated-vm');
|
||||
config.externals.push('worker_threads');
|
||||
config.externals.push('@node-rs/jieba');
|
||||
|
||||
if (config.name === 'server') {
|
||||
// config.output.globalObject = 'self';
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
"@fastgpt/service": "workspace:*",
|
||||
"@fastgpt/web": "workspace:*",
|
||||
"@fortaine/fetch-event-source": "^3.0.6",
|
||||
"@node-rs/jieba": "1.10.0",
|
||||
"@tanstack/react-query": "^4.24.10",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"axios": "^1.5.1",
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@
|
|||
"Confirm Import": "Import",
|
||||
"Confirm Move": "Move here",
|
||||
"Confirm Update": "Update",
|
||||
"Confirm to leave the page": "Are you sure to leave this page?",
|
||||
"Copy": "Copy",
|
||||
"Copy Successful": "Copy Successful",
|
||||
"Course": "",
|
||||
|
|
@ -278,6 +279,7 @@
|
|||
"Api request desc": "Access to the existing system through API, or enterprise micro, flying book, etc",
|
||||
"App intro": "App intro",
|
||||
"App params config": "App Config",
|
||||
"Auto Save time": "Auto-saved: {{time}}",
|
||||
"Chat Variable": "",
|
||||
"Config schedule plan": "Config schedule config",
|
||||
"Config whisper": "Config whisper",
|
||||
|
|
@ -289,6 +291,11 @@
|
|||
"Max histories": "Dialog round",
|
||||
"Max tokens": "Max tokens",
|
||||
"Name and avatar": "Avatar & Name",
|
||||
"Onclick to save": "Save",
|
||||
"Publish": "Publish",
|
||||
"Publish Confirm": "Sure to release the app? App status is immediately updated across all publishing channels.",
|
||||
"Publish Failed": "Publish error",
|
||||
"Publish Success": "Publish Success",
|
||||
"Question Guide": "Question Guide",
|
||||
"Question Guide Tip": "At the end of the conversation, three leading questions will be asked.",
|
||||
"Quote prompt": "Quote prompt",
|
||||
|
|
@ -1035,11 +1042,16 @@
|
|||
},
|
||||
"valueType": {
|
||||
"any": "Any",
|
||||
"arrayBoolean": "Array Boolean",
|
||||
"arrayNumber": "Array Number",
|
||||
"arrayObject": "Array Object",
|
||||
"arrayString": "Array String",
|
||||
"boolean": "Boolean",
|
||||
"chatHistory": "History",
|
||||
"datasetQuote": "Dataset Quote",
|
||||
"dynamicTargetInput": "Dynamic Input",
|
||||
"number": "Number",
|
||||
"object": "Object",
|
||||
"selectApp": "Select App",
|
||||
"selectDataset": "Select Dataset",
|
||||
"string": "String",
|
||||
|
|
@ -1128,6 +1140,7 @@
|
|||
"textarea": "Textarea"
|
||||
},
|
||||
"tool": {
|
||||
"Handle": "Tool handle",
|
||||
"Select Tool": "Select Tool"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@
|
|||
"Confirm Import": "确认导入",
|
||||
"Confirm Move": "移动到这",
|
||||
"Confirm Update": "确认更新",
|
||||
"Confirm to leave the page": "确认离开该页面?",
|
||||
"Copy": "复制",
|
||||
"Copy Successful": "复制成功",
|
||||
"Course": "",
|
||||
|
|
@ -278,6 +279,7 @@
|
|||
"Api request desc": "通过 API 接入到已有系统中,或企微、飞书等",
|
||||
"App intro": "应用介绍",
|
||||
"App params config": "应用配置",
|
||||
"Auto Save time": "自动保存: {{time}}",
|
||||
"Chat Variable": "对话框变量",
|
||||
"Config schedule plan": "配置定时执行",
|
||||
"Config whisper": "配置语音输入",
|
||||
|
|
@ -289,6 +291,11 @@
|
|||
"Max histories": "聊天记录数量",
|
||||
"Max tokens": "回复上限",
|
||||
"Name and avatar": "头像 & 名称",
|
||||
"Onclick to save": "点击保存",
|
||||
"Publish": "发布",
|
||||
"Publish Confirm": "确认发布应用?会立即更新所有发布渠道的应用状态。",
|
||||
"Publish Failed": "发布失败",
|
||||
"Publish Success": "发布成功",
|
||||
"Question Guide": "猜你想问",
|
||||
"Question Guide Tip": "对话结束后,会为生成 3 个引导性问题。",
|
||||
"Quote prompt": "引用模板提示词",
|
||||
|
|
@ -689,7 +696,7 @@
|
|||
"Data file progress": "数据上传进度",
|
||||
"Data process params": "数据处理参数",
|
||||
"Down load csv template": "点击下载 CSV 模板",
|
||||
"Embedding Estimated Price Tips": "仅使用索引模型,消耗少量Tokens: {{price}}积分/1k Tokens",
|
||||
"Embedding Estimated Price Tips": "仅使用索引模型,消耗少量AI积分: {{price}}积分/1k Tokens",
|
||||
"Estimated Price": "预估价格: {{amount}}{{unit}}",
|
||||
"Estimated Price Tips": "QA计费为\n输入: {{charsPointsPrice}}积分/1k Tokens",
|
||||
"Estimated points": "预估消耗 {{points}} 积分",
|
||||
|
|
@ -716,7 +723,7 @@
|
|||
"Preview chunks": "预览分段(最多5段)",
|
||||
"Preview raw text": "预览源文本(最多3000字)",
|
||||
"Process way": "处理方式",
|
||||
"QA Estimated Price Tips": "需调用文件处理模型,需要消耗较多Tokens: {{price}}积分/1k Tokens",
|
||||
"QA Estimated Price Tips": "需调用文件处理模型,需要消耗较多AI积分: {{price}}积分/1k Tokens",
|
||||
"QA Import": "QA拆分",
|
||||
"QA Import Tip": "根据一定规则,将文本拆成一段较大的段落,调用 AI 为该段落生成问答对。有非常高的检索精度,但是会丢失很多内容细节。",
|
||||
"Re Preview": "重新生成预览",
|
||||
|
|
@ -1036,11 +1043,16 @@
|
|||
},
|
||||
"valueType": {
|
||||
"any": "任意",
|
||||
"arrayBoolean": "布尔数组",
|
||||
"arrayNumber": "数字数组",
|
||||
"arrayObject": "对象数组",
|
||||
"arrayString": "字符串数组",
|
||||
"boolean": "布尔",
|
||||
"chatHistory": "聊天记录",
|
||||
"datasetQuote": "引用内容",
|
||||
"chatHistory": "历史记录",
|
||||
"datasetQuote": "知识库类型",
|
||||
"dynamicTargetInput": "动态字段输入",
|
||||
"number": "数字",
|
||||
"object": "对象",
|
||||
"selectApp": "应用选择",
|
||||
"selectDataset": "知识库选择",
|
||||
"string": "字符串",
|
||||
|
|
@ -1129,6 +1141,7 @@
|
|||
"textarea": "多行输入框"
|
||||
},
|
||||
"tool": {
|
||||
"Handle": "工具连接器",
|
||||
"Select Tool": "选择工具"
|
||||
}
|
||||
}
|
||||
|
|
@ -1421,7 +1434,7 @@
|
|||
"user": {
|
||||
"AI point standard": "AI积分标准",
|
||||
"Avatar": "头像",
|
||||
"Go laf env": "点击前往 laf 获取 PAT 凭证。",
|
||||
"Go laf env": "点击前往 {{env}} 获取 PAT 凭证。",
|
||||
"Laf account course": "查看绑定 laf 账号教程。",
|
||||
"Laf account intro": "绑定你的laf账号后,你将可以在工作流中使用 laf 模块,实现在线编写代码。",
|
||||
"Need to login": "请先登录",
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import { postWorkflowDebug } from '@/web/core/workflow/api';
|
|||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { checkNodeRunStatus } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
|
||||
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
|
||||
|
||||
|
|
@ -83,7 +84,7 @@ export type useFlowProviderStoreType = {
|
|||
sourceHandle?: string | undefined;
|
||||
targetHandle?: string | undefined;
|
||||
}) => void;
|
||||
initData: (e: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }) => void;
|
||||
initData: (e: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }) => Promise<void>;
|
||||
splitToolInputs: (
|
||||
inputs: FlowNodeInputItemType[],
|
||||
nodeId: string
|
||||
|
|
@ -147,7 +148,10 @@ const StateContext = createContext<useFlowProviderStoreType>({
|
|||
hasToolNode: false,
|
||||
connectingEdge: undefined,
|
||||
basicNodeTemplates: [],
|
||||
initData: function (e: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }): void {
|
||||
initData: function (e: {
|
||||
nodes: StoreNodeItemType[];
|
||||
edges: StoreEdgeItemType[];
|
||||
}): Promise<void> {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
hoverNodeId: undefined,
|
||||
|
|
@ -608,16 +612,14 @@ export const FlowProvider = ({
|
|||
);
|
||||
|
||||
const initData = useCallback(
|
||||
(e: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }) => {
|
||||
async (e: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }) => {
|
||||
setNodes(e.nodes?.map((item) => storeNode2FlowNode({ item })));
|
||||
|
||||
setEdges(e.edges?.map((item) => storeEdgesRenderEdge({ edge: item })));
|
||||
|
||||
setTimeout(() => {
|
||||
onFixView();
|
||||
}, 100);
|
||||
await delay(200);
|
||||
},
|
||||
[setEdges, setNodes, onFixView]
|
||||
[setEdges, setNodes]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -10,13 +10,11 @@ import { SmallAddIcon } from '@chakra-ui/icons';
|
|||
import { WorkflowIOValueTypeEnum, NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { getOneQuoteInputTemplate } from '@fastgpt/global/core/workflow/template/system/datasetConcat';
|
||||
import { useFlowProviderStore } from '../FlowProvider';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import MySlider from '@/components/Slider';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io.d';
|
||||
import RenderOutput from './render/RenderOutput';
|
||||
import Reference from './render/RenderInput/templates/Reference';
|
||||
import IOTitle from '../components/IOTitle';
|
||||
|
||||
const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
|
|
@ -47,46 +45,13 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||
return maxTokens;
|
||||
}, [llmModelList, nodeList]);
|
||||
|
||||
const RenderQuoteList = useMemo(() => {
|
||||
return (
|
||||
<Box mt={-2}>
|
||||
{quotes.map((quote, i) => (
|
||||
<Box key={quote.key} _notLast={{ mb: 4 }}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box fontWeight={'medium'} color={'myGray.600'}>
|
||||
{t('core.chat.Quote')}
|
||||
{i + 1}
|
||||
</Box>
|
||||
<MyIcon
|
||||
ml={2}
|
||||
w={'14px'}
|
||||
name={'delete'}
|
||||
cursor={'pointer'}
|
||||
color={'myGray.600'}
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={() => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'delInput',
|
||||
key: quote.key
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
<Reference nodeId={nodeId} item={quote} />
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
}, [nodeId, onChangeNode, quotes, t]);
|
||||
|
||||
const onAddField = useCallback(() => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'addInput',
|
||||
value: getOneQuoteInputTemplate()
|
||||
value: getOneQuoteInputTemplate({ index: quotes.length + 1 })
|
||||
});
|
||||
}, [nodeId, onChangeNode]);
|
||||
}, [nodeId, onChangeNode, quotes.length]);
|
||||
|
||||
const CustomComponent = useMemo(() => {
|
||||
return {
|
||||
|
|
@ -141,7 +106,7 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||
<NodeCard minW={'400px'} selected={selected} {...data}>
|
||||
<Container position={'relative'}>
|
||||
<RenderInput nodeId={nodeId} flowInputList={inputs} CustomComponent={CustomComponent} />
|
||||
{RenderQuoteList}
|
||||
{/* {RenderQuoteList} */}
|
||||
</Container>
|
||||
<Container>
|
||||
<IOTitle text={t('common.Output')} />
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ const NodeIfElse = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: 'condition',
|
||||
key: NodeInputKeyEnum.condition,
|
||||
value: {
|
||||
...conditionInput,
|
||||
value: conditionInput.value === 'OR' ? 'AND' : 'OR'
|
||||
|
|
@ -297,7 +297,11 @@ const ConditionSelect = ({
|
|||
valueType === WorkflowIOValueTypeEnum.datasetQuote ||
|
||||
valueType === WorkflowIOValueTypeEnum.dynamic ||
|
||||
valueType === WorkflowIOValueTypeEnum.selectApp ||
|
||||
valueType === WorkflowIOValueTypeEnum.tools
|
||||
valueType === WorkflowIOValueTypeEnum.arrayBoolean ||
|
||||
valueType === WorkflowIOValueTypeEnum.arrayNumber ||
|
||||
valueType === WorkflowIOValueTypeEnum.arrayObject ||
|
||||
valueType === WorkflowIOValueTypeEnum.arrayString ||
|
||||
valueType === WorkflowIOValueTypeEnum.object
|
||||
)
|
||||
return arrayConditionList;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,21 +5,14 @@ import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/index.d';
|
|||
import dynamic from 'next/dynamic';
|
||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum
|
||||
} from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import Container from '../components/Container';
|
||||
import { EditInputFieldMapType, EditNodeFieldType } from '@fastgpt/global/core/workflow/node/type';
|
||||
import {
|
||||
FlowNodeInputItemType,
|
||||
FlowNodeOutputItemType
|
||||
} from '@fastgpt/global/core/workflow/type/io';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useFlowProviderStore } from '../FlowProvider';
|
||||
import RenderInput from './render/RenderInput';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
|
||||
const FieldEditModal = dynamic(() => import('./render/FieldEditModal'));
|
||||
|
||||
|
|
@ -37,7 +30,7 @@ const createEditField: EditInputFieldMapType = {
|
|||
|
||||
const NodePluginOutput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
const { nodeId, inputs, outputs } = data;
|
||||
const { nodeId, inputs } = data;
|
||||
const { onChangeNode } = useFlowProviderStore();
|
||||
|
||||
const [createField, setCreateField] = useState<EditNodeFieldType>();
|
||||
|
|
@ -93,13 +86,6 @@ const NodePluginOutput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||
canEdit: true,
|
||||
editField: createEditField
|
||||
};
|
||||
const newOutput: FlowNodeOutputItemType = {
|
||||
id: getNanoid(),
|
||||
key: data.key,
|
||||
valueType: data.valueType,
|
||||
label: data.label,
|
||||
type: FlowNodeOutputTypeEnum.static
|
||||
};
|
||||
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const NodeStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||
}}
|
||||
{...data}
|
||||
>
|
||||
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'} textAlign={'end'}>
|
||||
<Container>
|
||||
<IOTitle text={t('common.Output')} />
|
||||
<RenderOutput nodeId={nodeId} flowOutputList={outputs} />
|
||||
</Container>
|
||||
|
|
|
|||
|
|
@ -149,13 +149,6 @@ const FieldEditModal = ({
|
|||
[showDynamicInputSelect, t]
|
||||
);
|
||||
|
||||
const dataTypeSelectList = Object.values(FlowValueTypeMap)
|
||||
.slice(0, -2)
|
||||
.map((item) => ({
|
||||
label: t(item.label),
|
||||
value: item.value
|
||||
}));
|
||||
|
||||
const { register, getValues, setValue, handleSubmit, watch } = useForm<EditNodeFieldType>({
|
||||
defaultValues: {
|
||||
...defaultValue,
|
||||
|
|
@ -224,6 +217,13 @@ const FieldEditModal = ({
|
|||
return inputType === FlowNodeInputTypeEnum.addInputParam;
|
||||
}, [inputType]);
|
||||
|
||||
const slicedTypeMap = Object.values(FlowValueTypeMap).slice(0, -1);
|
||||
|
||||
const dataTypeSelectList = slicedTypeMap.map((item) => ({
|
||||
label: t(item.label),
|
||||
value: item.value
|
||||
}));
|
||||
|
||||
const onSubmitSuccess = useCallback(
|
||||
(data: EditNodeFieldType) => {
|
||||
data.key = data?.key?.trim();
|
||||
|
|
@ -270,7 +270,7 @@ const FieldEditModal = ({
|
|||
changeKey: !keys.includes(data.key)
|
||||
});
|
||||
},
|
||||
[defaultField.key, keys, onSubmit, showValueTypeSelect, t, toast]
|
||||
[defaultField.key, inputTypeList, keys, onSubmit, showValueTypeSelect, t, toast]
|
||||
);
|
||||
const onSubmitError = useCallback(
|
||||
(e: Object) => {
|
||||
|
|
|
|||
|
|
@ -28,17 +28,9 @@ export const ToolTargetHandle = ({ nodeId }: ToolHandleProps) => {
|
|||
(connectingEdge?.handleId !== NodeOutputKeyEnum.selectedTools ||
|
||||
edges.some((edge) => edge.targetHandle === getHandleId(nodeId, 'target', 'top')));
|
||||
|
||||
const valueTypeMap = FlowValueTypeMap[WorkflowIOValueTypeEnum.tools];
|
||||
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
<MyTooltip
|
||||
label={t('app.module.type', {
|
||||
type: t(valueTypeMap?.label),
|
||||
description: valueTypeMap?.description
|
||||
})}
|
||||
shouldWrapChildren={false}
|
||||
>
|
||||
<MyTooltip label={t('core.workflow.tool.Handle')} shouldWrapChildren={false}>
|
||||
<Handle
|
||||
style={{
|
||||
borderRadius: '0',
|
||||
|
|
@ -63,7 +55,7 @@ export const ToolTargetHandle = ({ nodeId }: ToolHandleProps) => {
|
|||
</Handle>
|
||||
</MyTooltip>
|
||||
);
|
||||
}, [handleId, hidden, t, valueTypeMap?.description, valueTypeMap?.label]);
|
||||
}, [handleId, hidden, t]);
|
||||
|
||||
return Render;
|
||||
};
|
||||
|
|
@ -72,8 +64,6 @@ export const ToolSourceHandle = ({ nodeId }: ToolHandleProps) => {
|
|||
const { t } = useTranslation();
|
||||
const { setEdges } = useFlowProviderStore();
|
||||
|
||||
const valueTypeMap = FlowValueTypeMap[WorkflowIOValueTypeEnum.tools];
|
||||
|
||||
/* onConnect edge, delete tool input and switch */
|
||||
const onConnect = useCallback(
|
||||
(e: Connection) => {
|
||||
|
|
@ -90,13 +80,7 @@ export const ToolSourceHandle = ({ nodeId }: ToolHandleProps) => {
|
|||
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
<MyTooltip
|
||||
label={t('app.module.type', {
|
||||
type: t(valueTypeMap?.label),
|
||||
description: valueTypeMap?.description
|
||||
})}
|
||||
shouldWrapChildren={false}
|
||||
>
|
||||
<MyTooltip label={t('core.workflow.tool.Handle')} shouldWrapChildren={false}>
|
||||
<Handle
|
||||
style={{
|
||||
borderRadius: '0',
|
||||
|
|
@ -120,7 +104,7 @@ export const ToolSourceHandle = ({ nodeId }: ToolHandleProps) => {
|
|||
</Handle>
|
||||
</MyTooltip>
|
||||
);
|
||||
}, [onConnect, t, valueTypeMap?.description, valueTypeMap?.label]);
|
||||
}, [onConnect, t]);
|
||||
|
||||
return Render;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -513,11 +513,14 @@ const NodeDebugResponse = React.memo(function NodeDebugResponse({
|
|||
{/* result */}
|
||||
{debugResult.showResult && (
|
||||
<Card
|
||||
className="nowheel"
|
||||
position={'absolute'}
|
||||
right={'-430px'}
|
||||
top={0}
|
||||
zIndex={10}
|
||||
w={'420px'}
|
||||
maxH={'540px'}
|
||||
overflowY={'auto'}
|
||||
border={'base'}
|
||||
>
|
||||
{/* Status header */}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import {
|
||||
FlowNodeInputItemType,
|
||||
FlowNodeOutputItemType
|
||||
} from '@fastgpt/global/core/workflow/type/io.d';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io.d';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useFlowProviderStore } from '../../../FlowProvider';
|
||||
|
|
@ -14,7 +11,6 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
|||
|
||||
import dynamic from 'next/dynamic';
|
||||
import { EditNodeFieldType } from '@fastgpt/global/core/workflow/node/type';
|
||||
import { FlowValueTypeMap } from '@/web/core/workflow/constants/dataType';
|
||||
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import ValueTypeLabel from '../ValueTypeLabel';
|
||||
const FieldEditModal = dynamic(() => import('../FieldEditModal'));
|
||||
|
|
@ -37,16 +33,10 @@ const InputLabel = ({ nodeId, input }: Props) => {
|
|||
renderTypeList,
|
||||
valueType,
|
||||
canEdit,
|
||||
key,
|
||||
value
|
||||
key
|
||||
} = input;
|
||||
const [editField, setEditField] = useState<EditNodeFieldType>();
|
||||
|
||||
const valueTypeLabel = useMemo(
|
||||
() => (valueType ? t(FlowValueTypeMap[valueType]?.label) : ''),
|
||||
[t, valueType]
|
||||
);
|
||||
|
||||
const onChangeRenderType = useCallback(
|
||||
(e: string) => {
|
||||
const index = renderTypeList.findIndex((item) => item === e) || 0;
|
||||
|
|
@ -84,35 +74,35 @@ const InputLabel = ({ nodeId, input }: Props) => {
|
|||
)}
|
||||
</Box>
|
||||
{/* value type */}
|
||||
{renderType === FlowNodeInputTypeEnum.reference && !!valueTypeLabel && (
|
||||
<ValueTypeLabel>{valueTypeLabel}</ValueTypeLabel>
|
||||
)}
|
||||
{renderType === FlowNodeInputTypeEnum.reference && <ValueTypeLabel valueType={valueType} />}
|
||||
{/* edit config */}
|
||||
{canEdit && (
|
||||
<>
|
||||
<MyIcon
|
||||
name={'common/settingLight'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
ml={3}
|
||||
color={'myGray.600'}
|
||||
_hover={{ color: 'primary.500' }}
|
||||
onClick={() =>
|
||||
setEditField({
|
||||
inputType: renderTypeList[0],
|
||||
valueType: valueType,
|
||||
key,
|
||||
label,
|
||||
description,
|
||||
isToolInput: !!toolDescription,
|
||||
defaultValue: input.defaultValue,
|
||||
maxLength: input.maxLength,
|
||||
max: input.max,
|
||||
min: input.min,
|
||||
dynamicParamDefaultValue: input.dynamicParamDefaultValue
|
||||
})
|
||||
}
|
||||
/>
|
||||
{input.editField && Object.keys(input.editField).length > 0 && (
|
||||
<MyIcon
|
||||
name={'common/settingLight'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
ml={3}
|
||||
color={'myGray.600'}
|
||||
_hover={{ color: 'primary.500' }}
|
||||
onClick={() =>
|
||||
setEditField({
|
||||
inputType: renderTypeList[0],
|
||||
valueType: valueType,
|
||||
key,
|
||||
label,
|
||||
description,
|
||||
isToolInput: !!toolDescription,
|
||||
defaultValue: input.defaultValue,
|
||||
maxLength: input.maxLength,
|
||||
max: input.max,
|
||||
min: input.min,
|
||||
dynamicParamDefaultValue: input.dynamicParamDefaultValue
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
|
|
@ -207,8 +197,7 @@ const InputLabel = ({ nodeId, input }: Props) => {
|
|||
selectedTypeIndex,
|
||||
t,
|
||||
toolDescription,
|
||||
valueType,
|
||||
valueTypeLabel
|
||||
valueType
|
||||
]);
|
||||
|
||||
return RenderLabel;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import {
|
|||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import PromptTemplate from '@/components/PromptTemplate';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import Reference from './Reference';
|
||||
import { getSystemVariables } from '@/web/core/app/utils';
|
||||
|
|
@ -152,7 +152,7 @@ const SettingQuotePrompt = (props: RenderInputProps) => {
|
|||
<Box position={'relative'} color={'myGray.600'} fontWeight={'medium'}>
|
||||
{t('core.module.Dataset quote.label')}
|
||||
</Box>
|
||||
<ValueTypeLabel>{t('core.module.valueType.datasetQuote')}</ValueTypeLabel>
|
||||
<ValueTypeLabel valueType={WorkflowIOValueTypeEnum.datasetQuote} />
|
||||
|
||||
<MyTooltip label={t('core.module.Setting quote prompt')}>
|
||||
<MyIcon
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/workflow/node/const
|
|||
import { SourceHandle } from '../Handle';
|
||||
import { getHandleId } from '@fastgpt/global/core/workflow/utils';
|
||||
import { Position } from 'reactflow';
|
||||
import { FlowValueTypeMap } from '@/web/core/workflow/constants/dataType';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import ValueTypeLabel from '../ValueTypeLabel';
|
||||
|
||||
|
|
@ -14,11 +13,6 @@ const OutputLabel = ({ nodeId, output }: { nodeId: string; output: FlowNodeOutpu
|
|||
const { t } = useTranslation();
|
||||
const { label = '', description, valueType } = output;
|
||||
|
||||
const valueTypeLabel = useMemo(
|
||||
() => (valueType ? t(FlowValueTypeMap[valueType]?.label) : '-'),
|
||||
[t, valueType]
|
||||
);
|
||||
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
<Box position={'relative'}>
|
||||
|
|
@ -34,11 +28,15 @@ const OutputLabel = ({ nodeId, output }: { nodeId: string; output: FlowNodeOutpu
|
|||
}
|
||||
: {})}
|
||||
>
|
||||
<Box position={'relative'} mr={1}>
|
||||
<Box
|
||||
position={'relative'}
|
||||
mr={1}
|
||||
ml={output.type === FlowNodeOutputTypeEnum.source ? 1 : 0}
|
||||
>
|
||||
{t(label)}
|
||||
</Box>
|
||||
{description && <QuestionTip label={t(description)} />}
|
||||
<ValueTypeLabel>{valueTypeLabel}</ValueTypeLabel>
|
||||
<ValueTypeLabel valueType={valueType} />
|
||||
</Flex>
|
||||
{output.type === FlowNodeOutputTypeEnum.source && (
|
||||
<SourceHandle
|
||||
|
|
@ -50,7 +48,7 @@ const OutputLabel = ({ nodeId, output }: { nodeId: string; output: FlowNodeOutpu
|
|||
)}
|
||||
</Box>
|
||||
);
|
||||
}, [description, output.key, output.type, label, nodeId, t, valueTypeLabel]);
|
||||
}, [output.type, output.key, t, label, description, valueType, nodeId]);
|
||||
|
||||
return Render;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { Box, BoxProps } from '@chakra-ui/react';
|
||||
import { Handle, OnConnect, Position } from 'reactflow';
|
||||
import { FlowValueTypeMap } from '@/web/core/workflow/constants/dataType';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
|
||||
interface Props extends BoxProps {
|
||||
handleKey: string;
|
||||
valueType?: WorkflowIOValueTypeEnum;
|
||||
}
|
||||
|
||||
const TargetHandle = ({ handleKey, valueType, ...props }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const valType = valueType ?? WorkflowIOValueTypeEnum.any;
|
||||
const valueStyle = useMemo(
|
||||
() =>
|
||||
valueType && FlowValueTypeMap[valueType]
|
||||
? FlowValueTypeMap[valueType]?.handlerStyle
|
||||
: FlowValueTypeMap[WorkflowIOValueTypeEnum.any]?.handlerStyle,
|
||||
[valueType]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
position={'absolute'}
|
||||
top={'50%'}
|
||||
left={'-18px'}
|
||||
transform={'translate(0,-50%)'}
|
||||
{...props}
|
||||
>
|
||||
<MyTooltip
|
||||
label={t('app.module.type', {
|
||||
type: t(FlowValueTypeMap[valType]?.label),
|
||||
description: FlowValueTypeMap[valType]?.description
|
||||
})}
|
||||
>
|
||||
<Handle
|
||||
style={{
|
||||
width: '14px',
|
||||
height: '14px',
|
||||
borderWidth: '3.5px',
|
||||
backgroundColor: 'white',
|
||||
...valueStyle
|
||||
}}
|
||||
type="target"
|
||||
id={handleKey}
|
||||
position={Position.Left}
|
||||
/>
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(TargetHandle);
|
||||
|
|
@ -1,21 +1,33 @@
|
|||
import { FlowValueTypeMap } from '@/web/core/workflow/constants/dataType';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import React from 'react';
|
||||
|
||||
const ValueTypeLabel = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<Box
|
||||
bg={'myGray.100'}
|
||||
color={'myGray.500'}
|
||||
border={'base'}
|
||||
borderRadius={'sm'}
|
||||
ml={2}
|
||||
px={1}
|
||||
py={0.5}
|
||||
fontSize={'11px'}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
const ValueTypeLabel = ({ valueType }: { valueType?: WorkflowIOValueTypeEnum }) => {
|
||||
const valueTypeData = valueType ? FlowValueTypeMap[valueType] : undefined;
|
||||
|
||||
const label = valueTypeData?.label || '';
|
||||
const description = valueTypeData?.description || '';
|
||||
|
||||
return !!label ? (
|
||||
<MyTooltip label={description}>
|
||||
<Box
|
||||
bg={'myGray.100'}
|
||||
color={'myGray.500'}
|
||||
border={'base'}
|
||||
borderRadius={'sm'}
|
||||
ml={2}
|
||||
px={1}
|
||||
h={6}
|
||||
display={'flex'}
|
||||
alignItems={'center'}
|
||||
fontSize={'11px'}
|
||||
>
|
||||
{label}
|
||||
</Box>
|
||||
</MyTooltip>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export default React.memo(ValueTypeLabel);
|
||||
|
|
|
|||
|
|
@ -1,17 +1,6 @@
|
|||
import React from 'react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import {
|
||||
Box,
|
||||
Table,
|
||||
Thead,
|
||||
Tbody,
|
||||
Tr,
|
||||
Th,
|
||||
Td,
|
||||
TableContainer,
|
||||
Button,
|
||||
Flex
|
||||
} from '@chakra-ui/react';
|
||||
import { Box, Table, Thead, Tbody, Tr, Th, Td, TableContainer, Flex } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import type {
|
||||
EditInputFieldMapType,
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ const LafAccountModal = ({
|
|||
</Box>
|
||||
<Box>
|
||||
<Link textDecoration={'underline'} href={`${feConfigs.lafEnv}/`} isExternal>
|
||||
{t('support.user.Go laf env')}
|
||||
{t('support.user.Go laf env', { env: feConfigs.lafEnv })}
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import type { LLMModelItemType } from '../ai/model.d';
|
||||
import { AppTypeEnum } from './constants';
|
||||
import { AppSchema } from './type';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { AppSchema } from '@fastgpt/global/core/app/type';
|
||||
|
||||
export type CreateAppParams = {
|
||||
name?: string;
|
||||
|
|
@ -10,13 +9,19 @@ export type CreateAppParams = {
|
|||
edges?: AppSchema['edges'];
|
||||
};
|
||||
|
||||
export interface AppUpdateParams {
|
||||
export type AppUpdateParams = {
|
||||
name?: string;
|
||||
type?: `${AppTypeEnum}`;
|
||||
avatar?: string;
|
||||
intro?: string;
|
||||
modules?: AppSchema['modules'];
|
||||
nodes?: AppSchema['modules'];
|
||||
edges?: AppSchema['edges'];
|
||||
permission?: AppSchema['permission'];
|
||||
teamTags?: AppSchema['teamTags'];
|
||||
}
|
||||
};
|
||||
|
||||
export type PostPublishAppProps = {
|
||||
type: `${AppTypeEnum}`;
|
||||
nodes: AppSchema['modules'];
|
||||
edges: AppSchema['edges'];
|
||||
};
|
||||
|
|
@ -1,52 +1,68 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import type { CreateAppParams } from '@fastgpt/global/core/app/api.d';
|
||||
import type { CreateAppParams } from '@/global/core/app/api.d';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
|
||||
import { checkTeamAppLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const {
|
||||
name = 'APP',
|
||||
avatar,
|
||||
type = AppTypeEnum.advanced,
|
||||
modules,
|
||||
edges
|
||||
} = req.body as CreateAppParams;
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const {
|
||||
name = 'APP',
|
||||
avatar,
|
||||
type = AppTypeEnum.advanced,
|
||||
modules,
|
||||
edges
|
||||
} = req.body as CreateAppParams;
|
||||
|
||||
if (!name || !Array.isArray(modules)) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { teamId, tmbId } = await authUserNotVisitor({ req, authToken: true });
|
||||
|
||||
// 上限校验
|
||||
await checkTeamAppLimit(teamId);
|
||||
|
||||
// 创建模型
|
||||
const response = await MongoApp.create({
|
||||
avatar,
|
||||
name,
|
||||
teamId,
|
||||
tmbId,
|
||||
modules,
|
||||
edges,
|
||||
type,
|
||||
version: 'v2'
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
data: response._id
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
if (!name || !Array.isArray(modules)) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { teamId, tmbId } = await authUserNotVisitor({ req, authToken: true });
|
||||
|
||||
// 上限校验
|
||||
await checkTeamAppLimit(teamId);
|
||||
|
||||
// 创建模型
|
||||
const appId = await mongoSessionRun(async (session) => {
|
||||
const [{ _id: appId }] = await MongoApp.create(
|
||||
[
|
||||
{
|
||||
avatar,
|
||||
name,
|
||||
teamId,
|
||||
tmbId,
|
||||
modules,
|
||||
edges,
|
||||
type,
|
||||
version: 'v2'
|
||||
}
|
||||
],
|
||||
{ session }
|
||||
);
|
||||
|
||||
await MongoAppVersion.create(
|
||||
[
|
||||
{
|
||||
appId,
|
||||
nodes: modules,
|
||||
edges
|
||||
}
|
||||
],
|
||||
{ session }
|
||||
);
|
||||
|
||||
return appId;
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
data: appId
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
|
|||
|
|
@ -1,60 +1,59 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { appId } = req.query as { appId: string };
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const { appId } = req.query as { appId: string };
|
||||
|
||||
if (!appId) {
|
||||
throw new Error('参数错误');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
await authApp({ req, authToken: true, appId, per: 'owner' });
|
||||
|
||||
// 删除对应的聊天
|
||||
await mongoSessionRun(async (session) => {
|
||||
await MongoChatItem.deleteMany(
|
||||
{
|
||||
appId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
await MongoChat.deleteMany(
|
||||
{
|
||||
appId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
// 删除分享链接
|
||||
await MongoOutLink.deleteMany(
|
||||
{
|
||||
appId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
// delete app
|
||||
await MongoApp.deleteOne(
|
||||
{
|
||||
_id: appId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
if (!appId) {
|
||||
throw new Error('参数错误');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
await authApp({ req, authToken: true, appId, per: 'owner' });
|
||||
|
||||
// 删除对应的聊天
|
||||
await mongoSessionRun(async (session) => {
|
||||
await MongoChatItem.deleteMany(
|
||||
{
|
||||
appId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
await MongoChat.deleteMany(
|
||||
{
|
||||
appId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
// 删除分享链接
|
||||
await MongoOutLink.deleteMany(
|
||||
{
|
||||
appId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
// delete version
|
||||
await MongoAppVersion.deleteMany(
|
||||
{
|
||||
appId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
// delete app
|
||||
await MongoApp.deleteOne(
|
||||
{
|
||||
_id: appId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
|
|||
|
|
@ -2,27 +2,20 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
/* 获取我的模型 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { appId } = req.query as { appId: string };
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const { appId } = req.query as { appId: string };
|
||||
|
||||
if (!appId) {
|
||||
throw new Error('参数错误');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { app } = await authApp({ req, authToken: true, appId, per: 'w' });
|
||||
|
||||
jsonRes(res, {
|
||||
data: app
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
if (!appId) {
|
||||
throw new Error('参数错误');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { app } = await authApp({ req, authToken: true, appId, per: 'w' });
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import type { PagingData } from '@/types';
|
||||
import { AppLogsListItemType } from '@/types/app';
|
||||
|
|
@ -9,144 +7,140 @@ import { addDays } from 'date-fns';
|
|||
import type { GetAppChatLogsParams } from '@/global/core/api/appReq.d';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { ChatItemCollectionName } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const {
|
||||
pageNum = 1,
|
||||
pageSize = 20,
|
||||
appId,
|
||||
dateStart = addDays(new Date(), -7),
|
||||
dateEnd = new Date()
|
||||
} = req.body as GetAppChatLogsParams;
|
||||
async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
): Promise<PagingData<AppLogsListItemType>> {
|
||||
const {
|
||||
pageNum = 1,
|
||||
pageSize = 20,
|
||||
appId,
|
||||
dateStart = addDays(new Date(), -7),
|
||||
dateEnd = new Date()
|
||||
} = req.body as GetAppChatLogsParams;
|
||||
|
||||
if (!appId) {
|
||||
throw new Error('缺少参数');
|
||||
if (!appId) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { teamId } = await authApp({ req, authToken: true, appId, per: 'w' });
|
||||
|
||||
const where = {
|
||||
teamId: new Types.ObjectId(teamId),
|
||||
appId: new Types.ObjectId(appId),
|
||||
updateTime: {
|
||||
$gte: new Date(dateStart),
|
||||
$lte: new Date(dateEnd)
|
||||
}
|
||||
};
|
||||
|
||||
// 凭证校验
|
||||
const { teamId } = await authApp({ req, authToken: true, appId, per: 'w' });
|
||||
|
||||
const where = {
|
||||
teamId: new Types.ObjectId(teamId),
|
||||
appId: new Types.ObjectId(appId),
|
||||
updateTime: {
|
||||
$gte: new Date(dateStart),
|
||||
$lte: new Date(dateEnd)
|
||||
}
|
||||
};
|
||||
|
||||
const [data, total] = await Promise.all([
|
||||
MongoChat.aggregate([
|
||||
{ $match: where },
|
||||
{
|
||||
$sort: {
|
||||
userBadFeedbackCount: -1,
|
||||
userGoodFeedbackCount: -1,
|
||||
customFeedbacksCount: -1,
|
||||
updateTime: -1
|
||||
}
|
||||
},
|
||||
{ $skip: (pageNum - 1) * pageSize },
|
||||
{ $limit: pageSize },
|
||||
{
|
||||
$lookup: {
|
||||
from: ChatItemCollectionName,
|
||||
let: { chatId: '$chatId' },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: {
|
||||
$and: [
|
||||
{ $eq: ['$appId', new Types.ObjectId(appId)] },
|
||||
{ $eq: ['$chatId', '$$chatId'] }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
userGoodFeedback: 1,
|
||||
userBadFeedback: 1,
|
||||
customFeedbacks: 1,
|
||||
adminFeedback: 1
|
||||
}
|
||||
}
|
||||
],
|
||||
as: 'chatitems'
|
||||
}
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
userGoodFeedbackCount: {
|
||||
$size: {
|
||||
$filter: {
|
||||
input: '$chatitems',
|
||||
as: 'item',
|
||||
cond: { $ifNull: ['$$item.userGoodFeedback', false] }
|
||||
const [data, total] = await Promise.all([
|
||||
MongoChat.aggregate([
|
||||
{ $match: where },
|
||||
{
|
||||
$sort: {
|
||||
userBadFeedbackCount: -1,
|
||||
userGoodFeedbackCount: -1,
|
||||
customFeedbacksCount: -1,
|
||||
updateTime: -1
|
||||
}
|
||||
},
|
||||
{ $skip: (pageNum - 1) * pageSize },
|
||||
{ $limit: pageSize },
|
||||
{
|
||||
$lookup: {
|
||||
from: ChatItemCollectionName,
|
||||
let: { chatId: '$chatId' },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: {
|
||||
$and: [
|
||||
{ $eq: ['$appId', new Types.ObjectId(appId)] },
|
||||
{ $eq: ['$chatId', '$$chatId'] }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
userBadFeedbackCount: {
|
||||
$size: {
|
||||
$filter: {
|
||||
input: '$chatitems',
|
||||
as: 'item',
|
||||
cond: { $ifNull: ['$$item.userBadFeedback', false] }
|
||||
}
|
||||
{
|
||||
$project: {
|
||||
userGoodFeedback: 1,
|
||||
userBadFeedback: 1,
|
||||
customFeedbacks: 1,
|
||||
adminFeedback: 1
|
||||
}
|
||||
},
|
||||
customFeedbacksCount: {
|
||||
$size: {
|
||||
$filter: {
|
||||
input: '$chatitems',
|
||||
as: 'item',
|
||||
cond: { $gt: [{ $size: { $ifNull: ['$$item.customFeedbacks', []] } }, 0] }
|
||||
}
|
||||
}
|
||||
],
|
||||
as: 'chatitems'
|
||||
}
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
userGoodFeedbackCount: {
|
||||
$size: {
|
||||
$filter: {
|
||||
input: '$chatitems',
|
||||
as: 'item',
|
||||
cond: { $ifNull: ['$$item.userGoodFeedback', false] }
|
||||
}
|
||||
},
|
||||
markCount: {
|
||||
$size: {
|
||||
$filter: {
|
||||
input: '$chatitems',
|
||||
as: 'item',
|
||||
cond: { $ifNull: ['$$item.adminFeedback', false] }
|
||||
}
|
||||
}
|
||||
},
|
||||
userBadFeedbackCount: {
|
||||
$size: {
|
||||
$filter: {
|
||||
input: '$chatitems',
|
||||
as: 'item',
|
||||
cond: { $ifNull: ['$$item.userBadFeedback', false] }
|
||||
}
|
||||
}
|
||||
},
|
||||
customFeedbacksCount: {
|
||||
$size: {
|
||||
$filter: {
|
||||
input: '$chatitems',
|
||||
as: 'item',
|
||||
cond: { $gt: [{ $size: { $ifNull: ['$$item.customFeedbacks', []] } }, 0] }
|
||||
}
|
||||
}
|
||||
},
|
||||
markCount: {
|
||||
$size: {
|
||||
$filter: {
|
||||
input: '$chatitems',
|
||||
as: 'item',
|
||||
cond: { $ifNull: ['$$item.adminFeedback', false] }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 1,
|
||||
id: '$chatId',
|
||||
title: 1,
|
||||
source: 1,
|
||||
time: '$updateTime',
|
||||
messageCount: { $size: '$chatitems' },
|
||||
userGoodFeedbackCount: 1,
|
||||
userBadFeedbackCount: 1,
|
||||
customFeedbacksCount: 1,
|
||||
markCount: 1
|
||||
}
|
||||
}
|
||||
]),
|
||||
MongoChat.countDocuments(where)
|
||||
]);
|
||||
|
||||
jsonRes<PagingData<AppLogsListItemType>>(res, {
|
||||
data: {
|
||||
pageNum,
|
||||
pageSize,
|
||||
data,
|
||||
total
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 1,
|
||||
id: '$chatId',
|
||||
title: 1,
|
||||
source: 1,
|
||||
time: '$updateTime',
|
||||
messageCount: { $size: '$chatitems' },
|
||||
userGoodFeedbackCount: 1,
|
||||
userBadFeedbackCount: 1,
|
||||
customFeedbacksCount: 1,
|
||||
markCount: 1
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
]),
|
||||
MongoChat.countDocuments(where)
|
||||
]);
|
||||
|
||||
return {
|
||||
pageNum,
|
||||
pageSize,
|
||||
data,
|
||||
total
|
||||
};
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
|
|||
|
|
@ -1,38 +1,30 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { mongoRPermission } from '@fastgpt/global/support/permission/utils';
|
||||
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
||||
import { authUserRole } from '@fastgpt/service/support/permission/auth/user';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
// 凭证校验
|
||||
const { teamId, tmbId, teamOwner, role } = await authUserRole({ req, authToken: true });
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<AppListItemType[]> {
|
||||
// 凭证校验
|
||||
const { teamId, tmbId, teamOwner, role } = await authUserRole({ req, authToken: true });
|
||||
|
||||
// 根据 userId 获取模型信息
|
||||
const myApps = await MongoApp.find(
|
||||
{ ...mongoRPermission({ teamId, tmbId, role }) },
|
||||
'_id avatar name intro tmbId permission'
|
||||
).sort({
|
||||
updateTime: -1
|
||||
});
|
||||
jsonRes<AppListItemType[]>(res, {
|
||||
data: myApps.map((app) => ({
|
||||
_id: app._id,
|
||||
avatar: app.avatar,
|
||||
name: app.name,
|
||||
intro: app.intro,
|
||||
isOwner: teamOwner || String(app.tmbId) === tmbId,
|
||||
permission: app.permission
|
||||
}))
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
// 根据 userId 获取模型信息
|
||||
const myApps = await MongoApp.find(
|
||||
{ ...mongoRPermission({ teamId, tmbId, role }) },
|
||||
'_id avatar name intro tmbId permission'
|
||||
).sort({
|
||||
updateTime: -1
|
||||
});
|
||||
|
||||
return myApps.map((app) => ({
|
||||
_id: app._id,
|
||||
avatar: app.avatar,
|
||||
name: app.name,
|
||||
intro: app.intro,
|
||||
isOwner: teamOwner || String(app.tmbId) === tmbId,
|
||||
permission: app.permission
|
||||
}));
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
|
|||
|
|
@ -2,106 +2,50 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import type { AppUpdateParams } from '@fastgpt/global/core/app/api';
|
||||
import type { AppUpdateParams } from '@/global/core/app/api';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { getLLMModel } from '@fastgpt/service/core/ai/model';
|
||||
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time';
|
||||
import { getScheduleTriggerApp } from '@/service/core/app/utils';
|
||||
import { beforeUpdateAppFormat } from '@fastgpt/service/core/app/controller';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
/* 获取我的模型 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const {
|
||||
name,
|
||||
avatar,
|
||||
type,
|
||||
intro,
|
||||
modules: nodes,
|
||||
edges,
|
||||
permission,
|
||||
teamTags
|
||||
} = req.body as AppUpdateParams;
|
||||
const { appId } = req.query as { appId: string };
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const { name, avatar, type, intro, nodes, edges, permission, teamTags } =
|
||||
req.body as AppUpdateParams;
|
||||
const { appId } = req.query as { appId: string };
|
||||
|
||||
if (!appId) {
|
||||
throw new Error('appId is empty');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
await authApp({ req, authToken: true, appId, per: permission ? 'owner' : 'w' });
|
||||
|
||||
// format nodes data
|
||||
// 1. dataset search limit, less than model quoteMaxToken
|
||||
if (nodes) {
|
||||
let maxTokens = 3000;
|
||||
|
||||
nodes.forEach((item) => {
|
||||
if (
|
||||
item.flowNodeType === FlowNodeTypeEnum.chatNode ||
|
||||
item.flowNodeType === FlowNodeTypeEnum.tools
|
||||
) {
|
||||
const model =
|
||||
item.inputs.find((item) => item.key === NodeInputKeyEnum.aiModel)?.value || '';
|
||||
const chatModel = getLLMModel(model);
|
||||
const quoteMaxToken = chatModel.quoteMaxToken || 3000;
|
||||
|
||||
maxTokens = Math.max(maxTokens, quoteMaxToken);
|
||||
}
|
||||
});
|
||||
|
||||
nodes.forEach((item) => {
|
||||
if (item.flowNodeType === FlowNodeTypeEnum.datasetSearchNode) {
|
||||
item.inputs.forEach((input) => {
|
||||
if (input.key === NodeInputKeyEnum.datasetMaxTokens) {
|
||||
const val = input.value as number;
|
||||
if (val > maxTokens) {
|
||||
input.value = maxTokens;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// 2. get schedule plan
|
||||
const { scheduledTriggerConfig } = splitGuideModule(getGuideModule(nodes || []));
|
||||
|
||||
// 更新模型
|
||||
await MongoApp.updateOne(
|
||||
{
|
||||
_id: appId
|
||||
},
|
||||
{
|
||||
name,
|
||||
type,
|
||||
avatar,
|
||||
intro,
|
||||
permission,
|
||||
version: 'v2',
|
||||
teamTags: teamTags,
|
||||
...(nodes && {
|
||||
modules: nodes
|
||||
}),
|
||||
...(edges && {
|
||||
edges
|
||||
}),
|
||||
scheduledTriggerConfig,
|
||||
scheduledTriggerNextTime: scheduledTriggerConfig
|
||||
? getNextTimeByCronStringAndTimezone(scheduledTriggerConfig)
|
||||
: null
|
||||
}
|
||||
);
|
||||
|
||||
getScheduleTriggerApp();
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
if (!appId) {
|
||||
throw new Error('appId is empty');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
await authApp({ req, authToken: true, appId, per: permission ? 'owner' : 'w' });
|
||||
|
||||
// format nodes data
|
||||
// 1. dataset search limit, less than model quoteMaxToken
|
||||
const { nodes: formatNodes } = beforeUpdateAppFormat({ nodes });
|
||||
|
||||
// 更新模型
|
||||
await MongoApp.updateOne(
|
||||
{
|
||||
_id: appId
|
||||
},
|
||||
{
|
||||
name,
|
||||
type,
|
||||
avatar,
|
||||
intro,
|
||||
permission,
|
||||
version: 'v2',
|
||||
...(teamTags && teamTags),
|
||||
...(formatNodes && {
|
||||
modules: formatNodes
|
||||
}),
|
||||
...(edges && {
|
||||
edges
|
||||
})
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import type { AppUpdateParams } from '@fastgpt/global/core/app/api';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { getLLMModel } from '@fastgpt/service/core/ai/model';
|
||||
|
||||
/* 获取我的模型 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { name, avatar, type, intro, modules, permission, teamTags } =
|
||||
req.body as AppUpdateParams;
|
||||
const { appId } = req.query as { appId: string };
|
||||
|
||||
if (!appId) {
|
||||
throw new Error('appId is empty');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
await authApp({ req, authToken: true, appId, per: permission ? 'owner' : 'w' });
|
||||
|
||||
// check modules
|
||||
// 1. dataset search limit, less than model quoteMaxToken
|
||||
if (modules) {
|
||||
let maxTokens = 3000;
|
||||
|
||||
modules.forEach((item) => {
|
||||
if (item.flowNodeType === FlowNodeTypeEnum.chatNode) {
|
||||
const model =
|
||||
item.inputs.find((item) => item.key === NodeInputKeyEnum.aiModel)?.value || '';
|
||||
const chatModel = getLLMModel(model);
|
||||
const quoteMaxToken = chatModel.quoteMaxToken || 3000;
|
||||
|
||||
maxTokens = Math.max(maxTokens, quoteMaxToken);
|
||||
}
|
||||
});
|
||||
|
||||
modules.forEach((item) => {
|
||||
if (item.flowNodeType === FlowNodeTypeEnum.datasetSearchNode) {
|
||||
item.inputs.forEach((input) => {
|
||||
if (input.key === NodeInputKeyEnum.datasetMaxTokens) {
|
||||
const val = input.value as number;
|
||||
if (val > maxTokens) {
|
||||
input.value = maxTokens;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 更新模型
|
||||
await MongoApp.findOneAndUpdate(
|
||||
{
|
||||
_id: appId
|
||||
},
|
||||
{
|
||||
name,
|
||||
type,
|
||||
avatar,
|
||||
intro,
|
||||
permission,
|
||||
teamTags: teamTags,
|
||||
...(modules && {
|
||||
modules
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { beforeUpdateAppFormat } from '@fastgpt/service/core/app/controller';
|
||||
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time';
|
||||
import { PostPublishAppProps } from '@/global/core/app/api';
|
||||
|
||||
type Response = {};
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<{}> {
|
||||
const { appId } = req.query as { appId: string };
|
||||
const { nodes = [], edges = [], type } = req.body as PostPublishAppProps;
|
||||
|
||||
await authApp({ appId, req, per: 'w', authToken: true });
|
||||
|
||||
const { nodes: formatNodes } = beforeUpdateAppFormat({ nodes });
|
||||
|
||||
const { scheduledTriggerConfig } = splitGuideModule(getGuideModule(formatNodes || []));
|
||||
|
||||
await mongoSessionRun(async (session) => {
|
||||
// create version histories
|
||||
await MongoAppVersion.create(
|
||||
[
|
||||
{
|
||||
appId,
|
||||
nodes: formatNodes,
|
||||
edges
|
||||
}
|
||||
],
|
||||
{ session }
|
||||
);
|
||||
|
||||
// update app
|
||||
await MongoApp.findByIdAndUpdate(appId, {
|
||||
modules: formatNodes,
|
||||
edges,
|
||||
updateTime: new Date(),
|
||||
version: 'v2',
|
||||
type,
|
||||
scheduledTriggerConfig,
|
||||
scheduledTriggerNextTime: scheduledTriggerConfig
|
||||
? getNextTimeByCronStringAndTimezone(scheduledTriggerConfig)
|
||||
: null
|
||||
});
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
@ -9,6 +9,7 @@ import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
|||
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
|
|
@ -40,14 +41,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
}
|
||||
|
||||
// get app and history
|
||||
const { history } = await getChatItems({
|
||||
appId,
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value adminFeedback userBadFeedback userGoodFeedback ${
|
||||
DispatchNodeResponseKeyEnum.nodeResponse
|
||||
} ${loadCustomFeedbacks ? 'customFeedbacks' : ''}`
|
||||
});
|
||||
const [{ history }, { nodes }] = await Promise.all([
|
||||
getChatItems({
|
||||
appId,
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value adminFeedback userBadFeedback userGoodFeedback ${
|
||||
DispatchNodeResponseKeyEnum.nodeResponse
|
||||
} ${loadCustomFeedbacks ? 'customFeedbacks' : ''}`
|
||||
}),
|
||||
getAppLatestVersion(app._id, app)
|
||||
]);
|
||||
|
||||
jsonRes<InitChatResponse>(res, {
|
||||
data: {
|
||||
|
|
@ -58,8 +62,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
variables: chat?.variables || {},
|
||||
history,
|
||||
app: {
|
||||
userGuideModule: getGuideModule(app.modules),
|
||||
chatModels: getChatModelNameListByModules(app.modules),
|
||||
userGuideModule: getGuideModule(nodes),
|
||||
chatModels: getChatModelNameListByModules(nodes),
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
intro: app.intro
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
|||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
|
|
@ -40,14 +41,19 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
throw new Error(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
|
||||
const { history } = await getChatItems({
|
||||
appId: app._id,
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value userGoodFeedback userBadFeedback ${
|
||||
shareChat.responseDetail ? `adminFeedback ${DispatchNodeResponseKeyEnum.nodeResponse}` : ''
|
||||
} `
|
||||
});
|
||||
const [{ history }, { nodes }] = await Promise.all([
|
||||
getChatItems({
|
||||
appId: app._id,
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value userGoodFeedback userBadFeedback ${
|
||||
shareChat.responseDetail
|
||||
? `adminFeedback ${DispatchNodeResponseKeyEnum.nodeResponse}`
|
||||
: ''
|
||||
} `
|
||||
}),
|
||||
getAppLatestVersion(app._id, app)
|
||||
]);
|
||||
|
||||
// pick share response field
|
||||
history.forEach((item) => {
|
||||
|
|
@ -66,8 +72,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
variables: chat?.variables || {},
|
||||
history,
|
||||
app: {
|
||||
userGuideModule: getGuideModule(app.modules),
|
||||
chatModels: getChatModelNameListByModules(app.modules),
|
||||
userGuideModule: getGuideModule(nodes),
|
||||
chatModels: getChatModelNameListByModules(nodes),
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
intro: app.intro
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
|
|||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
import { filterPublicNodeResponseData } from '@fastgpt/global/core/chat/utils';
|
||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
|
|
@ -46,12 +47,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
}
|
||||
|
||||
// get app and history
|
||||
const { history } = await getChatItems({
|
||||
appId,
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value userGoodFeedback userBadFeedback adminFeedback ${DispatchNodeResponseKeyEnum.nodeResponse}`
|
||||
});
|
||||
const [{ history }, { nodes }] = await Promise.all([
|
||||
getChatItems({
|
||||
appId,
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value userGoodFeedback userBadFeedback adminFeedback ${DispatchNodeResponseKeyEnum.nodeResponse}`
|
||||
}),
|
||||
getAppLatestVersion(app._id, app)
|
||||
]);
|
||||
|
||||
// pick share response field
|
||||
history.forEach((item) => {
|
||||
|
|
@ -69,8 +73,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
variables: chat?.variables || {},
|
||||
history,
|
||||
app: {
|
||||
userGuideModule: getGuideModule(app.modules),
|
||||
chatModels: getChatModelNameListByModules(app.modules),
|
||||
userGuideModule: getGuideModule(nodes),
|
||||
chatModels: getChatModelNameListByModules(nodes),
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
intro: app.intro
|
||||
|
|
|
|||
|
|
@ -1,40 +1,32 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authDatasetData } from '@/service/support/permission/auth/dataset';
|
||||
import { deleteDatasetData } from '@/service/core/dataset/data/controller';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { id: dataId } = req.query as {
|
||||
id: string;
|
||||
};
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const { id: dataId } = req.query as {
|
||||
id: string;
|
||||
};
|
||||
|
||||
if (!dataId) {
|
||||
throw new Error('dataId is required');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { teamId, datasetData } = await authDatasetData({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
dataId,
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
await deleteDatasetData(datasetData);
|
||||
|
||||
jsonRes(res, {
|
||||
data: 'success'
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
if (!dataId) {
|
||||
throw new Error('dataId is required');
|
||||
}
|
||||
});
|
||||
|
||||
// 凭证校验
|
||||
const { teamId, datasetData } = await authDatasetData({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
dataId,
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
await deleteDatasetData(datasetData);
|
||||
|
||||
jsonRes(res, {
|
||||
data: 'success'
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authDatasetData } from '@/service/support/permission/auth/dataset';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
export type Response = {
|
||||
id: string;
|
||||
|
|
@ -10,29 +11,23 @@ export type Response = {
|
|||
source: string;
|
||||
};
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { id: dataId } = req.query as {
|
||||
id: string;
|
||||
};
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const { id: dataId } = req.query as {
|
||||
id: string;
|
||||
};
|
||||
|
||||
// 凭证校验
|
||||
const { datasetData } = await authDatasetData({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
dataId,
|
||||
per: 'r'
|
||||
});
|
||||
// 凭证校验
|
||||
const { datasetData } = await authDatasetData({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
dataId,
|
||||
per: 'r'
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
data: datasetData
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
jsonRes(res, {
|
||||
data: datasetData
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import { countPromptTokens } from '@fastgpt/service/common/string/tiktoken/index';
|
||||
import { getVectorModel } from '@fastgpt/service/core/ai/model';
|
||||
import { hasSameValue } from '@/service/core/dataset/data/utils';
|
||||
|
|
@ -16,92 +15,87 @@ import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
|
|||
import { InsertOneDatasetDataProps } from '@/global/core/dataset/api';
|
||||
import { simpleText } from '@fastgpt/global/common/string/tools';
|
||||
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { collectionId, q, a, indexes } = req.body as InsertOneDatasetDataProps;
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const { collectionId, q, a, indexes } = req.body as InsertOneDatasetDataProps;
|
||||
|
||||
if (!q) {
|
||||
throw new Error('q is required');
|
||||
}
|
||||
|
||||
if (!collectionId) {
|
||||
throw new Error('collectionId is required');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { teamId, tmbId } = await authDatasetCollection({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
collectionId,
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
await checkDatasetLimit({
|
||||
teamId,
|
||||
insertLen: 1
|
||||
});
|
||||
|
||||
// auth collection and get dataset
|
||||
const [
|
||||
{
|
||||
datasetId: { _id: datasetId, vectorModel }
|
||||
}
|
||||
] = await Promise.all([getCollectionWithDataset(collectionId)]);
|
||||
|
||||
// format data
|
||||
const formatQ = simpleText(q);
|
||||
const formatA = simpleText(a);
|
||||
const formatIndexes = indexes?.map((item) => ({
|
||||
...item,
|
||||
text: simpleText(item.text)
|
||||
}));
|
||||
|
||||
// token check
|
||||
const token = await countPromptTokens(formatQ + formatA, '');
|
||||
const vectorModelData = getVectorModel(vectorModel);
|
||||
|
||||
if (token > vectorModelData.maxToken) {
|
||||
return Promise.reject('Q Over Tokens');
|
||||
}
|
||||
|
||||
// Duplicate data check
|
||||
await hasSameValue({
|
||||
teamId,
|
||||
datasetId,
|
||||
collectionId,
|
||||
q: formatQ,
|
||||
a: formatA
|
||||
});
|
||||
|
||||
const { insertId, tokens } = await insertData2Dataset({
|
||||
teamId,
|
||||
tmbId,
|
||||
datasetId,
|
||||
collectionId,
|
||||
q: formatQ,
|
||||
a: formatA,
|
||||
chunkIndex: 0,
|
||||
model: vectorModelData.model,
|
||||
indexes: formatIndexes
|
||||
});
|
||||
|
||||
pushGenerateVectorUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
tokens,
|
||||
model: vectorModelData.model
|
||||
});
|
||||
|
||||
jsonRes<string>(res, {
|
||||
data: insertId
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
if (!q) {
|
||||
throw new Error('q is required');
|
||||
}
|
||||
});
|
||||
|
||||
if (!collectionId) {
|
||||
throw new Error('collectionId is required');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { teamId, tmbId } = await authDatasetCollection({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
collectionId,
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
await checkDatasetLimit({
|
||||
teamId,
|
||||
insertLen: 1
|
||||
});
|
||||
|
||||
// auth collection and get dataset
|
||||
const [
|
||||
{
|
||||
datasetId: { _id: datasetId, vectorModel }
|
||||
}
|
||||
] = await Promise.all([getCollectionWithDataset(collectionId)]);
|
||||
|
||||
// format data
|
||||
const formatQ = simpleText(q);
|
||||
const formatA = simpleText(a);
|
||||
const formatIndexes = indexes?.map((item) => ({
|
||||
...item,
|
||||
text: simpleText(item.text)
|
||||
}));
|
||||
|
||||
// token check
|
||||
const token = await countPromptTokens(formatQ + formatA, '');
|
||||
const vectorModelData = getVectorModel(vectorModel);
|
||||
|
||||
if (token > vectorModelData.maxToken) {
|
||||
return Promise.reject('Q Over Tokens');
|
||||
}
|
||||
|
||||
// Duplicate data check
|
||||
await hasSameValue({
|
||||
teamId,
|
||||
datasetId,
|
||||
collectionId,
|
||||
q: formatQ,
|
||||
a: formatA
|
||||
});
|
||||
|
||||
const { insertId, tokens } = await insertData2Dataset({
|
||||
teamId,
|
||||
tmbId,
|
||||
datasetId,
|
||||
collectionId,
|
||||
q: formatQ,
|
||||
a: formatA,
|
||||
chunkIndex: 0,
|
||||
model: vectorModelData.model,
|
||||
indexes: formatIndexes
|
||||
});
|
||||
|
||||
pushGenerateVectorUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
tokens,
|
||||
model: vectorModelData.model
|
||||
});
|
||||
|
||||
jsonRes<string>(res, {
|
||||
data: insertId
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
|
|||
|
|
@ -7,62 +7,57 @@ import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/
|
|||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { PagingData } from '@/types';
|
||||
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
let {
|
||||
pageNum = 1,
|
||||
pageSize = 10,
|
||||
searchText = '',
|
||||
collectionId
|
||||
} = req.body as GetDatasetDataListProps;
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
let {
|
||||
pageNum = 1,
|
||||
pageSize = 10,
|
||||
searchText = '',
|
||||
collectionId
|
||||
} = req.body as GetDatasetDataListProps;
|
||||
|
||||
pageSize = Math.min(pageSize, 30);
|
||||
pageSize = Math.min(pageSize, 30);
|
||||
|
||||
// 凭证校验
|
||||
const { teamId, collection } = await authDatasetCollection({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
collectionId,
|
||||
per: 'r'
|
||||
});
|
||||
// 凭证校验
|
||||
const { teamId, collection } = await authDatasetCollection({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
collectionId,
|
||||
per: 'r'
|
||||
});
|
||||
|
||||
searchText = replaceRegChars(searchText).replace(/'/g, '');
|
||||
searchText = replaceRegChars(searchText).replace(/'/g, '');
|
||||
|
||||
const match = {
|
||||
teamId,
|
||||
datasetId: collection.datasetId._id,
|
||||
collectionId,
|
||||
...(searchText
|
||||
? {
|
||||
$or: [{ q: new RegExp(searchText, 'i') }, { a: new RegExp(searchText, 'i') }]
|
||||
}
|
||||
: {})
|
||||
};
|
||||
const match = {
|
||||
teamId,
|
||||
datasetId: collection.datasetId._id,
|
||||
collectionId,
|
||||
...(searchText
|
||||
? {
|
||||
$or: [{ q: new RegExp(searchText, 'i') }, { a: new RegExp(searchText, 'i') }]
|
||||
}
|
||||
: {})
|
||||
};
|
||||
|
||||
const [data, total] = await Promise.all([
|
||||
MongoDatasetData.find(match, '_id datasetId collectionId q a chunkIndex')
|
||||
.sort({ chunkIndex: 1, updateTime: -1 })
|
||||
.skip((pageNum - 1) * pageSize)
|
||||
.limit(pageSize)
|
||||
.lean(),
|
||||
MongoDatasetData.countDocuments(match)
|
||||
]);
|
||||
const [data, total] = await Promise.all([
|
||||
MongoDatasetData.find(match, '_id datasetId collectionId q a chunkIndex')
|
||||
.sort({ chunkIndex: 1, updateTime: -1 })
|
||||
.skip((pageNum - 1) * pageSize)
|
||||
.limit(pageSize)
|
||||
.lean(),
|
||||
MongoDatasetData.countDocuments(match)
|
||||
]);
|
||||
|
||||
jsonRes<PagingData<DatasetDataListItemType>>(res, {
|
||||
data: {
|
||||
pageNum,
|
||||
pageSize,
|
||||
data,
|
||||
total
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
jsonRes<PagingData<DatasetDataListItemType>>(res, {
|
||||
data: {
|
||||
pageNum,
|
||||
pageSize,
|
||||
data,
|
||||
total
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import type {
|
||||
PushDatasetDataProps,
|
||||
PushDatasetDataResponse
|
||||
|
|
@ -11,53 +10,48 @@ import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/
|
|||
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
|
||||
import { pushDataListToTrainingQueue } from '@fastgpt/service/core/dataset/training/controller';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const body = req.body as PushDatasetDataProps;
|
||||
const { collectionId, data } = body;
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const body = req.body as PushDatasetDataProps;
|
||||
const { collectionId, data } = body;
|
||||
|
||||
if (!collectionId || !Array.isArray(data)) {
|
||||
throw new Error('collectionId or data is empty');
|
||||
}
|
||||
|
||||
if (data.length > 200) {
|
||||
throw new Error('Data is too long, max 200');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { teamId, tmbId, collection } = await authDatasetCollection({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
collectionId,
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
// auth dataset limit
|
||||
await checkDatasetLimit({
|
||||
teamId,
|
||||
insertLen: predictDataLimitLength(collection.trainingType, data)
|
||||
});
|
||||
|
||||
jsonRes<PushDatasetDataResponse>(res, {
|
||||
data: await pushDataListToTrainingQueue({
|
||||
...body,
|
||||
teamId,
|
||||
tmbId,
|
||||
datasetId: collection.datasetId._id,
|
||||
agentModel: collection.datasetId.agentModel,
|
||||
vectorModel: collection.datasetId.vectorModel
|
||||
})
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
if (!collectionId || !Array.isArray(data)) {
|
||||
throw new Error('collectionId or data is empty');
|
||||
}
|
||||
});
|
||||
|
||||
if (data.length > 200) {
|
||||
throw new Error('Data is too long, max 200');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { teamId, tmbId, collection } = await authDatasetCollection({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
collectionId,
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
// auth dataset limit
|
||||
await checkDatasetLimit({
|
||||
teamId,
|
||||
insertLen: predictDataLimitLength(collection.trainingType, data)
|
||||
});
|
||||
|
||||
jsonRes<PushDatasetDataResponse>(res, {
|
||||
data: await pushDataListToTrainingQueue({
|
||||
...body,
|
||||
teamId,
|
||||
tmbId,
|
||||
datasetId: collection.datasetId._id,
|
||||
agentModel: collection.datasetId.agentModel,
|
||||
vectorModel: collection.datasetId.vectorModel
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
|
|
|
|||
|
|
@ -1,59 +1,53 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { updateData2Dataset } from '@/service/core/dataset/data/controller';
|
||||
import { authDatasetData } from '@/service/support/permission/auth/dataset';
|
||||
import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
|
||||
import { UpdateDatasetDataProps } from '@/global/core/dataset/api';
|
||||
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { id, q = '', a, indexes = [] } = req.body as UpdateDatasetDataProps;
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const { id, q = '', a, indexes = [] } = req.body as UpdateDatasetDataProps;
|
||||
|
||||
// auth data permission
|
||||
const {
|
||||
collection: {
|
||||
datasetId: { vectorModel }
|
||||
},
|
||||
teamId,
|
||||
tmbId
|
||||
} = await authDatasetData({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
dataId: id,
|
||||
per: 'w'
|
||||
});
|
||||
// auth data permission
|
||||
const {
|
||||
collection: {
|
||||
datasetId: { vectorModel }
|
||||
},
|
||||
teamId,
|
||||
tmbId
|
||||
} = await authDatasetData({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
dataId: id,
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
// auth team balance
|
||||
await checkDatasetLimit({
|
||||
teamId,
|
||||
insertLen: 1
|
||||
});
|
||||
// auth team balance
|
||||
await checkDatasetLimit({
|
||||
teamId,
|
||||
insertLen: 1
|
||||
});
|
||||
|
||||
const { tokens } = await updateData2Dataset({
|
||||
dataId: id,
|
||||
q,
|
||||
a,
|
||||
indexes,
|
||||
model: vectorModel
|
||||
});
|
||||
const { tokens } = await updateData2Dataset({
|
||||
dataId: id,
|
||||
q,
|
||||
a,
|
||||
indexes,
|
||||
model: vectorModel
|
||||
});
|
||||
|
||||
pushGenerateVectorUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
tokens,
|
||||
model: vectorModel
|
||||
});
|
||||
pushGenerateVectorUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
tokens,
|
||||
model: vectorModel
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
});
|
||||
jsonRes(res);
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
|
|||
|
|
@ -1,94 +1,85 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes, responseWriteController } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { responseWriteController } from '@fastgpt/service/common/response';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { findDatasetAndAllChildren } from '@fastgpt/service/core/dataset/controller';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import {
|
||||
checkExportDatasetLimit,
|
||||
updateExportDatasetLimit
|
||||
} from '@fastgpt/service/support/user/utils';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
let { datasetId } = req.query as {
|
||||
datasetId: string;
|
||||
};
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
let { datasetId } = req.query as {
|
||||
datasetId: string;
|
||||
};
|
||||
|
||||
if (!datasetId || !global.pgClient) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { teamId } = await authDataset({ req, authToken: true, datasetId, per: 'w' });
|
||||
|
||||
await checkExportDatasetLimit({
|
||||
teamId,
|
||||
limitMinutes: global.feConfigs?.limit?.exportDatasetLimitMinutes
|
||||
});
|
||||
|
||||
const datasets = await findDatasetAndAllChildren({
|
||||
teamId,
|
||||
datasetId,
|
||||
fields: '_id'
|
||||
});
|
||||
|
||||
res.setHeader('Content-Type', 'text/csv; charset=utf-8;');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=dataset.csv; ');
|
||||
|
||||
const cursor = MongoDatasetData.find<{
|
||||
_id: string;
|
||||
collectionId: { name: string };
|
||||
q: string;
|
||||
a: string;
|
||||
}>(
|
||||
{
|
||||
teamId,
|
||||
datasetId: { $in: datasets.map((d) => d._id) }
|
||||
},
|
||||
'q a'
|
||||
)
|
||||
.limit(50000)
|
||||
.cursor();
|
||||
|
||||
const write = responseWriteController({
|
||||
res,
|
||||
readStream: cursor
|
||||
});
|
||||
|
||||
write(`\uFEFFindex,content`);
|
||||
|
||||
cursor.on('data', (doc) => {
|
||||
const q = doc.q.replace(/"/g, '""') || '';
|
||||
const a = doc.a.replace(/"/g, '""') || '';
|
||||
|
||||
write(`\n"${q}","${a}"`);
|
||||
});
|
||||
|
||||
cursor.on('end', () => {
|
||||
cursor.close();
|
||||
res.end();
|
||||
});
|
||||
|
||||
cursor.on('error', (err) => {
|
||||
addLog.error(`export dataset error`, err);
|
||||
res.status(500);
|
||||
res.end();
|
||||
});
|
||||
|
||||
updateExportDatasetLimit(teamId);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
addLog.error(`export dataset error`, err);
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
if (!datasetId || !global.pgClient) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
});
|
||||
|
||||
// 凭证校验
|
||||
const { teamId } = await authDataset({ req, authToken: true, datasetId, per: 'w' });
|
||||
|
||||
await checkExportDatasetLimit({
|
||||
teamId,
|
||||
limitMinutes: global.feConfigs?.limit?.exportDatasetLimitMinutes
|
||||
});
|
||||
|
||||
const datasets = await findDatasetAndAllChildren({
|
||||
teamId,
|
||||
datasetId,
|
||||
fields: '_id'
|
||||
});
|
||||
|
||||
res.setHeader('Content-Type', 'text/csv; charset=utf-8;');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=dataset.csv; ');
|
||||
|
||||
const cursor = MongoDatasetData.find<{
|
||||
_id: string;
|
||||
collectionId: { name: string };
|
||||
q: string;
|
||||
a: string;
|
||||
}>(
|
||||
{
|
||||
teamId,
|
||||
datasetId: { $in: datasets.map((d) => d._id) }
|
||||
},
|
||||
'q a'
|
||||
)
|
||||
.limit(50000)
|
||||
.cursor();
|
||||
|
||||
const write = responseWriteController({
|
||||
res,
|
||||
readStream: cursor
|
||||
});
|
||||
|
||||
write(`\uFEFFindex,content`);
|
||||
|
||||
cursor.on('data', (doc) => {
|
||||
const q = doc.q.replace(/"/g, '""') || '';
|
||||
const a = doc.a.replace(/"/g, '""') || '';
|
||||
|
||||
write(`\n"${q}","${a}"`);
|
||||
});
|
||||
|
||||
cursor.on('end', () => {
|
||||
cursor.close();
|
||||
res.end();
|
||||
});
|
||||
|
||||
cursor.on('error', (err) => {
|
||||
addLog.error(`export dataset error`, err);
|
||||
res.status(500);
|
||||
res.end();
|
||||
});
|
||||
|
||||
updateExportDatasetLimit(teamId);
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
|
|
|
|||
|
|
@ -1,54 +1,48 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import type { DatasetListItemType } from '@fastgpt/global/core/dataset/type.d';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
|
||||
import { mongoRPermission } from '@fastgpt/global/support/permission/utils';
|
||||
import { authUserRole } from '@fastgpt/service/support/permission/auth/user';
|
||||
import { getVectorModel } from '@fastgpt/service/core/ai/model';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { parentId, type } = req.query as { parentId?: string; type?: `${DatasetTypeEnum}` };
|
||||
// 凭证校验
|
||||
const { teamId, tmbId, teamOwner, role, canWrite } = await authUserRole({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true
|
||||
});
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const { parentId, type } = req.query as { parentId?: string; type?: `${DatasetTypeEnum}` };
|
||||
// 凭证校验
|
||||
const { teamId, tmbId, teamOwner, role, canWrite } = await authUserRole({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true
|
||||
});
|
||||
|
||||
const datasets = await MongoDataset.find({
|
||||
...mongoRPermission({ teamId, tmbId, role }),
|
||||
...(parentId !== undefined && { parentId: parentId || null }),
|
||||
...(type && { type })
|
||||
})
|
||||
.sort({ updateTime: -1 })
|
||||
.lean();
|
||||
const datasets = await MongoDataset.find({
|
||||
...mongoRPermission({ teamId, tmbId, role }),
|
||||
...(parentId !== undefined && { parentId: parentId || null }),
|
||||
...(type && { type })
|
||||
})
|
||||
.sort({ updateTime: -1 })
|
||||
.lean();
|
||||
|
||||
const data = await Promise.all(
|
||||
datasets.map<DatasetListItemType>((item) => ({
|
||||
_id: item._id,
|
||||
parentId: item.parentId,
|
||||
avatar: item.avatar,
|
||||
name: item.name,
|
||||
intro: item.intro,
|
||||
type: item.type,
|
||||
permission: item.permission,
|
||||
canWrite,
|
||||
isOwner: teamOwner || String(item.tmbId) === tmbId,
|
||||
vectorModel: getVectorModel(item.vectorModel)
|
||||
}))
|
||||
);
|
||||
const data = await Promise.all(
|
||||
datasets.map<DatasetListItemType>((item) => ({
|
||||
_id: item._id,
|
||||
parentId: item.parentId,
|
||||
avatar: item.avatar,
|
||||
name: item.name,
|
||||
intro: item.intro,
|
||||
type: item.type,
|
||||
permission: item.permission,
|
||||
canWrite,
|
||||
isOwner: teamOwner || String(item.tmbId) === tmbId,
|
||||
vectorModel: getVectorModel(item.vectorModel)
|
||||
}))
|
||||
);
|
||||
|
||||
jsonRes<DatasetListItemType[]>(res, {
|
||||
data
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
jsonRes<DatasetListItemType[]>(res, {
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import type { SearchTestProps, SearchTestResponse } from '@/global/core/dataset/api.d';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
|
||||
import { searchDatasetData } from '@fastgpt/service/core/dataset/search/controller';
|
||||
|
|
@ -14,95 +12,90 @@ import {
|
|||
checkTeamAIPoints,
|
||||
checkTeamReRankPermission
|
||||
} from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const {
|
||||
datasetId,
|
||||
text,
|
||||
limit = 1500,
|
||||
similarity,
|
||||
searchMode,
|
||||
usingReRank,
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
const {
|
||||
datasetId,
|
||||
text,
|
||||
limit = 1500,
|
||||
similarity,
|
||||
searchMode,
|
||||
usingReRank,
|
||||
|
||||
datasetSearchUsingExtensionQuery = false,
|
||||
datasetSearchExtensionModel,
|
||||
datasetSearchExtensionBg = ''
|
||||
} = req.body as SearchTestProps;
|
||||
datasetSearchUsingExtensionQuery = false,
|
||||
datasetSearchExtensionModel,
|
||||
datasetSearchExtensionBg = ''
|
||||
} = req.body as SearchTestProps;
|
||||
|
||||
if (!datasetId || !text) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
const start = Date.now();
|
||||
if (!datasetId || !text) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
const start = Date.now();
|
||||
|
||||
// auth dataset role
|
||||
const { dataset, teamId, tmbId, apikey } = await authDataset({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
datasetId,
|
||||
per: 'r'
|
||||
});
|
||||
// auth balance
|
||||
await checkTeamAIPoints(teamId);
|
||||
// auth dataset role
|
||||
const { dataset, teamId, tmbId, apikey } = await authDataset({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
datasetId,
|
||||
per: 'r'
|
||||
});
|
||||
// auth balance
|
||||
await checkTeamAIPoints(teamId);
|
||||
|
||||
// query extension
|
||||
const extensionModel =
|
||||
datasetSearchUsingExtensionQuery && datasetSearchExtensionModel
|
||||
? getLLMModel(datasetSearchExtensionModel)
|
||||
: undefined;
|
||||
const { concatQueries, rewriteQuery, aiExtensionResult } = await datasetSearchQueryExtension({
|
||||
query: text,
|
||||
extensionModel,
|
||||
extensionBg: datasetSearchExtensionBg
|
||||
});
|
||||
// query extension
|
||||
const extensionModel =
|
||||
datasetSearchUsingExtensionQuery && datasetSearchExtensionModel
|
||||
? getLLMModel(datasetSearchExtensionModel)
|
||||
: undefined;
|
||||
const { concatQueries, rewriteQuery, aiExtensionResult } = await datasetSearchQueryExtension({
|
||||
query: text,
|
||||
extensionModel,
|
||||
extensionBg: datasetSearchExtensionBg
|
||||
});
|
||||
|
||||
const { searchRes, tokens, ...result } = await searchDatasetData({
|
||||
teamId,
|
||||
reRankQuery: rewriteQuery,
|
||||
queries: concatQueries,
|
||||
model: dataset.vectorModel,
|
||||
limit: Math.min(limit, 20000),
|
||||
similarity,
|
||||
datasetIds: [datasetId],
|
||||
searchMode,
|
||||
usingReRank: usingReRank && (await checkTeamReRankPermission(teamId))
|
||||
});
|
||||
const { searchRes, tokens, ...result } = await searchDatasetData({
|
||||
teamId,
|
||||
reRankQuery: rewriteQuery,
|
||||
queries: concatQueries,
|
||||
model: dataset.vectorModel,
|
||||
limit: Math.min(limit, 20000),
|
||||
similarity,
|
||||
datasetIds: [datasetId],
|
||||
searchMode,
|
||||
usingReRank: usingReRank && (await checkTeamReRankPermission(teamId))
|
||||
});
|
||||
|
||||
// push bill
|
||||
const { totalPoints } = pushGenerateVectorUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
tokens,
|
||||
model: dataset.vectorModel,
|
||||
source: apikey ? UsageSourceEnum.api : UsageSourceEnum.fastgpt,
|
||||
// push bill
|
||||
const { totalPoints } = pushGenerateVectorUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
tokens,
|
||||
model: dataset.vectorModel,
|
||||
source: apikey ? UsageSourceEnum.api : UsageSourceEnum.fastgpt,
|
||||
|
||||
...(aiExtensionResult &&
|
||||
extensionModel && {
|
||||
extensionModel: extensionModel.name,
|
||||
extensionTokens: aiExtensionResult.tokens
|
||||
})
|
||||
});
|
||||
if (apikey) {
|
||||
updateApiKeyUsage({
|
||||
apikey,
|
||||
totalPoints: totalPoints
|
||||
});
|
||||
}
|
||||
|
||||
jsonRes<SearchTestResponse>(res, {
|
||||
data: {
|
||||
list: searchRes,
|
||||
duration: `${((Date.now() - start) / 1000).toFixed(3)}s`,
|
||||
usingQueryExtension: !!aiExtensionResult,
|
||||
...result
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
...(aiExtensionResult &&
|
||||
extensionModel && {
|
||||
extensionModel: extensionModel.name,
|
||||
extensionTokens: aiExtensionResult.tokens
|
||||
})
|
||||
});
|
||||
if (apikey) {
|
||||
updateApiKeyUsage({
|
||||
apikey,
|
||||
totalPoints: totalPoints
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
jsonRes<SearchTestResponse>(res, {
|
||||
data: {
|
||||
list: searchRes,
|
||||
duration: `${((Date.now() - start) / 1000).toFixed(3)}s`,
|
||||
usingQueryExtension: !!aiExtensionResult,
|
||||
...result
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import { getUploadModel } from '@fastgpt/service/common/file/multer';
|
||||
import { removeFilesByPaths } from '@fastgpt/service/common/file/utils';
|
||||
import fs from 'fs';
|
||||
|
|
@ -10,12 +9,13 @@ import { authChatCert } from '@/service/support/permission/auth/chat';
|
|||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
|
||||
const upload = getUploadModel({
|
||||
maxSize: 2
|
||||
});
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
let filePaths: string[] = [];
|
||||
|
||||
try {
|
||||
|
|
@ -81,7 +81,9 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||
}
|
||||
|
||||
removeFilesByPaths(filePaths);
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
|||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { sseErrRes, jsonRes } from '@fastgpt/service/common/response';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import { ChatRoleEnum, ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
|
||||
|
|
@ -42,6 +41,9 @@ import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runti
|
|||
|
||||
import { dispatchWorkFlowV1 } from '@fastgpt/service/core/workflow/dispatchV1';
|
||||
import { setEntryEntries } from '@fastgpt/service/core/workflow/dispatchV1/utils';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
|
||||
import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
|
||||
|
||||
type FastGptWebChatProps = {
|
||||
chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history
|
||||
|
|
@ -73,7 +75,7 @@ type AuthResponseType = {
|
|||
outLinkUserId?: string;
|
||||
};
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
res.on('close', () => {
|
||||
res.end();
|
||||
});
|
||||
|
|
@ -163,13 +165,16 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||
});
|
||||
})();
|
||||
|
||||
// get and concat history
|
||||
const { history } = await getChatItems({
|
||||
appId: app._id,
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value`
|
||||
});
|
||||
// 1. get and concat history; 2. get app workflow
|
||||
const [{ history }, { nodes, edges }] = await Promise.all([
|
||||
getChatItems({
|
||||
appId: app._id,
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value`
|
||||
}),
|
||||
getAppLatestVersion(app._id, app)
|
||||
]);
|
||||
const concatHistories = history.concat(chatMessages);
|
||||
const responseChatItemId: string | undefined = messages[messages.length - 1].dataId;
|
||||
|
||||
|
|
@ -185,8 +190,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||
appId: String(app._id),
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
runtimeNodes: storeNodes2RuntimeNodes(app.modules, getDefaultEntryNodeIds(app.modules)),
|
||||
runtimeEdges: initWorkflowEdgeStatus(app.edges),
|
||||
runtimeNodes: storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes)),
|
||||
runtimeEdges: initWorkflowEdgeStatus(edges),
|
||||
variables: {
|
||||
...variables,
|
||||
userChatInput: text
|
||||
|
|
@ -349,7 +354,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
export default NextAPI(handler);
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { getVectorsByText } from '@fastgpt/service/core/ai/embedding';
|
||||
|
|
@ -19,7 +18,7 @@ type Props = {
|
|||
type: `${EmbeddingTypeEnm}`;
|
||||
};
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
let { input, model, billId, type } = req.body as Props;
|
||||
await connectToDatabase();
|
||||
|
|
@ -80,4 +79,4 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||
error: err
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ import {
|
|||
checkWorkflowNodeAndConnection,
|
||||
filterSensitiveNodesData
|
||||
} from '@/web/core/workflow/utils';
|
||||
import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { formatTime2HM } from '@fastgpt/global/common/string/time';
|
||||
|
||||
const ImportSettings = dynamic(() => import('@/components/core/workflow/Flow/ImportSettings'));
|
||||
|
||||
|
|
@ -50,13 +54,14 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
|||
const { toast } = useToast();
|
||||
const { t } = useTranslation();
|
||||
const { copyData } = useCopyData();
|
||||
const { openConfirm: openConfirmOut, ConfirmModal } = useConfirm({
|
||||
content: t('core.app.edit.Out Ad Edit')
|
||||
const { openConfirm: openConfigPublish, ConfirmModal } = useConfirm({
|
||||
content: t('core.app.Publish Confirm')
|
||||
});
|
||||
const { isOpen: isOpenImport, onOpen: onOpenImport, onClose: onCloseImport } = useDisclosure();
|
||||
const { updateAppDetail } = useAppStore();
|
||||
const { publishApp, updateAppDetail } = useAppStore();
|
||||
const { edges, onUpdateNodeError } = useFlowProviderStore();
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [saveLabel, setSaveLabel] = useState(t('core.app.Onclick to save'));
|
||||
|
||||
const flowData2StoreDataAndCheck = useCallback(async () => {
|
||||
const { nodes } = await getWorkflowStore();
|
||||
|
|
@ -75,48 +80,95 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
|||
}
|
||||
}, [edges, onUpdateNodeError, t, toast]);
|
||||
|
||||
const onclickSave = useCallback(
|
||||
async ({ nodes, edges }: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }) => {
|
||||
setIsSaving(true);
|
||||
const onclickSave = useCallback(async () => {
|
||||
const { nodes } = await getWorkflowStore();
|
||||
|
||||
if (nodes.length === 0) return null;
|
||||
setIsSaving(true);
|
||||
|
||||
const storeWorkflow = flowNode2StoreNodes({ nodes, edges });
|
||||
|
||||
try {
|
||||
await updateAppDetail(app._id, {
|
||||
...storeWorkflow,
|
||||
type: AppTypeEnum.advanced,
|
||||
//@ts-ignore
|
||||
version: 'v2'
|
||||
});
|
||||
|
||||
setSaveLabel(
|
||||
t('core.app.Auto Save time', {
|
||||
time: formatTime2HM()
|
||||
})
|
||||
);
|
||||
ChatTestRef.current?.resetChatTest();
|
||||
} catch (error) {}
|
||||
|
||||
setIsSaving(false);
|
||||
|
||||
return null;
|
||||
}, [updateAppDetail, app._id, edges, ChatTestRef, t]);
|
||||
|
||||
const onclickPublish = useCallback(async () => {
|
||||
setIsSaving(true);
|
||||
const data = await flowData2StoreDataAndCheck();
|
||||
if (data) {
|
||||
try {
|
||||
await updateAppDetail(app._id, {
|
||||
modules: nodes,
|
||||
edges,
|
||||
await publishApp(app._id, {
|
||||
...data,
|
||||
type: AppTypeEnum.advanced,
|
||||
permission: undefined,
|
||||
//@ts-ignore
|
||||
version: 'v2'
|
||||
});
|
||||
toast({
|
||||
status: 'success',
|
||||
title: t('common.Save Success')
|
||||
title: t('core.app.Publish Success')
|
||||
});
|
||||
ChatTestRef.current?.resetChatTest();
|
||||
} catch (error) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(error, t('common.Save Failed'))
|
||||
title: getErrText(error, t('core.app.Publish Failed'))
|
||||
});
|
||||
}
|
||||
setIsSaving(false);
|
||||
},
|
||||
[ChatTestRef, app._id, t, toast, updateAppDetail]
|
||||
);
|
||||
}
|
||||
|
||||
setIsSaving(false);
|
||||
}, [flowData2StoreDataAndCheck, publishApp, app._id, toast, t, ChatTestRef]);
|
||||
|
||||
const saveAndBack = useCallback(async () => {
|
||||
try {
|
||||
const data = await flowData2StoreDataAndCheck();
|
||||
if (data) {
|
||||
await onclickSave(data);
|
||||
}
|
||||
await onclickSave();
|
||||
onClose();
|
||||
} catch (error) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(error)
|
||||
});
|
||||
} catch (error) {}
|
||||
}, [onClose, onclickSave]);
|
||||
|
||||
const onExportWorkflow = useCallback(async () => {
|
||||
const data = await flowData2StoreDataAndCheck();
|
||||
if (data) {
|
||||
copyData(
|
||||
JSON.stringify(
|
||||
{
|
||||
nodes: filterSensitiveNodesData(data.nodes),
|
||||
edges: data.edges
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
t('app.Export Config Successful')
|
||||
);
|
||||
}
|
||||
}, [flowData2StoreDataAndCheck, onClose, onclickSave, toast]);
|
||||
}, [copyData, flowData2StoreDataAndCheck, t]);
|
||||
|
||||
useBeforeunload({
|
||||
callback: onclickSave,
|
||||
tip: t('core.common.tip.leave page')
|
||||
});
|
||||
|
||||
useQuery(['autoSave'], onclickSave, {
|
||||
refetchInterval: 20 * 1000,
|
||||
enabled: !!app._id
|
||||
});
|
||||
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
|
|
@ -139,12 +191,29 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
|||
variant={'whiteBase'}
|
||||
aria-label={''}
|
||||
isLoading={isSaving}
|
||||
onClick={openConfirmOut(saveAndBack, onClose)}
|
||||
onClick={saveAndBack}
|
||||
/>
|
||||
<Box ml={[3, 6]} fontSize={['md', '2xl']} flex={1}>
|
||||
{app.name}
|
||||
<Box ml={[3, 5]}>
|
||||
<Box fontSize={['md', 'lg']} fontWeight={'bold'}>
|
||||
{app.name}
|
||||
</Box>
|
||||
<MyTooltip label={t('core.app.Onclick to save')}>
|
||||
<Box
|
||||
fontSize={'sm'}
|
||||
mt={1}
|
||||
display={'inline-block'}
|
||||
borderRadius={'xs'}
|
||||
cursor={'pointer'}
|
||||
onClick={onclickSave}
|
||||
color={'myGray.500'}
|
||||
>
|
||||
{saveLabel}
|
||||
</Box>
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
|
||||
<Box flex={1} />
|
||||
|
||||
<MyMenu
|
||||
Button={
|
||||
<IconButton
|
||||
|
|
@ -164,22 +233,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
|||
{
|
||||
label: t('app.Export Configs'),
|
||||
icon: 'export',
|
||||
onClick: async () => {
|
||||
const data = await flowData2StoreDataAndCheck();
|
||||
if (data) {
|
||||
copyData(
|
||||
JSON.stringify(
|
||||
{
|
||||
nodes: filterSensitiveNodesData(data.nodes),
|
||||
edges: data.edges
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
t('app.Export Config Successful')
|
||||
);
|
||||
}
|
||||
}
|
||||
onClick: onExportWorkflow
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
|
@ -202,34 +256,27 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
|||
<Button
|
||||
size={'sm'}
|
||||
isLoading={isSaving}
|
||||
leftIcon={<MyIcon name={'common/saveFill'} w={['14px', '16px']} />}
|
||||
onClick={async () => {
|
||||
const modules = await flowData2StoreDataAndCheck();
|
||||
if (modules) {
|
||||
onclickSave(modules);
|
||||
}
|
||||
}}
|
||||
leftIcon={<MyIcon name={'common/publishFill'} w={['14px', '16px']} />}
|
||||
onClick={openConfigPublish(onclickPublish)}
|
||||
>
|
||||
{t('common.Save')}
|
||||
{t('core.app.Publish')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<ConfirmModal
|
||||
closeText={t('core.app.edit.UnSave')}
|
||||
confirmText={t('core.app.edit.Save and out')}
|
||||
/>
|
||||
<ConfirmModal confirmText={t('core.app.Publish')} />
|
||||
</>
|
||||
);
|
||||
}, [
|
||||
ConfirmModal,
|
||||
app.name,
|
||||
copyData,
|
||||
flowData2StoreDataAndCheck,
|
||||
isSaving,
|
||||
onClose,
|
||||
onExportWorkflow,
|
||||
onOpenImport,
|
||||
onclickPublish,
|
||||
onclickSave,
|
||||
openConfirmOut,
|
||||
openConfigPublish,
|
||||
saveAndBack,
|
||||
saveLabel,
|
||||
setWorkflowTestData,
|
||||
t,
|
||||
theme.borders.base
|
||||
|
|
|
|||
|
|
@ -19,17 +19,15 @@ const Render = ({ app, onClose }: Props) => {
|
|||
|
||||
const { initData } = useFlowProviderStore();
|
||||
|
||||
const workflowStringData = JSON.stringify({
|
||||
nodes: app.modules || [],
|
||||
edges: app.edges || []
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isV2Workflow) return;
|
||||
initData(
|
||||
JSON.parse(
|
||||
JSON.stringify({
|
||||
nodes: app.modules || [],
|
||||
edges: app.edges || []
|
||||
})
|
||||
)
|
||||
);
|
||||
}, [isV2Workflow, app.edges, app.modules]);
|
||||
initData(JSON.parse(workflowStringData));
|
||||
}, [isV2Workflow, initData, workflowStringData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isV2Workflow) {
|
||||
|
|
@ -37,7 +35,7 @@ const Render = ({ app, onClose }: Props) => {
|
|||
initData(JSON.parse(JSON.stringify(v1Workflow2V2((app.modules || []) as any))));
|
||||
})();
|
||||
}
|
||||
}, [app.modules, isV2Workflow, openConfirm]);
|
||||
}, [app.modules, initData, isV2Workflow, openConfirm]);
|
||||
|
||||
const memoRender = useMemo(() => {
|
||||
return <Flow Header={<Header app={app} onClose={onClose} />} />;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ const EditForm = ({
|
|||
const theme = useTheme();
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const { appDetail, updateAppDetail } = useAppStore();
|
||||
const { appDetail, publishApp } = useAppStore();
|
||||
|
||||
const { loadAllDatasets, allDatasets } = useDatasetStore();
|
||||
const { isPc, llmModelList } = useSystemStore();
|
||||
|
|
@ -122,11 +122,10 @@ const EditForm = ({
|
|||
mutationFn: async (data: AppSimpleEditFormType) => {
|
||||
const { nodes, edges } = form2AppWorkflow(data);
|
||||
|
||||
await updateAppDetail(appDetail._id, {
|
||||
modules: nodes,
|
||||
await publishApp(appDetail._id, {
|
||||
nodes,
|
||||
edges,
|
||||
type: AppTypeEnum.simple,
|
||||
permission: undefined
|
||||
type: AppTypeEnum.simple
|
||||
});
|
||||
},
|
||||
successToast: t('common.Save Success'),
|
||||
|
|
|
|||
|
|
@ -28,13 +28,13 @@ const TagsEditModal = ({ onClose }: { onClose: () => void }) => {
|
|||
const { t } = useTranslation();
|
||||
const { appDetail } = useAppStore();
|
||||
const { toast } = useToast();
|
||||
const { replaceAppDetail } = useAppStore();
|
||||
const { updateAppDetail } = useAppStore();
|
||||
const [selectedTags, setSelectedTags] = useState<string[]>(appDetail?.teamTags || []);
|
||||
|
||||
// submit config
|
||||
const { mutate: saveSubmitSuccess, isLoading: btnLoading } = useRequest({
|
||||
mutationFn: async () => {
|
||||
await replaceAppDetail(appDetail._id, {
|
||||
await updateAppDetail(appDetail._id, {
|
||||
teamTags: selectedTags
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -82,22 +82,6 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||
|
||||
const onCloseFlowEdit = useCallback(() => setCurrentTab(TabEnum.simpleEdit), [setCurrentTab]);
|
||||
|
||||
useEffect(() => {
|
||||
const listen =
|
||||
process.env.NODE_ENV === 'production'
|
||||
? (e: any) => {
|
||||
e.preventDefault();
|
||||
e.returnValue = t('core.common.tip.leave page');
|
||||
}
|
||||
: () => {};
|
||||
window.addEventListener('beforeunload', listen);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', listen);
|
||||
clearAppModules();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useQuery([appId], () => loadAppDetail(appId, true), {
|
||||
onError(err: any) {
|
||||
toast({
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Box, Flex, Button, Card } from '@chakra-ui/react';
|
||||
import type { ShareAppItem } from '@/types/app';
|
||||
import { useRouter } from 'next/router';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
|
||||
const ShareModelList = ({
|
||||
models = [],
|
||||
onclickCollection
|
||||
}: {
|
||||
models: ShareAppItem[];
|
||||
onclickCollection: (appId: string) => void;
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
{models.map((model) => (
|
||||
<Card
|
||||
key={model._id}
|
||||
display={'flex'}
|
||||
w={'100%'}
|
||||
flexDirection={'column'}
|
||||
p={4}
|
||||
borderRadius={'md'}
|
||||
border={'1px solid '}
|
||||
userSelect={'none'}
|
||||
boxShadow={'none'}
|
||||
borderColor={'myGray.200'}
|
||||
_hover={{
|
||||
boxShadow: 'lg'
|
||||
}}
|
||||
>
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar
|
||||
src={model.avatar}
|
||||
w={['28px', '36px']}
|
||||
h={['28px', '36px']}
|
||||
borderRadius={'50%'}
|
||||
/>
|
||||
<Box fontWeight={'bold'} fontSize={'lg'} ml={5}>
|
||||
{model.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
<MyTooltip label={model.intro}>
|
||||
<Box
|
||||
className={'textEllipsis3'}
|
||||
flex={1}
|
||||
my={4}
|
||||
fontSize={'sm'}
|
||||
wordBreak={'break-all'}
|
||||
color={'blackAlpha.600'}
|
||||
>
|
||||
{model.intro || '这个应用还没有介绍~'}
|
||||
</Box>
|
||||
</MyTooltip>
|
||||
|
||||
<Flex justifyContent={'space-between'}>
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
color={model.isCollection ? 'primary.600' : 'blackAlpha.700'}
|
||||
onClick={() => onclickCollection(model._id)}
|
||||
>
|
||||
<MyIcon
|
||||
mr={1}
|
||||
name={model.isCollection ? 'collectionSolid' : 'collectionLight'}
|
||||
w={'16px'}
|
||||
/>
|
||||
{model.share.collection}
|
||||
</Flex>
|
||||
<Box>
|
||||
<Button
|
||||
size={'sm'}
|
||||
variant={'whitePrimary'}
|
||||
w={['60px', '70px']}
|
||||
onClick={() => router.push(`/chat?appId=${model._id}`)}
|
||||
>
|
||||
体验
|
||||
</Button>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Card>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShareModelList;
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
import React, { useState, useRef, useCallback } from 'react';
|
||||
import { Box, Flex, Grid } from '@chakra-ui/react';
|
||||
import { getShareModelList, triggerModelCollection } from '@/web/core/app/api';
|
||||
import type { ShareAppItem } from '@/types/app';
|
||||
import ShareModelList from './components/list';
|
||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
import { usePagination } from '@fastgpt/web/hooks/usePagination';
|
||||
import { useLoading } from '@fastgpt/web/hooks/useLoading';
|
||||
|
||||
const modelList = () => {
|
||||
const { Loading } = useLoading();
|
||||
const lastSearch = useRef('');
|
||||
const [searchText, setSearchText] = useState('');
|
||||
/* 加载模型 */
|
||||
const {
|
||||
data: models,
|
||||
isLoading,
|
||||
Pagination,
|
||||
getData,
|
||||
pageNum
|
||||
} = usePagination<ShareAppItem>({
|
||||
api: getShareModelList,
|
||||
pageSize: 24,
|
||||
params: {
|
||||
searchText
|
||||
}
|
||||
});
|
||||
|
||||
const onclickCollection = useCallback(
|
||||
async (appId: string) => {
|
||||
try {
|
||||
await triggerModelCollection(appId);
|
||||
getData(pageNum);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
[getData, pageNum]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box px={[5, 10]} py={[4, 6]} position={'relative'} minH={'109vh'}>
|
||||
<Flex alignItems={'center'} mb={2}>
|
||||
<Box className={'textlg'} fontWeight={'bold'} fontSize={'3xl'}>
|
||||
AI 应用市场
|
||||
</Box>
|
||||
{/* <Box mt={[2, 0]} textAlign={'right'}>
|
||||
<Input
|
||||
w={['200px', '250px']}
|
||||
size={'sm'}
|
||||
value={searchText}
|
||||
placeholder="搜索应用,回车确认"
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
onBlur={() => {
|
||||
if (searchText === lastSearch.current) return;
|
||||
getData(1);
|
||||
lastSearch.current = searchText;
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (searchText === lastSearch.current) return;
|
||||
if (e.key === 'Enter') {
|
||||
getData(1);
|
||||
lastSearch.current = searchText;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box> */}
|
||||
</Flex>
|
||||
<Grid
|
||||
templateColumns={[
|
||||
'repeat(1,1fr)',
|
||||
'repeat(2,1fr)',
|
||||
'repeat(3,1fr)',
|
||||
'repeat(4,1fr)',
|
||||
'repeat(5,1fr)'
|
||||
]}
|
||||
gridGap={4}
|
||||
mt={4}
|
||||
>
|
||||
<ShareModelList models={models} onclickCollection={onclickCollection} />
|
||||
</Grid>
|
||||
<Flex mt={4} justifyContent={'center'}>
|
||||
<Pagination />
|
||||
</Flex>
|
||||
|
||||
<Loading loading={isLoading} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export async function getServerSideProps(content: any) {
|
||||
return {
|
||||
props: {
|
||||
...(await serviceSideProps(content))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default modelList;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { Box, Flex, IconButton, useTheme, useDisclosure, Button } from '@chakra-ui/react';
|
||||
import { PluginItemSchema } from '@fastgpt/global/core/plugin/type';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
|
|
@ -7,16 +7,14 @@ import { useCopyData } from '@/web/common/hooks/useCopyData';
|
|||
import dynamic from 'next/dynamic';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { filterExportModules, flowNode2StoreNodes } from '@/components/core/workflow/utils';
|
||||
import { flowNode2StoreNodes } from '@/components/core/workflow/utils';
|
||||
import { putUpdatePlugin } from '@/web/core/plugin/api';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/index.d';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
||||
import {
|
||||
getWorkflowStore,
|
||||
useFlowProviderStore
|
||||
} from '@/components/core/workflow/Flow/FlowProvider';
|
||||
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import {
|
||||
checkWorkflowNodeAndConnection,
|
||||
filterSensitiveNodesData
|
||||
|
|
@ -51,107 +49,109 @@ const Header = ({ plugin, onClose }: Props) => {
|
|||
}, [edges, onUpdateNodeError, t, toast]);
|
||||
|
||||
const { mutate: onclickSave, isLoading } = useRequest({
|
||||
mutationFn: ({ nodes, edges }: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }) => {
|
||||
return putUpdatePlugin({
|
||||
id: plugin._id,
|
||||
modules: nodes,
|
||||
edges
|
||||
});
|
||||
},
|
||||
successToast: '保存配置成功',
|
||||
errorToast: '保存配置异常'
|
||||
mutationFn: async () => {
|
||||
const workflow = await flowData2StoreDataAndCheck();
|
||||
if (workflow) {
|
||||
await putUpdatePlugin({
|
||||
id: plugin._id,
|
||||
modules: workflow.nodes,
|
||||
edges: workflow.edges
|
||||
});
|
||||
toast({
|
||||
status: 'success',
|
||||
title: t('common.Save Success')
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
py={3}
|
||||
px={[2, 5, 8]}
|
||||
borderBottom={theme.borders.base}
|
||||
alignItems={'center'}
|
||||
userSelect={'none'}
|
||||
>
|
||||
<MyTooltip label={t('common.Back')} offset={[10, 10]}>
|
||||
<IconButton
|
||||
size={'smSquare'}
|
||||
icon={<MyIcon name={'common/backLight'} w={'14px'} />}
|
||||
variant={'whiteBase'}
|
||||
aria-label={''}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<Box ml={[3, 5]} fontSize={['md', '2xl']} flex={1}>
|
||||
{plugin.name}
|
||||
</Box>
|
||||
const onCopy = useCallback(async () => {
|
||||
const data = await flowData2StoreDataAndCheck();
|
||||
if (data) {
|
||||
copyData(
|
||||
JSON.stringify(
|
||||
{
|
||||
nodes: filterSensitiveNodesData(data.nodes),
|
||||
edges: data.edges
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
t('app.Export Config Successful')
|
||||
);
|
||||
}
|
||||
}, [copyData, flowData2StoreDataAndCheck, t]);
|
||||
|
||||
<MyMenu
|
||||
Button={
|
||||
<IconButton
|
||||
mr={[3, 5]}
|
||||
icon={<MyIcon name={'more'} w={'14px'} p={2} />}
|
||||
aria-label={''}
|
||||
size={'sm'}
|
||||
variant={'whitePrimary'}
|
||||
/>
|
||||
}
|
||||
menuList={[
|
||||
{ label: t('app.Import Configs'), icon: 'common/importLight', onClick: onOpenImport },
|
||||
{
|
||||
label: t('app.Export Configs'),
|
||||
icon: 'export',
|
||||
onClick: async () => {
|
||||
const data = await flowData2StoreDataAndCheck();
|
||||
if (data) {
|
||||
copyData(
|
||||
JSON.stringify(
|
||||
{
|
||||
nodes: filterSensitiveNodesData(data.nodes),
|
||||
edges: data.edges
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
t('app.Export Config Successful')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
]}
|
||||
/>
|
||||
{/* <MyTooltip label={t('module.Preview Plugin')}>
|
||||
<IconButton
|
||||
mr={[3, 5]}
|
||||
icon={<MyIcon name={'core/modules/previewLight'} w={['14px', '16px']} />}
|
||||
size={'smSquare'}
|
||||
aria-label={'save'}
|
||||
variant={'whitePrimary'}
|
||||
onClick={async () => {
|
||||
const modules = await flowData2StoreDataAndCheck();
|
||||
if (modules) {
|
||||
setPreviewModules(modules);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</MyTooltip> */}
|
||||
<Button
|
||||
size={'sm'}
|
||||
isLoading={isLoading}
|
||||
leftIcon={<MyIcon name={'common/saveFill'} w={['14px', '16px']} />}
|
||||
onClick={async () => {
|
||||
const modules = await flowData2StoreDataAndCheck();
|
||||
if (modules) {
|
||||
onclickSave(modules);
|
||||
}
|
||||
}}
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
py={3}
|
||||
px={[2, 5, 8]}
|
||||
borderBottom={theme.borders.base}
|
||||
alignItems={'center'}
|
||||
userSelect={'none'}
|
||||
>
|
||||
{t('common.Save')}
|
||||
</Button>
|
||||
</Flex>
|
||||
{isOpenImport && <ImportSettings onClose={onCloseImport} />}
|
||||
</>
|
||||
);
|
||||
<MyTooltip label={t('common.Back')} offset={[10, 10]}>
|
||||
<IconButton
|
||||
size={'smSquare'}
|
||||
icon={<MyIcon name={'common/backLight'} w={'14px'} />}
|
||||
variant={'whiteBase'}
|
||||
aria-label={''}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<Box ml={[3, 5]} fontSize={['md', '2xl']} flex={1}>
|
||||
{plugin.name}
|
||||
</Box>
|
||||
|
||||
<MyMenu
|
||||
Button={
|
||||
<IconButton
|
||||
mr={[3, 5]}
|
||||
icon={<MyIcon name={'more'} w={'14px'} p={2} />}
|
||||
aria-label={''}
|
||||
size={'sm'}
|
||||
variant={'whitePrimary'}
|
||||
/>
|
||||
}
|
||||
menuList={[
|
||||
{ label: t('app.Import Configs'), icon: 'common/importLight', onClick: onOpenImport },
|
||||
{
|
||||
label: t('app.Export Configs'),
|
||||
icon: 'export',
|
||||
onClick: onCopy
|
||||
}
|
||||
]}
|
||||
/>
|
||||
<Button
|
||||
size={'sm'}
|
||||
isLoading={isLoading}
|
||||
leftIcon={<MyIcon name={'common/saveFill'} w={['14px', '16px']} />}
|
||||
onClick={onclickSave}
|
||||
>
|
||||
{t('common.Save')}
|
||||
</Button>
|
||||
</Flex>
|
||||
{isOpenImport && <ImportSettings onClose={onCloseImport} />}
|
||||
</>
|
||||
);
|
||||
}, [
|
||||
isLoading,
|
||||
isOpenImport,
|
||||
onClose,
|
||||
onCloseImport,
|
||||
onCopy,
|
||||
onOpenImport,
|
||||
onclickSave,
|
||||
plugin.name,
|
||||
t,
|
||||
theme.borders.base
|
||||
]);
|
||||
|
||||
return Render;
|
||||
};
|
||||
|
||||
export default React.memo(Header);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { getErrText } from '@fastgpt/global/common/error/utils';
|
|||
import { useTranslation } from 'next-i18next';
|
||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { v1Workflow2V2 } from '@/web/core/workflow/adapt';
|
||||
import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload';
|
||||
|
||||
type Props = { pluginId: string };
|
||||
|
||||
|
|
@ -42,18 +43,16 @@ const Render = ({ pluginId }: Props) => {
|
|||
'检测到您的高级编排为旧版,系统将为您自动格式化成新版工作流。\n\n由于版本差异较大,会导致许多工作流无法正常排布,请重新手动连接工作流。如仍异常,可尝试删除对应节点后重新添加。\n\n你可以直接点击测试进行调试,无需点击保存,点击保存为新版工作流。'
|
||||
});
|
||||
|
||||
const workflowStringData = JSON.stringify({
|
||||
nodes: pluginDetail?.modules || [],
|
||||
edges: pluginDetail?.edges || []
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isV2Workflow) {
|
||||
initData(
|
||||
JSON.parse(
|
||||
JSON.stringify({
|
||||
nodes: pluginDetail?.modules || [],
|
||||
edges: pluginDetail?.edges || []
|
||||
})
|
||||
)
|
||||
);
|
||||
initData(JSON.parse(workflowStringData));
|
||||
}
|
||||
}, [isV2Workflow, pluginDetail?.edges, pluginDetail?.modules]);
|
||||
}, [initData, isV2Workflow, workflowStringData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isV2Workflow && pluginDetail) {
|
||||
|
|
@ -61,7 +60,11 @@ const Render = ({ pluginId }: Props) => {
|
|||
initData(JSON.parse(JSON.stringify(v1Workflow2V2((pluginDetail.modules || []) as any))));
|
||||
})();
|
||||
}
|
||||
}, [isV2Workflow, openConfirm, pluginDetail]);
|
||||
}, [initData, isV2Workflow, openConfirm, pluginDetail]);
|
||||
|
||||
useBeforeunload({
|
||||
tip: t('core.common.tip.leave page')
|
||||
});
|
||||
|
||||
return pluginDetail ? (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import type { NextApiResponse, NextApiHandler, NextApiRequest } from 'next';
|
||||
import { connectToDatabase } from '../mongo';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
|
||||
export const NextAPI = (...args: NextApiHandler[]): NextApiHandler => {
|
||||
return async function api(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await Promise.all([withNextCors(req, res), connectToDatabase()]);
|
||||
|
||||
let response = null;
|
||||
for (const handler of args) {
|
||||
response = await handler(req, res);
|
||||
}
|
||||
|
||||
if (!res.writableFinished) {
|
||||
return jsonRes(res, {
|
||||
code: 200,
|
||||
data: response
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
return jsonRes(res, {
|
||||
code: 500,
|
||||
error,
|
||||
url: req.url
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
|
||||
import type { AppDetailType, AppListItemType } from '@fastgpt/global/core/app/type.d';
|
||||
import { RequestPaging } from '@/types/index';
|
||||
import { addDays } from 'date-fns';
|
||||
import type { GetAppChatLogsParams } from '@/global/core/api/appReq.d';
|
||||
import type { CreateAppParams, AppUpdateParams } from '@fastgpt/global/core/app/api.d';
|
||||
import { AppUpdateParams, CreateAppParams } from '@/global/core/app/api';
|
||||
|
||||
/**
|
||||
* 获取模型列表
|
||||
|
|
@ -31,32 +29,6 @@ export const getModelById = (id: string) => GET<AppDetailType>(`/core/app/detail
|
|||
*/
|
||||
export const putAppById = (id: string, data: AppUpdateParams) =>
|
||||
PUT(`/core/app/update?appId=${id}`, data);
|
||||
export const replaceAppById = (id: string, data: AppUpdateParams) =>
|
||||
PUT(`/core/app/updateTeamTasg?appId=${id}`, data);
|
||||
|
||||
// updateTeamTasg
|
||||
export const putAppTagsById = (id: string, data: AppUpdateParams) =>
|
||||
PUT(`/core/app/updateTeamTasg?appId=${id}`, data);
|
||||
/* 共享市场 */
|
||||
/**
|
||||
* 获取共享市场模型
|
||||
*/
|
||||
export const getShareModelList = (data: { searchText?: string } & RequestPaging) =>
|
||||
POST(`/core/app/share/getModels`, data);
|
||||
|
||||
/**
|
||||
* 收藏/取消收藏模型
|
||||
*/
|
||||
export const triggerModelCollection = (appId: string) =>
|
||||
POST<number>(`/core/app/share/collection?appId=${appId}`);
|
||||
|
||||
// ====================== data
|
||||
export const getAppTotalUsage = (data: { appId: string }) =>
|
||||
POST<{ date: String; total: number }[]>(`/core/app/data/totalUsage`, {
|
||||
...data,
|
||||
start: addDays(new Date(), -13),
|
||||
end: addDays(new Date(), 1)
|
||||
}).then((res) => (res.length === 0 ? [{ date: new Date(), total: 0 }] : res));
|
||||
|
||||
// =================== chat logs
|
||||
export const getAppChatLogs = (data: GetAppChatLogsParams) => POST(`/core/app/getChatLogs`, data);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { create } from 'zustand';
|
||||
import { devtools, persist } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
import { getMyApps, getModelById, putAppById, replaceAppById } from '@/web/core/app/api';
|
||||
import { getMyApps, getModelById, putAppById } from '@/web/core/app/api';
|
||||
import { defaultApp } from '@/constants/app';
|
||||
import type { AppUpdateParams } from '@fastgpt/global/core/app/api.d';
|
||||
import type { AppUpdateParams } from '@/global/core/app/api.d';
|
||||
import { AppDetailType, AppListItemType } from '@fastgpt/global/core/app/type.d';
|
||||
import { PostPublishAppProps } from '@/global/core/app/api';
|
||||
import { postPublishApp } from '../versionApi';
|
||||
|
||||
type State = {
|
||||
myApps: AppListItemType[];
|
||||
|
|
@ -12,7 +14,7 @@ type State = {
|
|||
appDetail: AppDetailType;
|
||||
loadAppDetail: (id: string, init?: boolean) => Promise<AppDetailType>;
|
||||
updateAppDetail(appId: string, data: AppUpdateParams): Promise<void>;
|
||||
replaceAppDetail(appId: string, data: AppUpdateParams): Promise<void>;
|
||||
publishApp(appId: string, data: PostPublishAppProps): Promise<void>;
|
||||
clearAppModules(): void;
|
||||
};
|
||||
|
||||
|
|
@ -44,19 +46,22 @@ export const useAppStore = create<State>()(
|
|||
set((state) => {
|
||||
state.appDetail = {
|
||||
...state.appDetail,
|
||||
...data
|
||||
...data,
|
||||
modules: data?.nodes || state.appDetail.modules
|
||||
};
|
||||
});
|
||||
},
|
||||
async replaceAppDetail(appId: string, data: AppUpdateParams) {
|
||||
await replaceAppById(appId, { ...get().appDetail, ...data });
|
||||
async publishApp(appId: string, data: PostPublishAppProps) {
|
||||
await postPublishApp(appId, data);
|
||||
set((state) => {
|
||||
state.appDetail = {
|
||||
...state.appDetail,
|
||||
...data
|
||||
...data,
|
||||
modules: data?.nodes || state.appDetail.modules
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
clearAppModules() {
|
||||
set((state) => {
|
||||
state.appDetail = {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
import { PostPublishAppProps } from '@/global/core/app/api';
|
||||
import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
|
||||
|
||||
export const postPublishApp = (appId: string, data: PostPublishAppProps) =>
|
||||
POST(`/core/app/version/publish?appId=${appId}`, data);
|
||||
|
|
@ -15,7 +15,7 @@ import {
|
|||
FlowNodeTemplateType,
|
||||
StoreNodeItemType
|
||||
} from '@fastgpt/global/core/workflow/type';
|
||||
import { VARIABLE_NODE_ID } from './constants';
|
||||
import { VARIABLE_NODE_ID } from '@fastgpt/global/core/workflow/constants';
|
||||
import { getHandleId, splitGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { LLMModelTypeEnum } from '@fastgpt/global/core/ai/constants';
|
||||
|
|
|
|||
|
|
@ -2,34 +2,47 @@ import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants
|
|||
|
||||
export const FlowValueTypeMap = {
|
||||
[WorkflowIOValueTypeEnum.string]: {
|
||||
handlerStyle: {
|
||||
borderColor: '#36ADEF'
|
||||
},
|
||||
label: 'core.module.valueType.string',
|
||||
label: 'string',
|
||||
value: WorkflowIOValueTypeEnum.string,
|
||||
description: ''
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.number]: {
|
||||
handlerStyle: {
|
||||
borderColor: '#FB7C3C'
|
||||
},
|
||||
label: 'core.module.valueType.number',
|
||||
label: 'number',
|
||||
value: WorkflowIOValueTypeEnum.number,
|
||||
description: ''
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.boolean]: {
|
||||
handlerStyle: {
|
||||
borderColor: '#E7D118'
|
||||
},
|
||||
label: 'core.module.valueType.boolean',
|
||||
label: 'boolean',
|
||||
value: WorkflowIOValueTypeEnum.boolean,
|
||||
description: ''
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.object]: {
|
||||
label: 'object',
|
||||
value: WorkflowIOValueTypeEnum.object,
|
||||
description: ''
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.arrayString]: {
|
||||
label: 'array<string>',
|
||||
value: WorkflowIOValueTypeEnum.arrayString,
|
||||
description: ''
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.arrayNumber]: {
|
||||
label: 'array<number>',
|
||||
value: WorkflowIOValueTypeEnum.arrayNumber,
|
||||
description: ''
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.arrayBoolean]: {
|
||||
label: 'array<boolean>',
|
||||
value: WorkflowIOValueTypeEnum.arrayBoolean,
|
||||
description: ''
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.arrayObject]: {
|
||||
label: 'array<object>',
|
||||
value: WorkflowIOValueTypeEnum.arrayObject,
|
||||
description: ''
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.chatHistory]: {
|
||||
handlerStyle: {
|
||||
borderColor: '#00A9A6'
|
||||
},
|
||||
label: 'core.module.valueType.chatHistory',
|
||||
label: '历史记录',
|
||||
value: WorkflowIOValueTypeEnum.chatHistory,
|
||||
description: `{
|
||||
obj: System | Human | AI;
|
||||
|
|
@ -37,10 +50,7 @@ export const FlowValueTypeMap = {
|
|||
}[]`
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.datasetQuote]: {
|
||||
handlerStyle: {
|
||||
borderColor: '#A558C9'
|
||||
},
|
||||
label: 'core.module.valueType.datasetQuote',
|
||||
label: '知识库引用',
|
||||
value: WorkflowIOValueTypeEnum.datasetQuote,
|
||||
description: `{
|
||||
id: string;
|
||||
|
|
@ -52,42 +62,22 @@ export const FlowValueTypeMap = {
|
|||
a: string
|
||||
}[]`
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.any]: {
|
||||
handlerStyle: {
|
||||
borderColor: '#9CA2A8'
|
||||
},
|
||||
label: 'core.module.valueType.any',
|
||||
value: WorkflowIOValueTypeEnum.any,
|
||||
description: ''
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.selectApp]: {
|
||||
handlerStyle: {
|
||||
borderColor: '#6a6efa'
|
||||
},
|
||||
label: 'core.module.valueType.selectApp',
|
||||
label: '选择应用',
|
||||
value: WorkflowIOValueTypeEnum.selectApp,
|
||||
description: ''
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.selectDataset]: {
|
||||
handlerStyle: {
|
||||
borderColor: '#21ba45'
|
||||
},
|
||||
label: 'core.module.valueType.selectDataset',
|
||||
label: '选择知识库',
|
||||
value: WorkflowIOValueTypeEnum.selectDataset,
|
||||
description: ''
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.tools]: {
|
||||
handlerStyle: {
|
||||
borderColor: '#21ba45'
|
||||
},
|
||||
label: 'core.module.valueType.tools',
|
||||
value: WorkflowIOValueTypeEnum.tools,
|
||||
[WorkflowIOValueTypeEnum.any]: {
|
||||
label: 'any',
|
||||
value: WorkflowIOValueTypeEnum.any,
|
||||
description: ''
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.dynamic]: {
|
||||
handlerStyle: {
|
||||
borderColor: '#9CA2A8'
|
||||
},
|
||||
label: '动态数据',
|
||||
value: WorkflowIOValueTypeEnum.any,
|
||||
description: ''
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
export const VARIABLE_NODE_ID = 'VARIABLE_NODE_ID';
|
||||
|
|
@ -14,7 +14,7 @@ import { EmptyNode } from '@fastgpt/global/core/workflow/template/system/emptyNo
|
|||
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { systemConfigNode2VariableNode } from './adapt';
|
||||
import { VARIABLE_NODE_ID } from './constants';
|
||||
import { VARIABLE_NODE_ID } from '@fastgpt/global/core/workflow/constants';
|
||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
|
||||
export const nodeTemplate2FlowNode = ({
|
||||
|
|
|
|||
Loading…
Reference in New Issue