Introduced oxfmt (#17431)

Introduced oxfmt
This commit is contained in:
Zhichang Yu
2026-07-27 17:38:52 +08:00
committed by GitHub
parent 4551f945fc
commit 61d58f598e
35 changed files with 987 additions and 21035 deletions

View File

@@ -77,38 +77,39 @@ pre-commit:
- name: web-deps
glob: "web/**/*.{css,less,json,js,jsx,ts,tsx}"
run: |
# Ensure web/node_modules includes the tooling needed by the hook jobs.
# CI runners don't run `npm install` before lefthook, and `npx` without
# local node_modules fetches the latest packages from the registry - which
# breaks because the pinned prettier plugins
# (prettier-plugin-organize-imports, prettier-plugin-packagejson) aren't
# auto-resolved and ESLint 10 requires eslint.config.js.
if [ ! -f web/node_modules/.package-lock.json ] \
|| [ ! -d web/node_modules/prettier-plugin-organize-imports ] \
|| [ ! -d web/node_modules/@typescript-eslint/eslint-plugin ]; then
echo '==> web/node_modules missing required hook dependencies; running npm ci --prefix web --include=dev'
rm -rf web/node_modules
npm ci --prefix web --include=dev --no-audit --no-fund
# Install ONLY oxfmt + oxlint (prebuilt Rust binaries) into an isolated
# dir inside web/node_modules. This skips the full React dev-toolchain
# `npm ci`, which was the dominant CI cost of the old prettier/eslint
# setup. oxfmt/oxlint read web/.oxfmtrc.json / web/.oxlintrc.json from cwd.
HOOKBIN=web/node_modules/.hookbin
if [ ! -x "$HOOKBIN/node_modules/.bin/oxfmt" ] \
|| [ ! -x "$HOOKBIN/node_modules/.bin/oxlint" ]; then
echo '==> installing oxfmt + oxlint only (no full web toolchain)'
mkdir -p "$HOOKBIN"
[ -f "$HOOKBIN/package.json" ] \
|| printf '{"name":"hookbin","version":"1.0.0","private":true}\n' > "$HOOKBIN/package.json"
npm install --prefix "$HOOKBIN" --no-audit --no-fund --save-dev oxfmt oxlint
fi
- name: web-prettier
- name: web-oxfmt
glob: "web/**/*.{css,less,json,js,jsx,ts,tsx}"
exclude: ["web/**/*.min.js", "web/**/*.min.css"]
root: "web/"
stage_fixed: true
run: |
if [ "${LEFTHOOK_CHECK_ONLY:-}" = "1" ]; then
npx prettier --check --ignore-unknown {staged_files}
node_modules/.hookbin/node_modules/.bin/oxfmt --check {staged_files}
else
npx prettier --write --ignore-unknown {staged_files}
node_modules/.hookbin/node_modules/.bin/oxfmt {staged_files}
fi
- name: web-eslint
- name: web-oxlint
glob: "web/**/*.{js,jsx,ts,tsx}"
root: "web/"
stage_fixed: true
run: |
if [ "${LEFTHOOK_CHECK_ONLY:-}" = "1" ]; then
npx eslint {staged_files}
node_modules/.hookbin/node_modules/.bin/oxlint {staged_files}
else
npx eslint --fix {staged_files}
node_modules/.hookbin/node_modules/.bin/oxlint --fix {staged_files}
fi
- name: check-yaml
run: python3 tools/hooks/check_files.py yaml

View File

@@ -1,81 +0,0 @@
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
],
plugins: ['@typescript-eslint', 'react', 'react-refresh', 'check-file'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
settings: {
react: {
version: 'detect',
},
},
env: {
browser: true,
es2021: true,
node: true,
},
rules: {
'@typescript-eslint/no-use-before-define': [
'warn',
{
functions: false,
variables: true,
},
],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'react/prop-types': 'off',
'react/react-in-jsx-scope': 'off',
'react/no-unescaped-entities': [
'warn',
{
forbid: [
{
char: "'",
alternatives: [''', '''],
},
{
char: '"',
alternatives: ['"', '"'],
},
],
},
],
'react-refresh/only-export-components': 'off',
'no-console': ['warn', { allow: ['warn', 'error'] }],
'check-file/filename-naming-convention': [
'error',
{
'**/*.{jsx,tsx}': '[a-z0-9.-]*',
'**/*.{js,ts}': '[a-z0-9.-]*',
},
],
'check-file/folder-naming-convention': [
'error',
{
'src/**/': 'KEBAB_CASE',
'mocks/*/': 'KEBAB_CASE',
},
],
},
overrides: [
{
files: ['**/__tests__/**'],
rules: {
'check-file/folder-naming-convention': 'off',
},
},
],
};

View File

@@ -1,9 +1,7 @@
{
"printWidth": 80,
"singleQuote": true,
"printWidth": 80,
"trailingComma": "all",
"proseWrap": "never",
"overrides": [{ "files": ".prettierrc", "options": { "parser": "json" } }],
"plugins": [],
"endOfLine": "lf"
}

29
web/.oxlintrc.json Normal file
View File

@@ -0,0 +1,29 @@
{
"env": {
"browser": true,
"es2021": true,
"node": true
},
"plugins": ["@typescript-eslint", "react", "react-hooks"],
"settings": {
"react": {
"version": "18.2.0"
}
},
"rules": {
"@typescript-eslint/no-use-before-define": [
"warn",
{
"functions": false,
"variables": true
}
],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"react/react-in-jsx-scope": "off",
"react/no-unescaped-entities": "warn",
"no-console": ["warn", { "allow": ["warn", "error"] }]
}
}

View File

@@ -1,3 +0,0 @@
node_modules
.umi
.umi-production

View File

@@ -1,45 +1,43 @@
## Install front-end dependencies
```bash
npm install
```
```bash
npm install
```
## Launch front-end
```bash
npm run dev
```
```bash
npm run dev
```
_The following output confirms a successful launch of the system:_
_The following output confirms a successful launch of the system:_
![](https://github.com/user-attachments/assets/0daf462c-a24d-4496-a66f-92533534e187)
![](https://github.com/user-attachments/assets/0daf462c-a24d-4496-a66f-92533534e187)
## Login to RAGFlow web UI
Open your browser and navigate to:
Open your browser and navigate to:
```bash
http://localhost:9222 or http://[YOUR_MACHINE_IP]:9222
```
_Replace `[YOUR_MACHINE_IP]` with your actual machine IP address (e.g., `http://192.168.1.49:9222`)._
```bash
http://localhost:9222 or http://[YOUR_MACHINE_IP]:9222
```
_Replace `[YOUR_MACHINE_IP]` with your actual machine IP address (e.g., `http://192.168.1.49:9222`)._
## Login to RAGFlow web admin UI
Open your browser and navigate to:
Open your browser and navigate to:
```bash
http://localhost:9222/admin or http://[YOUR_MACHINE_IP]:9222/admin
```
_Replace `[YOUR_MACHINE_IP]` with your actual machine IP address (e.g., `http://192.168.1.49:9222/admin`)._
```bash
http://localhost:9222/admin or http://[YOUR_MACHINE_IP]:9222/admin
```
_Replace `[YOUR_MACHINE_IP]` with your actual machine IP address (e.g., `http://192.168.1.49:9222/admin`)._
## Shutdown front-end
Ctrl + C or
Ctrl + C or
```bash
kill -f "umi dev"
```
```bash
kill -f "umi dev"
```

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
@@ -11,4 +11,4 @@
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
</html>

3326
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,20 +8,14 @@
"build": "vite build --mode production",
"build-storybook": "storybook build",
"dev": "vite --host",
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
"lint": "eslint src --ext .ts,.tsx --report-unused-disable-directives",
"format": "oxfmt src",
"format:check": "oxfmt --check src",
"lint": "oxlint src",
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"test": "jest --no-cache --coverage",
"type-check": "tsc --noEmit"
},
"overrides": {
"@radix-ui/react-dismissable-layer": "1.1.4",
"monaco-editor": {
"dompurify": "3.3.2"
}
},
"dependencies": {
"@ant-design/icons": "^5.2.6",
"@antv/g2": "^5.2.10",
@@ -168,18 +162,12 @@
"@types/testing-library__jest-dom": "^6.0.0",
"@types/uuid": "^9.0.8",
"@types/webpack-env": "^1.18.4",
"@typescript-eslint/eslint-plugin": "^8.52.0",
"@typescript-eslint/parser": "^8.52.0",
"@vitejs/plugin-react": "^5.1.2",
"autoprefixer": "^10.4.21",
"oxfmt": "^0.60.0",
"oxlint": "^1.75.0",
"cross-env": "^7.0.3",
"esbuild-jest": "^0.5.0",
"eslint": "^8.56.0",
"eslint-plugin-check-file": "^2.8.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.26",
"eslint-plugin-storybook": "^9.1.4",
"html-loader": "^5.1.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
@@ -187,9 +175,6 @@
"less": "^4.4.2",
"postcss": "^8.5.6",
"postcss-loader": "^8.2.0",
"prettier": "^3.2.4",
"prettier-plugin-organize-imports": "^3.2.4",
"prettier-plugin-packagejson": "^2.4.9",
"react-dev-inspector": "^2.0.1",
"remark-loader": "^6.0.0",
"simple-icons": "^16.23.0",
@@ -203,6 +188,12 @@
"vite-plugin-static-copy": "^3.1.4",
"vite-svg-loader": "^5.1.0"
},
"overrides": {
"@radix-ui/react-dismissable-layer": "1.1.4",
"monaco-editor": {
"dompurify": "3.3.2"
}
},
"engines": {
"node": ">=18.20.4"
}

18263
web/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -159,7 +159,12 @@ export const useCatchDocumentError = (url: string) => {
const { data } = await axios.get(url, { headers: httpHeaders });
// Only treat as error if response is JSON with an error code
// Binary data (like PDF) won't have a code property
if (data && typeof data === 'object' && 'code' in data && data.code !== 0) {
if (
data &&
typeof data === 'object' &&
'code' in data &&
data.code !== 0
) {
setError(data?.message || 'Failed to load document');
}
} catch (e) {

View File

@@ -193,12 +193,7 @@ export const AuthenticatedImg = ({
if (!authenticatedSrc) return fallback ?? null;
return (
<img
src={authenticatedSrc}
alt={alt}
className={className}
{...props}
/>
<img src={authenticatedSrc} alt={alt} className={className} {...props} />
);
};

View File

@@ -28,8 +28,8 @@ div.jsoneditor th,
div.jsoneditor-field,
div.jsoneditor-value,
pre.jsoneditor-preview {
font-family: consolas, menlo, monaco, 'Ubuntu Mono', source-code-pro,
monospace;
font-family:
consolas, menlo, monaco, 'Ubuntu Mono', source-code-pro, monospace;
font-size: 14px;
color: rgb(var(--text-primary));
}

View File

@@ -51,9 +51,10 @@
--animate-scale-out: jsonjoy-scale-out 0.2s ease-out;
--animate-float: jsonjoy-float 3s ease-in-out infinite;
--animate-pulse-subtle: pulse-subtle 3s ease-in-out infinite;
--animate-enter: jsonjoy-fade-in 0.4s ease-out, jsonjoy-scale-in 0.3s ease-out;
--animate-exit: jsonjoy-fade-out 0.3s ease-out,
jsonjoy-scale-out 0.2s ease-out;
--animate-enter:
jsonjoy-fade-in 0.4s ease-out, jsonjoy-scale-in 0.3s ease-out;
--animate-exit:
jsonjoy-fade-out 0.3s ease-out, jsonjoy-scale-out 0.2s ease-out;
--font-sans: var(--font-sans), system-ui, sans-serif;
@@ -219,8 +220,9 @@
--radius: 0.8rem;
--font-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont,
'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
--font-sans:
'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', Arial, sans-serif;
}
.jsonjoy.dark {

View File

@@ -265,7 +265,7 @@ export function buildValidationTree(
children: {},
cumulativeChildrenErrors: validation.success
? 0
: validation.errors?.length ?? 0,
: (validation.errors?.length ?? 0),
};
return node;
@@ -362,7 +362,7 @@ export function buildValidationTree(
}
// Compute cumulative error counts (own + all descendants)
const ownErrors = validation.success ? 0 : validation.errors?.length ?? 0;
const ownErrors = validation.success ? 0 : (validation.errors?.length ?? 0);
const childrenErrors = Object.values(children).reduce(
(sum, child) => sum + child.cumulativeChildrenErrors,
0,

View File

@@ -22,14 +22,12 @@ const RagLineChart = ({ data, showLegend = false }: IProps) => {
// width={500}
// height={300}
data={data}
margin={
{
// top: 5,
// right: 30,
// left: 20,
// bottom: 10,
}
}
margin={{
// top: 5,
// right: 30,
// left: 20,
// bottom: 10,
}}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="xAxis" />

View File

@@ -26,7 +26,8 @@ const badgeVariants = cva(
);
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
extends
React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {

View File

@@ -1,19 +1,19 @@
export enum McpServerType {
Sse = 'sse',
StreamableHttp = 'streamable-http',
Sse = 'sse',
StreamableHttp = 'streamable-http',
}
export interface IMcpServerVariable {
key: string;
name: string;
key: string;
name: string;
}
export interface IMcpServerInfo {
id: string;
name: string;
url: string;
server_type: McpServerType;
description?: string;
variables?: IMcpServerVariable[];
headers: Map<string, string>;
id: string;
name: string;
url: string;
server_type: McpServerType;
description?: string;
variables?: IMcpServerVariable[];
headers: Map<string, string>;
}

View File

@@ -1,13 +1,13 @@
export type ILLMTools = ILLMToolMetadata[];
export interface ILLMToolMetadata {
name: string;
displayName: string;
displayDescription: string;
parameters: Map<string, ILLMToolParameter>;
name: string;
displayName: string;
displayDescription: string;
parameters: Map<string, ILLMToolParameter>;
}
export interface ILLMToolParameter {
type: string;
displayDescription: string;
type: string;
displayDescription: string;
}

View File

@@ -479,8 +479,7 @@ export default {
setDefaultTip: '',
setDefault: 'تعيين كافتراضي',
editLinkDataPipeline: 'تعديل مسار الابتلاع',
linkPipelineSetTip:
'إدارة ربط خط أنابيب الاستيعاب مع مجموعة البيانات هذه',
linkPipelineSetTip: 'إدارة ربط خط أنابيب الاستيعاب مع مجموعة البيانات هذه',
default: 'تقصير',
dataPipeline: 'تبديل أو تكوين مسار الابتلاع.',
linkDataPipeline: 'ربط خط أنابيب استيعاب',

View File

@@ -48,7 +48,11 @@ export function BGPTFormWidgets() {
<FormItem>
<FormLabel>{t('bgptApiKey')}</FormLabel>
<FormControl>
<Input {...field} type="password" placeholder={t('bgptApiKeyTip')} />
<Input
{...field}
type="password"
placeholder={t('bgptApiKeyTip')}
/>
</FormControl>
<FormMessage />
</FormItem>

View File

@@ -37,7 +37,9 @@ function CategorizeForm({ node }: INextOperatorForm) {
<Form {...form}>
<FormWrapper>
<QueryVariable></QueryVariable>
<LargeModelFormField ownerTenantId={ownerTenantId}></LargeModelFormField>
<LargeModelFormField
ownerTenantId={ownerTenantId}
></LargeModelFormField>
<MessageHistoryWindowSizeFormField
min={0}
></MessageHistoryWindowSizeFormField>

View File

@@ -178,7 +178,9 @@ function RetrievalForm({ node }: INextOperatorForm) {
<TopNFormField></TopNFormField>
{hideKnowledgeGraphField || (
<>
<RerankFormFields ownerTenantId={ownerTenantId}></RerankFormFields>
<RerankFormFields
ownerTenantId={ownerTenantId}
></RerankFormFields>
<MetadataFilter canReference></MetadataFilter>
</>
)}

View File

@@ -58,7 +58,9 @@ const RetrievalForm = () => {
<TopNFormField></TopNFormField>
{hideKnowledgeGraphField || (
<>
<RerankFormFields ownerTenantId={ownerTenantId}></RerankFormFields>
<RerankFormFields
ownerTenantId={ownerTenantId}
></RerankFormFields>
<MetadataFilter canReference></MetadataFilter>
</>
)}

View File

@@ -9,7 +9,7 @@ import { exportDsl } from '../utils/dsl-bridge';
/**
* Recursively clear sensitive fields (api_key) from the DSL object
*/
const clearSensitiveFields = <T,>(obj: T): T =>
const clearSensitiveFields = <T>(obj: T): T =>
cloneDeepWith(obj, (value) => {
if (
isPlainObject(value) &&

View File

@@ -58,7 +58,9 @@ export function AgentTagEditor({ agent, open, onOpenChange }: IProps) {
const addTag = (tag: string) => {
const next = tag.trim();
if (!next) return;
if (!tags.some((existing) => existing.toLowerCase() === next.toLowerCase())) {
if (
!tags.some((existing) => existing.toLowerCase() === next.toLowerCase())
) {
setTags([...tags, next]);
}
setDraft('');
@@ -100,9 +102,7 @@ export function AgentTagEditor({ agent, open, onOpenChange }: IProps) {
<DialogContent onClick={(e) => e.stopPropagation()}>
<DialogHeader>
<DialogTitle>{t('flow.editTags')}</DialogTitle>
<DialogDescription>
{t('flow.editTagsDescription')}
</DialogDescription>
<DialogDescription>{t('flow.editTagsDescription')}</DialogDescription>
</DialogHeader>
<div className="flex flex-wrap gap-1 min-h-8">

View File

@@ -88,7 +88,11 @@ export function useWikiDetailContent({
'';
const displayedArtifact = currentEntry
? { slug: currentEntry.slug, title: currentEntry.title, page_type: currentEntry.pageType }
? {
slug: currentEntry.slug,
title: currentEntry.title,
page_type: currentEntry.pageType,
}
: selectedArtifact;
const previousEntryTitle = previousEntry?.title || previousEntry?.slug;
@@ -164,9 +168,7 @@ export function useWikiDetailContent({
onSuccess: handleCommitSuccess,
});
const { documents } = useFetchDocumentsByIds(
pageData?.source_doc_ids ?? [],
);
const { documents } = useFetchDocumentsByIds(pageData?.source_doc_ids ?? []);
const referenceDocuments = useMemo<Docagg[]>(() => {
return documents.map(

View File

@@ -29,9 +29,7 @@ export function parseWikiLinkHref(
}
// Fallback to a plain {pageType}/{slug} form.
const simpleMatch = normalized.match(
/(?:^|\/)(entity|concept)\/([^/\s"']+)/,
);
const simpleMatch = normalized.match(/(?:^|\/)(entity|concept)\/([^/\s"']+)/);
if (simpleMatch) {
return {
pageType: simpleMatch[1] as WikiPageType,

View File

@@ -73,14 +73,12 @@ export const useMetadataColumns = ({
};
const saveEditedValue = useCallback(
(
newValue?: {
field: string;
value: string;
newValue: string;
valueType: MetadataValueType;
},
) => {
(newValue?: {
field: string;
value: string;
newValue: string;
valueType: MetadataValueType;
}) => {
const realValue = newValue || editingValue;
if (realValue) {
setTableData((prev) => {
@@ -308,7 +306,8 @@ export const useMetadataColumns = ({
handleEditValue(
row.getValue('field'),
value,
row.original.valueType || metadataValueTypeEnum.string,
row.original.valueType ||
metadataValueTypeEnum.string,
)
}
aria-label="Edit"
@@ -344,19 +343,20 @@ export const useMetadataColumns = ({
</div>
</Button>
)}
{hasMore && isRowExpanded && (
// <div className="self-end mt-1">
<Button
variant={'ghost'}
className="bg-transparent px-2 py-1"
onClick={handleToggleExpand}
>
<div className="text-text-secondary">
<ListChevronsDownUp size={14} />
</div>
</Button>
// </div>
)}
{hasMore &&
isRowExpanded && (
// <div className="self-end mt-1">
<Button
variant={'ghost'}
className="bg-transparent px-2 py-1"
onClick={handleToggleExpand}
>
<div className="text-text-secondary">
<ListChevronsDownUp size={14} />
</div>
</Button>
// </div>
)}
</div>
);
},

View File

@@ -15,7 +15,9 @@ export function BookConfiguration() {
return (
<MainContainer>
<ConfigurationFormContainer>
<LayoutRecognizeFormField ownerTenantId={ownerTenantId}></LayoutRecognizeFormField>
<LayoutRecognizeFormField
ownerTenantId={ownerTenantId}
></LayoutRecognizeFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>

View File

@@ -15,7 +15,9 @@ export function LawsConfiguration() {
return (
<MainContainer>
<ConfigurationFormContainer>
<LayoutRecognizeFormField ownerTenantId={ownerTenantId}></LayoutRecognizeFormField>
<LayoutRecognizeFormField
ownerTenantId={ownerTenantId}
></LayoutRecognizeFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>

View File

@@ -15,7 +15,9 @@ export function ManualConfiguration() {
return (
<MainContainer>
<ConfigurationFormContainer>
<LayoutRecognizeFormField ownerTenantId={ownerTenantId}></LayoutRecognizeFormField>
<LayoutRecognizeFormField
ownerTenantId={ownerTenantId}
></LayoutRecognizeFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>

View File

@@ -11,7 +11,9 @@ export function OneConfiguration() {
const ownerTenantId = useKnowledgeBaseContext().knowledgeBase?.tenant_id;
return (
<ConfigurationFormContainer>
<LayoutRecognizeFormField ownerTenantId={ownerTenantId}></LayoutRecognizeFormField>
<LayoutRecognizeFormField
ownerTenantId={ownerTenantId}
></LayoutRecognizeFormField>
<>
<AutoMetadata />
<AutoKeywordsFormField></AutoKeywordsFormField>

View File

@@ -15,7 +15,9 @@ export function PaperConfiguration() {
return (
<MainContainer>
<ConfigurationFormContainer>
<LayoutRecognizeFormField ownerTenantId={ownerTenantId}></LayoutRecognizeFormField>
<LayoutRecognizeFormField
ownerTenantId={ownerTenantId}
></LayoutRecognizeFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>

View File

@@ -15,7 +15,9 @@ export function PresentationConfiguration() {
return (
<MainContainer>
<ConfigurationFormContainer>
<LayoutRecognizeFormField ownerTenantId={ownerTenantId}></LayoutRecognizeFormField>
<LayoutRecognizeFormField
ownerTenantId={ownerTenantId}
></LayoutRecognizeFormField>
</ConfigurationFormContainer>
<ConfigurationFormContainer>