Feat: Show webhook schema dialog.

This commit is contained in:
bill 2025-12-09 15:31:10 +08:00
parent 137de4849d
commit 266edf1a5f
8 changed files with 177 additions and 47 deletions

View file

@ -42,9 +42,9 @@ import { FormWrapper } from '../components/form-wrapper';
import { Output } from '../components/output';
import { PromptEditor } from '../components/prompt-editor';
import { QueryVariable } from '../components/query-variable';
import { SchemaDialog } from '../components/schema-dialog';
import { SchemaPanel } from '../components/schema-panel';
import { AgentTools, Agents } from './agent-tools';
import { StructuredOutputDialog } from './structured-output-dialog';
import { StructuredOutputPanel } from './structured-output-panel';
import { useBuildPromptExtraPromptOptions } from './use-build-prompt-options';
import {
useHandleShowStructuredOutput,
@ -327,19 +327,17 @@ function AgentForm({ node }: INextOperatorForm) {
</Button>
</div>
<StructuredOutputPanel
value={structuredOutput}
></StructuredOutputPanel>
<SchemaPanel value={structuredOutput}></SchemaPanel>
</section>
)}
</FormWrapper>
</Form>
{structuredOutputDialogVisible && (
<StructuredOutputDialog
<SchemaDialog
hideModal={hideStructuredOutputDialog}
onOk={handleStructuredOutputDialogOk}
initialValues={structuredOutput}
></StructuredOutputDialog>
></SchemaDialog>
)}
</>
);

View file

@ -24,6 +24,7 @@ import { INextOperatorForm } from '../../interface';
import { ParameterDialog } from './parameter-dialog';
import { QueryTable } from './query-table';
import { useEditQueryRecord } from './use-edit-query';
import { useHandleModeChange } from './use-handle-mode-change';
import { useValues } from './use-values';
import { useWatchFormChange } from './use-watch-change';
import { WebHook } from './webhook';
@ -98,6 +99,8 @@ function BeginForm({ node }: INextOperatorForm) {
const previousModeRef = useRef(mode);
const { handleModeChange } = useHandleModeChange(form);
useEffect(() => {
if (
previousModeRef.current === AgentDialogueMode.Task &&
@ -137,6 +140,10 @@ function BeginForm({ node }: INextOperatorForm) {
placeholder={t('common.pleaseSelect')}
options={ModeOptions}
{...field}
onChange={(val) => {
handleModeChange(val);
field.onChange(val);
}}
></RAGFlowSelect>
</FormControl>
<FormMessage />
@ -185,44 +192,48 @@ function BeginForm({ node }: INextOperatorForm) {
/>
)}
{mode === AgentDialogueMode.Webhook && <WebHook></WebHook>}
{/* Create a hidden field to make Form instance record this */}
<FormField
control={form.control}
name={'inputs'}
render={() => <div></div>}
/>
<Collapse
title={
<div>
{t('flow.input')}
<FormTooltip tooltip={t('flow.beginInputTip')}></FormTooltip>
</div>
}
rightContent={
<Button
variant={'ghost'}
onClick={(e) => {
e.preventDefault();
showModal();
}}
{mode !== AgentDialogueMode.Webhook && (
<>
{/* Create a hidden field to make Form instance record this */}
<FormField
control={form.control}
name={'inputs'}
render={() => <div></div>}
/>
<Collapse
title={
<div>
{t('flow.input')}
<FormTooltip tooltip={t('flow.beginInputTip')}></FormTooltip>
</div>
}
rightContent={
<Button
variant={'ghost'}
onClick={(e) => {
e.preventDefault();
showModal();
}}
>
<Plus />
</Button>
}
>
<Plus />
</Button>
}
>
<QueryTable
data={inputs}
showModal={showModal}
deleteRecord={handleDeleteRecord}
></QueryTable>
</Collapse>
{visible && (
<ParameterDialog
hideModal={hideModal}
initialValue={currentRecord}
otherThanCurrentQuery={otherThanCurrentQuery}
submit={ok}
></ParameterDialog>
<QueryTable
data={inputs}
showModal={showModal}
deleteRecord={handleDeleteRecord}
></QueryTable>
</Collapse>
{visible && (
<ParameterDialog
hideModal={hideModal}
initialValue={currentRecord}
otherThanCurrentQuery={otherThanCurrentQuery}
submit={ok}
></ParameterDialog>
)}
</>
)}
</Form>
</section>

View file

@ -0,0 +1,60 @@
import { useCallback } from 'react';
import { UseFormReturn } from 'react-hook-form';
import { AgentDialogueMode } 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: '',
},
},
};
export function useHandleModeChange(form: UseFormReturn<any>) {
const handleModeChange = useCallback(
(mode: AgentDialogueMode) => {
if (mode === AgentDialogueMode.Webhook) {
form.setValue('schema', schema, { shouldDirty: true });
}
},
[form],
);
return { handleModeChange };
}

View file

@ -0,0 +1,28 @@
import { JSONSchema } from '@/components/jsonjoy-builder';
import { useSetModalState } from '@/hooks/common-hooks';
import { useCallback } from 'react';
import { UseFormReturn } from 'react-hook-form';
export function useShowSchemaDialog(form: UseFormReturn<any>) {
const {
visible: schemaDialogVisible,
showModal: showSchemaDialog,
hideModal: hideSchemaDialog,
} = useSetModalState();
const handleSchemaDialogOk = useCallback(
(values: JSONSchema) => {
// Sync data to canvas
form.setValue('schema', values);
hideSchemaDialog();
},
[form, hideSchemaDialog],
);
return {
schemaDialogVisible,
showSchemaDialog,
hideSchemaDialog,
handleSchemaDialogOk,
};
}

View file

@ -23,6 +23,7 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) {
const nextValues = {
...values,
inputs: transferInputsArrayToObject(values.inputs),
outputs: values.schema,
};
updateNodeForm(id, nextValues);

View file

@ -1,5 +1,6 @@
import { SelectWithSearch } from '@/components/originui/select-with-search';
import { RAGFlowFormItem } from '@/components/ragflow-form';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Separator } from '@/components/ui/separator';
import { Textarea } from '@/components/ui/textarea';
@ -13,6 +14,9 @@ import {
WebhookSecurityAuthType,
} from '../../../constant';
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 { WebhookResponse } from './response';
@ -27,6 +31,15 @@ export function WebHook() {
name: 'execution_mode',
});
const {
showSchemaDialog,
schemaDialogVisible,
hideSchemaDialog,
handleSchemaDialogOk,
} = useShowSchemaDialog(form);
const schema = form.getValues('schema');
return (
<>
<RAGFlowFormItem name="methods" label={t('flow.webhook.methods')}>
@ -76,7 +89,11 @@ export function WebHook() {
label={t('flow.webhook.ipWhitelist')}
></DynamicStringForm>
</>
<RAGFlowFormItem name="schema" label={t('flow.webhook.schema')}>
<RAGFlowFormItem
name="schema"
label={t('flow.webhook.schema')}
className="hidden"
>
<Textarea></Textarea>
</RAGFlowFormItem>
<RAGFlowFormItem
@ -90,6 +107,21 @@ export function WebHook() {
{executionMode === WebhookExecutionMode.Immediately && (
<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}
></SchemaDialog>
)}
</>
);
}

View file

@ -16,7 +16,7 @@ import { IModalProps } from '@/interfaces/common';
import { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
export function StructuredOutputDialog({
export function SchemaDialog({
hideModal,
onOk,
initialValues,

View file

@ -1,6 +1,6 @@
import { JSONSchema, JsonSchemaVisualizer } from '@/components/jsonjoy-builder';
export function StructuredOutputPanel({ value }: { value: JSONSchema }) {
export function SchemaPanel({ value }: { value: JSONSchema }) {
return (
<section className="h-48">
<JsonSchemaVisualizer