Feat: Display mode at the begin node #10427 (#12326)

### What problem does this PR solve?

Feat: Display mode at the begin node #10427
### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-12-30 19:53:24 +08:00
committed by GitHub
parent a7e466142d
commit 348265afc1
3 changed files with 94 additions and 52 deletions

View File

@ -1,16 +1,18 @@
import { IBeginNode } from '@/interfaces/database/flow'; import { BaseNode } from '@/interfaces/database/flow';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { NodeProps, Position } from '@xyflow/react'; import { NodeProps, Position } from '@xyflow/react';
import get from 'lodash/get'; import get from 'lodash/get';
import { memo } from 'react'; import { memo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
AgentDialogueMode,
BeginQueryType, BeginQueryType,
BeginQueryTypeIconMap, BeginQueryTypeIconMap,
NodeHandleId, NodeHandleId,
Operator, Operator,
} from '../../constant'; } from '../../constant';
import { BeginQuery } from '../../interface'; import { BeginFormSchemaType } from '../../form/begin-form/schema';
import { useBuildWebhookUrl } from '../../hooks/use-build-webhook-url';
import OperatorIcon from '../../operator-icon'; import OperatorIcon from '../../operator-icon';
import { LabelCard } from './card'; import { LabelCard } from './card';
import { CommonHandle } from './handle'; import { CommonHandle } from './handle';
@ -18,10 +20,19 @@ import { RightHandleStyle } from './handle-icon';
import styles from './index.less'; import styles from './index.less';
import { NodeWrapper } from './node-wrapper'; import { NodeWrapper } from './node-wrapper';
// TODO: do not allow other nodes to connect to this node function InnerBeginNode({
function InnerBeginNode({ data, id, selected }: NodeProps<IBeginNode>) { data,
id,
selected,
}: NodeProps<BaseNode<BeginFormSchemaType>>) {
const { t } = useTranslation(); const { t } = useTranslation();
const inputs: Record<string, BeginQuery> = get(data, 'form.inputs', {}); const inputs = get(data, 'form.inputs', {});
const mode = data.form?.mode;
const isWebhookMode = mode === AgentDialogueMode.Webhook;
const url = useBuildWebhookUrl();
return ( return (
<NodeWrapper selected={selected} id={id}> <NodeWrapper selected={selected} id={id}>
@ -40,13 +51,27 @@ function InnerBeginNode({ data, id, selected }: NodeProps<IBeginNode>) {
{t(`flow.begin`)} {t(`flow.begin`)}
</div> </div>
</section> </section>
<section className={cn(styles.generateParameters, 'flex gap-2 flex-col')}> <div className="text-accent-primary mt-2 p-1 bg-bg-accent w-fit rounded-sm text-xs">
{t(`flow.${isWebhookMode ? 'webhook.name' : mode}`)}
</div>
{isWebhookMode ? (
<LabelCard className="mt-2 flex gap-1 items-center">
<span className="font-bold">URL</span>
<span className="flex-1 truncate">{url}</span>
</LabelCard>
) : (
<section
className={cn(styles.generateParameters, 'flex gap-2 flex-col')}
>
{Object.entries(inputs).map(([key, val], idx) => { {Object.entries(inputs).map(([key, val], idx) => {
const Icon = BeginQueryTypeIconMap[val.type as BeginQueryType]; const Icon = BeginQueryTypeIconMap[val.type as BeginQueryType];
return ( return (
<LabelCard key={idx} className={cn('flex gap-1.5 items-center')}> <LabelCard key={idx} className={cn('flex gap-1.5 items-center')}>
<Icon className="size-3.5" /> <Icon className="size-3.5" />
<label htmlFor="" className="text-accent-primary text-sm italic"> <label
htmlFor=""
className="text-accent-primary text-sm italic"
>
{key} {key}
</label> </label>
<LabelCard className="py-0.5 truncate flex-1"> <LabelCard className="py-0.5 truncate flex-1">
@ -57,6 +82,7 @@ function InnerBeginNode({ data, id, selected }: NodeProps<IBeginNode>) {
); );
})} })}
</section> </section>
)}
</NodeWrapper> </NodeWrapper>
); );
} }

View File

@ -3,13 +3,16 @@ import { CopyToClipboardWithText } from '@/components/copy-to-clipboard';
import NumberInput from '@/components/originui/number-input'; import NumberInput from '@/components/originui/number-input';
import { SelectWithSearch } from '@/components/originui/select-with-search'; import { SelectWithSearch } from '@/components/originui/select-with-search';
import { RAGFlowFormItem } from '@/components/ragflow-form'; import { RAGFlowFormItem } from '@/components/ragflow-form';
import { Label } from '@/components/ui/label';
import { MultiSelect } from '@/components/ui/multi-select'; import { MultiSelect } from '@/components/ui/multi-select';
import { Separator } from '@/components/ui/separator';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { useBuildWebhookUrl } from '@/pages/agent/hooks/use-build-webhook-url';
import { buildOptions } from '@/utils/form'; import { buildOptions } from '@/utils/form';
import { upperFirst } from 'lodash';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useFormContext, useWatch } from 'react-hook-form'; import { useFormContext, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useParams } from 'umi';
import { import {
RateLimitPerList, RateLimitPerList,
WebhookMaxBodySize, WebhookMaxBodySize,
@ -22,7 +25,10 @@ import { Auth } from './auth';
import { WebhookRequestSchema } from './request-schema'; import { WebhookRequestSchema } from './request-schema';
import { WebhookResponse } from './response'; import { WebhookResponse } from './response';
const RateLimitPerOptions = buildOptions(RateLimitPerList); const RateLimitPerOptions = RateLimitPerList.map((x) => ({
value: x,
label: upperFirst(x),
}));
const RequestLimitMap = { const RequestLimitMap = {
[WebhookRateLimitPer.Second]: 100, [WebhookRateLimitPer.Second]: 100,
@ -33,7 +39,6 @@ const RequestLimitMap = {
export function WebHook() { export function WebHook() {
const { t } = useTranslation(); const { t } = useTranslation();
const { id } = useParams();
const form = useFormContext(); const form = useFormContext();
const rateLimitPer = useWatch({ const rateLimitPer = useWatch({
@ -45,7 +50,7 @@ export function WebHook() {
return RequestLimitMap[rateLimitPer as keyof typeof RequestLimitMap] ?? 100; return RequestLimitMap[rateLimitPer as keyof typeof RequestLimitMap] ?? 100;
}, []); }, []);
const text = `${location.protocol}//${location.host}/api/v1/webhook/${id}`; const text = useBuildWebhookUrl();
return ( return (
<> <>
@ -74,19 +79,20 @@ export function WebHook() {
></SelectWithSearch> ></SelectWithSearch>
</RAGFlowFormItem> </RAGFlowFormItem>
<Auth></Auth> <Auth></Auth>
<section>
<Label>{t('flow.webhook.limit')}</Label>
<div className="flex items-center mt-1 gap-2">
<RAGFlowFormItem <RAGFlowFormItem
name="security.rate_limit.limit" name="security.rate_limit.limit"
label={t('flow.webhook.limit')} className="flex-1"
> >
<NumberInput <NumberInput
max={getLimitRateLimitPerMax(rateLimitPer)} max={getLimitRateLimitPerMax(rateLimitPer)}
className="w-full" className="w-full"
></NumberInput> ></NumberInput>
</RAGFlowFormItem> </RAGFlowFormItem>
<RAGFlowFormItem <Separator className="w-2" />
name="security.rate_limit.per" <RAGFlowFormItem name="security.rate_limit.per">
label={t('flow.webhook.per')}
>
{(field) => ( {(field) => (
<SelectWithSearch <SelectWithSearch
options={RateLimitPerOptions} options={RateLimitPerOptions}
@ -101,6 +107,8 @@ export function WebHook() {
></SelectWithSearch> ></SelectWithSearch>
)} )}
</RAGFlowFormItem> </RAGFlowFormItem>
</div>
</section>
<RAGFlowFormItem <RAGFlowFormItem
name="security.max_body_size" name="security.max_body_size"
label={t('flow.webhook.maxBodySize')} label={t('flow.webhook.maxBodySize')}

View File

@ -0,0 +1,8 @@
import { useParams } from 'umi';
export function useBuildWebhookUrl() {
const { id } = useParams();
const text = `${location.protocol}//${location.host}/api/v1/webhook/${id}`;
return text;
}