From 0c86d6bfc9f88c0d1e13cf54448bacf260ec7583 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 3 Oct 2026 06:53:41 +0000 Subject: [PATCH] refactor(dashboard): migrate to zod 4 and openai 6 (#44345) * refactor(dashboard): migrate to zod 4 and openai 6 Bump the dashboard to real zod 4.6.5 and openai 6.49.0 so every module imports from bare "zod" instead of "zod/v4". Ports the ten files that still used the zod 3 API (error params, record, passthrough, strict, email/date validators, union discriminator codes) and adapts the LiteAdmin tool schemas and form plumbing where openai 6 and zod 4 changed behaviour. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * style(dashboard): prettier format zod 4 schema files Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(dashboard): restore system one missing state message under zod 4 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Yujong Lee Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ui/litellm-dashboard/eslint.config.mjs | 4 + ui/litellm-dashboard/package-lock.json | 287 +++--------------- ui/litellm-dashboard/package.json | 4 +- .../AccessGroupsModal/AccessGroupBaseForm.tsx | 2 +- .../_components/access-group-create/schema.ts | 2 +- .../admin-panel/_components/AdminPanel.tsx | 2 +- .../agents/_components/agent_identity.ts | 1 + .../budgets/_components/budget_modal.tsx | 2 +- .../change-password/ChangePasswordForm.tsx | 2 +- .../_components/PromptCompressionTab.tsx | 2 +- .../_components/TeamGuardrailsTab.tsx | 2 +- .../_components/MCPToolsetsTab.tsx | 2 +- .../_components/UserEnvVarsModal.tsx | 2 +- .../memory/_components/MemoryEditModal.tsx | 2 +- .../components/AccessGroupBudgetModal.tsx | 2 +- .../components/systemOneUI/lib/schemas.ts | 147 +++++---- .../systemOneUI/lib/validatePayload.test.ts | 123 ++++++-- .../_components/add_attachment_form.tsx | 2 +- .../policies/_components/add_policy_form.tsx | 2 +- .../ProjectModals/projectFormSchema.ts | 2 +- .../prompts/_components/add_prompt_form.tsx | 2 +- .../_components/CreateSearchTools.tsx | 2 +- .../search-tools/_components/SearchTools.tsx | 2 +- .../skills/_components/add_plugin_form.tsx | 2 +- .../_components/components/CreateTagModal.tsx | 2 +- .../tag-management/_components/tag_info.tsx | 2 +- .../default-user-settings/mapper.ts | 2 +- .../default-user-settings/schema.ts | 2 +- .../users/_components/user_edit_view.tsx | 2 +- .../_components/VectorStoreForm.tsx | 2 +- .../_components/vector_store_info.tsx | 2 +- .../src/app/login/LoginPage.tsx | 2 +- .../src/app/onboarding/OnboardingFormBody.tsx | 2 +- .../CloudZeroCreateModal.tsx | 2 +- .../CloudZeroUpdateModal.tsx | 2 +- .../src/components/ModelInfoEditForm.tsx | 2 +- ui/litellm-dashboard/src/components/SCIM.tsx | 2 +- .../CyberArk/EditCyberArkModal.tsx | 2 +- .../EditHashicorpVaultModal.tsx | 2 +- .../AdminSettings/PluginSettings/schema.ts | 2 +- .../Modals/BaseSSOSettingsForm.tsx | 2 +- .../TeamAdminEditableFieldsSettings.tsx | 2 +- ui/litellm-dashboard/src/components/Teams.tsx | 2 +- .../src/components/UIAccessControlForm.tsx | 2 +- .../add_model/add_auto_router_tab.tsx | 2 +- .../build_auto_router_routing_test_request.ts | 4 +- .../add_model/jev_classifier_config.ts | 5 +- .../src/components/add_pass_through.tsx | 2 +- .../src/components/cloudzero_export_modal.tsx | 2 +- .../MetadataKeyValueFields.test.tsx | 2 +- .../MetadataKeyValueFields.tsx | 2 +- .../editAutoRouterFormSchema.ts | 2 +- .../liteadmin/LiteAdmin.integration.test.tsx | 1 - .../src/components/liteadmin/agent.test.ts | 2 +- .../src/components/liteadmin/agent.ts | 2 +- .../src/components/liteadmin/operations.ts | 32 +- .../effectiveMcpServers.ts | 2 +- .../model_add/reuse_credentials.tsx | 2 +- .../organisms/RegenerateKeyModal.tsx | 2 +- .../organization/org-create/mapper.ts | 2 +- .../organization/org-settings/mapper.ts | 2 +- .../organization/org-settings/schema.ts | 2 +- .../src/components/pass_through_info.tsx | 2 +- .../routing_groups/RoutingGroupModal.tsx | 2 +- .../components/shared/form/FormField.test.tsx | 2 +- .../src/components/team/EditMembership.tsx | 2 +- .../components/team/TeamAdminSettingsForm.tsx | 2 +- .../src/components/team/TeamInfo.tsx | 2 +- .../components/team/teamAdminEditAccess.ts | 2 +- .../components/templates/keyEditFormValues.ts | 82 ++--- .../update_model_credentials_modal.tsx | 2 +- .../src/lib/forms/useZodForm.test.tsx | 2 +- .../src/lib/forms/useZodForm.ts | 26 +- 73 files changed, 360 insertions(+), 478 deletions(-) diff --git a/ui/litellm-dashboard/eslint.config.mjs b/ui/litellm-dashboard/eslint.config.mjs index f5e3b23b3ec..5bb7cc29792 100644 --- a/ui/litellm-dashboard/eslint.config.mjs +++ b/ui/litellm-dashboard/eslint.config.mjs @@ -58,6 +58,10 @@ const eslintConfig = [ message: "@tremor/react is being phased out; build new UI with shadcn/ui primitives instead of adding tremor imports.", }, + { + group: ["zod/*"], + message: 'Import Zod from "zod"; the dashboard uses Zod 4 only.', + }, ], }, ], diff --git a/ui/litellm-dashboard/package-lock.json b/ui/litellm-dashboard/package-lock.json index 1cb1951d96a..c64db5abb93 100644 --- a/ui/litellm-dashboard/package-lock.json +++ b/ui/litellm-dashboard/package-lock.json @@ -28,7 +28,7 @@ "next": "16.3.6", "next-themes": "^0.4.6", "nuqs": "^2.9.4", - "openai": "4.104.0", + "openai": "6.49.0", "openapi-fetch": "^0.17.0", "openapi-react-query": "^0.5.4", "papaparse": "5.5.3", @@ -44,7 +44,7 @@ "sonner": "2.0.8", "tailwind-merge": "3.4.0", "uuid": "14.0.0", - "zod": "3.25.76" + "zod": "4.6.5" }, "devDependencies": { "@eslint/js": "9.39.2", @@ -3780,16 +3780,6 @@ "undici-types": "~6.21.0" } }, - "node_modules/@types/node-fetch": { - "version": "2.6.13", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", - "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.4" - } - }, "node_modules/@types/papaparse": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.5.2.tgz", @@ -4547,18 +4537,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -4592,18 +4570,6 @@ "node": ">= 14" } }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "license": "MIT", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, "node_modules/ajv": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", @@ -4880,12 +4846,6 @@ "node": ">= 0.4" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -5047,6 +5007,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5241,18 +5202,6 @@ "dev": true, "license": "MIT" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -5645,15 +5594,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -5710,6 +5650,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -5834,6 +5775,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5843,6 +5785,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5887,6 +5830,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -5899,6 +5843,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -6545,15 +6490,6 @@ "node": ">=0.10.0" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/eventemitter3": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", @@ -6765,28 +6701,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/form-data": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", - "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.4", - "mime-types": "^2.1.35" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", - "license": "MIT" - }, "node_modules/format": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", @@ -6811,19 +6725,6 @@ "node": ">=18.3.0" } }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "license": "MIT", - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" - } - }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -6843,6 +6744,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6903,6 +6805,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -6927,6 +6830,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -7014,6 +6918,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7085,6 +6990,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7097,6 +7003,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -7112,6 +7019,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -7325,15 +7233,6 @@ "node": ">= 14" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -8239,16 +8138,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/knip/node_modules/zod": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", - "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -8684,6 +8573,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -9578,27 +9468,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -9774,26 +9643,6 @@ "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, "node_modules/node-exports-info": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", @@ -9823,48 +9672,6 @@ "semver": "bin/semver.js" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/node-releases": { "version": "2.0.54", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", @@ -10049,27 +9856,27 @@ } }, "node_modules/openai": { - "version": "4.104.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", - "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", + "version": "6.49.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.49.0.tgz", + "integrity": "sha512-aYCc0C6L864eR6WSYIwQGyXriw/nIyZx0ObvhzOEVuk0zoBDpynjSbrionWI7q65B5H8jJX0DXR9snEzM6bfPg==", "license": "Apache-2.0", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - }, - "bin": { - "openai": "bin/cli" - }, "peerDependencies": { + "@aws-sdk/credential-provider-node": ">=3.972.0 <4", + "@smithy/hash-node": ">=4.3.0 <5", + "@smithy/signature-v4": ">=5.4.0 <6", "ws": "^8.18.0", - "zod": "^3.23.8" + "zod": "^3.25 || ^4.0" }, "peerDependenciesMeta": { + "@aws-sdk/credential-provider-node": { + "optional": true + }, + "@smithy/hash-node": { + "optional": true + }, + "@smithy/signature-v4": { + "optional": true + }, "ws": { "optional": true }, @@ -10078,21 +9885,6 @@ } } }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/openai/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "license": "MIT" - }, "node_modules/openapi-fetch": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/openapi-fetch/-/openapi-fetch-0.17.0.tgz", @@ -12575,15 +12367,6 @@ "node": "20 || >=22" } }, - "node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, "node_modules/webidl-conversions": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", @@ -12836,9 +12619,9 @@ } }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", + "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/ui/litellm-dashboard/package.json b/ui/litellm-dashboard/package.json index 0830e233bbe..fb070871828 100644 --- a/ui/litellm-dashboard/package.json +++ b/ui/litellm-dashboard/package.json @@ -44,7 +44,7 @@ "next": "16.3.6", "next-themes": "^0.4.6", "nuqs": "^2.9.4", - "openai": "4.104.0", + "openai": "6.49.0", "openapi-fetch": "^0.17.0", "openapi-react-query": "^0.5.4", "papaparse": "5.5.3", @@ -60,7 +60,7 @@ "sonner": "2.0.8", "tailwind-merge": "3.4.0", "uuid": "14.0.0", - "zod": "3.25.76" + "zod": "4.6.5" }, "devDependencies": { "@eslint/js": "9.39.2", diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx index 33094565d6c..91e008de402 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx @@ -2,7 +2,7 @@ import { BotIcon, InfoIcon, LayersIcon, ServerIcon } from "lucide-react"; import type { UseFormReturn } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import { useAgents } from "@/app/(dashboard)/hooks/agents/useAgents"; import { useMCPServers } from "@/app/(dashboard)/hooks/mcpServers/useMCPServers"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/access-group-create/schema.ts b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/access-group-create/schema.ts index 5561f1b5469..2af4c922cfe 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/access-group-create/schema.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/access-group-create/schema.ts @@ -1,4 +1,4 @@ -import { z } from "zod/v4"; +import { z } from "zod"; export const accessGroupCreateSchema = z.object({ name: z.string().refine((value) => value.trim() !== "", "Please enter the access group name"), diff --git a/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx index 386cbebd38d..44c62349005 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx @@ -30,7 +30,7 @@ import { type SSOSettingsFormValues, } from "@/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm"; import UIAccessControlForm from "@/components/UIAccessControlForm"; -import { z } from "zod/v4"; +import { z } from "zod"; import { FieldGroup } from "@/components/ui/field"; import { FormField } from "@/components/shared/form/FormField"; import { Input } from "@/components/ui/input"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_identity.ts b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_identity.ts index 23045adcf20..545e4baaa91 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_identity.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/agents/_components/agent_identity.ts @@ -13,6 +13,7 @@ export const IDENTITY_UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a const stringGrants = (fallback: string[]) => z .unknown() + .optional() .transform((value) => Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === "string") : fallback, ); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_modal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_modal.tsx index 5068cbed453..01f1595b365 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_modal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/budgets/_components/budget_modal.tsx @@ -1,6 +1,6 @@ import { ChevronRight } from "lucide-react"; import React from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { useCreateBudget } from "@/app/(dashboard)/hooks/budgets/useBudgets"; import { applyBudgetPrecision } from "./budgetPrecision"; import { toast } from "@/lib/toast"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/change-password/ChangePasswordForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/change-password/ChangePasswordForm.tsx index 7f4831629e8..ad35b5c90a3 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/change-password/ChangePasswordForm.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/change-password/ChangePasswordForm.tsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import { CircleAlert } from "lucide-react"; -import { z } from "zod/v4"; +import { z } from "zod"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import { Alert, AlertTitle } from "@/components/shared/Alert"; import { PasswordInput } from "@/components/shared/PasswordInput"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/PromptCompressionTab.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/PromptCompressionTab.tsx index eb0d1ada42e..a00cc0ca4d6 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/PromptCompressionTab.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/PromptCompressionTab.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState } from "react"; import { CircleHelp } from "lucide-react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { createGuardrailCall, getGuardrailsList } from "@/components/networking"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/TeamGuardrailsTab.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/TeamGuardrailsTab.tsx index d45cfc3fe7d..b29ad63171c 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/TeamGuardrailsTab.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/TeamGuardrailsTab.tsx @@ -17,7 +17,7 @@ import { InfoIcon, CircleHelp, } from "lucide-react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { listGuardrailSubmissions, approveGuardrailSubmission, diff --git a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPToolsetsTab.tsx b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPToolsetsTab.tsx index 60a1da40d08..de7d203bd90 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPToolsetsTab.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPToolsetsTab.tsx @@ -1,5 +1,5 @@ import React, { useState, useCallback } from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { SortingState } from "@tanstack/react-table"; import { Inbox, Plus, X } from "lucide-react"; import { useMCPToolsets } from "@/app/(dashboard)/hooks/mcpServers/useMCPToolsets"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/UserEnvVarsModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/UserEnvVarsModal.tsx index b5867c5e7fe..1984ca92a52 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/UserEnvVarsModal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/UserEnvVarsModal.tsx @@ -1,7 +1,7 @@ import React from "react"; import { CircleAlert, Info } from "lucide-react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { z } from "zod/v4"; +import { z } from "zod"; import { MCPServer, MCPUserEnvVarsStatus, MCPUserEnvVarSpec } from "@/components/mcp_tools/types"; import { clearMCPUserEnvVars, getMCPUserEnvVars, storeMCPUserEnvVars } from "@/components/networking"; import { toast } from "@/lib/toast"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/memory/_components/MemoryEditModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/memory/_components/MemoryEditModal.tsx index 33b75286969..dac209057f1 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/memory/_components/MemoryEditModal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/memory/_components/MemoryEditModal.tsx @@ -2,7 +2,7 @@ import { CircleHelp } from "lucide-react"; import React, { useEffect, useState } from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import type { MemoryRow } from "@/components/networking"; import { FieldGroup } from "@/components/ui/field"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AccessGroupBudgetModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AccessGroupBudgetModal.tsx index cf5cf78d08a..a66f31a0dc0 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AccessGroupBudgetModal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AccessGroupBudgetModal.tsx @@ -2,7 +2,7 @@ import { CircleHelp } from "lucide-react"; import React from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import BudgetDurationDropdown from "@/components/common_components/budget_duration_dropdown"; import { FieldGroup } from "@/components/ui/field"; import { FormField } from "@/components/shared/form/FormField"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/lib/schemas.ts b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/lib/schemas.ts index 34cd3d49ecb..1c4a9f071f2 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/lib/schemas.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/lib/schemas.ts @@ -3,89 +3,79 @@ import { z } from "zod"; const MAX_CHOICE_OPTIONS = 255; const nonEmptyString = (message: string) => - z.string({ invalid_type_error: message }).refine((value) => value.trim().length > 0, message); + z.string({ error: message }).refine((value) => value.trim().length > 0, message); const instructions = z.string({ - required_error: "Required property 'instructions' is missing.", - invalid_type_error: "Instructions must be a string.", + error: (iss) => + iss.input === undefined ? "Required property 'instructions' is missing." : "Instructions must be a string.", }); -const choiceQuestionSchema = z - .object({ - type: z.literal("choice"), - instructions, - criteria: z - .record(z.string({ invalid_type_error: "Choice descriptions must be strings." }), { - required_error: "Choice criteria must be an object mapping labels to descriptions.", - invalid_type_error: "Choice criteria must be an object mapping labels to descriptions.", - }) - .refine((criteria) => { - const count = Object.keys(criteria).length; - return count >= 1 && count <= MAX_CHOICE_OPTIONS; - }, `Choice criteria must contain between 1 and ${MAX_CHOICE_OPTIONS} options.`), - }) - .passthrough(); +const choiceQuestionSchema = z.looseObject({ + type: z.literal("choice"), + instructions, + criteria: z + .record(z.string(), z.string({ error: "Choice descriptions must be strings." }), { + error: "Choice criteria must be an object mapping labels to descriptions.", + }) + .refine((criteria) => { + const count = Object.keys(criteria).length; + return count >= 1 && count <= MAX_CHOICE_OPTIONS; + }, `Choice criteria must contain between 1 and ${MAX_CHOICE_OPTIONS} options.`), +}); -const noulQuestionSchema = z - .object({ - type: z.literal("noul"), - instructions, - criteria: z - .object( - { - true: z.string({ invalid_type_error: "Noul true criteria must be a string." }).optional(), - false: z.string({ invalid_type_error: "Noul false criteria must be a string." }).optional(), - }, - { invalid_type_error: "Noul criteria, when provided, must be an object." }, - ) - .passthrough() - .optional(), - }) - .passthrough(); +const noulQuestionSchema = z.looseObject({ + type: z.literal("noul"), + instructions, + criteria: z + .looseObject( + { + true: z.string({ error: "Noul true criteria must be a string." }).optional(), + false: z.string({ error: "Noul false criteria must be a string." }).optional(), + }, + { error: "Noul criteria, when provided, must be an object." }, + ) + .optional(), +}); -const scoreQuestionSchema = z - .object({ - type: z.literal("score"), - instructions, - criteria: z - .array(z.string({ invalid_type_error: "Score levels must be strings." }), { - required_error: "Score criteria must be an array of levels.", - invalid_type_error: "Score criteria must be an array of levels.", - }) - .min(2, "Score criteria must contain at least 2 levels."), - }) - .passthrough(); +const scoreQuestionSchema = z.looseObject({ + type: z.literal("score"), + instructions, + criteria: z + .array(z.string({ error: "Score levels must be strings." }), { + error: "Score criteria must be an array of levels.", + }) + .min(2, "Score criteria must contain at least 2 levels."), +}); -const QUESTION_ERROR_MESSAGES: Partial> = { - [z.ZodIssueCode.invalid_union_discriminator]: "Question type must be choice, noul, or score.", - [z.ZodIssueCode.invalid_type]: "Question must be an object.", +const QUESTION_ERROR_MESSAGES: Partial> = { + invalid_union: "Question type must be choice, noul, or score.", + invalid_type: "Question must be an object.", }; const questionSchema = z.discriminatedUnion("type", [choiceQuestionSchema, noulQuestionSchema, scoreQuestionSchema], { - errorMap: (issue, context) => ({ message: QUESTION_ERROR_MESSAGES[issue.code] ?? context.defaultError }), + error: (iss) => QUESTION_ERROR_MESSAGES[iss.code], }); -export const systemOneRequestSchema = z - .object( - { - model: nonEmptyString("Model must be a non-empty string.").optional(), - state: z.unknown(), - questions: z - .record(questionSchema, { - required_error: "Required property 'questions' is missing.", - invalid_type_error: "Questions must be a non-empty object.", - }) - .refine((questions) => Object.keys(questions).length > 0, "At least one question is required."), - }, - { invalid_type_error: "Payload must be a JSON object." }, - ) - .passthrough() - .refine((request) => "state" in request, { message: "Required property 'state' is missing.", path: ["state"] }); +export const systemOneRequestSchema = z.looseObject( + { + model: nonEmptyString("Model must be a non-empty string.").optional(), + state: z.unknown().nonoptional({ error: "Required property 'state' is missing." }), + questions: z + .record(z.string(), questionSchema, { + error: (iss) => + iss.input === undefined + ? "Required property 'questions' is missing." + : "Questions must be a non-empty object.", + }) + .refine((questions) => Object.keys(questions).length > 0, "At least one question is required."), + }, + { error: "Payload must be a JSON object." }, +); const probability = z.number().finite().min(0).max(1); -const probabilities = z.record(probability); +const probabilities = z.record(z.string(), probability); -const noulAnswerSchema = z.object({ type: z.literal("noul"), noul: probability }).passthrough(); +const noulAnswerSchema = z.looseObject({ type: z.literal("noul"), noul: probability }); const choiceAnswerShape = { type: z.literal("choice"), @@ -93,26 +83,27 @@ const choiceAnswerShape = { confidence: probability.optional(), probabilities, }; -const choiceAnswerSchema = z.object(choiceAnswerShape).passthrough(); +const choiceAnswerSchema = z.looseObject(choiceAnswerShape); const scoreAnswerShape = { type: z.literal("score"), score: z.number().finite(), confidence: probability.optional(), - legend: z.record(z.string()).optional(), + legend: z.record(z.string(), z.string()).optional(), probabilities, }; -const scoreAnswerSchema = z.object(scoreAnswerShape).passthrough(); +const scoreAnswerSchema = z.looseObject(scoreAnswerShape); const tokenCount = z.number().finite().nonnegative(); -export const systemOneResponseSchema = z - .object({ - model: nonEmptyString("Model must be a non-empty string.").nullish(), - answers: z.record(z.discriminatedUnion("type", [noulAnswerSchema, choiceAnswerSchema, scoreAnswerSchema])), - usage: z.object({ input_tokens: tokenCount, output_tokens: tokenCount }).optional(), - }) - .passthrough(); +export const systemOneResponseSchema = z.looseObject({ + model: nonEmptyString("Model must be a non-empty string.").nullish(), + answers: z.record( + z.string(), + z.discriminatedUnion("type", [noulAnswerSchema, choiceAnswerSchema, scoreAnswerSchema]), + ), + usage: z.object({ input_tokens: tokenCount, output_tokens: tokenCount }).optional(), +}); export type SystemOneRequest = z.infer; export type SystemOneQuestion = z.infer; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/lib/validatePayload.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/lib/validatePayload.test.ts index d034196fc0d..603c22c0830 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/lib/validatePayload.test.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/lib/validatePayload.test.ts @@ -23,37 +23,56 @@ describe("validateSystemOnePayload", () => { }); it("requires an object root", () => { - expect(validateSystemOnePayload("[]").issues[0]?.path).toBe("root"); + expect(validateSystemOnePayload("[]").issues).toContainEqual( + expect.objectContaining({ path: "root", message: "Payload must be a JSON object.", severity: "error" }), + ); }); it("requires the state key while accepting null state", () => { expect(validateSystemOnePayload(requestWith({ state: undefined })).issues).toContainEqual( - expect.objectContaining({ path: "state", severity: "error" }), + expect.objectContaining({ + path: "state", + message: "Required property 'state' is missing.", + severity: "error", + }), ); expect(validateSystemOnePayload(requestWith({ state: null })).isValid).toBe(true); }); it.each([null, "", 123])("requires a non-empty string model when present: %s", (model) => { expect(validateSystemOnePayload(requestWith({ model })).issues).toContainEqual( - expect.objectContaining({ path: "model", severity: "error" }), + expect.objectContaining({ path: "model", message: "Model must be a non-empty string.", severity: "error" }), ); }); it("requires questions", () => { expect(validateSystemOnePayload(JSON.stringify({ state: null })).issues).toContainEqual( - expect.objectContaining({ path: "questions", severity: "error" }), + expect.objectContaining({ + path: "questions", + message: "Required property 'questions' is missing.", + severity: "error", + }), ); }); - it.each([null, [], "questions", {}])("requires a non-empty questions object: %s", (questions) => { + it.each([ + [null, "Questions must be a non-empty object."], + [[], "Questions must be a non-empty object."], + ["questions", "Questions must be a non-empty object."], + [{}, "At least one question is required."], + ])("requires a non-empty questions object: %s", (questions, message) => { expect(validateSystemOnePayload(requestWith({ questions })).issues).toContainEqual( - expect.objectContaining({ path: "questions", severity: "error" }), + expect.objectContaining({ path: "questions", message, severity: "error" }), ); }); it.each([null, [], "question"])("requires each question to be an object: %s", (question) => { expect(validateSystemOnePayload(requestWith({ questions: { invalid: question } })).issues).toContainEqual( - expect.objectContaining({ path: "questions.invalid", severity: "error" }), + expect.objectContaining({ + path: "questions.invalid", + message: "Question must be an object.", + severity: "error", + }), ); }); @@ -61,12 +80,22 @@ describe("validateSystemOnePayload", () => { expect( validateSystemOnePayload(requestWith({ questions: { invalid: { type: "other", instructions: "Do this" } } })) .issues, - ).toContainEqual(expect.objectContaining({ path: "questions.invalid.type", severity: "error" })); + ).toContainEqual( + expect.objectContaining({ + path: "questions.invalid.type", + message: "Question type must be choice, noul, or score.", + severity: "error", + }), + ); }); it("requires instructions", () => { expect(validateSystemOnePayload(requestWith({ questions: { category: { type: "noul" } } })).issues).toContainEqual( - expect.objectContaining({ path: "questions.category.instructions", severity: "error" }), + expect.objectContaining({ + path: "questions.category.instructions", + message: "Required property 'instructions' is missing.", + severity: "error", + }), ); }); @@ -78,7 +107,11 @@ describe("validateSystemOnePayload", () => { ); expect(result.isValid).toBe(false); expect(result.issues).toContainEqual( - expect.objectContaining({ path: "questions.category.instructions", severity: "error" }), + expect.objectContaining({ + path: "questions.category.instructions", + message: "Instructions must be a string.", + severity: "error", + }), ); }); @@ -87,7 +120,13 @@ describe("validateSystemOnePayload", () => { validateSystemOnePayload( requestWith({ questions: { category: { type: "choice", instructions: "Route", criteria } } }), ).issues, - ).toContainEqual(expect.objectContaining({ path: "questions.category.criteria", severity: "error" })); + ).toContainEqual( + expect.objectContaining({ + path: "questions.category.criteria", + message: "Choice criteria must be an object mapping labels to descriptions.", + severity: "error", + }), + ); }); it("requires choice descriptions to be strings", () => { @@ -98,7 +137,11 @@ describe("validateSystemOnePayload", () => { ); expect(result.isValid).toBe(false); expect(result.issues).toContainEqual( - expect.objectContaining({ path: "questions.category.criteria.support", severity: "error" }), + expect.objectContaining({ + path: "questions.category.criteria.support", + message: "Choice descriptions must be strings.", + severity: "error", + }), ); }); @@ -109,7 +152,13 @@ describe("validateSystemOnePayload", () => { validateSystemOnePayload( requestWith({ questions: { category: { type: "choice", instructions: "Route", criteria } } }), ).issues, - ).toContainEqual(expect.objectContaining({ path: "questions.category.criteria", severity: "error" })); + ).toContainEqual( + expect.objectContaining({ + path: "questions.category.criteria", + message: "Choice criteria must contain between 1 and 255 options.", + severity: "error", + }), + ); }, ); @@ -137,7 +186,13 @@ describe("validateSystemOnePayload", () => { validateSystemOnePayload( requestWith({ questions: { urgency: { type: "score", instructions: "Rate urgency", criteria } } }), ).issues, - ).toContainEqual(expect.objectContaining({ path: "questions.urgency.criteria", severity: "error" })); + ).toContainEqual( + expect.objectContaining({ + path: "questions.urgency.criteria", + message: "Score criteria must be an array of levels.", + severity: "error", + }), + ); }); it("requires at least two score levels", () => { @@ -145,7 +200,13 @@ describe("validateSystemOnePayload", () => { validateSystemOnePayload( requestWith({ questions: { urgency: { type: "score", instructions: "Rate urgency", criteria: ["Low"] } } }), ).issues, - ).toContainEqual(expect.objectContaining({ path: "questions.urgency.criteria", severity: "error" })); + ).toContainEqual( + expect.objectContaining({ + path: "questions.urgency.criteria", + message: "Score criteria must contain at least 2 levels.", + severity: "error", + }), + ); }); it("requires score levels to be strings", () => { @@ -158,7 +219,11 @@ describe("validateSystemOnePayload", () => { ); expect(result.isValid).toBe(false); expect(result.issues).toContainEqual( - expect.objectContaining({ path: "questions.urgency.criteria.1", severity: "error" }), + expect.objectContaining({ + path: "questions.urgency.criteria.1", + message: "Score levels must be strings.", + severity: "error", + }), ); }); @@ -176,7 +241,11 @@ describe("validateSystemOnePayload", () => { ); expect(result.isValid).toBe(true); expect(result.issues).toContainEqual( - expect.objectContaining({ path: "questions.urgency.criteria", severity: "warning" }), + expect.objectContaining({ + path: "questions.urgency.criteria", + message: "More than 10 score levels may reduce result quality.", + severity: "warning", + }), ); }); @@ -195,7 +264,13 @@ describe("validateSystemOnePayload", () => { validateSystemOnePayload( requestWith({ questions: { escalation: { type: "noul", instructions: "Escalate?", criteria } } }), ).issues, - ).toContainEqual(expect.objectContaining({ path: "questions.escalation.criteria", severity: "error" })); + ).toContainEqual( + expect.objectContaining({ + path: "questions.escalation.criteria", + message: "Noul criteria, when provided, must be an object.", + severity: "error", + }), + ); }); it("allows omitted noul criteria", () => { @@ -220,8 +295,16 @@ describe("validateSystemOnePayload", () => { expect(result.isValid).toBe(false); expect(result.issues).toEqual( expect.arrayContaining([ - expect.objectContaining({ path: "questions.escalation.criteria.true", severity: "error" }), - expect.objectContaining({ path: "questions.escalation.criteria.false", severity: "error" }), + expect.objectContaining({ + path: "questions.escalation.criteria.true", + message: "Noul true criteria must be a string.", + severity: "error", + }), + expect.objectContaining({ + path: "questions.escalation.criteria.false", + message: "Noul false criteria must be a string.", + severity: "error", + }), ]), ); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/add_attachment_form.tsx b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/add_attachment_form.tsx index 5cd240a0838..2029c92a08f 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/add_attachment_form.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/add_attachment_form.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { CircleHelp } from "lucide-react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { Policy } from "@/components/policies/types"; import { teamListCall, keyListCall, modelAvailableCall, estimateAttachmentImpactCall } from "@/components/networking"; import { toast } from "@/lib/toast"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/add_policy_form.tsx b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/add_policy_form.tsx index d14165dd849..b43bcba7bd0 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/add_policy_form.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/add_policy_form.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { Policy, PolicyCreateRequest, PolicyUpdateRequest } from "@/components/policies/types"; import { Guardrail } from "@/components/guardrails/types"; import { getResolvedGuardrails, modelAvailableCall } from "@/components/networking"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/projectFormSchema.ts b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/projectFormSchema.ts index 6e6318a9609..37b9e6c914c 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/projectFormSchema.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/projects/_components/ProjectModals/projectFormSchema.ts @@ -1,4 +1,4 @@ -import { z } from "zod/v4"; +import { z } from "zod"; export const ALL_TEAM_MODELS = "all-team-models"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/add_prompt_form.tsx b/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/add_prompt_form.tsx index 65079a55f85..b12f5fdef4d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/add_prompt_form.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/add_prompt_form.tsx @@ -1,6 +1,6 @@ import React, { useRef, useState } from "react"; import { Upload as UploadIcon, X } from "lucide-react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { convertPromptFileToJson, createPromptCall } from "@/components/networking"; import { toast } from "@/lib/toast"; import { Field, FieldDescription, FieldGroup, FieldSeparator, FieldTitle } from "@/components/ui/field"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/CreateSearchTools.tsx b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/CreateSearchTools.tsx index eb778726f22..f777113c2d8 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/CreateSearchTools.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/CreateSearchTools.tsx @@ -3,7 +3,7 @@ import { useQuery } from "@tanstack/react-query"; import { CircleHelp } from "lucide-react"; import React, { useCallback, useMemo, useState } from "react"; import { useWatch } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import { Logo } from "@/components/molecules/logo/Logo"; import { toast } from "@/lib/toast"; import { createSearchTool, fetchAvailableSearchProviders } from "@/components/networking"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchTools.tsx b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchTools.tsx index eddee602f0b..fdd0dd1029d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchTools.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/search-tools/_components/SearchTools.tsx @@ -1,7 +1,7 @@ import { isAdminRole } from "@/utils/roles"; import { useQuery } from "@tanstack/react-query"; import React, { useState } from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import DeleteResourceModal from "@/components/common_components/DeleteResourceModal"; import { toast } from "@/lib/toast"; import { diff --git a/ui/litellm-dashboard/src/app/(dashboard)/skills/_components/add_plugin_form.tsx b/ui/litellm-dashboard/src/app/(dashboard)/skills/_components/add_plugin_form.tsx index 0c114dce700..63b8b1be7de 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/skills/_components/add_plugin_form.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/skills/_components/add_plugin_form.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { CircleHelp } from "lucide-react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { toast } from "@/lib/toast"; import { registerClaudeCodePlugin } from "@/components/networking"; import { FieldGroup } from "@/components/ui/field"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/tag-management/_components/components/CreateTagModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/tag-management/_components/components/CreateTagModal.tsx index adb5a167c35..5a51b3f21b3 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/tag-management/_components/components/CreateTagModal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/tag-management/_components/components/CreateTagModal.tsx @@ -2,7 +2,7 @@ import { ChevronRight, CircleHelp } from "lucide-react"; import React from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import BudgetDurationDropdown from "@/components/common_components/budget_duration_dropdown"; import { FieldGroup } from "@/components/ui/field"; import { FormField } from "@/components/shared/form/FormField"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/tag-management/_components/tag_info.tsx b/ui/litellm-dashboard/src/app/(dashboard)/tag-management/_components/tag_info.tsx index 1648a99bb0e..334cfdf4ad5 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/tag-management/_components/tag_info.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/tag-management/_components/tag_info.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react"; import { SimpleTooltip } from "@/components/ui/tooltip"; -import { z } from "zod/v4"; +import { z } from "zod"; import { fetchUserModels } from "@/components/organisms/create_key_button"; import { getModelDisplayName } from "@/components/key_team_helpers/fetch_available_models_team_key"; import { tagInfoCall, tagUpdateCall } from "@/components/networking"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/mapper.ts b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/mapper.ts index ac528542ae2..7aa3b946239 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/mapper.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/mapper.ts @@ -1,4 +1,4 @@ -import { z } from "zod/v4"; +import { z } from "zod"; import type { components } from "@/lib/http/schema"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/schema.ts b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/schema.ts index cd66c3addc8..c1a29aef893 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/schema.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/schema.ts @@ -1,4 +1,4 @@ -import { z } from "zod/v4"; +import { z } from "zod"; const isBlank = (value: string): boolean => value.trim() === ""; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/user_edit_view.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/user_edit_view.tsx index b7a3486c78e..8254134f956 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/user_edit_view.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/user_edit_view.tsx @@ -1,5 +1,5 @@ import React, { useMemo, useState } from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { all_admin_roles } from "@/utils/roles"; import BudgetDurationDropdown from "@/components/common_components/budget_duration_dropdown"; import { ModelMaxBudget, ModelMaxBudgetField } from "@/components/key_team_helpers/ModelMaxBudgetEditor"; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx index 67ef1b795ba..01abfe82920 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react"; import { CircleHelp, Eye, EyeOff, Info } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert"; import { useWatch } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import { CredentialItem, vectorStoreCreateCall } from "@/components/networking"; import { VectorStoreProviders, diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/vector_store_info.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/vector_store_info.tsx index 92d4145b9f2..b8d82eee0a2 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/vector_store_info.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/vector_store_info.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { ArrowLeft, CircleHelp, Lock } from "lucide-react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { vectorStoreInfoCall, vectorStoreUpdateCall, diff --git a/ui/litellm-dashboard/src/app/login/LoginPage.tsx b/ui/litellm-dashboard/src/app/login/LoginPage.tsx index 11cf5ffad3d..14d50555dec 100644 --- a/ui/litellm-dashboard/src/app/login/LoginPage.tsx +++ b/ui/litellm-dashboard/src/app/login/LoginPage.tsx @@ -21,7 +21,7 @@ import { consumeReturnUrl, getLoginUrl, getReturnUrl, isValidReturnUrl } from "@ import { CircleAlert, Info, TriangleAlert, X } from "lucide-react"; import { useRouter } from "next/navigation"; import { useEffect, useId, useState } from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { useWorker } from "@/hooks/useWorker"; const loginSchema = z.object({ diff --git a/ui/litellm-dashboard/src/app/onboarding/OnboardingFormBody.tsx b/ui/litellm-dashboard/src/app/onboarding/OnboardingFormBody.tsx index 96eb165d43c..bf6f1d09558 100644 --- a/ui/litellm-dashboard/src/app/onboarding/OnboardingFormBody.tsx +++ b/ui/litellm-dashboard/src/app/onboarding/OnboardingFormBody.tsx @@ -1,6 +1,6 @@ import React from "react"; import { CircleAlert, Info } from "lucide-react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert"; import { PasswordInput } from "@/components/shared/PasswordInput"; import { Field, FieldLabel, FieldGroup } from "@/components/ui/field"; diff --git a/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroCreateModal.tsx b/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroCreateModal.tsx index 5b3c4b52b19..113169ea0d1 100644 --- a/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroCreateModal.tsx +++ b/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroCreateModal.tsx @@ -1,5 +1,5 @@ import { useEffect } from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { useCloudZeroCreate } from "@/app/(dashboard)/hooks/cloudzero/useCloudZeroCreate"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; diff --git a/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroUpdateModal.tsx b/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroUpdateModal.tsx index 34cf1b87717..51b9ac7dfeb 100644 --- a/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroUpdateModal.tsx +++ b/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroUpdateModal.tsx @@ -1,5 +1,5 @@ import { useEffect } from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { useCloudZeroUpdateSettings } from "@/app/(dashboard)/hooks/cloudzero/useCloudZeroSettings"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; diff --git a/ui/litellm-dashboard/src/components/ModelInfoEditForm.tsx b/ui/litellm-dashboard/src/components/ModelInfoEditForm.tsx index b4fefe1d2c3..9ee49c262d8 100644 --- a/ui/litellm-dashboard/src/components/ModelInfoEditForm.tsx +++ b/ui/litellm-dashboard/src/components/ModelInfoEditForm.tsx @@ -5,7 +5,7 @@ import { CircleHelp } from "lucide-react"; import type { Dayjs } from "dayjs"; import * as React from "react"; import { useForm, type Resolver } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import { TagsInput } from "@/app/(dashboard)/guardrails/_components/content_filter/TagsInput"; import { FormField } from "@/components/shared/form/FormField"; diff --git a/ui/litellm-dashboard/src/components/SCIM.tsx b/ui/litellm-dashboard/src/components/SCIM.tsx index 12b47aeee6a..efe87454968 100644 --- a/ui/litellm-dashboard/src/components/SCIM.tsx +++ b/ui/litellm-dashboard/src/components/SCIM.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { keyCreateCall } from "./networking"; import { CopyToClipboard } from "react-copy-to-clipboard"; import { CircleAlert, CirclePlus, Copy, Info, KeyRound, Link } from "lucide-react"; diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/CyberArk/EditCyberArkModal.tsx b/ui/litellm-dashboard/src/components/Settings/AdminSettings/CyberArk/EditCyberArkModal.tsx index 7a093e63f2b..dc88e85b93f 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/CyberArk/EditCyberArkModal.tsx +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/CyberArk/EditCyberArkModal.tsx @@ -5,7 +5,7 @@ import { useUpdateCyberArkConfig } from "@/app/(dashboard)/hooks/configOverrides import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import { toast } from "@/lib/toast"; import React, { useMemo } from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { FieldGroup } from "@/components/ui/field"; import { FormField } from "@/components/shared/form/FormField"; import { PasswordInput } from "@/components/shared/PasswordInput"; diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/HashicorpVault/EditHashicorpVaultModal.tsx b/ui/litellm-dashboard/src/components/Settings/AdminSettings/HashicorpVault/EditHashicorpVaultModal.tsx index e16adb41c10..86fc8ec5e60 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/HashicorpVault/EditHashicorpVaultModal.tsx +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/HashicorpVault/EditHashicorpVaultModal.tsx @@ -5,7 +5,7 @@ import { useUpdateHashicorpVaultConfig } from "@/app/(dashboard)/hooks/configOve import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import { toast } from "@/lib/toast"; import React, { useMemo } from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { FieldGroup } from "@/components/ui/field"; import { FormField } from "@/components/shared/form/FormField"; import { PasswordInput } from "@/components/shared/PasswordInput"; diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/PluginSettings/schema.ts b/ui/litellm-dashboard/src/components/Settings/AdminSettings/PluginSettings/schema.ts index bd3374c911a..85684ba1959 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/PluginSettings/schema.ts +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/PluginSettings/schema.ts @@ -1,4 +1,4 @@ -import { z } from "zod/v4"; +import { z } from "zod"; const IPV4 = "(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}"; const SEG = "[a-fA-F\\d]{1,4}"; diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.tsx b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.tsx index cb97304f77a..e4725294e05 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.tsx +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.tsx @@ -2,7 +2,7 @@ import React from "react"; import { FormProvider, useFormContext, useWatch, type UseFormReturn } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import { ssoProviderLogoMap, ssoProviderDisplayNames } from "../constants"; import { Logo } from "@/components/molecules/logo/Logo"; import { FieldGroup } from "@/components/ui/field"; diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/UISettings/TeamAdminEditableFieldsSettings.tsx b/ui/litellm-dashboard/src/components/Settings/AdminSettings/UISettings/TeamAdminEditableFieldsSettings.tsx index e671a4a47b5..f0482d67a14 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/UISettings/TeamAdminEditableFieldsSettings.tsx +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/UISettings/TeamAdminEditableFieldsSettings.tsx @@ -1,7 +1,7 @@ "use client"; import { Controller } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings"; import { useUpdateUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUpdateUISettings"; diff --git a/ui/litellm-dashboard/src/components/Teams.tsx b/ui/litellm-dashboard/src/components/Teams.tsx index dfc0a59f0a0..9e7f622bdab 100644 --- a/ui/litellm-dashboard/src/components/Teams.tsx +++ b/ui/litellm-dashboard/src/components/Teams.tsx @@ -19,7 +19,7 @@ import { TagsInput } from "@/app/(dashboard)/guardrails/_components/content_filt import { TabsContent } from "@/components/ui/tabs"; import { ChevronDown, Plus, Users } from "lucide-react"; import React, { useEffect, useMemo, useState } from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { PageHeader, PageHeaderControls, PageHeaderDescription, PageHeaderTitle } from "@/components/shared/PageHeader"; import { ToolbarSeparator } from "@/components/shared/ToolbarSeparator"; diff --git a/ui/litellm-dashboard/src/components/UIAccessControlForm.tsx b/ui/litellm-dashboard/src/components/UIAccessControlForm.tsx index ed502e8d761..fb23ae1a995 100644 --- a/ui/litellm-dashboard/src/components/UIAccessControlForm.tsx +++ b/ui/litellm-dashboard/src/components/UIAccessControlForm.tsx @@ -1,7 +1,7 @@ import { CircleHelp } from "lucide-react"; import React, { useEffect, useState } from "react"; import { useWatch } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import { FieldGroup } from "@/components/ui/field"; import { FormField } from "@/components/shared/form/FormField"; diff --git a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx index 0888723203e..910bcaa1bf2 100644 --- a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx +++ b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx @@ -4,7 +4,7 @@ import { getForecastConfigError, isForecastClassifier } from "../add_model/forec import React, { useEffect, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { useWatch } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import { FieldGroup } from "@/components/ui/field"; import { FormField } from "@/components/shared/form/FormField"; import { Button } from "@/components/ui/button"; diff --git a/ui/litellm-dashboard/src/components/add_model/build_auto_router_routing_test_request.ts b/ui/litellm-dashboard/src/components/add_model/build_auto_router_routing_test_request.ts index 9a5be0837a0..24f40307ff4 100644 --- a/ui/litellm-dashboard/src/components/add_model/build_auto_router_routing_test_request.ts +++ b/ui/litellm-dashboard/src/components/add_model/build_auto_router_routing_test_request.ts @@ -24,11 +24,11 @@ export const buildSavedJevConnectionTestRequest = ( const result = z .object({ classifier_type: z.enum(["jev", "oss_classifier"]), - tiers: z.record(z.unknown()), + tiers: z.record(z.string(), z.unknown()), jev_classifier_config: jevClassifierConfigSchema.optional(), opensource_classifier_config: jevClassifierConfigSchema.optional(), }) - .passthrough() + .loose() .safeParse(parsed); if (!result.success) return undefined; const { jev_classifier_config, opensource_classifier_config, ...config } = result.data; diff --git a/ui/litellm-dashboard/src/components/add_model/jev_classifier_config.ts b/ui/litellm-dashboard/src/components/add_model/jev_classifier_config.ts index 18cc18e3d69..a9a7cc18711 100644 --- a/ui/litellm-dashboard/src/components/add_model/jev_classifier_config.ts +++ b/ui/litellm-dashboard/src/components/add_model/jev_classifier_config.ts @@ -16,7 +16,8 @@ const jevClassifierConfigFields = { instructions: z .string() .nullish() - .transform((value) => value ?? undefined), + .transform((value) => value ?? undefined) + .optional(), circuit_breaker_enabled: z.boolean().optional(), circuit_breaker_cooldown_seconds: z.number().finite().positive().optional(), }; @@ -34,7 +35,7 @@ export const jevClassifierConfigSchema = z !config.provider || config.provider === "jev" || OSS_CLASSIFIER_MODELS[config.provider].some((model) => model === config.model), - { message: "Select a supported classifier model", path: ["model"] }, + { error: "Select a supported classifier model", path: ["model"] }, ); export type JevClassifierConfig = z.infer; diff --git a/ui/litellm-dashboard/src/components/add_pass_through.tsx b/ui/litellm-dashboard/src/components/add_pass_through.tsx index e334c3fa5a7..a240f1f6bce 100644 --- a/ui/litellm-dashboard/src/components/add_pass_through.tsx +++ b/ui/litellm-dashboard/src/components/add_pass_through.tsx @@ -4,7 +4,7 @@ import React, { useState } from "react"; import { CircleHelp, Info, Plug } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert"; import { useWatch } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import { createPassThroughEndpoint } from "./networking"; import NumericalInput from "./shared/numerical_input"; diff --git a/ui/litellm-dashboard/src/components/cloudzero_export_modal.tsx b/ui/litellm-dashboard/src/components/cloudzero_export_modal.tsx index 91ba75552dc..aa3b9671662 100644 --- a/ui/litellm-dashboard/src/components/cloudzero_export_modal.tsx +++ b/ui/litellm-dashboard/src/components/cloudzero_export_modal.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { CircleCheck, FileDown } from "lucide-react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { getGlobalLitellmHeaderName } from "@/components/networking"; import { toast } from "@/lib/toast"; import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert"; diff --git a/ui/litellm-dashboard/src/components/common_components/MetadataKeyValueFields.test.tsx b/ui/litellm-dashboard/src/components/common_components/MetadataKeyValueFields.test.tsx index 655d46ec8fe..f62cd418775 100644 --- a/ui/litellm-dashboard/src/components/common_components/MetadataKeyValueFields.test.tsx +++ b/ui/litellm-dashboard/src/components/common_components/MetadataKeyValueFields.test.tsx @@ -2,7 +2,7 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import React from "react"; import { describe, expect, it, vi } from "vitest"; -import { z } from "zod/v4"; +import { z } from "zod"; import { TeamMetadataField } from "@/app/(dashboard)/hooks/teams/useTeamMetadataSchema"; import { useZodForm } from "@/lib/forms/useZodForm"; import MetadataKeyValueFields, { diff --git a/ui/litellm-dashboard/src/components/common_components/MetadataKeyValueFields.tsx b/ui/litellm-dashboard/src/components/common_components/MetadataKeyValueFields.tsx index 6f20676c9e4..baa154ffb3b 100644 --- a/ui/litellm-dashboard/src/components/common_components/MetadataKeyValueFields.tsx +++ b/ui/litellm-dashboard/src/components/common_components/MetadataKeyValueFields.tsx @@ -8,7 +8,7 @@ import { type FieldValues, type UseFormGetValues, } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import { TeamMetadataField } from "@/app/(dashboard)/hooks/teams/useTeamMetadataSchema"; import { FormField } from "@/components/shared/form/FormField"; diff --git a/ui/litellm-dashboard/src/components/edit_auto_router/editAutoRouterFormSchema.ts b/ui/litellm-dashboard/src/components/edit_auto_router/editAutoRouterFormSchema.ts index af84a0897c6..394fb821db4 100644 --- a/ui/litellm-dashboard/src/components/edit_auto_router/editAutoRouterFormSchema.ts +++ b/ui/litellm-dashboard/src/components/edit_auto_router/editAutoRouterFormSchema.ts @@ -1,4 +1,4 @@ -import { z } from "zod/v4"; +import { z } from "zod"; const sharedShape = { auto_router_name: z.string().min(1, "Auto router name is required"), diff --git a/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.integration.test.tsx b/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.integration.test.tsx index eaee2d520ac..b8f10964744 100644 --- a/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.integration.test.tsx +++ b/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.integration.test.tsx @@ -1,4 +1,3 @@ -import "openai/shims/web"; import { act, fireEvent, render, screen, waitFor, within } from "@testing-library/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/ui/litellm-dashboard/src/components/liteadmin/agent.test.ts b/ui/litellm-dashboard/src/components/liteadmin/agent.test.ts index ab893cab792..772b8525ee1 100644 --- a/ui/litellm-dashboard/src/components/liteadmin/agent.test.ts +++ b/ui/litellm-dashboard/src/components/liteadmin/agent.test.ts @@ -250,7 +250,7 @@ describe("fixed management operations", () => { it("provides strict JSON schemas for nullable and transformed tool arguments", async () => { const model = transport([completion()]); await runLiteAdmin(options(), model.client); - const tools = model.requests[0].tools!; + const tools = model.requests[0].tools!.filter((tool) => tool.type === "function"); for (const tool of tools) expect(tool.function.strict).toBe(true); const createUser = tools.find((tool) => tool.function.name === "user_create")!; const expectedSchema = { diff --git a/ui/litellm-dashboard/src/components/liteadmin/agent.ts b/ui/litellm-dashboard/src/components/liteadmin/agent.ts index d57b3383089..e2f652eaf55 100644 --- a/ui/litellm-dashboard/src/components/liteadmin/agent.ts +++ b/ui/litellm-dashboard/src/components/liteadmin/agent.ts @@ -105,7 +105,7 @@ export async function runLiteAdmin(options: LiteAdminOptions, client?: OpenAI): max_tokens: 2_048, }; const runnerOptions = { signal: options.signal, maxChatCompletions: 6, maxRetries: 0, timeout: 60_000 }; - const runner = modelClient.beta.chat.completions.runTools(parameters, runnerOptions); + const runner = modelClient.chat.completions.runTools(parameters, runnerOptions); runner.on("chatCompletion", (completion) => { active(); const message = completion.choices[0]?.message; diff --git a/ui/litellm-dashboard/src/components/liteadmin/operations.ts b/ui/litellm-dashboard/src/components/liteadmin/operations.ts index a063d7027df..6983503c52b 100644 --- a/ui/litellm-dashboard/src/components/liteadmin/operations.ts +++ b/ui/litellm-dashboard/src/components/liteadmin/operations.ts @@ -27,8 +27,12 @@ export interface OperationContext { type Schemas = components["schemas"]; const text = z.string().min(1).max(200); const hash = z.string().regex(/^[a-f0-9]{64}$/i, "Use the key hash from a lookup, not a raw API key."); -const optional = >(schema: Schema) => - schema.nullable().transform((value) => value ?? undefined); +const optional = >(schema: Schema) => schema.nullable(); +type NullsStripped = { [K in keyof T]: Exclude }; +const stripNulls = >(args: T): NullsStripped => + Object.fromEntries( + Object.entries(args).map(([key, value]) => [key, value === null ? undefined : value]), + ) as NullsStripped; const optionalText = optional(text); const amount = optional(z.number().finite().nonnegative()); const limit = optional(z.number().int().nonnegative()); @@ -47,15 +51,15 @@ const keyFields = { }; const teamFields = { team_alias: optionalText, organization_id: optionalText, models, ...limits }; const userFields = { - user_email: optional(z.string().email().max(200)), + user_email: optional(z.email().max(200)), user_alias: optionalText, user_role: optional(z.enum(["proxy_admin", "proxy_admin_viewer", "internal_user", "internal_user_viewer"])), models, ...limits, }; -const object = (shape: Shape) => z.object(shape).strict(); +const object = (shape: Shape) => z.strictObject(shape); const dated = (shape: Shape) => - object({ start_date: z.string().date(), end_date: z.string().date(), ...shape }).refine((value) => { + object({ start_date: z.iso.date(), end_date: z.iso.date(), ...shape }).refine((value: Record) => { if (typeof value.start_date !== "string" || typeof value.end_date !== "string") return false; const days = (Date.parse(value.end_date) - Date.parse(value.start_date)) / 86_400_000; return days >= 0 && days <= 366; @@ -108,18 +112,19 @@ export function createLiteAdminOperations(context: OperationContext) { }; const auth = { accessToken: context.accessToken, signal: context.signal }; const operation = - (mode: "read" | "write" | "delete", kind: ResultKind) => + (mode: "read" | "write" | "delete", kind: ResultKind, extraArguments?: Record) => >>( name: string, title: string, schema: Schema, - execute: (args: z.output) => Promise, + execute: (args: NullsStripped>) => Promise, ) => { const definition = { name, description: `${title}. Null means omitted or unchanged. ${mode === "read" ? "Read-only." : "Requires the administrator to review and confirm the change."}`, parameters: schema, - function: async (args: z.output) => { + function: async (rawArgs: unknown) => { + const args = stripNulls(rawArgs as z.output); active(); context.beforeTool(); const action: LiteAdminAction | undefined = @@ -129,7 +134,10 @@ export function createLiteAdminOperations(context: OperationContext) { id: crypto.randomUUID(), name, title, - arguments: Object.fromEntries(Object.entries(args).filter(([, value]) => value !== undefined)), + arguments: { + ...Object.fromEntries(Object.entries(args).filter(([, value]) => value !== undefined)), + ...extraArguments, + }, destructive: mode === "delete", }; if (action) { @@ -140,7 +148,7 @@ export function createLiteAdminOperations(context: OperationContext) { const outcome = await Promise.resolve() .then(() => { active(); - return execute(args); + return execute({ ...args, ...extraArguments } as NullsStripped>); }) .then( (value) => ({ ok: true, value }) as const, @@ -256,10 +264,10 @@ export function createLiteAdminOperations(context: OperationContext) { operation("read", "user")("user_info", "View a user", object({ user_id: text }), (a) => apiClient.get("/v2/user/info", { ...auth, query: a }), ), - operation("write", "user")( + operation("write", "user", { auto_create_key: false })( "user_create", "Create a user without a key", - object({ user_id: optionalText, ...userFields }).transform((a) => ({ ...a, auto_create_key: false })), + object({ user_id: optionalText, ...userFields }), (a) => apiClient.post("/user/new", { ...auth, body: a satisfies Partial }), ), operation("write", "user")("user_update", "Update a user", object({ user_id: text, ...userFields }), (a) => diff --git a/ui/litellm-dashboard/src/components/mcp_server_management/effectiveMcpServers.ts b/ui/litellm-dashboard/src/components/mcp_server_management/effectiveMcpServers.ts index c9e85fef31b..fd17a1725ec 100644 --- a/ui/litellm-dashboard/src/components/mcp_server_management/effectiveMcpServers.ts +++ b/ui/litellm-dashboard/src/components/mcp_server_management/effectiveMcpServers.ts @@ -1,4 +1,4 @@ -import { z } from "zod/v4"; +import { z } from "zod"; import { MCPServer, MCPToolset } from "../mcp_tools/types"; import { MCP_ALL_TOOLS_WILDCARD } from "../mcp_tools/constants"; diff --git a/ui/litellm-dashboard/src/components/model_add/reuse_credentials.tsx b/ui/litellm-dashboard/src/components/model_add/reuse_credentials.tsx index 772e52dd0a5..b7e49cef250 100644 --- a/ui/litellm-dashboard/src/components/model_add/reuse_credentials.tsx +++ b/ui/litellm-dashboard/src/components/model_add/reuse_credentials.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"; import { FormField } from "@/components/shared/form/FormField"; import { Button } from "@/components/ui/button"; diff --git a/ui/litellm-dashboard/src/components/organisms/RegenerateKeyModal.tsx b/ui/litellm-dashboard/src/components/organisms/RegenerateKeyModal.tsx index 84724d01a1d..58557c35d97 100644 --- a/ui/litellm-dashboard/src/components/organisms/RegenerateKeyModal.tsx +++ b/ui/litellm-dashboard/src/components/organisms/RegenerateKeyModal.tsx @@ -6,7 +6,7 @@ import { Check, CircleHelp, Copy, RefreshCw, TriangleAlert } from "lucide-react" import React, { useEffect, useMemo, useState } from "react"; import { useWatch } from "react-hook-form"; import { CopyToClipboard } from "react-copy-to-clipboard"; -import { z } from "zod/v4"; +import { z } from "zod"; import { KeyResponse } from "../key_team_helpers/key_list"; import { toast } from "@/lib/toast"; import { regenerateKeyCall } from "../networking"; diff --git a/ui/litellm-dashboard/src/components/organization/org-create/mapper.ts b/ui/litellm-dashboard/src/components/organization/org-create/mapper.ts index ebf18c63710..11eced74b93 100644 --- a/ui/litellm-dashboard/src/components/organization/org-create/mapper.ts +++ b/ui/litellm-dashboard/src/components/organization/org-create/mapper.ts @@ -1,4 +1,4 @@ -import { z } from "zod/v4"; +import { z } from "zod"; import type { components } from "@/lib/http/schema"; diff --git a/ui/litellm-dashboard/src/components/organization/org-settings/mapper.ts b/ui/litellm-dashboard/src/components/organization/org-settings/mapper.ts index 2b8ec385152..221cd9335e9 100644 --- a/ui/litellm-dashboard/src/components/organization/org-settings/mapper.ts +++ b/ui/litellm-dashboard/src/components/organization/org-settings/mapper.ts @@ -1,4 +1,4 @@ -import { z } from "zod/v4"; +import { z } from "zod"; import type { Organization } from "@/components/networking"; import type { components } from "@/lib/http/schema"; diff --git a/ui/litellm-dashboard/src/components/organization/org-settings/schema.ts b/ui/litellm-dashboard/src/components/organization/org-settings/schema.ts index c3122f7ff7a..f120a6cb0b0 100644 --- a/ui/litellm-dashboard/src/components/organization/org-settings/schema.ts +++ b/ui/litellm-dashboard/src/components/organization/org-settings/schema.ts @@ -1,4 +1,4 @@ -import { z } from "zod/v4"; +import { z } from "zod"; const isBlank = (value: string): boolean => value.trim() === ""; diff --git a/ui/litellm-dashboard/src/components/pass_through_info.tsx b/ui/litellm-dashboard/src/components/pass_through_info.tsx index ce7c60dcec0..f44afdd9bf8 100644 --- a/ui/litellm-dashboard/src/components/pass_through_info.tsx +++ b/ui/litellm-dashboard/src/components/pass_through_info.tsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import { updatePassThroughEndpoint, deletePassThroughEndpointsCall } from "./networking"; import { Eye, EyeOff } from "lucide-react"; import { useWatch } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import RoutePreview from "./route_preview"; import { toast } from "@/lib/toast"; import PassThroughSecuritySection from "./common_components/PassThroughSecuritySection"; diff --git a/ui/litellm-dashboard/src/components/routing_groups/RoutingGroupModal.tsx b/ui/litellm-dashboard/src/components/routing_groups/RoutingGroupModal.tsx index 01bdbae4bf2..ffe1b413778 100644 --- a/ui/litellm-dashboard/src/components/routing_groups/RoutingGroupModal.tsx +++ b/ui/litellm-dashboard/src/components/routing_groups/RoutingGroupModal.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useMemo } from "react"; import { useWatch } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import { FieldGroup } from "@/components/ui/field"; import { FormField } from "@/components/shared/form/FormField"; import { diff --git a/ui/litellm-dashboard/src/components/shared/form/FormField.test.tsx b/ui/litellm-dashboard/src/components/shared/form/FormField.test.tsx index baeac29402f..a9c68417b1a 100644 --- a/ui/litellm-dashboard/src/components/shared/form/FormField.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/form/FormField.test.tsx @@ -4,7 +4,7 @@ import userEvent from "@testing-library/user-event"; import * as React from "react"; import { useForm } from "react-hook-form"; import { describe, expect, it, vi } from "vitest"; -import { z } from "zod/v4"; +import { z } from "zod"; import { Input } from "@/components/ui/input"; diff --git a/ui/litellm-dashboard/src/components/team/EditMembership.tsx b/ui/litellm-dashboard/src/components/team/EditMembership.tsx index 5f342571964..3e6cb7d1284 100644 --- a/ui/litellm-dashboard/src/components/team/EditMembership.tsx +++ b/ui/litellm-dashboard/src/components/team/EditMembership.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useMemo, useState } from "react"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; -import { z } from "zod/v4"; +import { z } from "zod"; import NumericalInput from "../shared/numerical_input"; import BudgetDurationDropdown from "../common_components/budget_duration_dropdown"; import { FieldGroup } from "@/components/ui/field"; diff --git a/ui/litellm-dashboard/src/components/team/TeamAdminSettingsForm.tsx b/ui/litellm-dashboard/src/components/team/TeamAdminSettingsForm.tsx index ebd7a603bde..d9f9dcfbd40 100644 --- a/ui/litellm-dashboard/src/components/team/TeamAdminSettingsForm.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamAdminSettingsForm.tsx @@ -2,7 +2,7 @@ import { Save } from "lucide-react"; import { useWatch } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import { FormField } from "@/components/shared/form/FormField"; import { Button } from "@/components/ui/button"; diff --git a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx index 5dfcf1d8e35..1128d8ebe1a 100644 --- a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx @@ -48,7 +48,7 @@ import { toast } from "@/lib/toast"; import { CheckIcon, ChevronDown, CircleMinus, CopyIcon, Info, Pencil, Plus, Save } from "lucide-react"; import React, { useEffect, useMemo, useState } from "react"; import { useFieldArray } from "react-hook-form"; -import { z } from "zod/v4"; +import { z } from "zod"; import GuardrailsSelect from "./GuardrailsSelect"; import { type CallerEditAccess, diff --git a/ui/litellm-dashboard/src/components/team/teamAdminEditAccess.ts b/ui/litellm-dashboard/src/components/team/teamAdminEditAccess.ts index 1d8cd90c517..686ad06b121 100644 --- a/ui/litellm-dashboard/src/components/team/teamAdminEditAccess.ts +++ b/ui/litellm-dashboard/src/components/team/teamAdminEditAccess.ts @@ -1,4 +1,4 @@ -import { z } from "zod/v4"; +import { z } from "zod"; export const TEAM_ADMIN_EDITABLE_TEAM_FIELDS_SETTING = "team_admin_editable_team_fields"; diff --git a/ui/litellm-dashboard/src/components/templates/keyEditFormValues.ts b/ui/litellm-dashboard/src/components/templates/keyEditFormValues.ts index 7436380d6ee..0ae60bbcc81 100644 --- a/ui/litellm-dashboard/src/components/templates/keyEditFormValues.ts +++ b/ui/litellm-dashboard/src/components/templates/keyEditFormValues.ts @@ -1,4 +1,4 @@ -import { z } from "zod/v4"; +import { z } from "zod"; import { KeyResponse } from "../key_team_helpers/key_list"; import { extractLoggingSettings, formatMetadataForDisplay, stripTagsFromMetadata } from "../key_info_utils"; @@ -122,50 +122,52 @@ export const toKeyEditFormValues = (keyData: KeyResponse): KeyEditFormValues => }); export const keyEditFormSchema = z.object({ - key_alias: z.custom(), - models: z.custom(), - allowed_routes: z.custom(), - max_budget: z.custom(), - soft_budget: z.custom(), - budget_duration: z.custom(), - tpm_limit: z.custom(), - tpm_limit_type: z.custom(), - rpm_limit: z.custom(), - rpm_limit_type: z.custom(), - tpd_limit: z.custom(), - throttle_on_budget_exceeded: z.custom(), - enable_prompt_caching: z.custom(), - max_parallel_requests: z.custom(), - model_tpm_limit: z.custom(), - model_rpm_limit: z.custom(), + key_alias: z.custom().optional(), + models: z.custom().optional(), + allowed_routes: z.custom().optional(), + max_budget: z.custom().optional(), + soft_budget: z.custom().optional(), + budget_duration: z.custom().optional(), + tpm_limit: z.custom().optional(), + tpm_limit_type: z.custom().optional(), + rpm_limit: z.custom().optional(), + rpm_limit_type: z.custom().optional(), + tpd_limit: z.custom().optional(), + throttle_on_budget_exceeded: z.custom().optional(), + enable_prompt_caching: z.custom().optional(), + max_parallel_requests: z.custom().optional(), + model_tpm_limit: z.custom().optional(), + model_rpm_limit: z.custom().optional(), default_estimated_output_tokens: z .custom() - .refine(estimateChecks.positive.isValid, estimateChecks.positive.message), + .refine(estimateChecks.positive.isValid, estimateChecks.positive.message) + .optional(), default_estimated_output_tokens_per_model: z .custom() - .refine(estimateChecks.perModel.isValid, estimateChecks.perModel.message), - guardrails: z.custom(), - disable_global_guardrails: z.custom(), - policies: z.custom(), - tags: z.custom(), - prompts: z.custom(), - access_group_ids: z.custom(), - allowed_passthrough_routes: z.custom(), - vector_stores: z.custom(), - mcp_servers_and_groups: z.custom(), - mcp_tool_permissions: z.custom | undefined>(), - agents_and_groups: z.custom(), - skills: z.custom(), - organization_id: z.custom(), - team_id: z.custom(), + .refine(estimateChecks.perModel.isValid, estimateChecks.perModel.message) + .optional(), + guardrails: z.custom().optional(), + disable_global_guardrails: z.custom().optional(), + policies: z.custom().optional(), + tags: z.custom().optional(), + prompts: z.custom().optional(), + access_group_ids: z.custom().optional(), + allowed_passthrough_routes: z.custom().optional(), + vector_stores: z.custom().optional(), + mcp_servers_and_groups: z.custom().optional(), + mcp_tool_permissions: z.custom | undefined>().optional(), + agents_and_groups: z.custom().optional(), + skills: z.custom().optional(), + organization_id: z.custom().optional(), + team_id: z.custom().optional(), project_id: z.string().nullable().optional(), - logging_settings: z.custom(), - metadata: z.custom(), - duration: z.custom(), - token: z.custom(), - disabled_callbacks: z.custom(), - auto_rotate: z.custom(), - rotation_interval: z.custom(), + logging_settings: z.custom().optional(), + metadata: z.custom().optional(), + duration: z.custom().optional(), + token: z.custom().optional(), + disabled_callbacks: z.custom().optional(), + auto_rotate: z.custom().optional(), + rotation_interval: z.custom().optional(), }); export interface MountedFieldGates { diff --git a/ui/litellm-dashboard/src/components/update_model_credentials_modal.tsx b/ui/litellm-dashboard/src/components/update_model_credentials_modal.tsx index e948bcb87d6..48861aa4947 100644 --- a/ui/litellm-dashboard/src/components/update_model_credentials_modal.tsx +++ b/ui/litellm-dashboard/src/components/update_model_credentials_modal.tsx @@ -1,6 +1,6 @@ import { TriangleAlert } from "lucide-react"; import { useState } from "react"; -import { z } from "zod/v4"; +import { z } from "zod"; import { modelPatchUpdateCall } from "./networking"; import { toast } from "@/lib/toast"; import { FieldGroup } from "@/components/ui/field"; diff --git a/ui/litellm-dashboard/src/lib/forms/useZodForm.test.tsx b/ui/litellm-dashboard/src/lib/forms/useZodForm.test.tsx index 866451aba61..2119041793e 100644 --- a/ui/litellm-dashboard/src/lib/forms/useZodForm.test.tsx +++ b/ui/litellm-dashboard/src/lib/forms/useZodForm.test.tsx @@ -2,7 +2,7 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import * as React from "react"; import { describe, expect, it, vi } from "vitest"; -import { z } from "zod/v4"; +import { z } from "zod"; import { useZodForm } from "./useZodForm"; diff --git a/ui/litellm-dashboard/src/lib/forms/useZodForm.ts b/ui/litellm-dashboard/src/lib/forms/useZodForm.ts index a76d00a752d..684090a21da 100644 --- a/ui/litellm-dashboard/src/lib/forms/useZodForm.ts +++ b/ui/litellm-dashboard/src/lib/forms/useZodForm.ts @@ -2,17 +2,27 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useForm, type FieldValues, type UseFormProps, type UseFormReturn } from "react-hook-form"; -import type { $ZodType } from "zod/v4/core"; +import { z } from "zod"; + +type FormValuesOf = Extract; // TInput is what the widgets hold (strings), TOutput is what handleSubmit receives after // zod runs coerce/transform; typing useForm with z.infer would collapse the two and lie -// about defaultValues. zod's $ZodType declares them as , in that order. -// The `unknown` in the middle is RHF's context generic, which we never use. -export const useZodForm = ( - schema: $ZodType, +// about defaultValues. The `unknown` in the middle is RHF's context generic, which we never use. +export function useZodForm( + schema: Schema, + props?: Omit>, unknown, FormValuesOf>>, "resolver">, +): UseFormReturn>, unknown, FormValuesOf>>; +export function useZodForm( + schema: z.core.$ZodType, props?: Omit, "resolver">, -): UseFormReturn => - useForm({ +): UseFormReturn; +export function useZodForm( + schema: z.core.$ZodType, + props?: Omit, "resolver">, +): UseFormReturn { + return useForm({ ...props, - resolver: zodResolver(schema), + resolver: zodResolver(schema as z.core.$ZodType), }); +}