ModelRatioSettings.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import React, { useEffect, useState, useRef } from 'react';
  2. import { Button, Col, Form, Popconfirm, Row, Space, 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 ModelRatioSettings(props) {
  13. const [loading, setLoading] = useState(false);
  14. const [inputs, setInputs] = useState({
  15. ModelPrice: '',
  16. ModelRatio: '',
  17. CacheRatio: '',
  18. CompletionRatio: '',
  19. });
  20. const refForm = useRef();
  21. const [inputsRow, setInputsRow] = useState(inputs);
  22. const { t } = useTranslation();
  23. async function onSubmit() {
  24. try {
  25. await refForm.current.validate().then(() => {
  26. const updateArray = compareObjects(inputs, inputsRow);
  27. if (!updateArray.length) return showWarning(t('你似乎并没有修改什么'));
  28. const requestQueue = updateArray.map((item) => {
  29. const value = typeof inputs[item.key] === 'boolean'
  30. ? String(inputs[item.key])
  31. : inputs[item.key];
  32. return API.put('/api/option/', { key: item.key, value });
  33. });
  34. setLoading(true);
  35. Promise.all(requestQueue)
  36. .then((res) => {
  37. if (res.includes(undefined)) {
  38. return showError(requestQueue.length > 1 ? t('部分保存失败,请重试') : t('保存失败'));
  39. }
  40. for (let i = 0; i < res.length; i++) {
  41. if (!res[i].data.success) {
  42. return showError(res[i].data.message);
  43. }
  44. }
  45. showSuccess(t('保存成功'));
  46. props.refresh();
  47. })
  48. .catch(error => {
  49. console.error('Unexpected error:', error);
  50. showError(t('保存失败,请重试'));
  51. })
  52. .finally(() => {
  53. setLoading(false);
  54. });
  55. }).catch(() => {
  56. showError(t('请检查输入'));
  57. });
  58. } catch (error) {
  59. showError(t('请检查输入'));
  60. console.error(error);
  61. }
  62. }
  63. async function resetModelRatio() {
  64. try {
  65. let res = await API.post(`/api/option/rest_model_ratio`);
  66. if (res.data.success) {
  67. showSuccess(res.data.message);
  68. props.refresh();
  69. } else {
  70. showError(res.data.message);
  71. }
  72. } catch (error) {
  73. showError(error);
  74. }
  75. }
  76. useEffect(() => {
  77. const currentInputs = {};
  78. for (let key in props.options) {
  79. if (Object.keys(inputs).includes(key)) {
  80. currentInputs[key] = props.options[key];
  81. }
  82. }
  83. setInputs(currentInputs);
  84. setInputsRow(structuredClone(currentInputs));
  85. refForm.current.setValues(currentInputs);
  86. }, [props.options]);
  87. return (
  88. <Spin spinning={loading}>
  89. <Form
  90. values={inputs}
  91. getFormApi={(formAPI) => (refForm.current = formAPI)}
  92. style={{ marginBottom: 15 }}
  93. >
  94. <Form.Section>
  95. <Row gutter={16}>
  96. <Col xs={24} sm={16}>
  97. <Form.TextArea
  98. label={t('模型固定价格')}
  99. extraText={t('一次调用消耗多少刀,优先级大于模型倍率')}
  100. placeholder={t('为一个 JSON 文本,键为模型名称,值为一次调用消耗多少刀,比如 "gpt-4-gizmo-*": 0.1,一次消耗0.1刀')}
  101. field={'ModelPrice'}
  102. autosize={{ minRows: 6, maxRows: 12 }}
  103. trigger='blur'
  104. stopValidateWithError
  105. rules={[
  106. {
  107. validator: (rule, value) => verifyJSON(value),
  108. message: '不是合法的 JSON 字符串'
  109. }
  110. ]}
  111. onChange={(value) => setInputs({ ...inputs, ModelPrice: value })}
  112. />
  113. </Col>
  114. </Row>
  115. <Row gutter={16}>
  116. <Col xs={24} sm={16}>
  117. <Form.TextArea
  118. label={t('模型倍率')}
  119. placeholder={t('为一个 JSON 文本,键为模型名称,值为倍率')}
  120. field={'ModelRatio'}
  121. autosize={{ minRows: 6, maxRows: 12 }}
  122. trigger='blur'
  123. stopValidateWithError
  124. rules={[
  125. {
  126. validator: (rule, value) => verifyJSON(value),
  127. message: '不是合法的 JSON 字符串'
  128. }
  129. ]}
  130. onChange={(value) => setInputs({ ...inputs, ModelRatio: value })}
  131. />
  132. </Col>
  133. </Row>
  134. <Row gutter={16}>
  135. <Col xs={24} sm={16}>
  136. <Form.TextArea
  137. label={t('提示缓存倍率')}
  138. placeholder={t('为一个 JSON 文本,键为模型名称,值为倍率')}
  139. field={'CacheRatio'}
  140. autosize={{ minRows: 6, maxRows: 12 }}
  141. trigger='blur'
  142. stopValidateWithError
  143. rules={[
  144. {
  145. validator: (rule, value) => verifyJSON(value),
  146. message: '不是合法的 JSON 字符串'
  147. }
  148. ]}
  149. onChange={(value) => setInputs({ ...inputs, CacheRatio: value })}
  150. />
  151. </Col>
  152. </Row>
  153. <Row gutter={16}>
  154. <Col xs={24} sm={16}>
  155. <Form.TextArea
  156. label={t('模型补全倍率(仅对自定义模型有效)')}
  157. extraText={t('仅对自定义模型有效')}
  158. placeholder={t('为一个 JSON 文本,键为模型名称,值为倍率')}
  159. field={'CompletionRatio'}
  160. autosize={{ minRows: 6, maxRows: 12 }}
  161. trigger='blur'
  162. stopValidateWithError
  163. rules={[
  164. {
  165. validator: (rule, value) => verifyJSON(value),
  166. message: '不是合法的 JSON 字符串'
  167. }
  168. ]}
  169. onChange={(value) => setInputs({ ...inputs, CompletionRatio: value })}
  170. />
  171. </Col>
  172. </Row>
  173. </Form.Section>
  174. </Form>
  175. <Space>
  176. <Button onClick={onSubmit}>{t('保存模型倍率设置')}</Button>
  177. <Popconfirm
  178. title={t('确定重置模型倍率吗?')}
  179. content={t('此修改将不可逆')}
  180. okType={'danger'}
  181. position={'top'}
  182. onConfirm={resetModelRatio}
  183. >
  184. <Button type={'danger'}>{t('重置模型倍率')}</Button>
  185. </Popconfirm>
  186. </Space>
  187. </Spin>
  188. );
  189. }