### What problem does this PR solve? Feat: Let system variables appear in operator prompts #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.19.1
| import { useTranslate } from '@/hooks/common-hooks'; | import { useTranslate } from '@/hooks/common-hooks'; | ||||
| import { Form, Slider } from 'antd'; | import { Form, Slider } from 'antd'; | ||||
| import { useFormContext } from 'react-hook-form'; | |||||
| import { SingleFormSlider } from './ui/dual-range-slider'; | |||||
| import { | |||||
| FormControl, | |||||
| FormField, | |||||
| FormItem, | |||||
| FormLabel, | |||||
| FormMessage, | |||||
| } from './ui/form'; | |||||
| import { SliderInputFormField } from './slider-input-form-field'; | |||||
| type FieldType = { | type FieldType = { | ||||
| top_n?: number; | top_n?: number; | ||||
| } | } | ||||
| export function TopNFormField({ max = 30 }: SimilaritySliderFormFieldProps) { | export function TopNFormField({ max = 30 }: SimilaritySliderFormFieldProps) { | ||||
| const form = useFormContext(); | |||||
| const { t } = useTranslate('chat'); | const { t } = useTranslate('chat'); | ||||
| return ( | return ( | ||||
| <FormField | |||||
| control={form.control} | |||||
| <SliderInputFormField | |||||
| name={'top_n'} | name={'top_n'} | ||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <FormLabel tooltip={t('topNTip')}>{t('topN')}</FormLabel> | |||||
| <FormControl> | |||||
| <SingleFormSlider {...field} max={max}></SingleFormSlider> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| label={t('topN')} | |||||
| max={max} | |||||
| tooltip={t('topNTip')} | |||||
| ></SliderInputFormField> | |||||
| ); | ); | ||||
| } | } |
| module.exports = { main }; | module.exports = { main }; | ||||
| `, | `, | ||||
| }; | }; | ||||
| export enum AgentGlobals { | |||||
| SysQuery = 'sys.query', | |||||
| SysUserId = 'sys.user_id', | |||||
| SysConversationTurns = 'sys.conversation_turns', | |||||
| SysFiles = 'sys.files', | |||||
| } |
| import { AgentGlobals } from '@/constants/agent'; | |||||
| import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow'; | import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow'; | ||||
| import i18n from '@/locales/config'; | import i18n from '@/locales/config'; | ||||
| import { BeginId } from '@/pages/agent/constant'; | import { BeginId } from '@/pages/agent/constant'; | ||||
| history: [], | history: [], | ||||
| path: [], | path: [], | ||||
| globals: { | globals: { | ||||
| 'sys.query': '', | |||||
| 'sys.user_id': '', | |||||
| 'sys.conversation_turns': 0, | |||||
| 'sys.files': [], | |||||
| [AgentGlobals.SysQuery]: '', | |||||
| [AgentGlobals.SysUserId]: '', | |||||
| [AgentGlobals.SysConversationTurns]: 0, | |||||
| [AgentGlobals.SysFiles]: [], | |||||
| }, | }, | ||||
| }; | }; | ||||
| messages: Message[]; | messages: Message[]; | ||||
| reference: IReference[]; | reference: IReference[]; | ||||
| globals: Record<string, any>; | globals: Record<string, any>; | ||||
| retrieval: IReference[]; | |||||
| } | } | ||||
| export interface IOperator { | export interface IOperator { |
| } from '../hooks'; | } from '../hooks'; | ||||
| import { useAddNode } from '../hooks/use-add-node'; | import { useAddNode } from '../hooks/use-add-node'; | ||||
| import { useBeforeDelete } from '../hooks/use-before-delete'; | import { useBeforeDelete } from '../hooks/use-before-delete'; | ||||
| import { useShowDrawer } from '../hooks/use-show-drawer'; | |||||
| import { useShowDrawer, useShowLogSheet } from '../hooks/use-show-drawer'; | |||||
| import { LogSheet } from '../log-sheet'; | |||||
| import RunSheet from '../run-sheet'; | import RunSheet from '../run-sheet'; | ||||
| import { ButtonEdge } from './edge'; | import { ButtonEdge } from './edge'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| hideDrawer, | hideDrawer, | ||||
| }); | }); | ||||
| const { showLogSheet, logSheetVisible, hideLogSheet } = useShowLogSheet(); | |||||
| const { handleBeforeDelete } = useBeforeDelete(); | const { handleBeforeDelete } = useBeforeDelete(); | ||||
| useWatchNodeFormDataChange(); | useWatchNodeFormDataChange(); | ||||
| hideModal={hideRunOrChatDrawer} | hideModal={hideRunOrChatDrawer} | ||||
| ></ChatSheet> | ></ChatSheet> | ||||
| )} | )} | ||||
| {runVisible && ( | {runVisible && ( | ||||
| <RunSheet | <RunSheet | ||||
| hideModal={hideRunOrChatDrawer} | hideModal={hideRunOrChatDrawer} | ||||
| showModal={showChatModal} | showModal={showChatModal} | ||||
| ></RunSheet> | ></RunSheet> | ||||
| )} | )} | ||||
| {logSheetVisible && ( | |||||
| <LogSheet hideModal={hideLogSheet} showModal={showLogSheet}></LogSheet> | |||||
| )} | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| } | } |
| return ( | return ( | ||||
| <> | <> | ||||
| <section className="flex flex-1 flex-col pl-5 h-[90vh]"> | <section className="flex flex-1 flex-col pl-5 h-[90vh]"> | ||||
| <div className="flex-1 "> | |||||
| <div className="flex-1 overflow-auto"> | |||||
| <div> | <div> | ||||
| <Spin spinning={loading}> | <Spin spinning={loading}> | ||||
| {derivedMessages?.map((message, i) => { | {derivedMessages?.map((message, i) => { |
| export const useSelectNextMessages = () => { | export const useSelectNextMessages = () => { | ||||
| const { data: flowDetail, loading } = useFetchAgent(); | const { data: flowDetail, loading } = useFetchAgent(); | ||||
| const reference = flowDetail.dsl.reference; | |||||
| const reference = flowDetail.dsl.retrieval; | |||||
| const { | const { | ||||
| derivedMessages, | derivedMessages, | ||||
| ref, | ref, | ||||
| addNewestAnswer({ | addNewestAnswer({ | ||||
| answer: content, | answer: content, | ||||
| id: id, | id: id, | ||||
| reference: { | |||||
| chunks: [], | |||||
| doc_aggs: [], | |||||
| total: 0, | |||||
| }, | |||||
| }); | }); | ||||
| } | } | ||||
| }, [answerList, addNewestAnswer]); | }, [answerList, addNewestAnswer]); | ||||
| if (prologue) { | if (prologue) { | ||||
| addNewestAnswer({ | addNewestAnswer({ | ||||
| answer: prologue, | answer: prologue, | ||||
| reference: { | |||||
| chunks: [], | |||||
| doc_aggs: [], | |||||
| total: 0, | |||||
| }, | |||||
| }); | }); | ||||
| } | } | ||||
| }, [addNewestAnswer, prologue]); | }, [addNewestAnswer, prologue]); |
| initialKeywordsSimilarityWeightValue, | initialKeywordsSimilarityWeightValue, | ||||
| initialSimilarityThresholdValue, | initialSimilarityThresholdValue, | ||||
| } from '@/components/similarity-slider'; | } from '@/components/similarity-slider'; | ||||
| import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent'; | |||||
| import { | |||||
| AgentGlobals, | |||||
| CodeTemplateStrMap, | |||||
| ProgrammingLanguage, | |||||
| } from '@/constants/agent'; | |||||
| export enum AgentDialogueMode { | export enum AgentDialogueMode { | ||||
| Conversational = 'conversational', | Conversational = 'conversational', | ||||
| News = 'news', | News = 'news', | ||||
| } | } | ||||
| export enum PromptRole { | |||||
| User = 'user', | |||||
| Assistant = 'assistant', | |||||
| } | |||||
| import { | import { | ||||
| BranchesOutlined, | BranchesOutlined, | ||||
| DatabaseOutlined, | DatabaseOutlined, | ||||
| export const initialAgentValues = { | export const initialAgentValues = { | ||||
| ...initialLlmBaseValues, | ...initialLlmBaseValues, | ||||
| sys_prompt: ``, | sys_prompt: ``, | ||||
| prompts: [], | |||||
| prompts: [{ role: PromptRole.User, content: `{${AgentGlobals.SysQuery}}` }], | |||||
| message_history_window_size: 12, | message_history_window_size: 12, | ||||
| tools: [], | tools: [], | ||||
| outputs: { | outputs: { |
| export enum PromptRole { | |||||
| User = 'user', | |||||
| Assistant = 'assistant', | |||||
| } |
| import { memo } from 'react'; | import { memo } from 'react'; | ||||
| import { useFieldArray, useFormContext } from 'react-hook-form'; | import { useFieldArray, useFormContext } from 'react-hook-form'; | ||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import { PromptRole } from '../../constant'; | |||||
| import { PromptEditor } from '../components/prompt-editor'; | import { PromptEditor } from '../components/prompt-editor'; | ||||
| import { PromptRole } from './constant'; | |||||
| const options = [ | const options = [ | ||||
| { label: 'User', value: PromptRole.User }, | { label: 'User', value: PromptRole.User }, |
| import { useEffect } from 'react'; | import { useEffect } from 'react'; | ||||
| import { UseFormReturn, useWatch } from 'react-hook-form'; | import { UseFormReturn, useWatch } from 'react-hook-form'; | ||||
| import { PromptRole } from '../../constant'; | |||||
| import useGraphStore from '../../store'; | import useGraphStore from '../../store'; | ||||
| import { PromptRole } from './constant'; | |||||
| export function useWatchFormChange(id?: string, form?: UseFormReturn) { | export function useWatchFormChange(id?: string, form?: UseFormReturn) { | ||||
| let values = useWatch({ control: form?.control }); | let values = useWatch({ control: form?.control }); |
| $isRangeSelection, | $isRangeSelection, | ||||
| TextNode, | TextNode, | ||||
| } from 'lexical'; | } from 'lexical'; | ||||
| import React, { | |||||
| ReactElement, | |||||
| useCallback, | |||||
| useContext, | |||||
| useEffect, | |||||
| useRef, | |||||
| } from 'react'; | |||||
| import React, { ReactElement, useCallback, useEffect, useRef } from 'react'; | |||||
| import * as ReactDOM from 'react-dom'; | import * as ReactDOM from 'react-dom'; | ||||
| import { $createVariableNode } from './variable-node'; | import { $createVariableNode } from './variable-node'; | ||||
| import { AgentFormContext } from '@/pages/agent/context'; | |||||
| import { useBuildComponentIdSelectOptions } from '@/pages/agent/hooks/use-get-begin-query'; | |||||
| import { useBuildQueryVariableOptions } from '@/pages/agent/hooks/use-get-begin-query'; | |||||
| import { ProgrammaticTag } from './constant'; | import { ProgrammaticTag } from './constant'; | ||||
| import './index.css'; | import './index.css'; | ||||
| class VariableInnerOption extends MenuOption { | class VariableInnerOption extends MenuOption { | ||||
| const [editor] = useLexicalComposerContext(); | const [editor] = useLexicalComposerContext(); | ||||
| const isFirstRender = useRef(true); | const isFirstRender = useRef(true); | ||||
| const node = useContext(AgentFormContext); | |||||
| const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('/', { | const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('/', { | ||||
| minLength: 0, | minLength: 0, | ||||
| }); | }); | ||||
| const [queryString, setQueryString] = React.useState<string | null>(''); | const [queryString, setQueryString] = React.useState<string | null>(''); | ||||
| const options = useBuildComponentIdSelectOptions(node?.id, node?.parentId); | |||||
| const options = useBuildQueryVariableOptions(); | |||||
| const buildNextOptions = useCallback(() => { | const buildNextOptions = useCallback(() => { | ||||
| let filteredOptions = options; | let filteredOptions = options; |
| FormLabel, | FormLabel, | ||||
| FormMessage, | FormMessage, | ||||
| } from '@/components/ui/form'; | } from '@/components/ui/form'; | ||||
| import { useFetchAgent } from '@/hooks/use-agent-request'; | |||||
| import { useContext, useMemo } from 'react'; | |||||
| import { useFormContext } from 'react-hook-form'; | import { useFormContext } from 'react-hook-form'; | ||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import { AgentFormContext } from '../../context'; | |||||
| import { useBuildComponentIdSelectOptions } from '../../hooks/use-get-begin-query'; | |||||
| import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query'; | |||||
| export function QueryVariable() { | export function QueryVariable() { | ||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const form = useFormContext(); | const form = useFormContext(); | ||||
| const { data } = useFetchAgent(); | |||||
| const node = useContext(AgentFormContext); | |||||
| const options = useBuildComponentIdSelectOptions(node?.id, node?.parentId); | |||||
| const nextOptions = useMemo(() => { | |||||
| const globalOptions = Object.keys(data?.dsl?.globals ?? {}).map((x) => ({ | |||||
| label: x, | |||||
| value: x, | |||||
| })); | |||||
| return [ | |||||
| { ...options[0], options: [...options[0]?.options, ...globalOptions] }, | |||||
| ...options.slice(1), | |||||
| ]; | |||||
| }, [data.dsl.globals, options]); | |||||
| const nextOptions = useBuildQueryVariableOptions(); | |||||
| return ( | return ( | ||||
| <FormField | <FormField |
| import { useFetchAgent } from '@/hooks/use-agent-request'; | |||||
| import { RAGFlowNodeType } from '@/interfaces/database/flow'; | import { RAGFlowNodeType } from '@/interfaces/database/flow'; | ||||
| import { Edge } from '@xyflow/react'; | import { Edge } from '@xyflow/react'; | ||||
| import { DefaultOptionType } from 'antd/es/select'; | import { DefaultOptionType } from 'antd/es/select'; | ||||
| import { isEmpty } from 'lodash'; | import { isEmpty } from 'lodash'; | ||||
| import get from 'lodash/get'; | import get from 'lodash/get'; | ||||
| import { useCallback, useEffect, useMemo, useState } from 'react'; | |||||
| import { useCallback, useContext, useEffect, useMemo, useState } from 'react'; | |||||
| import { BeginId, Operator } from '../constant'; | import { BeginId, Operator } from '../constant'; | ||||
| import { AgentFormContext } from '../context'; | |||||
| import { buildBeginInputListFromObject } from '../form/begin-form/utils'; | import { buildBeginInputListFromObject } from '../form/begin-form/utils'; | ||||
| import { BeginQuery } from '../interface'; | import { BeginQuery } from '../interface'; | ||||
| import useGraphStore from '../store'; | import useGraphStore from '../store'; | ||||
| ); | ); | ||||
| return getLabel; | return getLabel; | ||||
| }; | }; | ||||
| export function useBuildQueryVariableOptions() { | |||||
| const { data } = useFetchAgent(); | |||||
| const node = useContext(AgentFormContext); | |||||
| const options = useBuildComponentIdSelectOptions(node?.id, node?.parentId); | |||||
| const nextOptions = useMemo(() => { | |||||
| const globalOptions = Object.keys(data?.dsl?.globals ?? {}).map((x) => ({ | |||||
| label: x, | |||||
| value: x, | |||||
| })); | |||||
| return [ | |||||
| { ...options[0], options: [...options[0]?.options, ...globalOptions] }, | |||||
| ...options.slice(1), | |||||
| ]; | |||||
| }, [data.dsl.globals, options]); | |||||
| return nextOptions; | |||||
| } |
| showChatModal, | showChatModal, | ||||
| }; | }; | ||||
| } | } | ||||
| export function useShowLogSheet() { | |||||
| const { visible, showModal, hideModal } = useSetModalState(); | |||||
| return { | |||||
| logSheetVisible: visible, | |||||
| hideLogSheet: hideModal, | |||||
| showLogSheet: showModal, | |||||
| }; | |||||
| } |
| import { | |||||
| Sheet, | |||||
| SheetContent, | |||||
| SheetDescription, | |||||
| SheetHeader, | |||||
| SheetTitle, | |||||
| } from '@/components/ui/sheet'; | |||||
| import { IModalProps } from '@/interfaces/common'; | |||||
| export function LogSheet({ hideModal }: IModalProps<any>) { | |||||
| return ( | |||||
| <Sheet open onOpenChange={hideModal}> | |||||
| <SheetContent> | |||||
| <SheetHeader> | |||||
| <SheetTitle>Are you absolutely sure?</SheetTitle> | |||||
| <SheetDescription> | |||||
| This action cannot be undone. This will permanently delete your | |||||
| account and remove your data from our servers. | |||||
| </SheetDescription> | |||||
| </SheetHeader> | |||||
| </SheetContent> | |||||
| </Sheet> | |||||
| ); | |||||
| } |
| conversation: { message: IMessage[]; reference: IReference[] }, | conversation: { message: IMessage[]; reference: IReference[] }, | ||||
| message: IMessage, | message: IMessage, | ||||
| ) => { | ) => { | ||||
| const assistantMessages = conversation.message | |||||
| ?.filter((x) => x.role === MessageType.Assistant) | |||||
| .slice(1); | |||||
| const assistantMessages = conversation.message?.filter( | |||||
| (x) => x.role === MessageType.Assistant, | |||||
| ); | |||||
| const referenceIndex = assistantMessages.findIndex( | const referenceIndex = assistantMessages.findIndex( | ||||
| (x) => x.id === message.id, | (x) => x.id === message.id, | ||||
| ); | ); |