4.6.8 supplement (#831)

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
Archer
2024-02-15 12:26:02 +08:00
committed by GitHub
parent 51bbdf26a3
commit 91bcf8c53e
200 changed files with 4387 additions and 2749 deletions

View File

@@ -121,7 +121,9 @@ export type moduleDispatchResType = {
extractResult?: Record<string, any>;
// http
params?: Record<string, any>;
body?: Record<string, any>;
headers?: Record<string, any>;
httpResult?: Record<string, any>;
// plugin output

View File

@@ -1,14 +1,11 @@
import { VectorModelItemType } from '../ai/model.d';
import { DYNAMIC_INPUT_KEY } from './constants';
export type SelectedDatasetType = { datasetId: string; vectorModel: VectorModelItemType }[];
export type HttpBodyType<T = any> = {
appId: string;
chatId?: string;
responseChatItemId?: string;
variables: Record<string, any>;
data: T;
};
[DYNAMIC_INPUT_KEY]: Record<string, any>;
} & T;
export type HttpQueryType = {
appId: string;
chatId?: string;

View File

@@ -74,8 +74,10 @@ export enum ModuleInputKeyEnum {
// http
httpReqUrl = 'system_httpReqUrl',
httpHeader = 'system_httpHeader',
httpHeaders = 'system_httpHeader',
httpMethod = 'system_httpMethod',
httpParams = 'system_httpParams',
httpJsonBody = 'system_httpJsonBody',
abandon_httpUrl = 'url',
// app

View File

@@ -53,6 +53,7 @@ export enum FlowNodeTypeEnum {
classifyQuestion = 'classifyQuestion',
contentExtract = 'contentExtract',
httpRequest = 'httpRequest',
httpRequest468 = 'httpRequest468',
runApp = 'app',
pluginModule = 'pluginModule',
pluginInput = 'pluginInput',

View File

@@ -2,15 +2,19 @@ import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type';
import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
} from '../../../node/constant';
import { FlowModuleTemplateType } from '../../../type';
import {
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleTemplateTypeEnum
} from '../../../constants';
import {
Input_Template_AddInputParam,
Input_Template_DynamicInput,
Input_Template_Switch
} from '../input';
import { Output_Template_AddOutput, Output_Template_Finish } from '../output';
} from '../../input';
import { Output_Template_AddOutput, Output_Template_Finish } from '../../output';
export const HttpModule: FlowModuleTemplateType = {
id: FlowNodeTypeEnum.httpRequest,
@@ -18,7 +22,8 @@ export const HttpModule: FlowModuleTemplateType = {
flowType: FlowNodeTypeEnum.httpRequest,
avatar: '/imgs/module/http.png',
name: 'core.module.template.Http request',
intro: 'core.module.template.Http request intro',
intro:
'该Http模块已被弃用将于2024/3/31 不再提供服务。请尽快删除该模块并重新添加新的 Http 模块。',
showStatus: true,
inputs: [
Input_Template_Switch,
@@ -54,9 +59,10 @@ export const HttpModule: FlowModuleTemplateType = {
showTargetInPlugin: false
},
{
key: ModuleInputKeyEnum.httpHeader,
key: ModuleInputKeyEnum.httpHeaders,
type: FlowNodeInputTypeEnum.JSONEditor,
valueType: ModuleIOValueTypeEnum.string,
value: '',
label: 'core.module.input.label.Http Request Header',
description: 'core.module.input.description.Http Request Header',
placeholder: 'core.module.input.description.Http Request Header',

View File

@@ -23,7 +23,7 @@ export const AiCFR: FlowModuleTemplateType = {
flowType: FlowNodeTypeEnum.cfr,
avatar: '/imgs/module/cfr.svg',
name: 'core.module.template.Query extension',
intro: '该模块已合并到知识库搜索参数中,无需单独使用。',
intro: '该模块已合并到知识库搜索参数中,无需单独使用。模块将于2024/3/31弃用请尽快修改。',
showStatus: true,
inputs: [
Input_Template_Switch,

View File

@@ -0,0 +1,122 @@
import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type';
import {
DYNAMIC_INPUT_KEY,
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleTemplateTypeEnum
} from '../../constants';
import {
Input_Template_AddInputParam,
Input_Template_DynamicInput,
Input_Template_Switch
} from '../input';
import { Output_Template_AddOutput, Output_Template_Finish } from '../output';
export const HttpModule468: FlowModuleTemplateType = {
id: FlowNodeTypeEnum.httpRequest468,
templateType: ModuleTemplateTypeEnum.externalCall,
flowType: FlowNodeTypeEnum.httpRequest468,
avatar: '/imgs/module/http.png',
name: 'core.module.template.Http request',
intro: 'core.module.template.Http request intro',
showStatus: true,
inputs: [
Input_Template_Switch,
{
key: ModuleInputKeyEnum.httpMethod,
type: FlowNodeInputTypeEnum.custom,
valueType: ModuleIOValueTypeEnum.string,
label: '',
value: 'POST',
required: true,
showTargetInApp: false,
showTargetInPlugin: false
},
{
key: ModuleInputKeyEnum.httpReqUrl,
type: FlowNodeInputTypeEnum.hidden,
valueType: ModuleIOValueTypeEnum.string,
label: '',
description: 'core.module.input.description.Http Request Url',
placeholder: 'https://api.ai.com/getInventory',
required: false,
showTargetInApp: false,
showTargetInPlugin: false
},
{
key: ModuleInputKeyEnum.httpHeaders,
type: FlowNodeInputTypeEnum.custom,
valueType: ModuleIOValueTypeEnum.any,
value: [],
label: '',
description: 'core.module.input.description.Http Request Header',
placeholder: 'core.module.input.description.Http Request Header',
required: false,
showTargetInApp: false,
showTargetInPlugin: false
},
{
key: ModuleInputKeyEnum.httpParams,
type: FlowNodeInputTypeEnum.hidden,
valueType: ModuleIOValueTypeEnum.any,
value: [],
label: '',
required: false,
showTargetInApp: false,
showTargetInPlugin: false
},
{
key: ModuleInputKeyEnum.httpJsonBody,
type: FlowNodeInputTypeEnum.hidden,
valueType: ModuleIOValueTypeEnum.any,
value: '',
label: '',
required: false,
showTargetInApp: false,
showTargetInPlugin: false
},
Input_Template_DynamicInput,
{
...Input_Template_AddInputParam,
editField: {
key: true,
name: true,
description: true,
required: true,
dataType: true
},
defaultEditField: {
label: '',
key: '',
description: '',
inputType: FlowNodeInputTypeEnum.target,
valueType: ModuleIOValueTypeEnum.string,
required: true
}
}
],
outputs: [
Output_Template_Finish,
{
...Output_Template_AddOutput,
editField: {
key: true,
name: true,
description: true,
dataType: true
},
defaultEditField: {
label: '',
key: '',
description: '',
outputType: FlowNodeOutputTypeEnum.source,
valueType: ModuleIOValueTypeEnum.string
}
}
]
};

View File

@@ -6,7 +6,6 @@ export const RunPluginModule: FlowModuleTemplateType = {
id: FlowNodeTypeEnum.pluginModule,
templateType: ModuleTemplateTypeEnum.externalCall,
flowType: FlowNodeTypeEnum.pluginModule,
avatar: '',
intro: '',
name: '',
showStatus: false,

View File

@@ -1,6 +1,7 @@
export enum sseResponseEventEnum {
error = 'error',
answer = 'answer',
answer = 'answer', // animation stream
response = 'response', // direct response, not animation
moduleStatus = 'moduleStatus',
appStreamResponse = 'appStreamResponse' // sse response request
}

View File

@@ -1,17 +1,20 @@
import React, { useEffect } from 'react';
import Editor, { loader, useMonaco } from '@monaco-editor/react';
import { useCallback, useRef, useState } from 'react';
import React, { useEffect, useCallback, useRef, useState } from 'react';
import Editor, { Monaco, loader, useMonaco } from '@monaco-editor/react';
import { Box, BoxProps } from '@chakra-ui/react';
import MyIcon from '../../Icon';
import { EditorVariablePickerType } from '../PromptEditor/type';
import { useToast } from '../../../../hooks/useToast';
import { useTranslation } from 'next-i18next';
loader.config({
paths: { vs: 'https://cdn.staticfile.net/monaco-editor/0.43.0/min/vs' }
paths: { vs: '/js/monaco-editor.0.45.0/vs' }
});
type Props = Omit<BoxProps, 'onChange' | 'resize' | 'height'> & {
type EditorVariablePickerType = {
key: string;
label: string;
};
type Props = Omit<BoxProps, 'resize' | 'onChange'> & {
height?: number;
resize?: boolean;
defaultValue?: string;
@@ -42,43 +45,95 @@ const options = {
tabSize: 2
};
const JSONEditor = ({ defaultValue, value, onChange, resize, variables, ...props }: Props) => {
const JSONEditor = ({ defaultValue, value, onChange, resize, variables = [], ...props }: Props) => {
const { toast } = useToast();
const { t } = useTranslation();
const [height, setHeight] = useState(props.height || 100);
const initialY = useRef(0);
const completionRegisterRef = useRef<any>();
const monaco = useMonaco();
const triggerChar = useRef<string>();
useEffect(() => {
completionRegisterRef.current = monaco?.languages.registerCompletionItemProvider('json', {
triggerCharacters: ['"'],
provideCompletionItems: function (model, position) {
var word = model.getWordUntilPosition(position);
var range = {
if (!monaco) return;
// 自定义补全提供者
completionRegisterRef.current = monaco.languages.registerCompletionItemProvider('json', {
triggerCharacters: ['{'],
provideCompletionItems: function (model, position, context) {
const lineContent = model.getLineContent(position.lineNumber);
if (context.triggerCharacter) {
console.log(context.triggerCharacter);
triggerChar.current = context.triggerCharacter;
}
const word = model.getWordUntilPosition(position);
const range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn
};
const startText = lineContent.substring(0, position.column - 1); // 光标前的文本
const endText = lineContent.substring(position.column - 1); // 光标后的文本
const before2Char = startText[startText.length - 2];
const beforeChar = startText[startText.length - 1];
const afterChar = endText[0];
const after2Char = endText[1];
if (before2Char !== '{' && beforeChar !== '"') {
return {
suggestions: []
};
}
return {
suggestions:
variables?.map((item) => ({
label: `${item.label}`,
kind: monaco.languages.CompletionItemKind.Function,
documentation: item.label,
insertText: `{{${item.label}}}`,
range: range
})) || [],
dispose: () => {}
variables?.map((item) => {
let insertText = item.key;
if (before2Char !== '{') {
insertText = `{${insertText}`;
}
if (afterChar !== '}') {
insertText = `${insertText}}`;
}
if (after2Char !== '}') {
insertText = `${insertText}}`;
}
return {
label: item.key,
kind: monaco.languages.CompletionItemKind.Variable,
detail: item.label,
insertText: insertText,
range
};
}) || []
};
}
});
// 自定义语法高亮
monaco.languages.setMonarchTokensProvider('json', {
tokenizer: {
root: [
// 匹配variables里的变量
[new RegExp(`{{(${variables.map((item) => item.key).join('|')})}}`), 'variable'],
[/".*?"/, 'string'], // 匹配字符串
[/[{}\[\]]/, '@brackets'], // 匹配括号
[/[0-9]+/, 'number'], // 匹配数字
[/true|false/, 'keyword'], // 匹配布尔值
[/:/, 'delimiter'], // 匹配冒号
[/,/, 'delimiter.comma'] // 匹配逗号
]
}
});
return () => {
completionRegisterRef.current?.dispose();
};
}, [monaco, completionRegisterRef.current]);
}, [monaco, variables]);
const handleMouseDown = useCallback((e: React.MouseEvent) => {
initialY.current = e.clientY;
@@ -98,6 +153,48 @@ const JSONEditor = ({ defaultValue, value, onChange, resize, variables, ...props
document.addEventListener('mouseup', handleMouseUp);
}, []);
const onBlur = useCallback(() => {
if (!value) return;
// replace {{xx}} to true
const replaceValue = value?.replace(/{{(.*?)}}/g, 'true');
try {
JSON.parse(replaceValue);
} catch (error) {
toast({
status: 'warning',
title: t('common.jsonEditor.Parse error')
});
}
}, [value]);
const beforeMount = useCallback((monaco: Monaco) => {
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: false,
allowComments: false,
schemas: [
{
uri: 'http://myserver/foo-schema.json', // 一个假设的 URI
fileMatch: ['*'], // 匹配所有文件
schema: {} // 空的 Schema
}
]
});
monaco.editor.defineTheme('JSONEditorTheme', {
base: 'vs', // 可以基于已有的主题进行定制
inherit: true, // 继承基础主题的设置
rules: [{ token: 'variable', foreground: '2B5FD9' }],
colors: {
'editor.background': '#ffffff00',
'editorLineNumber.foreground': '#aaa',
'editorOverviewRuler.border': '#ffffff00',
'editor.lineHighlightBackground': '#F7F8FA',
'scrollbarSlider.background': '#E8EAEC',
'editorIndentGuide.activeBackground': '#ddd',
'editorIndentGuide.background': '#eee'
}
});
}, []);
return (
<Box position={'relative'}>
{resize && (
@@ -105,7 +202,7 @@ const JSONEditor = ({ defaultValue, value, onChange, resize, variables, ...props
position={'absolute'}
right={'0'}
bottom={'0'}
zIndex={999}
zIndex={10}
cursor={'ns-resize'}
px={'4px'}
onMouseDown={handleMouseDown}
@@ -119,47 +216,20 @@ const JSONEditor = ({ defaultValue, value, onChange, resize, variables, ...props
borderRadius={'md'}
borderColor={'myGray.200'}
py={2}
{...props}
height={'auto'}
{...props}
>
<Editor
height={height}
defaultLanguage="json"
options={options as any}
theme={'JSONEditorTheme'}
beforeMount={(monaco) => {
monaco?.editor.defineTheme('JSONEditorTheme', {
base: 'vs',
inherit: true,
rules: [],
colors: {
'editor.background': '#ffffff00',
'editorLineNumber.foreground': '#aaa',
'editorOverviewRuler.border': '#ffffff00',
'editor.lineHighlightBackground': '#F7F8FA',
'scrollbarSlider.background': '#E8EAEC',
'editorIndentGuide.activeBackground': '#ddd',
'editorIndentGuide.background': '#eee'
}
});
}}
theme="JSONEditorTheme"
beforeMount={beforeMount}
defaultValue={defaultValue}
value={value}
onChange={(e) => onChange?.(e || '')}
wrapperProps={{
onBlur: () => {
if (!value) return;
try {
JSON.parse(value as string);
} catch (error: any) {
toast({
title: t('common.Invalid Json'),
description: error.message,
status: 'warning',
isClosable: true
});
}
}
onBlur
}}
/>
</Box>

View File

@@ -10,6 +10,7 @@ import { useCallback, useTransition } from 'react';
const PromptEditor = ({
showOpenModal = true,
showResize = true,
variables = [],
value,
onChange,
@@ -19,6 +20,7 @@ const PromptEditor = ({
title
}: {
showOpenModal?: boolean;
showResize?: boolean;
variables?: EditorVariablePickerType[];
value?: string;
onChange?: (text: string) => void;
@@ -48,7 +50,7 @@ const PromptEditor = ({
return (
<>
<Editor
showResize
showResize={showResize}
showOpenModal={showOpenModal}
onOpenModal={onOpen}
variables={variables}

View File

@@ -100,7 +100,7 @@ export default function VariablePickerPlugin({
p={2}
borderRadius={'md'}
position={'fixed'}
w={'200px'}
w={'auto'}
overflow={'hidden'}
zIndex={99999}
>
@@ -113,6 +113,8 @@ export default function VariablePickerPlugin({
py={2}
borderRadius={'sm'}
cursor={'pointer'}
maxH={'300px'}
overflow={'auto'}
_notLast={{
mb: 2
}}
@@ -133,8 +135,11 @@ export default function VariablePickerPlugin({
setHighlightedIndex(index);
}}
>
<MyIcon name={item.icon as any} w={'14px'} />
<Box ml={2} fontSize={'sm'}>{`${item.key}(${item.label})`}</Box>
<MyIcon name={(item.icon as any) || 'core/modules/variable'} w={'14px'} />
<Box ml={2} fontSize={'sm'}>
{item.key}
{item.key !== item.label && `(${item.label})`}
</Box>
</Flex>
))}
</Box>,