mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-19 20:16:49 +08:00
### What problem does this PR solve? Feat: Flatten the request schema of the webhook #10427 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -2008,6 +2008,9 @@ Important structured information may include: names, dates, locations, events, k
|
|||||||
basic: 'Basic',
|
basic: 'Basic',
|
||||||
bearer: 'Bearer',
|
bearer: 'Bearer',
|
||||||
apiKey: 'Api Key',
|
apiKey: 'Api Key',
|
||||||
|
queryParameters: 'Query parameters',
|
||||||
|
headerParameters: 'Header parameters',
|
||||||
|
requestBodyParameters: 'Request body parameters',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
llmTools: {
|
llmTools: {
|
||||||
|
|||||||
@ -1802,6 +1802,9 @@ Tokenizer 会根据所选方式将内容存储为对应的数据结构。`,
|
|||||||
basic: '基础认证',
|
basic: '基础认证',
|
||||||
bearer: '承载令牌',
|
bearer: '承载令牌',
|
||||||
apiKey: 'API密钥',
|
apiKey: 'API密钥',
|
||||||
|
queryParameters: '查询参数',
|
||||||
|
headerParameters: '请求头参数',
|
||||||
|
requestBodyParameters: '请求体参数',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
|
|||||||
@ -12,18 +12,17 @@ import { RAGFlowSelect } from '@/components/ui/select';
|
|||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { FormTooltip } from '@/components/ui/tooltip';
|
import { FormTooltip } from '@/components/ui/tooltip';
|
||||||
import { WebhookAlgorithmList } from '@/constants/agent';
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { memo, useEffect, useRef } from 'react';
|
import { memo, useEffect, useRef } from 'react';
|
||||||
import { useForm, useWatch } from 'react-hook-form';
|
import { useForm, useWatch } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { z } from 'zod';
|
|
||||||
import { AgentDialogueMode } from '../../constant';
|
import { AgentDialogueMode } from '../../constant';
|
||||||
import { INextOperatorForm } from '../../interface';
|
import { INextOperatorForm } from '../../interface';
|
||||||
import { ParameterDialog } from './parameter-dialog';
|
import { ParameterDialog } from './parameter-dialog';
|
||||||
import { QueryTable } from './query-table';
|
import { QueryTable } from './query-table';
|
||||||
|
import { BeginFormSchema } from './schema';
|
||||||
import { useEditQueryRecord } from './use-edit-query';
|
import { useEditQueryRecord } from './use-edit-query';
|
||||||
import { useHandleModeChange } from './use-handle-mode-change';
|
import { useHandleModeChange } from './use-handle-mode-change';
|
||||||
import { useValues } from './use-values';
|
import { useValues } from './use-values';
|
||||||
@ -36,55 +35,6 @@ const ModeOptions = [
|
|||||||
{ value: AgentDialogueMode.Webhook, label: t('flow.webhook.name') },
|
{ value: AgentDialogueMode.Webhook, label: t('flow.webhook.name') },
|
||||||
];
|
];
|
||||||
|
|
||||||
const FormSchema = z.object({
|
|
||||||
enablePrologue: z.boolean().optional(),
|
|
||||||
prologue: z.string().trim().optional(),
|
|
||||||
mode: z.string(),
|
|
||||||
inputs: z
|
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
key: z.string(),
|
|
||||||
type: z.string(),
|
|
||||||
value: z.string(),
|
|
||||||
optional: z.boolean(),
|
|
||||||
name: z.string(),
|
|
||||||
options: z.array(z.union([z.number(), z.string(), z.boolean()])),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.optional(),
|
|
||||||
methods: z.string().optional(),
|
|
||||||
content_types: z.string().optional(),
|
|
||||||
security: z
|
|
||||||
.object({
|
|
||||||
auth_type: z.string(),
|
|
||||||
ip_whitelist: z.array(z.object({ value: z.string() })),
|
|
||||||
rate_limit: z.object({
|
|
||||||
limit: z.number(),
|
|
||||||
per: z.string().optional(),
|
|
||||||
}),
|
|
||||||
max_body_size: z.string(),
|
|
||||||
jwt: z
|
|
||||||
.object({
|
|
||||||
algorithm: z.string().default(WebhookAlgorithmList[0]).optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
schema: z.record(z.any()).optional(),
|
|
||||||
response: z
|
|
||||||
.object({
|
|
||||||
status: z.number(),
|
|
||||||
headers_template: z.array(
|
|
||||||
z.object({ key: z.string(), value: z.string() }),
|
|
||||||
),
|
|
||||||
body_template: z.array(z.object({ key: z.string(), value: z.string() })),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
execution_mode: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type BeginFormSchemaType = z.infer<typeof FormSchema>;
|
|
||||||
|
|
||||||
function BeginForm({ node }: INextOperatorForm) {
|
function BeginForm({ node }: INextOperatorForm) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@ -92,7 +42,7 @@ function BeginForm({ node }: INextOperatorForm) {
|
|||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
defaultValues: values,
|
defaultValues: values,
|
||||||
resolver: zodResolver(FormSchema),
|
resolver: zodResolver(BeginFormSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
useWatchFormChange(node?.id, form);
|
useWatchFormChange(node?.id, form);
|
||||||
|
|||||||
81
web/src/pages/agent/form/begin-form/schema.ts
Normal file
81
web/src/pages/agent/form/begin-form/schema.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { WebhookAlgorithmList } from '@/constants/agent';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const BeginFormSchema = z.object({
|
||||||
|
enablePrologue: z.boolean().optional(),
|
||||||
|
prologue: z.string().trim().optional(),
|
||||||
|
mode: z.string(),
|
||||||
|
inputs: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
key: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
value: z.string(),
|
||||||
|
optional: z.boolean(),
|
||||||
|
name: z.string(),
|
||||||
|
options: z.array(z.union([z.number(), z.string(), z.boolean()])),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
methods: z.array(z.string()).optional(),
|
||||||
|
content_types: z.string().optional(),
|
||||||
|
security: z
|
||||||
|
.object({
|
||||||
|
auth_type: z.string(),
|
||||||
|
ip_whitelist: z.array(z.object({ value: z.string() })),
|
||||||
|
rate_limit: z.object({
|
||||||
|
limit: z.number(),
|
||||||
|
per: z.string().optional(),
|
||||||
|
}),
|
||||||
|
max_body_size: z.string(),
|
||||||
|
jwt: z
|
||||||
|
.object({
|
||||||
|
algorithm: z.string().default(WebhookAlgorithmList[0]).optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
schema: z
|
||||||
|
.object({
|
||||||
|
query: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
key: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
required: z.boolean(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
headers: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
key: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
required: z.boolean(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
body: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
key: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
required: z.boolean(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
response: z
|
||||||
|
.object({
|
||||||
|
status: z.number(),
|
||||||
|
headers_template: z.array(
|
||||||
|
z.object({ key: z.string(), value: z.string() }),
|
||||||
|
),
|
||||||
|
body_template: z.array(z.object({ key: z.string(), value: z.string() })),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
execution_mode: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type BeginFormSchemaType = z.infer<typeof BeginFormSchema>;
|
||||||
@ -3,62 +3,21 @@ import { UseFormReturn } from 'react-hook-form';
|
|||||||
import {
|
import {
|
||||||
AgentDialogueMode,
|
AgentDialogueMode,
|
||||||
RateLimitPerList,
|
RateLimitPerList,
|
||||||
|
WebhookContentType,
|
||||||
WebhookExecutionMode,
|
WebhookExecutionMode,
|
||||||
WebhookMaxBodySize,
|
WebhookMaxBodySize,
|
||||||
|
WebhookMethod,
|
||||||
WebhookSecurityAuthType,
|
WebhookSecurityAuthType,
|
||||||
} from '../../constant';
|
} from '../../constant';
|
||||||
|
|
||||||
// const WebhookSchema = {
|
|
||||||
// query: {
|
|
||||||
// type: 'object',
|
|
||||||
// required: [],
|
|
||||||
// properties: {
|
|
||||||
// // debug: { type: 'boolean' },
|
|
||||||
// // event: { type: 'string' },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
|
|
||||||
// headers: {
|
|
||||||
// type: 'object',
|
|
||||||
// required: [],
|
|
||||||
// properties: {
|
|
||||||
// // 'X-Trace-ID': { type: 'string' },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
|
|
||||||
// body: {
|
|
||||||
// type: 'object',
|
|
||||||
// required: [],
|
|
||||||
// properties: {
|
|
||||||
// id: { type: 'string' },
|
|
||||||
// payload: { type: 'object' },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
|
|
||||||
const schema = {
|
|
||||||
properties: {
|
|
||||||
query: {
|
|
||||||
type: 'object',
|
|
||||||
description: '',
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
type: 'object',
|
|
||||||
description: '',
|
|
||||||
},
|
|
||||||
body: {
|
|
||||||
type: 'object',
|
|
||||||
description: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialFormValuesMap = {
|
const initialFormValuesMap = {
|
||||||
schema: schema,
|
methods: [WebhookMethod.Get],
|
||||||
|
schema: {},
|
||||||
'security.auth_type': WebhookSecurityAuthType.Basic,
|
'security.auth_type': WebhookSecurityAuthType.Basic,
|
||||||
'security.rate_limit.per': RateLimitPerList[0],
|
'security.rate_limit.per': RateLimitPerList[0],
|
||||||
'security.max_body_size': WebhookMaxBodySize[0],
|
'security.max_body_size': WebhookMaxBodySize[0],
|
||||||
execution_mode: WebhookExecutionMode.Immediately,
|
execution_mode: WebhookExecutionMode.Immediately,
|
||||||
|
content_types: WebhookContentType.ApplicationJson,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useHandleModeChange(form: UseFormReturn<any>) {
|
export function useHandleModeChange(form: UseFormReturn<any>) {
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import { omit } from 'lodash';
|
import { isEmpty, omit } from 'lodash';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { UseFormReturn, useWatch } from 'react-hook-form';
|
import { UseFormReturn, useWatch } from 'react-hook-form';
|
||||||
import { AgentDialogueMode } from '../../constant';
|
import { AgentDialogueMode } from '../../constant';
|
||||||
import { BeginQuery } from '../../interface';
|
import { BeginQuery } from '../../interface';
|
||||||
import useGraphStore from '../../store';
|
import useGraphStore from '../../store';
|
||||||
|
import { BeginFormSchemaType } from './schema';
|
||||||
|
|
||||||
export function transferInputsArrayToObject(inputs: BeginQuery[] = []) {
|
export function transferInputsArrayToObject(inputs: BeginQuery[] = []) {
|
||||||
return inputs.reduce<Record<string, Omit<BeginQuery, 'key'>>>((pre, cur) => {
|
return inputs.reduce<Record<string, Omit<BeginQuery, 'key'>>>((pre, cur) => {
|
||||||
@ -13,6 +14,20 @@ export function transferInputsArrayToObject(inputs: BeginQuery[] = []) {
|
|||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function transformRequestSchemaToOutput(schema: BeginFormSchemaType['schema']) {
|
||||||
|
const outputs: Record<string, any> = {};
|
||||||
|
|
||||||
|
Object.entries(schema || {}).forEach(([key, value]) => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
for (const cur of value) {
|
||||||
|
outputs[`${key}.${cur.key}`] = { type: cur.type };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
|
||||||
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 });
|
||||||
const updateNodeForm = useGraphStore((state) => state.updateNodeForm);
|
const updateNodeForm = useGraphStore((state) => state.updateNodeForm);
|
||||||
@ -27,9 +42,9 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) {
|
|||||||
// Each property (body, headers, query) should be able to show secondary menu
|
// Each property (body, headers, query) should be able to show secondary menu
|
||||||
if (
|
if (
|
||||||
values.mode === AgentDialogueMode.Webhook &&
|
values.mode === AgentDialogueMode.Webhook &&
|
||||||
values.schema?.properties
|
!isEmpty(values.schema)
|
||||||
) {
|
) {
|
||||||
outputs = { ...values.schema.properties };
|
outputs = transformRequestSchemaToOutput(values.schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextValues = {
|
const nextValues = {
|
||||||
|
|||||||
103
web/src/pages/agent/form/begin-form/webhook/dynamic-request.tsx
Normal file
103
web/src/pages/agent/form/begin-form/webhook/dynamic-request.tsx
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
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';
|
||||||
|
import { Separator } from '@/components/ui/separator';
|
||||||
|
import { Switch } from '@/components/ui/switch';
|
||||||
|
import { loader } from '@monaco-editor/react';
|
||||||
|
import { X } from 'lucide-react';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||||
|
import { TypesWithArray } from '../../../constant';
|
||||||
|
import { buildConversationVariableSelectOptions } from '../../../utils';
|
||||||
|
import { DynamicFormHeader } from '../../components/dynamic-fom-header';
|
||||||
|
|
||||||
|
loader.config({ paths: { vs: '/vs' } });
|
||||||
|
|
||||||
|
type SelectKeysProps = {
|
||||||
|
name: string;
|
||||||
|
label: ReactNode;
|
||||||
|
tooltip?: string;
|
||||||
|
keyField?: string;
|
||||||
|
operatorField?: string;
|
||||||
|
requiredField?: string;
|
||||||
|
nodeId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const VariableTypeOptions = buildConversationVariableSelectOptions();
|
||||||
|
|
||||||
|
export function DynamicRequest({
|
||||||
|
name,
|
||||||
|
label,
|
||||||
|
tooltip,
|
||||||
|
keyField = 'key',
|
||||||
|
operatorField = 'type',
|
||||||
|
requiredField = 'required',
|
||||||
|
}: SelectKeysProps) {
|
||||||
|
const form = useFormContext();
|
||||||
|
|
||||||
|
const { fields, remove, append } = useFieldArray({
|
||||||
|
name: name,
|
||||||
|
control: form.control,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="space-y-2">
|
||||||
|
<DynamicFormHeader
|
||||||
|
label={label}
|
||||||
|
tooltip={tooltip}
|
||||||
|
onClick={() =>
|
||||||
|
append({
|
||||||
|
[keyField]: '',
|
||||||
|
[operatorField]: TypesWithArray.String,
|
||||||
|
[requiredField]: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
></DynamicFormHeader>
|
||||||
|
<div className="space-y-5">
|
||||||
|
{fields.map((field, index) => {
|
||||||
|
const keyFieldAlias = `${name}.${index}.${keyField}`;
|
||||||
|
const operatorFieldAlias = `${name}.${index}.${operatorField}`;
|
||||||
|
const requiredFieldAlias = `${name}.${index}.${requiredField}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section key={field.id} className="flex gap-2">
|
||||||
|
<div className="flex-1 space-y-3 min-w-0">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<RAGFlowFormItem name={keyFieldAlias} className="flex-1 ">
|
||||||
|
<KeyInput></KeyInput>
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
<Separator className="w-2" />
|
||||||
|
<RAGFlowFormItem name={operatorFieldAlias} className="flex-1">
|
||||||
|
{(field) => (
|
||||||
|
<SelectWithSearch
|
||||||
|
value={field.value}
|
||||||
|
onChange={(val) => {
|
||||||
|
field.onChange(val);
|
||||||
|
}}
|
||||||
|
options={VariableTypeOptions}
|
||||||
|
></SelectWithSearch>
|
||||||
|
)}
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
<Separator className="w-2" />
|
||||||
|
<RAGFlowFormItem name={requiredFieldAlias}>
|
||||||
|
{(field) => (
|
||||||
|
<Switch
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
></Switch>
|
||||||
|
)}
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button variant={'ghost'} onClick={() => remove(index)}>
|
||||||
|
<X />
|
||||||
|
</Button>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,9 +1,8 @@
|
|||||||
import { Collapse } from '@/components/collapse';
|
import { Collapse } from '@/components/collapse';
|
||||||
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 { Button } from '@/components/ui/button';
|
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { MultiSelect } from '@/components/ui/multi-select';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { buildOptions } from '@/utils/form';
|
import { buildOptions } from '@/utils/form';
|
||||||
import { useFormContext, useWatch } from 'react-hook-form';
|
import { useFormContext, useWatch } from 'react-hook-form';
|
||||||
@ -17,10 +16,8 @@ import {
|
|||||||
WebhookSecurityAuthType,
|
WebhookSecurityAuthType,
|
||||||
} from '../../../constant';
|
} from '../../../constant';
|
||||||
import { DynamicStringForm } from '../../components/dynamic-string-form';
|
import { DynamicStringForm } from '../../components/dynamic-string-form';
|
||||||
import { SchemaDialog } from '../../components/schema-dialog';
|
|
||||||
import { SchemaPanel } from '../../components/schema-panel';
|
|
||||||
import { useShowSchemaDialog } from '../use-show-schema-dialog';
|
|
||||||
import { Auth } from './auth';
|
import { Auth } from './auth';
|
||||||
|
import { WebhookRequestSchema } from './request-schema';
|
||||||
import { WebhookResponse } from './response';
|
import { WebhookResponse } from './response';
|
||||||
|
|
||||||
const RateLimitPerOptions = buildOptions(RateLimitPerList);
|
const RateLimitPerOptions = buildOptions(RateLimitPerList);
|
||||||
@ -34,21 +31,19 @@ export function WebHook() {
|
|||||||
name: 'execution_mode',
|
name: 'execution_mode',
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
|
||||||
showSchemaDialog,
|
|
||||||
schemaDialogVisible,
|
|
||||||
hideSchemaDialog,
|
|
||||||
handleSchemaDialogOk,
|
|
||||||
} = useShowSchemaDialog(form);
|
|
||||||
|
|
||||||
const schema = form.getValues('schema');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RAGFlowFormItem name="methods" label={t('flow.webhook.methods')}>
|
<RAGFlowFormItem name="methods" label={t('flow.webhook.methods')}>
|
||||||
<SelectWithSearch
|
{(field) => (
|
||||||
options={buildOptions(WebhookMethod)}
|
<MultiSelect
|
||||||
></SelectWithSearch>
|
options={buildOptions(WebhookMethod)}
|
||||||
|
placeholder={t('fileManager.pleaseSelect')}
|
||||||
|
maxCount={100}
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value}
|
||||||
|
modalPopover
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</RAGFlowFormItem>
|
</RAGFlowFormItem>
|
||||||
<RAGFlowFormItem
|
<RAGFlowFormItem
|
||||||
name="content_types"
|
name="content_types"
|
||||||
@ -95,6 +90,7 @@ export function WebHook() {
|
|||||||
></DynamicStringForm>
|
></DynamicStringForm>
|
||||||
</section>
|
</section>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
<WebhookRequestSchema></WebhookRequestSchema>
|
||||||
<RAGFlowFormItem
|
<RAGFlowFormItem
|
||||||
name="schema"
|
name="schema"
|
||||||
label={t('flow.webhook.schema')}
|
label={t('flow.webhook.schema')}
|
||||||
@ -113,22 +109,6 @@ export function WebHook() {
|
|||||||
{executionMode === WebhookExecutionMode.Immediately && (
|
{executionMode === WebhookExecutionMode.Immediately && (
|
||||||
<WebhookResponse></WebhookResponse>
|
<WebhookResponse></WebhookResponse>
|
||||||
)}
|
)}
|
||||||
<Separator></Separator>
|
|
||||||
<section className="flex justify-between items-center">
|
|
||||||
Schema
|
|
||||||
<Button variant={'ghost'} onClick={showSchemaDialog}>
|
|
||||||
{t('flow.structuredOutput.configuration')}
|
|
||||||
</Button>
|
|
||||||
</section>
|
|
||||||
<SchemaPanel value={schema}></SchemaPanel>
|
|
||||||
{schemaDialogVisible && (
|
|
||||||
<SchemaDialog
|
|
||||||
initialValues={schema}
|
|
||||||
hideModal={hideSchemaDialog}
|
|
||||||
onOk={handleSchemaDialogOk}
|
|
||||||
pattern={''}
|
|
||||||
></SchemaDialog>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
import { Collapse } from '@/components/collapse';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { DynamicRequest } from './dynamic-request';
|
||||||
|
|
||||||
|
export function WebhookRequestSchema() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Collapse title={<div>{t('flow.webhook.schema')}</div>}>
|
||||||
|
<section className="space-y-4">
|
||||||
|
<DynamicRequest
|
||||||
|
name="schema.query"
|
||||||
|
label={t('flow.webhook.queryParameters')}
|
||||||
|
></DynamicRequest>
|
||||||
|
<DynamicRequest
|
||||||
|
name="schema.headers"
|
||||||
|
label={t('flow.webhook.headerParameters')}
|
||||||
|
></DynamicRequest>
|
||||||
|
<DynamicRequest
|
||||||
|
name="schema.body"
|
||||||
|
label={t('flow.webhook.requestBodyParameters')}
|
||||||
|
></DynamicRequest>
|
||||||
|
</section>
|
||||||
|
</Collapse>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -35,7 +35,7 @@ import {
|
|||||||
Operator,
|
Operator,
|
||||||
TypesWithArray,
|
TypesWithArray,
|
||||||
} from './constant';
|
} from './constant';
|
||||||
import { BeginFormSchemaType } from './form/begin-form';
|
import { BeginFormSchemaType } from './form/begin-form/schema';
|
||||||
import { DataOperationsFormSchemaType } from './form/data-operations-form';
|
import { DataOperationsFormSchemaType } from './form/data-operations-form';
|
||||||
import { ExtractorFormSchemaType } from './form/extractor-form';
|
import { ExtractorFormSchemaType } from './form/extractor-form';
|
||||||
import { HierarchicalMergerFormSchemaType } from './form/hierarchical-merger-form';
|
import { HierarchicalMergerFormSchemaType } from './form/hierarchical-merger-form';
|
||||||
@ -326,10 +326,31 @@ export function transformArrayToObject(
|
|||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function transformRequestSchemaToJsonschema(
|
||||||
|
schema: BeginFormSchemaType['schema'],
|
||||||
|
) {
|
||||||
|
const jsonSchema: Record<string, any> = {};
|
||||||
|
Object.entries(schema || {}).forEach(([key, value]) => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
jsonSchema[key] = {
|
||||||
|
type: 'object',
|
||||||
|
required: value.filter((x) => x.required).map((x) => x.key),
|
||||||
|
properties: value.reduce<Record<string, any>>((pre, cur) => {
|
||||||
|
pre[cur.key] = { type: cur.type };
|
||||||
|
return pre;
|
||||||
|
}, {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return jsonSchema;
|
||||||
|
}
|
||||||
|
|
||||||
function transformBeginParams(params: BeginFormSchemaType) {
|
function transformBeginParams(params: BeginFormSchemaType) {
|
||||||
if (params.mode === AgentDialogueMode.Webhook) {
|
if (params.mode === AgentDialogueMode.Webhook) {
|
||||||
return {
|
return {
|
||||||
...params,
|
...params,
|
||||||
|
schema: transformRequestSchemaToJsonschema(params.schema),
|
||||||
security: {
|
security: {
|
||||||
...params.security,
|
...params.security,
|
||||||
ip_whitelist: params.security?.ip_whitelist.map((x) => x.value),
|
ip_whitelist: params.security?.ip_whitelist.map((x) => x.value),
|
||||||
|
|||||||
Reference in New Issue
Block a user