From e95bdb990e7a7db769f86f3c4bddf2599f096165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20Sowi=C5=84ski?= Date: Mon, 27 Oct 2025 15:36:07 +0100 Subject: [PATCH] feat: Use JSON schema to generate dynamic form --- frontend/package-lock.json | 316 +++++++++++- frontend/package.json | 3 + frontend/pnpm-lock.yaml | 134 ++++++ .../agents-table/agent-configuration-form.tsx | 184 ------- .../agents-table/agent-form-modal.tsx | 70 ++- .../agents-table/dynamic-config-input.tsx | 136 ------ .../agents-table/hooks/use-agent-form.ts | 301 ++++-------- .../data-sets/add-edit-source-modal.tsx | 36 +- .../data-sets/hooks/use-source-form.ts | 268 +++++------ frontend/src/components/rjsf/index.ts | 2 + frontend/src/components/rjsf/rjsf-form.tsx | 307 ++++++++++++ .../rjsf/rjsf-json-editor-widget.tsx | 69 +++ .../rjsf/rjsf-multischema-field-template.tsx | 183 +++++++ frontend/src/components/rjsf/rjsf-theme.tsx | 453 ++++++++++++++++++ frontend/src/components/rjsf/schema-utils.ts | 15 + .../src/components/ui/configuration-form.tsx | 51 -- .../{agents-table => ui}/json-editor.tsx | 23 +- .../util/application-context-provider.tsx | 8 +- frontend/src/lib/api/agents.ts | 14 +- frontend/src/lib/api/data-sets.ts | 37 +- frontend/src/lib/form-utils.ts | 8 +- frontend/src/lib/types.ts | 28 +- .../enthusiast_common/utils.py | 22 +- server/agent/serializers/configuration.py | 18 +- server/agent/tests/test_views.py | 2 +- server/sync/serializers.py | 3 +- server/utils.py | 34 -- server/utils/functions.py | 29 +- server/utils/serializers.py | 16 - server/utils/tests/test_functions.py | 82 +--- 30 files changed, 1901 insertions(+), 951 deletions(-) delete mode 100644 frontend/src/components/agents-table/agent-configuration-form.tsx delete mode 100644 frontend/src/components/agents-table/dynamic-config-input.tsx create mode 100644 frontend/src/components/rjsf/index.ts create mode 100644 frontend/src/components/rjsf/rjsf-form.tsx create mode 100644 frontend/src/components/rjsf/rjsf-json-editor-widget.tsx create mode 100644 frontend/src/components/rjsf/rjsf-multischema-field-template.tsx create mode 100644 frontend/src/components/rjsf/rjsf-theme.tsx create mode 100644 frontend/src/components/rjsf/schema-utils.ts delete mode 100644 frontend/src/components/ui/configuration-form.tsx rename frontend/src/components/{agents-table => ui}/json-editor.tsx (90%) delete mode 100644 server/utils.py diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 330b3d34..20afc8e7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,6 +23,9 @@ "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.5", "@radix-ui/react-tooltip": "^1.2.7", + "@rjsf/core": "^6.2.5", + "@rjsf/utils": "^6.2.5", + "@rjsf/validator-ajv8": "^6.2.5", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "1.0.0", @@ -1711,6 +1714,85 @@ "node": ">=14.0.0" } }, + "node_modules/@rjsf/core": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-6.2.5.tgz", + "integrity": "sha512-k/2aAKj9IY7JBcnPrYv7frgHkfK0KsS7h8PgPW14GJREh+X5EX/icrypcQu5ge/Ggbwi+90plJll07YiRV/lFg==", + "dependencies": { + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "markdown-to-jsx": "^8.0.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@rjsf/utils": "^6.2.x", + "react": ">=18" + } + }, + "node_modules/@rjsf/utils": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-6.2.5.tgz", + "integrity": "sha512-29SvRuY3gKyAHUUnIiJiAF/mTnokgrE7XqUXMj+CZK+sGcmAegwhlnQMJgLQciTodMwTwOaDyV1Fxc47VKTHFw==", + "dependencies": { + "@x0k/json-schema-merge": "^1.0.2", + "fast-uri": "^3.1.0", + "jsonpointer": "^5.0.1", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-is": "^18.3.1" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@rjsf/utils/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/@rjsf/validator-ajv8": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-6.2.5.tgz", + "integrity": "sha512-+yLhFRuT2aY91KiUujhUKg8SyTBrUuQP3QAFINeGi+RljA3S+NQN56oeCaNdz9X+35+Sdy6jqmxy/0Q2K+K9vQ==", + "dependencies": { + "ajv": "^8.17.1", + "ajv-formats": "^2.1.1", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@rjsf/utils": "^6.2.x" + } + }, + "node_modules/@rjsf/validator-ajv8/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@rjsf/validator-ajv8/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.27", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", @@ -1799,7 +1881,6 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, "license": "MIT" }, "node_modules/@types/node": { @@ -2139,6 +2220,14 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/@x0k/json-schema-merge": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@x0k/json-schema-merge/-/json-schema-merge-1.0.2.tgz", + "integrity": "sha512-1734qiJHNX3+cJGDMMw2yz7R+7kpbAtl5NdPs1c/0gO5kYT6s4dMbLXiIfpZNsOYhGZI3aH7FWrj4Zxz7epXNg==", + "dependencies": { + "@types/json-schema": "^7.0.15" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -2179,6 +2268,42 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -3304,7 +3429,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -3349,6 +3473,21 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -3797,6 +3936,14 @@ "node": ">=6" } }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3855,6 +4002,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" + }, + "node_modules/lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3893,6 +4050,22 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, + "node_modules/markdown-to-jsx": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-8.0.0.tgz", + "integrity": "sha512-hWEaRxeCDjes1CVUQqU+Ov0mCqBqkGhLKjL98KdbwHSgEWZZSJQeGlJQatVfeZ3RaxrfTrZZ3eczl2dhp5c/pA==", + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "react": ">= 0.14.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, "node_modules/marked": { "version": "14.1.4", "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.4.tgz", @@ -4575,6 +4748,14 @@ "node": ">=8.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -6301,6 +6482,66 @@ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==" }, + "@rjsf/core": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-6.2.5.tgz", + "integrity": "sha512-k/2aAKj9IY7JBcnPrYv7frgHkfK0KsS7h8PgPW14GJREh+X5EX/icrypcQu5ge/Ggbwi+90plJll07YiRV/lFg==", + "requires": { + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "markdown-to-jsx": "^8.0.0", + "prop-types": "^15.8.1" + } + }, + "@rjsf/utils": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-6.2.5.tgz", + "integrity": "sha512-29SvRuY3gKyAHUUnIiJiAF/mTnokgrE7XqUXMj+CZK+sGcmAegwhlnQMJgLQciTodMwTwOaDyV1Fxc47VKTHFw==", + "requires": { + "@x0k/json-schema-merge": "^1.0.2", + "fast-uri": "^3.1.0", + "jsonpointer": "^5.0.1", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-is": "^18.3.1" + }, + "dependencies": { + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + } + } + }, + "@rjsf/validator-ajv8": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-6.2.5.tgz", + "integrity": "sha512-+yLhFRuT2aY91KiUujhUKg8SyTBrUuQP3QAFINeGi+RljA3S+NQN56oeCaNdz9X+35+Sdy6jqmxy/0Q2K+K9vQ==", + "requires": { + "ajv": "^8.17.1", + "ajv-formats": "^2.1.1", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21" + }, + "dependencies": { + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, "@rolldown/pluginutils": { "version": "1.0.0-beta.27", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", @@ -6373,8 +6614,7 @@ "@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "@types/node": { "version": "22.17.1", @@ -6579,6 +6819,14 @@ "react-refresh": "^0.17.0" } }, + "@x0k/json-schema-merge": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@x0k/json-schema-merge/-/json-schema-merge-1.0.2.tgz", + "integrity": "sha512-1734qiJHNX3+cJGDMMw2yz7R+7kpbAtl5NdPs1c/0gO5kYT6s4dMbLXiIfpZNsOYhGZI3aH7FWrj4Zxz7epXNg==", + "requires": { + "@types/json-schema": "^7.0.15" + } + }, "acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -6604,6 +6852,32 @@ "uri-js": "^4.2.2" } }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, "ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -7254,8 +7528,7 @@ "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { "version": "3.3.3", @@ -7291,6 +7564,11 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==" + }, "fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -7576,6 +7854,11 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" + }, "keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7614,6 +7897,16 @@ "p-locate": "^5.0.0" } }, + "lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" + }, + "lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==" + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -7643,6 +7936,12 @@ "integrity": "sha512-BU7gy8MfBMqvEdDPH79VhOXSEgyG8TSPOKWaExWGCQVqnGH7wGgDngPbofu+KdtVjPQBWbEmnfMTq90CTiiDRg==", "requires": {} }, + "markdown-to-jsx": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-8.0.0.tgz", + "integrity": "sha512-hWEaRxeCDjes1CVUQqU+Ov0mCqBqkGhLKjL98KdbwHSgEWZZSJQeGlJQatVfeZ3RaxrfTrZZ3eczl2dhp5c/pA==", + "requires": {} + }, "marked": { "version": "14.1.4", "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.4.tgz", @@ -8021,6 +8320,11 @@ "picomatch": "^2.2.1" } }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, "resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", diff --git a/frontend/package.json b/frontend/package.json index 7d00f0cc..dde8a8da 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -26,6 +26,9 @@ "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.5", "@radix-ui/react-tooltip": "^1.2.7", + "@rjsf/core": "^6.2.5", + "@rjsf/utils": "^6.2.5", + "@rjsf/validator-ajv8": "^6.2.5", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "1.0.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 83a66f92..21aa11c2 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -53,6 +53,15 @@ importers: '@radix-ui/react-tooltip': specifier: ^1.2.7 version: 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rjsf/core': + specifier: ^6.2.5 + version: 6.2.5(@rjsf/utils@6.2.5(react@18.3.1))(react@18.3.1) + '@rjsf/utils': + specifier: ^6.2.5 + version: 6.2.5(react@18.3.1) + '@rjsf/validator-ajv8': + specifier: ^6.2.5 + version: 6.2.5(@rjsf/utils@6.2.5(react@18.3.1)) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -1105,6 +1114,25 @@ packages: resolution: {integrity: sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==} engines: {node: '>=14.0.0'} + '@rjsf/core@6.2.5': + resolution: {integrity: sha512-k/2aAKj9IY7JBcnPrYv7frgHkfK0KsS7h8PgPW14GJREh+X5EX/icrypcQu5ge/Ggbwi+90plJll07YiRV/lFg==} + engines: {node: '>=20'} + peerDependencies: + '@rjsf/utils': ^6.2.x + react: '>=18' + + '@rjsf/utils@6.2.5': + resolution: {integrity: sha512-29SvRuY3gKyAHUUnIiJiAF/mTnokgrE7XqUXMj+CZK+sGcmAegwhlnQMJgLQciTodMwTwOaDyV1Fxc47VKTHFw==} + engines: {node: '>=20'} + peerDependencies: + react: '>=18' + + '@rjsf/validator-ajv8@6.2.5': + resolution: {integrity: sha512-+yLhFRuT2aY91KiUujhUKg8SyTBrUuQP3QAFINeGi+RljA3S+NQN56oeCaNdz9X+35+Sdy6jqmxy/0Q2K+K9vQ==} + engines: {node: '>=20'} + peerDependencies: + '@rjsf/utils': ^6.2.x + '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} @@ -1322,6 +1350,9 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@x0k/json-schema-merge@1.0.2': + resolution: {integrity: sha512-1734qiJHNX3+cJGDMMw2yz7R+7kpbAtl5NdPs1c/0gO5kYT6s4dMbLXiIfpZNsOYhGZI3aH7FWrj4Zxz7epXNg==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1332,9 +1363,20 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1577,6 +1619,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -1725,6 +1770,9 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -1733,6 +1781,10 @@ packages: engines: {node: '>=6'} hasBin: true + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1751,9 +1803,15 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash-es@4.17.23: + resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -1769,6 +1827,15 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + markdown-to-jsx@8.0.0: + resolution: {integrity: sha512-hWEaRxeCDjes1CVUQqU+Ov0mCqBqkGhLKjL98KdbwHSgEWZZSJQeGlJQatVfeZ3RaxrfTrZZ3eczl2dhp5c/pA==} + engines: {node: '>= 10'} + peerDependencies: + react: '>= 0.14.0' + peerDependenciesMeta: + react: + optional: true + marked@14.1.4: resolution: {integrity: sha512-vkVZ8ONmUdPnjCKc5uTRvmkRbx4EAi2OkTOXmfTDhZz3OFqMNBM1oTTWwTr4HY4uAEojhzPf+Fy8F1DWa3Sndg==} engines: {node: '>= 18'} @@ -1950,6 +2017,9 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-refresh@0.17.0: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} @@ -2018,6 +2088,10 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -3173,6 +3247,33 @@ snapshots: '@remix-run/router@1.23.0': {} + '@rjsf/core@6.2.5(@rjsf/utils@6.2.5(react@18.3.1))(react@18.3.1)': + dependencies: + '@rjsf/utils': 6.2.5(react@18.3.1) + lodash: 4.17.23 + lodash-es: 4.17.23 + markdown-to-jsx: 8.0.0(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + + '@rjsf/utils@6.2.5(react@18.3.1)': + dependencies: + '@x0k/json-schema-merge': 1.0.2 + fast-uri: 3.1.0 + jsonpointer: 5.0.1 + lodash: 4.17.23 + lodash-es: 4.17.23 + react: 18.3.1 + react-is: 18.3.1 + + '@rjsf/validator-ajv8@6.2.5(@rjsf/utils@6.2.5(react@18.3.1))': + dependencies: + '@rjsf/utils': 6.2.5(react@18.3.1) + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + lodash: 4.17.23 + lodash-es: 4.17.23 + '@rolldown/pluginutils@1.0.0-beta.27': {} '@rollup/rollup-android-arm-eabi@4.53.3': @@ -3393,12 +3494,20 @@ snapshots: transitivePeerDependencies: - supports-color + '@x0k/json-schema-merge@1.0.2': + dependencies: + '@types/json-schema': 7.0.15 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 acorn@8.15.0: {} + ajv-formats@2.1.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -3406,6 +3515,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-regex@5.0.1: {} ansi-regex@6.1.0: {} @@ -3673,6 +3789,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.1.0: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -3797,10 +3915,14 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json5@2.2.3: {} + jsonpointer@5.0.1: {} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -3818,8 +3940,12 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash-es@4.17.23: {} + lodash.merge@4.6.2: {} + lodash@4.17.23: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -3834,6 +3960,10 @@ snapshots: dependencies: react: 18.3.1 + markdown-to-jsx@8.0.0(react@18.3.1): + optionalDependencies: + react: 18.3.1 + marked@14.1.4: {} merge2@1.4.1: {} @@ -3985,6 +4115,8 @@ snapshots: react-is@16.13.1: {} + react-is@18.3.1: {} + react-refresh@0.17.0: {} react-remove-scroll-bar@2.3.8(@types/react@18.3.23)(react@18.3.1): @@ -4049,6 +4181,8 @@ snapshots: dependencies: picomatch: 2.3.1 + require-from-string@2.0.2: {} + resolve-from@4.0.0: {} resolve@1.22.10: diff --git a/frontend/src/components/agents-table/agent-configuration-form.tsx b/frontend/src/components/agents-table/agent-configuration-form.tsx deleted file mode 100644 index 8af23625..00000000 --- a/frontend/src/components/agents-table/agent-configuration-form.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import { Button } from "@/components/ui/button"; -import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; -import { ChevronDownIcon, ChevronRightIcon } from "lucide-react"; -import { useEffect } from "react"; -import { DynamicConfigInput } from "./dynamic-config-input"; -import { TypeInfo } from "@/lib/types"; - -interface AgentConfigurationFormProps { - agentConfigSections: Record | Record[]>; - openSections: Record; - setOpenSections: (sections: Record) => void; - config: Record; - setConfig: (config: Record) => void; - fieldErrors: Record; -} - -export function AgentConfigurationForm({ - agentConfigSections, - openSections, - setOpenSections, - config, - setConfig, - fieldErrors -}: AgentConfigurationFormProps) { - const handleConfigChange = (key: string, value: string | number | boolean) => { - setConfig({ ...config, [key]: value }); - }; - - useEffect(() => { - const checkForSectionErrors = (section: string, fields: Record | Record[]) => { - if (Array.isArray(fields)) { - return fields.some(obj => { - if (obj && typeof obj === 'object') { - return Object.keys(obj).some(key => fieldErrors[`${section}_${key}`]); - } - return false; - }); - } else if (fields && typeof fields === 'object') { - return Object.keys(fields).some(key => fieldErrors[`${section}_${key}`]); - } - return false; - }; - - const expandSectionsWithErrors = () => { - if (Object.keys(fieldErrors).length === 0) return; - - const sectionsToExpand: Record = { ...openSections }; - let hasChanges = false; - - Object.entries(agentConfigSections).forEach(([section, fields]) => { - const sectionHasErrors = checkForSectionErrors(section, fields); - - if (sectionHasErrors && !openSections[section]) { - sectionsToExpand[section] = true; - hasChanges = true; - } - }); - - if (hasChanges) { - setOpenSections(sectionsToExpand); - } - }; - - expandSectionsWithErrors(); - }, [fieldErrors, agentConfigSections, openSections, setOpenSections]); - - const hasConfigFields = Object.values(agentConfigSections).some(section => { - if (Array.isArray(section)) { - return section.some(obj => obj && typeof obj === 'object' && Object.keys(obj).length > 0); - } else if (section && typeof section === 'object') { - return Object.keys(section).length > 0; - } - return false; - }); - - const renderConfigField = (key: string, schema: unknown, configKey: string) => { - const s = schema as Record; - const fieldTitle = (s && typeof s.title === 'string') ? s.title || key : String(key); - const typeInfo = s?.type as TypeInfo | undefined; - const description = typeof s?.description === 'string' ? s.description : undefined; - const currentValue = config[configKey] ?? ''; - - return ( - handleConfigChange(configKey, value)} - typeInfo={typeInfo} - description={description} - error={fieldErrors[configKey]} - /> - ); - }; - - const renderToolsArrayFields = (section: string, fields: Record[]) => { - return fields.map((obj, idx) => { - const sObj = obj && typeof obj === 'object' ? obj : {}; - if (Object.keys(sObj).length === 0) return null; - - return ( -
-
- {Object.entries(sObj).map(([key, schema]) => { - const configKey = `${section}_${key}`; - return typeof key === 'string' ? renderConfigField(key, schema, configKey) : null; - })} -
-
- ); - }); - }; - - const renderRegularFields = (section: string, fields: Record) => { - return Object.entries(fields).map(([key, schema]) => { - const configKey = `${section}_${key}`; - return typeof key === 'string' ? renderConfigField(key, schema, configKey) : null; - }); - }; - - const renderConfigSection = (section: string, fields: Record | Record[]) => { - let hasFields = false; - if (Array.isArray(fields)) { - hasFields = fields.some(obj => obj && typeof obj === 'object' && Object.keys(obj).length > 0); - } else if (fields && typeof fields === 'object') { - hasFields = Object.keys(fields).length > 0; - } - if (!hasFields) return null; - - const sectionTitle = String(section).replace(/_/g, ' '); - - return ( - setOpenSections({ ...openSections, [section]: open })} - > - - - - -
- {Array.isArray(fields) - ? renderToolsArrayFields(section, fields) - : renderRegularFields(section, fields as Record)} -
-
-
- ); - }; - - if (!hasConfigFields) { - return ( -
-
-

No configuration required

-

This agent type doesn't require any additional configuration parameters.

-
-
- ); - } - - return ( -
-
-

Configuration

-

Configure agent parameters

-
- - {Object.entries(agentConfigSections).map(([section, fields]) => - renderConfigSection(section, fields) - )} -
- ); -} diff --git a/frontend/src/components/agents-table/agent-form-modal.tsx b/frontend/src/components/agents-table/agent-form-modal.tsx index bdef27ba..faa6bd92 100644 --- a/frontend/src/components/agents-table/agent-form-modal.tsx +++ b/frontend/src/components/agents-table/agent-form-modal.tsx @@ -1,13 +1,29 @@ import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Label } from "@/components/ui/label"; -import { Agent } from "@/lib/types"; +import { Agent, AGENT_CONFIG_SECTION_KEYS, type AgentConfig, type AgentConfigSectionKey } from "@/lib/types"; import { useAgentForm } from "./hooks/use-agent-form"; -import { AgentConfigurationForm } from "./agent-configuration-form"; -import {AgentChoice} from "@/lib/api/agents.ts"; -import {Textarea} from "@/components/ui/textarea.tsx"; +import type { AgentChoice } from "@/lib/api/agents"; +import { Textarea } from "@/components/ui/textarea"; import { FormModal } from "@/components/ui/form-modal"; import { useEffect } from "react"; +import { RjsfForm } from "@/components/rjsf"; + +function useScrollToFirstError(fieldErrors: Record) { + useEffect(() => { + if (Object.keys(fieldErrors).length === 0) return; + const timer = setTimeout(() => { + document.querySelector("[data-field-error]")?.scrollIntoView({ behavior: "smooth", block: "nearest" }); + }, 100); + return () => clearTimeout(timer); + }, [fieldErrors]); +} + +const SECTION_TITLES: Record = { + agent_args: "Agent args", + prompt_input: "Prompt input", + prompt_extension: "Prompt extension", +}; interface AgentFormModalProps { open: boolean; @@ -36,8 +52,8 @@ export function AgentFormModal({ type, setType, config, - setConfig, - agentConfigSections, + selectedType, + updateConfigSection, openSections, setOpenSections, fieldErrors, @@ -54,6 +70,8 @@ export function AgentFormModal({ } }, [open, resetForm]); + useScrollToFirstError(fieldErrors); + const onSubmit = async () => { await handleSubmit(); }; @@ -73,7 +91,7 @@ export function AgentFormModal({ disabled={!name || !type} loading={loadingTypes} loadingText="Loading agent types..." - dependencies={[type, agentConfigSections, openSections, generalError, fieldErrors]} + dependencies={[type, selectedType, config, openSections, generalError, fieldErrors]} > {generalError && (
@@ -81,9 +99,9 @@ export function AgentFormModal({
)} -
+
- @@ -93,7 +111,7 @@ export function AgentFormModal({ ))} - {agentTypes.length == 0 && + {agentTypes.length === 0 &&

First, you'll need to install an agent.
To get started quickly, you can choose on of our pre-built ones to get started quick.

} {fieldErrors.type && ( @@ -101,7 +119,7 @@ export function AgentFormModal({ )}
-
+
-
+