diff --git a/webui/src/components/InferenceForm.tsx b/webui/src/components/InferenceForm.tsx
index 0800a03d8c8..ad0048542f8 100644
--- a/webui/src/components/InferenceForm.tsx
+++ b/webui/src/components/InferenceForm.tsx
@@ -1,5 +1,5 @@
-import type { Key } from 'react'
-import { useMemo, useState } from 'react'
+import type { FC, Key } from 'react'
+import { createElement, useMemo, useState } from 'react'
import { useStyletron } from 'baseui'
import { Select } from 'baseui/select'
import { FormControl } from 'baseui/form-control'
@@ -8,13 +8,19 @@ import { FormConsumer } from '@formily/react'
import { FlexGrid, FlexGridItem } from 'baseui/flex-grid'
import { Tab, Tabs } from 'baseui/tabs'
import useCurrentPath from '../hooks/useCurrentPath'
-import { useFormSubmit, useSchema } from '../hooks/useQuery'
+import { transformData, useFormSubmit, useSchema } from '../hooks/useQuery'
import Form from './form/Form'
import FormField, { generateFormSchema } from './form/FormField'
import Submit from './form/Submit'
+import type { IClientProps } from './code/Base'
import CURL from './code/CURL'
import Python from './code/Python'
+const codeExamples = {
+ Python,
+ CURL,
+}
+
export default function InferenceForm() {
const [css, theme] = useStyletron()
const data = useSchema()
@@ -96,16 +102,19 @@ export default function InferenceForm() {
Submit
-
-
- {() => }
-
-
-
-
- {() => }
-
-
+ {
+ Object.entries(codeExamples).map(([title, comp]) => (
+
+
+ {() => createElement(comp as FC, {
+ path: currentRoute?.route,
+ values: transformData(form.values, currentRoute?.input),
+ schema: currentRoute?.input,
+ })}
+
+
+ ))
+ }
diff --git a/webui/src/components/code/CURL.tsx b/webui/src/components/code/CURL.tsx
index ef12caef9a6..15d5e3f654c 100644
--- a/webui/src/components/code/CURL.tsx
+++ b/webui/src/components/code/CURL.tsx
@@ -1,5 +1,5 @@
import isEmpty from 'lodash/isEmpty'
-import { convert, hasFileInSchema } from '../../hooks/useQuery'
+import { hasFileInSchema, splitFileAndNonFileFields } from '../../hooks/useQuery'
import type { TObject } from '../../types'
import type { IClientProps } from './Base'
import Code, { formatJSON } from './Base'
@@ -28,7 +28,7 @@ function formatFiles(files: { [field: string]: File | File[] }, indent = 4) {
function generateCode(data: object, path = '/', schema?: TObject) {
const hasFiles = hasFileInSchema(schema ? { schema } : {})
if (hasFiles) {
- const { nonFileFields, fileFields } = convert(data)
+ const { nonFileFields, fileFields } = splitFileAndNonFileFields(data)
return `$ curl -s -X POST \\
-F '__data=${formatJSON(nonFileFields, 4)}' \\
diff --git a/webui/src/hooks/useQuery.ts b/webui/src/hooks/useQuery.ts
index ab111f1e9e3..a9daa72d811 100644
--- a/webui/src/hooks/useQuery.ts
+++ b/webui/src/hooks/useQuery.ts
@@ -2,6 +2,7 @@ import { useCallback, useContext } from 'react'
import type { Form } from '@formily/core'
import isObject from 'lodash/isObject'
import findKey from 'lodash/findKey'
+import transform from 'lodash/transform'
import { JSONSchemaContext } from '../components/JSONSchema'
import type { DataType, TObject } from '../types'
@@ -45,12 +46,45 @@ export function isFileField(value: DataType): boolean {
}
/**
- * Converts an object by separating File objects and other data.
- * @param srcValue The source object to convert.
+ * Transforms the source value according to the provided schema.
+ * @param {any} srcValue - The value to be transformed.
+ * @param {DataType} [schema] - The schema defining the transformation rules.
+ * @returns {any} - The transformed data.
+ */
+export function transformData(srcValue: any, schema?: DataType) {
+ if (!schema)
+ return srcValue
+ switch (schema?.type) {
+ case 'object':
+ if (schema.properties) {
+ return transform(schema.properties, (res, value, key) => {
+ res[key] = transformData(srcValue[key], value)
+ return res
+ }, {})
+ }
+ else {
+ try {
+ return JSON.parse(srcValue)
+ }
+ catch {
+ // return empty object when JSON string parse failed
+ return {}
+ }
+ }
+ case 'array':
+ return srcValue?.map((item: any) => transformData(item, schema.items))
+ default:
+ return srcValue
+ }
+}
+
+/**
+ * Splits the source object into two separate objects based on field types: non-file fields and file fields.
+ * @param srcValue The source object containing the fields to be split. This object can be nested.
* @param parentPath The path used to construct the key for nested objects.
* @returns An object containing separated nonFileFields and fileFields.
*/
-export function convert(srcValue: object, parentPath = '') {
+export function splitFileAndNonFileFields(srcValue: object, parentPath = '') {
const nonFileFields: { [key: string]: unknown } = {}
const fileFields: { [key: string]: File | File[] } = {}
@@ -62,7 +96,7 @@ export function convert(srcValue: object, parentPath = '') {
fileFields[path] = value
}
else if (isObject(value)) {
- const res = convert(value, path)
+ const res = splitFileAndNonFileFields(value, path)
nonFileFields[key] = res.nonFileFields
Object.assign(fileFields, res.fileFields)
}
@@ -77,10 +111,11 @@ export function convert(srcValue: object, parentPath = '') {
export function useFormSubmit(form: Form, input?: TObject) {
return useCallback(async (url: string) => {
const submittedFormData = await form.submit