import React, { useEffect, useState, useRef } from 'react'; import { Button, Col, Form, Row, Spin } from '@douyinfe/semi-ui'; import { compareObjects, API, showError, showSuccess, showWarning, } from '../../../helpers'; import { useTranslation } from 'react-i18next'; export default function RequestRateLimit(props) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); const [inputs, setInputs] = useState({ ModelRequestRateLimitEnabled: false, ModelRequestRateLimitCount: -1, ModelRequestRateLimitSuccessCount: 1000, ModelRequestRateLimitDurationMinutes: 1, ModelRequestRateLimitGroup: '{}', // 添加新字段并设置默认值 }); const refForm = useRef(); const [inputsRow, setInputsRow] = useState(inputs); function onSubmit() { const updateArray = compareObjects(inputs, inputsRow); if (!updateArray.length) return showWarning(t('你似乎并没有修改什么')); const requestQueue = updateArray.map((item) => { let value = ''; if (typeof inputs[item.key] === 'boolean') { value = String(inputs[item.key]); } else { value = inputs[item.key]; } // 校验 ModelRequestRateLimitGroup 是否为有效的 JSON 对象字符串 if (item.key === 'ModelRequestRateLimitGroup') { try { JSON.parse(value); } catch (e) { showError(t('用户组速率限制配置不是有效的 JSON 格式!')); // 阻止请求发送 return Promise.reject('Invalid JSON format'); } } return API.put('/api/option/', { key: item.key, value, }); }); // 过滤掉无效的请求(例如,无效的 JSON) const validRequests = requestQueue.filter(req => req !== null && req !== undefined && typeof req.then === 'function'); if (validRequests.length === 0 && requestQueue.length > 0) { // 如果所有请求都被过滤掉了(因为 JSON 无效),则不继续执行 return; } setLoading(true); Promise.all(validRequests) .then((res) => { if (validRequests.length === 1) { if (res.includes(undefined)) return; } else if (validRequests.length > 1) { if (res.includes(undefined)) return showError(t('部分保存失败,请重试')); } showSuccess(t('保存成功')); props.refresh(); // 更新 inputsRow 以反映保存后的状态 setInputsRow(structuredClone(inputs)); }) .catch((error) => { // 检查是否是由于无效 JSON 导致的错误 if (error !== 'Invalid JSON format') { showError(t('保存失败,请重试')); } }) .finally(() => { setLoading(false); }); } useEffect(() => { const currentInputs = {}; for (let key in props.options) { if (Object.keys(inputs).includes(key)) { currentInputs[key] = props.options[key]; } } setInputs(currentInputs); setInputsRow(structuredClone(currentInputs)); // 检查 refForm.current 是否存在 if (refForm.current) { refForm.current.setValues(currentInputs); } }, [props.options]); // 依赖项保持不变,因为 inputs 状态的结构已固定 return ( <>
(refForm.current = formAPI)} style={{ marginBottom: 15 }} > { setInputs({ ...inputs, ModelRequestRateLimitEnabled: value, }); }} /> setInputs({ ...inputs, ModelRequestRateLimitDurationMinutes: String(value), }) } /> setInputs({ ...inputs, ModelRequestRateLimitCount: String(value), }) } /> setInputs({ ...inputs, ModelRequestRateLimitSuccessCount: String(value), }) } /> {/* 用户组速率限制配置项 */}

{t('说明:')}

  • {t('使用 JSON 对象格式,键为用户组名 (字符串),值为包含两个整数的数组 [总次数限制, 成功次数限制]。')}
  • {t('总次数限制: 周期内允许的总请求次数 (含失败),0 代表不限制。')}
  • {t('成功次数限制: 周期内允许的成功请求次数 (HTTP < 400),必须大于 0。')}
  • {t('此配置将优先于上方的全局限制设置。')}
  • {t('未在此处配置的用户组将使用全局限制。')}
  • {t('限制周期统一使用上方配置的“限制周期”值。')}
  • {t('输入无效的 JSON 将无法保存。')}
} autosize={{ minRows: 5, maxRows: 15 }} style={{ fontFamily: 'monospace' }} onChange={(value) => { setInputs({ ...inputs, ModelRequestRateLimitGroup: value, // 直接更新字符串值 }); }} />
); }