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 { NodeProps, Position } from '@xyflow/react';
import get from 'lodash/get';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
AgentDialogueMode,
BeginQueryType,
BeginQueryTypeIconMap,
NodeHandleId,
Operator,
} 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 { LabelCard } from './card';
import { CommonHandle } from './handle';
@ -18,10 +20,19 @@ import { RightHandleStyle } from './handle-icon';
import styles from './index.less';
import { NodeWrapper } from './node-wrapper';
// TODO: do not allow other nodes to connect to this node
function InnerBeginNode({ data, id, selected }: NodeProps<IBeginNode>) {
function InnerBeginNode({
data,
id,
selected,
}: NodeProps<BaseNode<BeginFormSchemaType>>) {
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 (
<NodeWrapper selected={selected} id={id}>
@ -40,23 +51,38 @@ function InnerBeginNode({ data, id, selected }: NodeProps<IBeginNode>) {
{t(`flow.begin`)}
</div>
</section>
<section className={cn(styles.generateParameters, 'flex gap-2 flex-col')}>
{Object.entries(inputs).map(([key, val], idx) => {
const Icon = BeginQueryTypeIconMap[val.type as BeginQueryType];
return (
<LabelCard key={idx} className={cn('flex gap-1.5 items-center')}>
<Icon className="size-3.5" />
<label htmlFor="" className="text-accent-primary text-sm italic">
{key}
</label>
<LabelCard className="py-0.5 truncate flex-1">
{val.name}
<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) => {
const Icon = BeginQueryTypeIconMap[val.type as BeginQueryType];
return (
<LabelCard key={idx} className={cn('flex gap-1.5 items-center')}>
<Icon className="size-3.5" />
<label
htmlFor=""
className="text-accent-primary text-sm italic"
>
{key}
</label>
<LabelCard className="py-0.5 truncate flex-1">
{val.name}
</LabelCard>
<span className="flex-1">{val.optional ? 'Yes' : 'No'}</span>
</LabelCard>
<span className="flex-1">{val.optional ? 'Yes' : 'No'}</span>
</LabelCard>
);
})}
</section>
);
})}
</section>
)}
</NodeWrapper>
);
}

View File

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