mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 12:32:30 +08:00
Feat: Globally defined conversation variables can be selected in the operator's query variables. #10427 (#11135)
### What problem does this PR solve? Feat: Globally defined conversation variables can be selected in the operator's query variables. #10427 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
import { KeyInput } from '@/components/key-input';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
@ -112,10 +113,10 @@ const AddFieldButton: FC<AddFieldButtonProps> = ({
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<Input
|
||||
<KeyInput
|
||||
id={fieldNameId}
|
||||
value={fieldName}
|
||||
onChange={(e) => setFieldName(e.target.value)}
|
||||
onChange={setFieldName}
|
||||
placeholder={t.fieldNamePlaceholder}
|
||||
className="font-mono text-sm w-full"
|
||||
required
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { KeyInput } from '@/components/key-input';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { ChevronDown, ChevronRight, X } from 'lucide-react';
|
||||
@ -114,9 +115,9 @@ export const SchemaPropertyEditor: React.FC<SchemaPropertyEditorProps> = ({
|
||||
<div className="flex items-center gap-2 grow min-w-0 overflow-visible">
|
||||
<div className="flex items-center gap-2 min-w-0 grow overflow-visible">
|
||||
{isEditingName ? (
|
||||
<Input
|
||||
<KeyInput
|
||||
value={tempName}
|
||||
onChange={(e) => setTempName(e.target.value)}
|
||||
onChange={setTempName}
|
||||
onBlur={handleNameSubmit}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleNameSubmit()}
|
||||
className="h-8 text-sm font-medium min-w-[120px] max-w-full z-10"
|
||||
|
||||
23
web/src/components/key-input.tsx
Normal file
23
web/src/components/key-input.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { Input, InputProps } from '@/components/ui/input';
|
||||
import { ChangeEvent, forwardRef, useCallback } from 'react';
|
||||
|
||||
type KeyInputProps = {
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
searchValue?: string | RegExp;
|
||||
} & Omit<InputProps, 'onChange'>;
|
||||
|
||||
export const KeyInput = forwardRef<HTMLInputElement, KeyInputProps>(
|
||||
function KeyInput({ value, onChange, searchValue = /[^a-zA-Z0-9_]/g }, ref) {
|
||||
const handleChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value ?? '';
|
||||
const filteredValue = value.replace(searchValue, '');
|
||||
onChange?.(filteredValue);
|
||||
},
|
||||
[onChange, searchValue],
|
||||
);
|
||||
|
||||
return <Input value={value} onChange={handleChange} ref={ref} />;
|
||||
},
|
||||
);
|
||||
@ -45,7 +45,7 @@ export interface DSL {
|
||||
messages?: Message[];
|
||||
reference?: IReference[];
|
||||
globals: Record<string, any>;
|
||||
variables: Record<string, GobalVariableType>;
|
||||
variables: Record<string, GlobalVariableType>;
|
||||
retrieval: IReference[];
|
||||
}
|
||||
|
||||
@ -285,7 +285,7 @@ export interface IPipeLineListRequest {
|
||||
canvas_category?: AgentCategory;
|
||||
}
|
||||
|
||||
export interface GobalVariableType {
|
||||
export interface GlobalVariableType {
|
||||
name: string;
|
||||
value: any;
|
||||
description: string;
|
||||
|
||||
@ -985,7 +985,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
|
||||
'Variable name can only contain letters and underscores',
|
||||
variableDescription: 'Variable Description',
|
||||
defaultValue: 'Default Value',
|
||||
gobalVariable: 'Global Variable',
|
||||
conversationVariable: 'Conversation variable',
|
||||
recommended: 'Recommended',
|
||||
customerSupport: 'Customer Support',
|
||||
marketing: 'Marketing',
|
||||
|
||||
@ -933,7 +933,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
||||
variableNameMessage: '名称只能包含字母和下划线',
|
||||
variableDescription: '变量的描述',
|
||||
defaultValue: '默认值',
|
||||
gobalVariable: '全局变量',
|
||||
conversationVariable: '会话变量',
|
||||
recommended: '推荐',
|
||||
customerSupport: '客户支持',
|
||||
marketing: '营销',
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { ChangeEvent, useCallback } from 'react';
|
||||
|
||||
type KeyInputProps = {
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
searchValue?: string | RegExp;
|
||||
};
|
||||
|
||||
export function KeyInput({
|
||||
value,
|
||||
onChange,
|
||||
searchValue = /[^a-zA-Z0-9_]/g,
|
||||
}: KeyInputProps) {
|
||||
const handleChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value ?? '';
|
||||
const filteredValue = value.replace(searchValue, '');
|
||||
onChange?.(filteredValue);
|
||||
},
|
||||
[onChange, searchValue],
|
||||
);
|
||||
|
||||
return <Input value={value} onChange={handleChange} />;
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
import { KeyInput } from '@/components/key-input';
|
||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@ -8,7 +9,6 @@ import { ReactNode } from 'react';
|
||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||
import { DataOperationsOperatorOptions } from '../../constant';
|
||||
import { DynamicFormHeader } from '../components/dynamic-fom-header';
|
||||
import { KeyInput } from '../components/key-input';
|
||||
import { PromptEditor } from '../components/prompt-editor';
|
||||
|
||||
type SelectKeysProps = {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { KeyInput } from '@/components/key-input';
|
||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
@ -5,7 +6,6 @@ import { X } from 'lucide-react';
|
||||
import { ReactNode } from 'react';
|
||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||
import { DynamicFormHeader } from '../components/dynamic-fom-header';
|
||||
import { KeyInput } from '../components/key-input';
|
||||
import { PromptEditor } from '../components/prompt-editor';
|
||||
|
||||
type SelectKeysProps = {
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
} from '@/components/ui/sheet';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useFetchAgent } from '@/hooks/use-agent-request';
|
||||
import { GobalVariableType } from '@/interfaces/database/agent';
|
||||
import { GlobalVariableType } from '@/interfaces/database/agent';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { t } from 'i18next';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
@ -85,7 +85,7 @@ export const GobalParamSheet = (props: IGobalParamModalProps) => {
|
||||
const param = {
|
||||
...(data.dsl?.variables || {}),
|
||||
[value.name]: value,
|
||||
} as Record<string, GobalVariableType>;
|
||||
} as Record<string, GlobalVariableType>;
|
||||
|
||||
const res = await saveGraph(undefined, {
|
||||
gobalVariables: param,
|
||||
@ -100,7 +100,7 @@ export const GobalParamSheet = (props: IGobalParamModalProps) => {
|
||||
const handleDeleteGobalVariable = async (key: string) => {
|
||||
const param = {
|
||||
...(data.dsl?.variables || {}),
|
||||
} as Record<string, GobalVariableType>;
|
||||
} as Record<string, GlobalVariableType>;
|
||||
delete param[key];
|
||||
const res = await saveGraph(undefined, {
|
||||
gobalVariables: param,
|
||||
@ -124,7 +124,7 @@ export const GobalParamSheet = (props: IGobalParamModalProps) => {
|
||||
>
|
||||
<SheetHeader className="p-5">
|
||||
<SheetTitle className="flex items-center gap-2.5">
|
||||
{t('flow.gobalVariable')}
|
||||
{t('flow.conversationVariable')}
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
|
||||
@ -185,7 +185,7 @@ export const GobalParamSheet = (props: IGobalParamModalProps) => {
|
||||
</div>
|
||||
</SheetContent>
|
||||
<Modal
|
||||
title={t('flow.add') + t('flow.gobalVariable')}
|
||||
title={t('flow.add') + t('flow.conversationVariable')}
|
||||
open={visible}
|
||||
onCancel={hideAddModal}
|
||||
showfooter={false}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useFetchAgent } from '@/hooks/use-agent-request';
|
||||
import { GobalVariableType } from '@/interfaces/database/agent';
|
||||
import { GlobalVariableType } from '@/interfaces/database/agent';
|
||||
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
||||
import { useCallback } from 'react';
|
||||
import { Operator } from '../constant';
|
||||
@ -13,7 +13,7 @@ export const useBuildDslData = () => {
|
||||
const buildDslData = useCallback(
|
||||
(
|
||||
currentNodes?: RAGFlowNodeType[],
|
||||
otherParam?: { gobalVariables: Record<string, GobalVariableType> },
|
||||
otherParam?: { gobalVariables: Record<string, GlobalVariableType> },
|
||||
) => {
|
||||
const nodesToProcess = currentNodes ?? nodes;
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import { DefaultOptionType } from 'antd/es/select';
|
||||
import { t } from 'i18next';
|
||||
import { isEmpty, toLower } from 'lodash';
|
||||
import get from 'lodash/get';
|
||||
import { MessageCircleCode } from 'lucide-react';
|
||||
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
AgentDialogueMode,
|
||||
@ -141,6 +142,38 @@ export function useBuildBeginVariableOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
const Env = 'env.';
|
||||
|
||||
export function useBuildConversationVariableOptions() {
|
||||
const { data } = useFetchAgent();
|
||||
|
||||
const conversationVariables = useMemo(
|
||||
() => data?.dsl?.variables ?? {},
|
||||
[data?.dsl?.variables],
|
||||
);
|
||||
|
||||
const options = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
label: <span>{t('flow.conversationVariable')}</span>,
|
||||
title: t('flow.conversationVariable'),
|
||||
options: Object.entries(conversationVariables).map(([key, value]) => {
|
||||
const keyWithPrefix = `${Env}${key}`;
|
||||
return {
|
||||
label: keyWithPrefix,
|
||||
parentLabel: <span>{t('flow.conversationVariable')}</span>,
|
||||
icon: <MessageCircleCode className="size-3" />,
|
||||
value: keyWithPrefix,
|
||||
type: value.type,
|
||||
};
|
||||
}),
|
||||
},
|
||||
];
|
||||
}, [conversationVariables]);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
export const useBuildVariableOptions = (nodeId?: string, parentId?: string) => {
|
||||
const nodeOutputOptions = useBuildNodeOutputOptions(nodeId);
|
||||
const parentNodeOutputOptions = useBuildNodeOutputOptions(parentId);
|
||||
@ -157,22 +190,32 @@ export function useBuildQueryVariableOptions(n?: RAGFlowNodeType) {
|
||||
const { data } = useFetchAgent();
|
||||
const node = useContext(AgentFormContext) || n;
|
||||
const options = useBuildVariableOptions(node?.id, node?.parentId);
|
||||
|
||||
const conversationOptions = useBuildConversationVariableOptions();
|
||||
|
||||
const nextOptions = useMemo(() => {
|
||||
const globals = data?.dsl?.globals ?? {};
|
||||
const globalOptions = Object.entries(globals).map(([key, value]) => ({
|
||||
label: key,
|
||||
value: key,
|
||||
icon: <OperatorIcon name={Operator.Begin} className="block" />,
|
||||
parentLabel: <span>{t('flow.beginInput')}</span>,
|
||||
type: Array.isArray(value)
|
||||
? `${VariableType.Array}${key === AgentGlobals.SysFiles ? '<file>' : ''}`
|
||||
: typeof value,
|
||||
}));
|
||||
const globalOptions = Object.entries(globals)
|
||||
.filter(([key]) => !key.startsWith(Env))
|
||||
.map(([key, value]) => ({
|
||||
label: key,
|
||||
value: key,
|
||||
icon: <OperatorIcon name={Operator.Begin} className="block" />,
|
||||
parentLabel: <span>{t('flow.beginInput')}</span>,
|
||||
type: Array.isArray(value)
|
||||
? `${VariableType.Array}${key === AgentGlobals.SysFiles ? '<file>' : ''}`
|
||||
: typeof value,
|
||||
}));
|
||||
|
||||
return [
|
||||
{ ...options[0], options: [...options[0]?.options, ...globalOptions] },
|
||||
{
|
||||
...options[0],
|
||||
options: [...options[0]?.options, ...globalOptions],
|
||||
},
|
||||
...options.slice(1),
|
||||
...conversationOptions,
|
||||
];
|
||||
}, [data.dsl?.globals, options]);
|
||||
}, [conversationOptions, data?.dsl?.globals, options]);
|
||||
|
||||
return nextOptions;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import {
|
||||
useResetAgent,
|
||||
useSetAgent,
|
||||
} from '@/hooks/use-agent-request';
|
||||
import { GobalVariableType } from '@/interfaces/database/agent';
|
||||
import { GlobalVariableType } from '@/interfaces/database/agent';
|
||||
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import { useDebounceEffect } from 'ahooks';
|
||||
@ -21,7 +21,7 @@ export const useSaveGraph = (showMessage: boolean = true) => {
|
||||
const saveGraph = useCallback(
|
||||
async (
|
||||
currentNodes?: RAGFlowNodeType[],
|
||||
otherParam?: { gobalVariables: Record<string, GobalVariableType> },
|
||||
otherParam?: { gobalVariables: Record<string, GlobalVariableType> },
|
||||
) => {
|
||||
return setAgent({
|
||||
id,
|
||||
|
||||
@ -218,7 +218,7 @@ export default function Agent() {
|
||||
onClick={() => showGobalParamSheet()}
|
||||
loading={loading}
|
||||
>
|
||||
{t('flow.gobalVariable')}
|
||||
{t('flow.conversationVariable')}
|
||||
</ButtonLoading>
|
||||
<Button variant={'secondary'} onClick={handleButtonRunClick}>
|
||||
<CirclePlay />
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {
|
||||
DSL,
|
||||
GobalVariableType,
|
||||
GlobalVariableType,
|
||||
IAgentForm,
|
||||
ICategorizeForm,
|
||||
ICategorizeItem,
|
||||
@ -350,7 +350,7 @@ export const buildDslComponentsByGraph = (
|
||||
|
||||
export const buildDslGobalVariables = (
|
||||
dsl: DSL,
|
||||
gobalVariables?: Record<string, GobalVariableType>,
|
||||
gobalVariables?: Record<string, GlobalVariableType>,
|
||||
) => {
|
||||
if (!gobalVariables) {
|
||||
return { globals: dsl.globals, variables: dsl.variables || {} };
|
||||
|
||||
Reference in New Issue
Block a user