diff --git a/ui/litellm-dashboard/eslint-budgets.json b/ui/litellm-dashboard/eslint-budgets.json index 8dedb9ac9ca..f08e1bb6160 100644 --- a/ui/litellm-dashboard/eslint-budgets.json +++ b/ui/litellm-dashboard/eslint-budgets.json @@ -2,5 +2,7 @@ "@typescript-eslint/no-explicit-any": { "max": 2040, "target": 1500 }, "no-console": { "max": 484, "target": 0 }, "complexity": { "max": 140, "target": 80 }, - "max-depth": { "max": 70, "target": 30 } + "max-depth": { "max": 70, "target": 30 }, + "local/no-large-inline-object-arg": { "max": 560, "target": 300 }, + "local/no-long-condition-chain": { "max": 265, "target": 120 } } diff --git a/ui/litellm-dashboard/eslint-metrics.json b/ui/litellm-dashboard/eslint-metrics.json index 51cef1169f9..f4dc89c5b80 100644 --- a/ui/litellm-dashboard/eslint-metrics.json +++ b/ui/litellm-dashboard/eslint-metrics.json @@ -1,6 +1,8 @@ { "@typescript-eslint/no-explicit-any": 1988, "complexity": 128, + "local/no-large-inline-object-arg": 513, + "local/no-long-condition-chain": 233, "max-depth": 59, "no-console": 15 } diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index 67b19471aaf..b077338c75b 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -1,4 +1,9 @@ { + "scripts/check-lint-budgets.mjs": { + "no-nested-ternary": { + "count": 1 + } + }, "src/app/(dashboard)/api-reference/APIReferenceView.tsx": { "no-restricted-imports": { "count": 1 @@ -64,6 +69,9 @@ } }, "src/app/(dashboard)/cost-tracking/components/cost_tracking_settings.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -133,11 +141,21 @@ "count": 1 } }, + "src/app/(dashboard)/guardrails-monitor/components/GuardrailDetail.tsx": { + "no-nested-ternary": { + "count": 3 + } + }, "src/app/(dashboard)/guardrails-monitor/components/GuardrailsMonitorView.tsx": { "no-restricted-imports": { "count": 1 } }, + "src/app/(dashboard)/guardrails-monitor/components/GuardrailsOverview.tsx": { + "no-nested-ternary": { + "count": 8 + } + }, "src/app/(dashboard)/guardrails-monitor/components/ScoreChart.test.tsx": { "react/display-name": { "count": 1 @@ -343,6 +361,9 @@ } }, "src/app/(dashboard)/models-and-endpoints/components/ModelRetrySettingsTab.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -361,6 +382,9 @@ } }, "src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx": { + "no-nested-ternary": { + "count": 2 + }, "react-hooks/set-state-in-effect": { "count": 5 } @@ -370,7 +394,15 @@ "count": 1 } }, + "src/app/(dashboard)/playground/components/chat_ui/ChatMessageBubble.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx": { + "no-nested-ternary": { + "count": 7 + }, "no-restricted-imports": { "count": 1 }, @@ -382,6 +414,9 @@ } }, "src/app/(dashboard)/playground/components/chat_ui/CodeInterpreterOutput.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-syntax": { "count": 2 } @@ -392,6 +427,9 @@ } }, "src/app/(dashboard)/playground/components/chat_ui/RealtimePlayground.tsx": { + "no-nested-ternary": { + "count": 2 + }, "react-hooks/immutability": { "count": 2 }, @@ -400,16 +438,27 @@ } }, "src/app/(dashboard)/playground/components/compareUI/CompareUI.tsx": { + "no-nested-ternary": { + "count": 4 + }, "react-hooks/set-state-in-effect": { "count": 1 } }, + "src/app/(dashboard)/playground/components/compareUI/components/MessageDisplay.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/app/(dashboard)/playground/components/compareUI/components/ModelSelector.tsx": { "no-restricted-imports": { "count": 1 } }, "src/app/(dashboard)/playground/components/complianceUI/ComplianceUI.tsx": { + "no-nested-ternary": { + "count": 8 + }, "react-hooks/preserve-manual-memoization": { "count": 3 } @@ -474,6 +523,9 @@ } }, "src/app/(dashboard)/projects/components/ProjectDetailsPage.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 } @@ -499,6 +551,9 @@ } }, "src/app/(dashboard)/prompts/components/index.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -517,6 +572,9 @@ } }, "src/app/(dashboard)/prompts/components/prompt_editor_view/PromptCodeSnippets.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -550,6 +608,9 @@ } }, "src/app/(dashboard)/prompts/components/prompt_editor_view/VersionHistorySidePanel.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/immutability": { "count": 1 } @@ -570,6 +631,9 @@ } }, "src/app/(dashboard)/prompts/components/prompt_info.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 }, @@ -578,6 +642,9 @@ } }, "src/app/(dashboard)/prompts/components/prompt_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -635,6 +702,9 @@ } }, "src/app/(dashboard)/users/_components/user_edit_view.test.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react/display-name": { "count": 1 } @@ -648,6 +718,9 @@ } }, "src/app/(dashboard)/users/_components/view_users.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -664,6 +737,9 @@ } }, "src/app/(dashboard)/users/_components/view_users/table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -677,6 +753,9 @@ } }, "src/app/(dashboard)/workflows/WorkflowRuns.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-syntax": { "count": 3 }, @@ -684,6 +763,11 @@ "count": 1 } }, + "src/app/chat/page.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/app/login/LoginPage.tsx": { "react-hooks/set-state-in-effect": { "count": 2 @@ -715,6 +799,9 @@ } }, "src/components/AIHub/ModelHubTable.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -746,6 +833,9 @@ } }, "src/components/AIHub/forms/MakeMCPPublicForm.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 }, @@ -778,11 +868,17 @@ } }, "src/components/DeletedKeysPage/DeletedKeysTable/DeletedKeysTable.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } }, "src/components/DeletedTeamsPage/DeletedTeamsTable/DeletedTeamsTable.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -815,11 +911,26 @@ "count": 1 } }, + "src/components/GuardrailSettingsView.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/GuardrailsMonitor/LogViewer.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/HelpLink.test.tsx": { "unused-imports/no-unused-imports": { "count": 1 } }, + "src/components/ModelSelect/PaginatedModelSelect/PaginatedModelSelect.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/Navbar/BlogDropdown/BlogDropdown.test.tsx": { "max-nested-callbacks": { "count": 12 @@ -836,6 +947,9 @@ } }, "src/components/OldTeams.tsx": { + "no-nested-ternary": { + "count": 4 + }, "no-restricted-imports": { "count": 1 }, @@ -856,7 +970,15 @@ "count": 1 } }, + "src/components/Settings/AdminSettings/HashicorpVault/HashicorpVault.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/Settings/AdminSettings/MCPSemanticFilterSettings/MCPSemanticFilterSettings.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -871,6 +993,11 @@ "count": 1 } }, + "src/components/Settings/AdminSettings/SSOSettings/RedactableField.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/Settings/AdminSettings/SSOSettings/SSOSettingsLoadingSkeleton.test.tsx": { "max-nested-callbacks": { "count": 4 @@ -881,7 +1008,15 @@ "count": 2 } }, + "src/components/Settings/AdminSettings/UISettings/UISettings.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/Settings/LoggingAndAlerts/LoggingCallbacks/LoggingCallbacksTable.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -907,12 +1042,20 @@ "count": 1 } }, + "src/components/TeamSSOSettings.test.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/ToolDetail.tsx": { "unused-imports/no-unused-imports": { "count": 2 } }, "src/components/ToolPolicies.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -932,6 +1075,9 @@ } }, "src/components/UsageIndicator.tsx": { + "no-nested-ternary": { + "count": 4 + }, "no-restricted-imports": { "count": 1 }, @@ -949,6 +1095,11 @@ "count": 1 } }, + "src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/UsagePage/components/EntityUsage/EntityUsage.tsx": { "no-restricted-imports": { "count": 1 @@ -975,11 +1126,17 @@ } }, "src/components/UsagePage/components/UsageAIChatPanel.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/immutability": { "count": 1 } }, "src/components/UsagePage/components/UsagePageView.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -999,16 +1156,25 @@ } }, "src/components/VirtualKeysPage/VirtualKeysTable.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } }, "src/components/activity_metrics.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } }, "src/components/add_model/AddModelForm.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1045,11 +1211,22 @@ } }, "src/components/add_model/litellm_model_name.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } }, + "src/components/add_model/model_connection_test.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, "src/components/add_model/provider_specific_fields.tsx": { + "no-nested-ternary": { + "count": 5 + }, "no-restricted-imports": { "count": 1 }, @@ -1079,6 +1256,9 @@ } }, "src/components/agents/add_agent_form.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 }, @@ -1102,7 +1282,15 @@ "count": 1 } }, + "src/components/agents/agent_form_fields.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/agents/agent_info.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1110,7 +1298,20 @@ "count": 1 } }, + "src/components/agents/agent_virtual_keys.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/agents/dynamic_agent_form_fields.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, "src/components/alerting/dynamic_form.tsx": { + "no-nested-ternary": { + "count": 4 + }, "no-restricted-imports": { "count": 1 } @@ -1123,6 +1324,31 @@ "count": 1 } }, + "src/components/chat/KeysPanel.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/chat/MCPAppsPanel.tsx": { + "no-nested-ternary": { + "count": 7 + } + }, + "src/components/chat/MCPConnectPicker.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/chat/MCPCredentialsTab.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/chat/UsagePanel.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, "src/components/claude_code_plugins.tsx": { "no-restricted-imports": { "count": 1 @@ -1145,6 +1371,9 @@ } }, "src/components/claude_code_plugins/plugin_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1235,6 +1464,9 @@ } }, "src/components/common_components/chartUtils.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1250,6 +1482,9 @@ } }, "src/components/common_components/simple_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1286,6 +1521,9 @@ } }, "src/components/general_settings.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 2 } @@ -1300,17 +1538,28 @@ "count": 1 } }, + "src/components/guardrails/GuardrailTestPlayground.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/guardrails/GuardrailTestResults.tsx": { "no-restricted-imports": { "count": 1 } }, "src/components/guardrails/TeamGuardrailsTab.tsx": { + "no-nested-ternary": { + "count": 2 + }, "react-hooks/set-state-in-effect": { "count": 1 } }, "src/components/guardrails/add_guardrail_form.tsx": { + "no-nested-ternary": { + "count": 4 + }, "react-hooks/set-state-in-effect": { "count": 1 }, @@ -1319,11 +1568,17 @@ } }, "src/components/guardrails/content_filter/CompetitorIntentConfiguration.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/set-state-in-effect": { "count": 1 } }, "src/components/guardrails/content_filter/ContentCategoryConfiguration.tsx": { + "no-nested-ternary": { + "count": 3 + }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -1342,6 +1597,9 @@ } }, "src/components/guardrails/custom_code/CustomCodeModal.tsx": { + "no-nested-ternary": { + "count": 6 + }, "no-restricted-imports": { "count": 1 }, @@ -1372,16 +1630,25 @@ } }, "src/components/guardrails/guardrail_optional_params.tsx": { + "no-nested-ternary": { + "count": 5 + }, "react-hooks/set-state-in-effect": { "count": 1 } }, "src/components/guardrails/guardrail_provider_fields.tsx": { + "no-nested-ternary": { + "count": 5 + }, "react-hooks/set-state-in-effect": { "count": 1 } }, "src/components/guardrails/guardrail_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 2 } @@ -1407,6 +1674,9 @@ "src/components/llm_calls/chat_completion.tsx": { "max-params": { "count": 1 + }, + "no-nested-ternary": { + "count": 1 } }, "src/components/llm_calls/responses_api.tsx": { @@ -1444,7 +1714,15 @@ "count": 1 } }, + "src/components/mcp_tools/MCPToolArgumentsForm.tsx": { + "no-nested-ternary": { + "count": 5 + } + }, "src/components/mcp_tools/MCPToolsetsTab.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1456,11 +1734,17 @@ } }, "src/components/mcp_tools/McpCrudPermissionPanel.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 } }, "src/components/mcp_tools/OAuthFormFields.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1471,6 +1755,9 @@ } }, "src/components/mcp_tools/ToolTestPanel.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 }, @@ -1478,12 +1765,20 @@ "count": 1 } }, + "src/components/mcp_tools/UserEnvVarsModal.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, "src/components/mcp_tools/create_mcp_server.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, "react-hooks/set-state-in-effect": { - "count": 5 + "count": 4 } }, "src/components/mcp_tools/mcp_connect.tsx": { @@ -1495,6 +1790,9 @@ } }, "src/components/mcp_tools/mcp_connection_status.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 } @@ -1515,6 +1813,9 @@ } }, "src/components/mcp_tools/mcp_server_edit.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1531,6 +1832,9 @@ } }, "src/components/mcp_tools/mcp_servers.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1544,6 +1848,9 @@ } }, "src/components/mcp_tools/mcp_tools.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1575,6 +1882,9 @@ } }, "src/components/model_dashboard/HealthCheckComponent.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 }, @@ -1583,6 +1893,9 @@ } }, "src/components/model_dashboard/all_models_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1591,11 +1904,17 @@ "max-params": { "count": 1 }, + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } }, "src/components/model_dashboard/table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1619,6 +1938,9 @@ } }, "src/components/model_info_view.tsx": { + "no-nested-ternary": { + "count": 14 + }, "no-restricted-imports": { "count": 1 }, @@ -1627,6 +1949,9 @@ } }, "src/components/molecules/filter.tsx": { + "no-nested-ternary": { + "count": 2 + }, "react-hooks/use-memo": { "count": 1 } @@ -1643,6 +1968,9 @@ "max-params": { "count": 1 }, + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -1656,6 +1984,9 @@ "max-params": { "count": 23 }, + "no-nested-ternary": { + "count": 5 + }, "no-restricted-syntax": { "count": 154 } @@ -1731,6 +2062,9 @@ } }, "src/components/permissions/MCPServerPermissions.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 } @@ -1740,6 +2074,11 @@ "count": 1 } }, + "src/components/policies/PolicySelector.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/policies/add_attachment_form.tsx": { "no-restricted-imports": { "count": 1 @@ -1760,6 +2099,9 @@ } }, "src/components/policies/ai_suggestion_modal.tsx": { + "no-nested-ternary": { + "count": 10 + }, "no-restricted-imports": { "count": 1 }, @@ -1773,11 +2115,17 @@ } }, "src/components/policies/attachment_table.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } }, "src/components/policies/guardrail_selection_modal.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -1788,6 +2136,9 @@ } }, "src/components/policies/impact_popover.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1806,6 +2157,9 @@ } }, "src/components/policies/pipeline_flow_builder.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -1827,6 +2181,9 @@ } }, "src/components/policies/policy_table.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -1856,6 +2213,9 @@ } }, "src/components/public_model_hub.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1865,12 +2225,25 @@ "count": 1 } }, + "src/components/router_settings/ReliabilityRetriesSection.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/routing_groups/index.tsx": { "react-hooks/preserve-manual-memoization": { "count": 1 } }, + "src/components/search_tools/SearchToolSelector.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/settings.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -1925,6 +2298,9 @@ } }, "src/components/team/EditMembership.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 } @@ -1935,6 +2311,9 @@ } }, "src/components/team/TeamInfo.tsx": { + "no-nested-ternary": { + "count": 3 + }, "no-restricted-imports": { "count": 1 }, @@ -1943,6 +2322,9 @@ } }, "src/components/team/TeamVirtualKeysTable.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -1966,6 +2348,9 @@ } }, "src/components/templates/key_edit_view.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 } @@ -1976,6 +2361,9 @@ } }, "src/components/templates/key_info_view.tsx": { + "no-nested-ternary": { + "count": 1 + }, "no-restricted-imports": { "count": 1 }, @@ -2016,6 +2404,9 @@ } }, "src/components/vector_store_management/VectorStoreForm.tsx": { + "no-nested-ternary": { + "count": 2 + }, "no-restricted-imports": { "count": 1 }, @@ -2044,12 +2435,38 @@ "count": 1 } }, + "src/components/view_logs/EvalViewer/EvalViewer.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/view_logs/GuardrailViewer/CompliancePanel.tsx": { + "no-nested-ternary": { + "count": 2 + }, "react-hooks/set-state-in-effect": { "count": 1 } }, + "src/components/view_logs/GuardrailViewer/ContentFilterDetails.tsx": { + "no-nested-ternary": { + "count": 1 + } + }, + "src/components/view_logs/GuardrailViewer/GuardrailViewer.tsx": { + "no-nested-ternary": { + "count": 4 + } + }, + "src/components/view_logs/LogDetailsDrawer/LogDetailContent.tsx": { + "no-nested-ternary": { + "count": 4 + } + }, "src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx": { + "no-nested-ternary": { + "count": 3 + }, "react-hooks/set-state-in-effect": { "count": 2 } @@ -2059,11 +2476,21 @@ "count": 2 } }, + "src/components/view_logs/LogDetailsDrawer/prettyMessagesUtils.ts": { + "no-nested-ternary": { + "count": 1 + } + }, "src/components/view_logs/LogDetailsDrawer/useKeyboardNavigation.ts": { "react-hooks/immutability": { "count": 2 } }, + "src/components/view_logs/LogsTableToolbar.tsx": { + "no-nested-ternary": { + "count": 4 + } + }, "src/components/view_logs/columns.tsx": { "no-restricted-imports": { "count": 1 @@ -2077,6 +2504,11 @@ "count": 1 } }, + "src/components/view_logs/table.tsx": { + "no-nested-ternary": { + "count": 2 + } + }, "src/components/view_user_spend.tsx": { "react-hooks/set-state-in-effect": { "count": 2 @@ -2113,6 +2545,9 @@ } }, "src/hooks/useTestMCPConnection.tsx": { + "no-nested-ternary": { + "count": 1 + }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -2130,6 +2565,11 @@ "count": 1 } }, + "src/lib/http/client.ts": { + "no-nested-ternary": { + "count": 1 + } + }, "src/utils/dataUtils.test.ts": { "max-nested-callbacks": { "count": 1 diff --git a/ui/litellm-dashboard/eslint.config.mjs b/ui/litellm-dashboard/eslint.config.mjs index acdd0c91309..0cf5b4ff655 100644 --- a/ui/litellm-dashboard/eslint.config.mjs +++ b/ui/litellm-dashboard/eslint.config.mjs @@ -3,6 +3,7 @@ import tseslint from "typescript-eslint"; import nextCoreWebVitals from "eslint-config-next/core-web-vitals"; import prettier from "eslint-config-prettier/flat"; import unusedImports from "eslint-plugin-unused-imports"; +import local from "./scripts/eslint-rules/index.mjs"; const eslintConfig = [ { @@ -13,9 +14,11 @@ const eslintConfig = [ ...nextCoreWebVitals, prettier, { - plugins: { "unused-imports": unusedImports }, + plugins: { "unused-imports": unusedImports, local }, rules: { "unused-imports/no-unused-imports": "error", + "local/no-large-inline-object-arg": "warn", + "local/no-long-condition-chain": "warn", "@typescript-eslint/no-explicit-any": "warn", "no-console": ["warn", { allow: ["warn", "error"] }], "@typescript-eslint/no-unused-vars": "off", @@ -28,6 +31,7 @@ const eslintConfig = [ "no-useless-escape": "off", "no-self-assign": "error", "no-var": "error", + "no-nested-ternary": "error", "react/no-danger": "error", complexity: ["warn", 20], "max-depth": ["warn", 4], diff --git a/ui/litellm-dashboard/scripts/eslint-rules/index.mjs b/ui/litellm-dashboard/scripts/eslint-rules/index.mjs new file mode 100644 index 00000000000..150ba1d02e9 --- /dev/null +++ b/ui/litellm-dashboard/scripts/eslint-rules/index.mjs @@ -0,0 +1,11 @@ +import noLargeInlineObjectArg from "./no-large-inline-object-arg.mjs"; +import noLongConditionChain from "./no-long-condition-chain.mjs"; + +const plugin = { + rules: { + "no-large-inline-object-arg": noLargeInlineObjectArg, + "no-long-condition-chain": noLongConditionChain, + }, +}; + +export default plugin; diff --git a/ui/litellm-dashboard/scripts/eslint-rules/no-large-inline-object-arg.mjs b/ui/litellm-dashboard/scripts/eslint-rules/no-large-inline-object-arg.mjs new file mode 100644 index 00000000000..5c5ae170e23 --- /dev/null +++ b/ui/litellm-dashboard/scripts/eslint-rules/no-large-inline-object-arg.mjs @@ -0,0 +1,41 @@ +const DEFAULT_MIN_PROPERTIES = 4; + +const isArgumentOf = (node) => { + const parent = node.parent; + if (parent == null) return false; + if (parent.type !== "CallExpression" && parent.type !== "NewExpression") return false; + return parent.arguments.includes(node); +}; + +const rule = { + meta: { + type: "suggestion", + docs: { + description: + "Disallow passing a large object literal inline as a call argument; assign it to a named variable first.", + }, + schema: [ + { + type: "object", + properties: { minProperties: { type: "integer", minimum: 1 } }, + additionalProperties: false, + }, + ], + messages: { + tooLarge: + "Object literal with {{count}} properties passed inline as an argument; assign it to a named variable first.", + }, + }, + create(context) { + const minProperties = context.options[0]?.minProperties ?? DEFAULT_MIN_PROPERTIES; + return { + ObjectExpression(node) { + if (!isArgumentOf(node)) return; + if (node.properties.length < minProperties) return; + context.report({ node, messageId: "tooLarge", data: { count: node.properties.length } }); + }, + }; + }, +}; + +export default rule; diff --git a/ui/litellm-dashboard/scripts/eslint-rules/no-long-condition-chain.mjs b/ui/litellm-dashboard/scripts/eslint-rules/no-long-condition-chain.mjs new file mode 100644 index 00000000000..638e57442e2 --- /dev/null +++ b/ui/litellm-dashboard/scripts/eslint-rules/no-long-condition-chain.mjs @@ -0,0 +1,41 @@ +const DEFAULT_MIN_CONDITIONS = 4; + +const isBooleanLogical = (node) => + node?.type === "LogicalExpression" && (node.operator === "&&" || node.operator === "||"); + +const countConditions = (node) => + isBooleanLogical(node) ? countConditions(node.left) + countConditions(node.right) : 1; + +const rule = { + meta: { + type: "suggestion", + docs: { + description: + "Disallow logical expressions that combine many conditions; extract the condition into a named boolean.", + }, + schema: [ + { + type: "object", + properties: { minConditions: { type: "integer", minimum: 2 } }, + additionalProperties: false, + }, + ], + messages: { + tooMany: "Boolean expression combines {{count}} conditions; extract it into a named variable.", + }, + }, + create(context) { + const minConditions = context.options[0]?.minConditions ?? DEFAULT_MIN_CONDITIONS; + return { + LogicalExpression(node) { + if (!isBooleanLogical(node)) return; + if (isBooleanLogical(node.parent)) return; + const count = countConditions(node); + if (count < minConditions) return; + context.report({ node, messageId: "tooMany", data: { count } }); + }, + }; + }, +}; + +export default rule; diff --git a/ui/litellm-dashboard/tests/eslint-rules/no-large-inline-object-arg.test.ts b/ui/litellm-dashboard/tests/eslint-rules/no-large-inline-object-arg.test.ts new file mode 100644 index 00000000000..dfe22ea8266 --- /dev/null +++ b/ui/litellm-dashboard/tests/eslint-rules/no-large-inline-object-arg.test.ts @@ -0,0 +1,46 @@ +import { RuleTester } from "eslint"; +import rule from "../../scripts/eslint-rules/no-large-inline-object-arg.mjs"; + +const ruleTester = new RuleTester({ + languageOptions: { ecmaVersion: "latest", sourceType: "module" }, +}); + +ruleTester.run("no-large-inline-object-arg", rule as never, { + valid: [ + "foo({ a: 1, b: 2, c: 3 });", + "foo({});", + "const opts = { a: 1, b: 2, c: 3, d: 4 }; foo(opts);", + "const x = { a: 1, b: 2, c: 3, d: 4 };", + "function f() { return { a: 1, b: 2, c: 3, d: 4 }; }", + "const arr = [{ a: 1, b: 2, c: 3, d: 4 }];", + "foo(1, 2, { a: 1, b: 2 });", + { code: "foo({ a: 1, b: 2, c: 3, d: 4 });", options: [{ minProperties: 5 }] }, + ], + invalid: [ + { + code: "foo({ a: 1, b: 2, c: 3, d: 4 });", + errors: [{ messageId: "tooLarge", data: { count: 4 } }], + }, + { + code: "new Widget({ a: 1, b: 2, c: 3, d: 4, e: 5 });", + errors: [{ messageId: "tooLarge", data: { count: 5 } }], + }, + { + code: "foo(1, { a: 1, b: 2, c: 3, d: 4 });", + errors: [{ messageId: "tooLarge" }], + }, + { + code: "foo({ a: 1, ...rest, c: 3, d: 4 });", + errors: [{ messageId: "tooLarge", data: { count: 4 } }], + }, + { + code: "foo({ a: 1, b: 2, c: 3 });", + options: [{ minProperties: 3 }], + errors: [{ messageId: "tooLarge", data: { count: 3 } }], + }, + { + code: "outer({ a: 1, b: 2, c: 3, d: 4 }, inner({ e: 5, f: 6, g: 7, h: 8 }));", + errors: [{ messageId: "tooLarge" }, { messageId: "tooLarge" }], + }, + ], +}); diff --git a/ui/litellm-dashboard/tests/eslint-rules/no-long-condition-chain.test.ts b/ui/litellm-dashboard/tests/eslint-rules/no-long-condition-chain.test.ts new file mode 100644 index 00000000000..4a7fb677190 --- /dev/null +++ b/ui/litellm-dashboard/tests/eslint-rules/no-long-condition-chain.test.ts @@ -0,0 +1,51 @@ +import { RuleTester } from "eslint"; +import rule from "../../scripts/eslint-rules/no-long-condition-chain.mjs"; + +const ruleTester = new RuleTester({ + languageOptions: { ecmaVersion: "latest", sourceType: "module" }, +}); + +ruleTester.run("no-long-condition-chain", rule as never, { + valid: [ + "const x = a && b && c;", + "const x = a || b || c;", + "const x = a && (b || c);", + "const x = a && b;", + "if (a || b || c) {}", + "const x = a ?? b ?? c;", + "const url = a ?? b ?? c ?? d;", + "const x = (a && b) ?? c ?? d;", + { code: "const x = a && b && c && d;", options: [{ minConditions: 5 }] }, + ], + invalid: [ + { + code: "const x = a && b && c && d;", + errors: [{ messageId: "tooMany", data: { count: 4 } }], + }, + { + code: "const x = a || b || c || d || e;", + errors: [{ messageId: "tooMany", data: { count: 5 } }], + }, + { + code: "const x = a && b || c && d;", + errors: [{ messageId: "tooMany", data: { count: 4 } }], + }, + { + code: "if (!a && !b && !c && !d) {}", + errors: [{ messageId: "tooMany", data: { count: 4 } }], + }, + { + code: "const x = a && (b || c);", + options: [{ minConditions: 3 }], + errors: [{ messageId: "tooMany", data: { count: 3 } }], + }, + { + code: "const x = (a && b && c && d) || (e && f && g && h);", + errors: [{ messageId: "tooMany", data: { count: 8 } }], + }, + { + code: "const x = (a && b && c && d) ?? fallback;", + errors: [{ messageId: "tooMany", data: { count: 4 } }], + }, + ], +});