SettingsRequestRateLimit.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import React, { useEffect, useState, useRef } from 'react';
  2. import { Button, Col, Form, Row, Spin } from '@douyinfe/semi-ui';
  3. import {
  4. compareObjects,
  5. API,
  6. showError,
  7. showSuccess,
  8. showWarning,
  9. verifyJSON,
  10. } from '../../../helpers';
  11. import { useTranslation } from 'react-i18next';
  12. export default function RequestRateLimit(props) {
  13. const { t } = useTranslation();
  14. const [loading, setLoading] = useState(false);
  15. const [inputs, setInputs] = useState({
  16. ModelRequestRateLimitEnabled: false,
  17. ModelRequestRateLimitCount: -1,
  18. ModelRequestRateLimitSuccessCount: 1000,
  19. ModelRequestRateLimitDurationMinutes: 1,
  20. ModelRequestRateLimitGroup: '',
  21. });
  22. const refForm = useRef();
  23. const [inputsRow, setInputsRow] = useState(inputs);
  24. function onSubmit() {
  25. const updateArray = compareObjects(inputs, inputsRow);
  26. if (!updateArray.length) return showWarning(t('你似乎并没有修改什么'));
  27. const requestQueue = updateArray.map((item) => {
  28. let value = '';
  29. if (typeof inputs[item.key] === 'boolean') {
  30. value = String(inputs[item.key]);
  31. } else {
  32. value = inputs[item.key];
  33. }
  34. return API.put('/api/option/', {
  35. key: item.key,
  36. value,
  37. });
  38. });
  39. setLoading(true);
  40. Promise.all(requestQueue)
  41. .then((res) => {
  42. if (requestQueue.length === 1) {
  43. if (res.includes(undefined)) return;
  44. } else if (requestQueue.length > 1) {
  45. if (res.includes(undefined))
  46. return showError(t('部分保存失败,请重试'));
  47. }
  48. for (let i = 0; i < res.length; i++) {
  49. if (!res[i].data.success) {
  50. return showError(res[i].data.message);
  51. }
  52. }
  53. showSuccess(t('保存成功'));
  54. props.refresh();
  55. })
  56. .catch(() => {
  57. showError(t('保存失败,请重试'));
  58. })
  59. .finally(() => {
  60. setLoading(false);
  61. });
  62. }
  63. useEffect(() => {
  64. const currentInputs = {};
  65. for (let key in props.options) {
  66. if (Object.keys(inputs).includes(key)) {
  67. currentInputs[key] = props.options[key];
  68. }
  69. }
  70. setInputs(currentInputs);
  71. setInputsRow(structuredClone(currentInputs));
  72. refForm.current.setValues(currentInputs);
  73. }, [props.options]);
  74. return (
  75. <>
  76. <Spin spinning={loading}>
  77. <Form
  78. values={inputs}
  79. getFormApi={(formAPI) => (refForm.current = formAPI)}
  80. style={{ marginBottom: 15 }}
  81. >
  82. <Form.Section text={t('模型请求速率限制')}>
  83. <Row gutter={16}>
  84. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  85. <Form.Switch
  86. field={'ModelRequestRateLimitEnabled'}
  87. label={t('启用用户模型请求速率限制(可能会影响高并发性能)')}
  88. size='default'
  89. checkedText='|'
  90. uncheckedText='〇'
  91. onChange={(value) => {
  92. setInputs({
  93. ...inputs,
  94. ModelRequestRateLimitEnabled: value,
  95. });
  96. }}
  97. />
  98. </Col>
  99. </Row>
  100. <Row>
  101. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  102. <Form.InputNumber
  103. label={t('限制周期')}
  104. step={1}
  105. min={0}
  106. suffix={t('分钟')}
  107. extraText={t('频率限制的周期(分钟)')}
  108. field={'ModelRequestRateLimitDurationMinutes'}
  109. onChange={(value) =>
  110. setInputs({
  111. ...inputs,
  112. ModelRequestRateLimitDurationMinutes: String(value),
  113. })
  114. }
  115. />
  116. </Col>
  117. </Row>
  118. <Row>
  119. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  120. <Form.InputNumber
  121. label={t('用户每周期最多请求次数')}
  122. step={1}
  123. min={0}
  124. suffix={t('次')}
  125. extraText={t('包括失败请求的次数,0代表不限制')}
  126. field={'ModelRequestRateLimitCount'}
  127. onChange={(value) =>
  128. setInputs({
  129. ...inputs,
  130. ModelRequestRateLimitCount: String(value),
  131. })
  132. }
  133. />
  134. </Col>
  135. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  136. <Form.InputNumber
  137. label={t('用户每周期最多请求完成次数')}
  138. step={1}
  139. min={1}
  140. suffix={t('次')}
  141. extraText={t('只包括请求成功的次数')}
  142. field={'ModelRequestRateLimitSuccessCount'}
  143. onChange={(value) =>
  144. setInputs({
  145. ...inputs,
  146. ModelRequestRateLimitSuccessCount: String(value),
  147. })
  148. }
  149. />
  150. </Col>
  151. </Row>
  152. <Row>
  153. <Col xs={24} sm={16}>
  154. <Form.TextArea
  155. label={t('分组速率限制')}
  156. placeholder={t(
  157. '{\n "default": [200, 100],\n "vip": [0, 1000]\n}',
  158. )}
  159. field={'ModelRequestRateLimitGroup'}
  160. autosize={{ minRows: 5, maxRows: 15 }}
  161. trigger='blur'
  162. stopValidateWithError
  163. rules={[
  164. {
  165. validator: (rule, value) => verifyJSON(value),
  166. message: t('不是合法的 JSON 字符串'),
  167. },
  168. ]}
  169. extraText={
  170. <div>
  171. <p style={{ marginBottom: -15 }}>{t('说明:')}</p>
  172. <ul>
  173. <li>{t('使用 JSON 对象格式,格式为:{"组名": [最多请求次数, 最多请求完成次数]}')}</li>
  174. <li>{t('示例:{"default": [200, 100], "vip": [0, 1000]}。')}</li>
  175. <li>{t('[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。')}</li>
  176. <li>{t('分组速率配置优先级高于全局速率限制。')}</li>
  177. <li>{t('限制周期统一使用上方配置的“限制周期”值。')}</li>
  178. </ul>
  179. </div>
  180. }
  181. onChange={(value) => {
  182. setInputs({ ...inputs, ModelRequestRateLimitGroup: value });
  183. }}
  184. />
  185. </Col>
  186. </Row>
  187. <Row>
  188. <Button size='default' onClick={onSubmit}>
  189. {t('保存模型速率限制')}
  190. </Button>
  191. </Row>
  192. </Form.Section>
  193. </Form>
  194. </Spin>
  195. </>
  196. );
  197. }