Kaynağa Gözat

Feat: Add the JS code (or other) executor component to Agent. #4977 (#7677)

### What problem does this PR solve?

Feat: Add the JS code (or other) executor component to Agent. #4977

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
tags/v0.19.0
balibabu 5 ay önce
ebeveyn
işleme
008e55a65e
No account linked to committer's email address

+ 26
- 0
web/src/constants/agent.ts Dosyayı Görüntüle

export enum ProgrammingLanguage {
Python = 'python',
Javascript = 'javascript',
}

export const CodeTemplateStrMap = {
[ProgrammingLanguage.Python]: `
def main(arg1: str, arg2: str) -> dict:
return {
"result": arg1 + arg2,
}
`,
[ProgrammingLanguage.Javascript]: `
const axios = require('axios');
async function main(args) {
try {
const response = await axios.get('https://github.com/infiniflow/ragflow');
console.log('Body:', response.data);
} catch (error) {
console.error('Error:', error.message);
}
}

module.exports = { main };
`,
};

+ 7
- 0
web/src/interfaces/database/flow.ts Dosyayı Görüntüle

kb_ids: string[]; kb_ids: string[];
} }


export interface ICodeForm {
inputs?: Array<{ name?: string; component_id?: string }>;
lang: string;
script?: string;
}

export type BaseNodeData<TForm extends any> = { export type BaseNodeData<TForm extends any> = {
label: string; // operator type label: string; // operator type
name: string; // operator name name: string; // operator name
export type IIterationNode = BaseNode; export type IIterationNode = BaseNode;
export type IIterationStartNode = BaseNode; export type IIterationStartNode = BaseNode;
export type IKeywordNode = BaseNode; export type IKeywordNode = BaseNode;
export type ICodeNode = BaseNode<ICodeForm>;


export type RAGFlowNodeType = export type RAGFlowNodeType =
| IBeginNode | IBeginNode

+ 3
- 0
web/src/locales/en.ts Dosyayı Görüntüle

knowledgeBasesTip: knowledgeBasesTip:
'Select the knowledge bases to associate with this chat assistant, or choose variables containing knowledge base IDs below.', 'Select the knowledge bases to associate with this chat assistant, or choose variables containing knowledge base IDs below.',
knowledgeBaseVars: 'Knowledge base variables', knowledgeBaseVars: 'Knowledge base variables',
code: 'Code',
codeDescription: 'It allows developers to write custom Python logic.',
inputVariables: 'Input variables',
runningHintText: 'is running...🕞', runningHintText: 'is running...🕞',
}, },
}, },

+ 2
- 1
web/src/locales/ja.ts Dosyayı Görüntüle

{knowledge} {knowledge}
上記がナレッジベースです。`, 上記がナレッジベースです。`,
systemMessage: '入力してください!', systemMessage: '入力してください!',
systemTip: 'LLMが質問に答える際に従う指示を設定します。モデルがネイティブで推論をサポートしている場合、推論を停止するためにプロンプトに //no_thinking を追加できます。',
systemTip:
'LLMが質問に答える際に従う指示を設定します。モデルがネイティブで推論をサポートしている場合、推論を停止するためにプロンプトに //no_thinking を追加できます。',
topN: 'トップN', topN: 'トップN',
topNTip: `類似度スコアがしきい値を超えるチャンクのうち、上位N件のみがLLMに供給されます。`, topNTip: `類似度スコアがしきい値を超えるチャンクのうち、上位N件のみがLLMに供給されます。`,
variable: '変数', variable: '変数',

+ 3
- 0
web/src/locales/zh-traditional.ts Dosyayı Görüntüle

promptMessage: '提示詞是必填項', promptMessage: '提示詞是必填項',
promptTip: promptTip:
'系統提示為大型模型提供任務描述、規定回覆方式,以及設定其他各種要求。系統提示通常與 key(變數)合用,透過變數設定大型模型的輸入資料。你可以透過斜線或 (x) 按鈕顯示可用的 key。', '系統提示為大型模型提供任務描述、規定回覆方式,以及設定其他各種要求。系統提示通常與 key(變數)合用,透過變數設定大型模型的輸入資料。你可以透過斜線或 (x) 按鈕顯示可用的 key。',
code: '程式碼',
codeDescription: '它允許開發人員編寫自訂 Python 邏輯。',
inputVariables: '輸入變數',
runningHintText: '正在運行...🕞', runningHintText: '正在運行...🕞',
}, },
footer: { footer: {

+ 4
- 0
web/src/locales/zh.ts Dosyayı Görüntüle

'系统提示为大模型提供任务描述、规定回复方式,以及设置其他各种要求。系统提示通常与 key (变量)合用,通过变量设置大模型的输入数据。你可以通过斜杠或者 (x) 按钮显示可用的 key。', '系统提示为大模型提供任务描述、规定回复方式,以及设置其他各种要求。系统提示通常与 key (变量)合用,通过变量设置大模型的输入数据。你可以通过斜杠或者 (x) 按钮显示可用的 key。',
knowledgeBasesTip: '选择关联的知识库,或者在下方选择包含知识库ID的变量。', knowledgeBasesTip: '选择关联的知识库,或者在下方选择包含知识库ID的变量。',
knowledgeBaseVars: '知识库变量', knowledgeBaseVars: '知识库变量',
code: '代码',
codeDescription: '它允许开发人员编写自定义 Python 逻辑。',
inputVariables: '输入变量',
addVariable: '新增变量',
runningHintText: '正在运行中...🕞', runningHintText: '正在运行中...🕞',
}, },
footer: { footer: {

+ 23
- 0
web/src/pages/flow/constant.tsx Dosyayı Görüntüle

import { ReactComponent as TuShareIcon } from '@/assets/svg/tushare.svg'; import { ReactComponent as TuShareIcon } from '@/assets/svg/tushare.svg';
import { ReactComponent as WenCaiIcon } from '@/assets/svg/wencai.svg'; import { ReactComponent as WenCaiIcon } from '@/assets/svg/wencai.svg';
import { ReactComponent as YahooFinanceIcon } from '@/assets/svg/yahoo-finance.svg'; import { ReactComponent as YahooFinanceIcon } from '@/assets/svg/yahoo-finance.svg';
import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent';


// 邮件功能 // 邮件功能


import { import {
CirclePower, CirclePower,
CloudUpload, CloudUpload,
CodeXml,
Database, Database,
IterationCcw, IterationCcw,
ListOrdered, ListOrdered,
Email = 'Email', Email = 'Email',
Iteration = 'Iteration', Iteration = 'Iteration',
IterationStart = 'IterationItem', IterationStart = 'IterationItem',
Code = 'Code',
} }


export const CommonOperatorList = Object.values(Operator).filter( export const CommonOperatorList = Object.values(Operator).filter(
[Operator.Email]: EmailIcon, [Operator.Email]: EmailIcon,
[Operator.Iteration]: IterationCcw, [Operator.Iteration]: IterationCcw,
[Operator.IterationStart]: CirclePower, [Operator.IterationStart]: CirclePower,
[Operator.Code]: CodeXml,
}; };


export const operatorMap: Record< export const operatorMap: Record<
[Operator.Email]: { backgroundColor: '#e6f7ff' }, [Operator.Email]: { backgroundColor: '#e6f7ff' },
[Operator.Iteration]: { backgroundColor: '#e6f7ff' }, [Operator.Iteration]: { backgroundColor: '#e6f7ff' },
[Operator.IterationStart]: { backgroundColor: '#e6f7ff' }, [Operator.IterationStart]: { backgroundColor: '#e6f7ff' },
[Operator.Code]: { backgroundColor: '#4c5458' },
}; };


export const componentMenuList = [ export const componentMenuList = [
{ {
name: Operator.Iteration, name: Operator.Iteration,
}, },
{
name: Operator.Code,
},
{ {
name: Operator.Note, name: Operator.Note,
}, },
}; };
export const initialIterationStartValues = {}; export const initialIterationStartValues = {};


export const initialCodeValues = {
lang: 'python',
script: CodeTemplateStrMap[ProgrammingLanguage.Python],
arguments: [
{
name: 'arg1',
},
{
name: 'arg2',
},
],
};

export const CategorizeAnchorPointPositions = [ export const CategorizeAnchorPointPositions = [
{ top: 1, right: 34 }, { top: 1, right: 34 },
{ top: 8, right: 18 }, { top: 8, right: 18 },
[Operator.Email]: [Operator.Begin], [Operator.Email]: [Operator.Begin],
[Operator.Iteration]: [Operator.Begin], [Operator.Iteration]: [Operator.Begin],
[Operator.IterationStart]: [Operator.Begin], [Operator.IterationStart]: [Operator.Begin],
[Operator.Code]: [Operator.Begin],
}; };


export const NodeMap = { export const NodeMap = {
[Operator.Email]: 'emailNode', [Operator.Email]: 'emailNode',
[Operator.Iteration]: 'group', [Operator.Iteration]: 'group',
[Operator.IterationStart]: 'iterationStartNode', [Operator.IterationStart]: 'iterationStartNode',
[Operator.Code]: 'ragNode',
}; };


export const LanguageOptions = [ export const LanguageOptions = [

+ 2
- 0
web/src/pages/flow/flow-drawer/index.tsx Dosyayı Görüntüle

import BeginForm from '../form/begin-form'; import BeginForm from '../form/begin-form';
import BingForm from '../form/bing-form'; import BingForm from '../form/bing-form';
import CategorizeForm from '../form/categorize-form'; import CategorizeForm from '../form/categorize-form';
import CodeForm from '../form/code-form';
import CrawlerForm from '../form/crawler-form'; import CrawlerForm from '../form/crawler-form';
import DeepLForm from '../form/deepl-form'; import DeepLForm from '../form/deepl-form';
import DuckDuckGoForm from '../form/duckduckgo-form'; import DuckDuckGoForm from '../form/duckduckgo-form';
[Operator.Email]: EmailForm, [Operator.Email]: EmailForm,
[Operator.Iteration]: IterationForm, [Operator.Iteration]: IterationForm,
[Operator.IterationStart]: () => <></>, [Operator.IterationStart]: () => <></>,
[Operator.Code]: CodeForm,
}; };


const EmptyContent = () => <div></div>; const EmptyContent = () => <div></div>;

+ 66
- 0
web/src/pages/flow/form/code-form/dynamic-input-variable.tsx Dosyayı Görüntüle

import { RAGFlowNodeType } from '@/interfaces/database/flow';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Form, Input, Select } from 'antd';
import { useTranslation } from 'react-i18next';
import { useBuildComponentIdSelectOptions } from '../../hooks/use-get-begin-query';
import { FormCollapse } from '../components/dynamic-input-variable';

type DynamicInputVariableProps = {
name?: string;
node?: RAGFlowNodeType;
};

export const DynamicInputVariable = ({
name = 'arguments',
node,
}: DynamicInputVariableProps) => {
const { t } = useTranslation();

const valueOptions = useBuildComponentIdSelectOptions(
node?.id,
node?.parentId,
);

return (
<FormCollapse title={t('flow.inputVariables')}>
<Form.List name={name}>
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, ...restField }) => (
<div key={key} className="flex items-center gap-2 pb-4">
<Form.Item
{...restField}
name={[name, 'name']}
className="m-0 flex-1"
>
<Input />
</Form.Item>
<Form.Item
{...restField}
name={[name, 'component_id']}
className="m-0 flex-1"
>
<Select
placeholder={t('common.pleaseSelect')}
options={valueOptions}
></Select>
</Form.Item>
<MinusCircleOutlined onClick={() => remove(name)} />
</div>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
block
icon={<PlusOutlined />}
>
{t('flow.addVariable')}
</Button>
</Form.Item>
</>
)}
</Form.List>
</FormCollapse>
);
};

+ 66
- 0
web/src/pages/flow/form/code-form/dynamic-output-variable.tsx Dosyayı Görüntüle

import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Form, Input, Select } from 'antd';
import { useTranslation } from 'react-i18next';
import { FormCollapse } from '../components/dynamic-input-variable';

type DynamicOutputVariableProps = {
name?: string;
};

const options = [
'String',
'Number',
'Boolean',
'Array[String]',
'Array[Number]',
'Object',
].map((x) => ({ label: x, value: x }));

export const DynamicOutputVariable = ({
name = 'output',
}: DynamicOutputVariableProps) => {
const { t } = useTranslation();

return (
<FormCollapse title={t('flow.output')}>
<Form.List name={name}>
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, ...restField }) => (
<div key={key} className="flex items-center gap-2 pb-4">
<Form.Item
{...restField}
name={[name, 'first']}
className="m-0 flex-1"
>
<Input />
</Form.Item>
<Form.Item
{...restField}
name={[name, 'last']}
className="m-0 flex-1"
>
<Select
placeholder={t('common.pleaseSelect')}
options={options}
></Select>
</Form.Item>
<MinusCircleOutlined onClick={() => remove(name)} />
</div>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
block
icon={<PlusOutlined />}
>
{t('flow.addVariable')}
</Button>
</Form.Item>
</>
)}
</Form.List>
</FormCollapse>
);
};

+ 16
- 0
web/src/pages/flow/form/code-form/index.less Dosyayı Görüntüle

.languageItem {
margin: 0;
:global(.ant-select-selector) {
background: transparent !important;
border: none !important;
box-shadow: none !important;
}
:global(.ant-select-selector:hover) {
border: none !important;
box-shadow: none !important;
}
:global(.ant-select-focused .ant-select-selector) {
border: none !important;
box-shadow: none !important;
}
}

+ 67
- 0
web/src/pages/flow/form/code-form/index.tsx Dosyayı Görüntüle

import Editor, { loader } from '@monaco-editor/react';
import { Form, Select } from 'antd';
import { IOperatorForm } from '../../interface';
import { DynamicInputVariable } from './dynamic-input-variable';

import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent';
import { ICodeForm } from '@/interfaces/database/flow';
import { useEffect } from 'react';
import styles from './index.less';

loader.config({ paths: { vs: '/vs' } });

const options = [
ProgrammingLanguage.Python,
ProgrammingLanguage.Javascript,
].map((x) => ({ value: x, label: x }));

const CodeForm = ({ onValuesChange, form, node }: IOperatorForm) => {
const formData = node?.data.form as ICodeForm;

useEffect(() => {
setTimeout(() => {
// TODO: Direct operation zustand is more elegant
form?.setFieldValue(
'script',
CodeTemplateStrMap[formData.lang as ProgrammingLanguage],
);
}, 0);
}, [form, formData.lang]);

return (
<Form
name="basic"
autoComplete="off"
form={form}
onValuesChange={onValuesChange}
layout={'vertical'}
>
<DynamicInputVariable node={node}></DynamicInputVariable>
<Form.Item
name={'script'}
label={
<Form.Item name={'lang'} className={styles.languageItem}>
<Select
defaultValue={'python'}
popupMatchSelectWidth={false}
options={options}
/>
</Form.Item>
}
className="bg-gray-100 rounded dark:bg-gray-800"
>
<Editor
height={200}
theme="vs-dark"
language={formData.lang}
options={{
minimap: { enabled: false },
automaticLayout: true,
}}
/>
</Form.Item>
</Form>
);
};

export default CodeForm;

+ 6
- 6
web/src/pages/flow/form/components/dynamic-input-variable.tsx Dosyayı Görüntüle

type === VariableType.Reference ? 'component_id' : 'value'; type === VariableType.Reference ? 'component_id' : 'value';


const DynamicVariableForm = ({ name: formName, node }: IProps) => { const DynamicVariableForm = ({ name: formName, node }: IProps) => {
formName = formName || 'query';
const nextFormName = formName || 'query';
const { t } = useTranslation(); const { t } = useTranslation();
const valueOptions = useBuildComponentIdSelectOptions( const valueOptions = useBuildComponentIdSelectOptions(
node?.id, node?.id,
const handleTypeChange = useCallback( const handleTypeChange = useCallback(
(name: number) => () => { (name: number) => () => {
setTimeout(() => { setTimeout(() => {
form.setFieldValue([formName, name, 'component_id'], undefined);
form.setFieldValue([formName, name, 'value'], undefined);
form.setFieldValue([nextFormName, name, 'component_id'], undefined);
form.setFieldValue([nextFormName, name, 'value'], undefined);
}, 0); }, 0);
}, },
[form],
[form, nextFormName],
); );


return ( return (
<Form.List name={formName}>
<Form.List name={nextFormName}>
{(fields, { add, remove }) => ( {(fields, { add, remove }) => (
<> <>
{fields.map(({ key, name, ...restField }) => ( {fields.map(({ key, name, ...restField }) => (
</Form.Item> </Form.Item>
<Form.Item noStyle dependencies={[name, 'type']}> <Form.Item noStyle dependencies={[name, 'type']}>
{({ getFieldValue }) => { {({ getFieldValue }) => {
const type = getFieldValue([formName, name, 'type']);
const type = getFieldValue([nextFormName, name, 'type']);
return ( return (
<Form.Item <Form.Item
{...restField} {...restField}

+ 2
- 0
web/src/pages/flow/hooks.tsx Dosyayı Görüntüle

initialBeginValues, initialBeginValues,
initialBingValues, initialBingValues,
initialCategorizeValues, initialCategorizeValues,
initialCodeValues,
initialConcentratorValues, initialConcentratorValues,
initialCrawlerValues, initialCrawlerValues,
initialDeepLValues, initialDeepLValues,
[Operator.Email]: initialEmailValues, [Operator.Email]: initialEmailValues,
[Operator.Iteration]: initialIterationValues, [Operator.Iteration]: initialIterationValues,
[Operator.IterationStart]: initialIterationValues, [Operator.IterationStart]: initialIterationValues,
[Operator.Code]: initialCodeValues,
}; };
}, [llmId]); }, [llmId]);



Loading…
İptal
Kaydet