/* Copyright (C) 2023-2026 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { zodResolver } from '@hookform/resolvers/zod' import { Code2, Palette } from 'lucide-react' import { useEffect, useState } from 'react' import { useForm } from 'react-hook-form' import { useTranslation } from 'react-i18next' import * as z from 'zod' import { JsonCodeEditor } from '@/components/json-code-editor' import { Button } from '@/components/ui/button' import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from '@/components/ui/form' import { Input } from '@/components/ui/input' import { Switch } from '@/components/ui/switch' import { SettingsForm, SettingsSwitchContent, SettingsSwitchItem, } from '../components/settings-form-layout' import { SettingsPageFormActions } from '../components/settings-page-context' import { SettingsSection } from '../components/settings-section' import { useUpdateOption } from '../hooks/use-update-option' import { RateLimitVisualEditor } from './rate-limit-visual-editor' const isValidJSON = (value: string | undefined) => { if (!value || value.trim() === '') return true try { const parsed = JSON.parse(value) if (typeof parsed !== 'object' || Array.isArray(parsed)) { return false } for (const [, val] of Object.entries(parsed)) { if (!Array.isArray(val) || val.length !== 2) return false if (typeof val[0] !== 'number' || typeof val[1] !== 'number') return false if (val[0] < 0 || val[1] < 1) return false if (val[0] > 2147483647 || val[1] > 2147483647) return false } return true } catch { return false } } const createRateLimitSchema = (t: (key: string) => string) => z.object({ ModelRequestRateLimitEnabled: z.boolean(), ModelRequestRateLimitDurationMinutes: z.number().min(0), ModelRequestRateLimitCount: z.number().min(0).max(100000000), ModelRequestRateLimitSuccessCount: z.number().min(1).max(100000000), ModelRequestRateLimitGroup: z .string() .optional() .refine(isValidJSON, { message: t('Invalid JSON format or values out of allowed range'), }), }) type RateLimitFormValues = z.infer> type RateLimitSectionProps = { defaultValues: RateLimitFormValues } export function RateLimitSection({ defaultValues }: RateLimitSectionProps) { const { t } = useTranslation() const updateOption = useUpdateOption() const [useVisualEditor, setUseVisualEditor] = useState(true) const rateLimitSchema = createRateLimitSchema(t) const form = useForm({ resolver: zodResolver(rateLimitSchema), mode: 'onChange', // Enable real-time validation defaultValues, }) useEffect(() => { form.reset(defaultValues) }, [defaultValues, form]) const onSubmit = async (values: RateLimitFormValues) => { const updates = Object.entries(values).filter( ([key, value]) => value !== defaultValues[key as keyof RateLimitFormValues] ) for (const [key, value] of updates) { await updateOption.mutateAsync({ key, value: value ?? '' }) } } return (
( {t('Enable rate limiting')} {t( 'This controls model request rate limiting. Web/API route throttling is configured by environment variables and may still return 429.' )} )} />
( {t('Limit period')}
field.onChange(parseInt(e.target.value) || 0) } /> {t('minutes')}
{t('Time window for rate limiting')}
)} /> ( {t('Max requests per period')}
field.onChange(parseInt(e.target.value) || 0) } /> {t('times')}
{t('Including failed requests, 0 = unlimited')}
)} /> ( {t('Max successful requests')}
field.onChange(parseInt(e.target.value) || 1) } /> {t('times')}
{t('Only successful requests')}
)} />
(
{t('Group-based rate limits')}
{useVisualEditor ? ( ) : ( )} {!useVisualEditor && (

{t('Format:')}

  • {t('JSON object:')}{' '} {`{"groupName": [maxRequests, maxSuccess]}`}
  • {t('Example:')}{' '} {`{"default": [200, 100], "vip": [0, 1000]}`}
  • {t( 'maxRequests ≥ 0, maxSuccess ≥ 1, both ≤ 2,147,483,647' )}
  • {t( 'Group config overrides global limits, shares the same period' )}
)}
)} />
) }