SettingGeminiModel.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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. import Text from '@douyinfe/semi-ui/lib/es/typography/text.js';
  13. const GEMINI_SETTING_EXAMPLE = {
  14. default: 'OFF',
  15. HARM_CATEGORY_CIVIC_INTEGRITY: 'BLOCK_NONE',
  16. };
  17. const GEMINI_VERSION_EXAMPLE = {
  18. default: 'v1beta',
  19. };
  20. export default function SettingGeminiModel(props) {
  21. const { t } = useTranslation();
  22. const [loading, setLoading] = useState(false);
  23. const [inputs, setInputs] = useState({
  24. 'gemini.safety_settings': '',
  25. 'gemini.version_settings': '',
  26. 'gemini.supported_imagine_models': [],
  27. 'gemini.thinking_adapter_enabled': false,
  28. 'gemini.thinking_adapter_budget_tokens_percentage': 0.6,
  29. });
  30. const refForm = useRef();
  31. const [inputsRow, setInputsRow] = useState(inputs);
  32. function onSubmit() {
  33. const updateArray = compareObjects(inputs, inputsRow);
  34. if (!updateArray.length) return showWarning(t('你似乎并没有修改什么'));
  35. const requestQueue = updateArray.map((item) => {
  36. let value = '';
  37. if (typeof inputs[item.key] === 'boolean') {
  38. value = String(inputs[item.key]);
  39. } else {
  40. value = inputs[item.key];
  41. }
  42. return API.put('/api/option/', {
  43. key: item.key,
  44. value,
  45. });
  46. });
  47. setLoading(true);
  48. Promise.all(requestQueue)
  49. .then((res) => {
  50. if (requestQueue.length === 1) {
  51. if (res.includes(undefined)) return;
  52. } else if (requestQueue.length > 1) {
  53. if (res.includes(undefined))
  54. return showError(t('部分保存失败,请重试'));
  55. }
  56. showSuccess(t('保存成功'));
  57. props.refresh();
  58. })
  59. .catch(() => {
  60. showError(t('保存失败,请重试'));
  61. })
  62. .finally(() => {
  63. setLoading(false);
  64. });
  65. }
  66. useEffect(() => {
  67. const currentInputs = {};
  68. for (let key in props.options) {
  69. if (Object.keys(inputs).includes(key)) {
  70. currentInputs[key] = props.options[key];
  71. }
  72. }
  73. setInputs(currentInputs);
  74. setInputsRow(structuredClone(currentInputs));
  75. refForm.current.setValues(currentInputs);
  76. }, [props.options]);
  77. return (
  78. <>
  79. <Spin spinning={loading}>
  80. <Form
  81. values={inputs}
  82. getFormApi={(formAPI) => (refForm.current = formAPI)}
  83. style={{ marginBottom: 15 }}
  84. >
  85. <Form.Section text={t('Gemini设置')}>
  86. <Row>
  87. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  88. <Form.TextArea
  89. label={t('Gemini安全设置')}
  90. placeholder={
  91. t('为一个 JSON 文本,例如:') +
  92. '\n' +
  93. JSON.stringify(GEMINI_SETTING_EXAMPLE, null, 2)
  94. }
  95. field={'gemini.safety_settings'}
  96. extraText={t(
  97. 'default为默认设置,可单独设置每个分类的安全等级',
  98. )}
  99. autosize={{ minRows: 6, maxRows: 12 }}
  100. trigger='blur'
  101. stopValidateWithError
  102. rules={[
  103. {
  104. validator: (rule, value) => verifyJSON(value),
  105. message: t('不是合法的 JSON 字符串'),
  106. },
  107. ]}
  108. onChange={(value) =>
  109. setInputs({ ...inputs, 'gemini.safety_settings': value })
  110. }
  111. />
  112. </Col>
  113. </Row>
  114. <Row>
  115. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  116. <Form.TextArea
  117. label={t('Gemini版本设置')}
  118. placeholder={
  119. t('为一个 JSON 文本,例如:') +
  120. '\n' +
  121. JSON.stringify(GEMINI_VERSION_EXAMPLE, null, 2)
  122. }
  123. field={'gemini.version_settings'}
  124. extraText={t('default为默认设置,可单独设置每个模型的版本')}
  125. autosize={{ minRows: 6, maxRows: 12 }}
  126. trigger='blur'
  127. stopValidateWithError
  128. rules={[
  129. {
  130. validator: (rule, value) => verifyJSON(value),
  131. message: t('不是合法的 JSON 字符串'),
  132. },
  133. ]}
  134. onChange={(value) =>
  135. setInputs({ ...inputs, 'gemini.version_settings': value })
  136. }
  137. />
  138. </Col>
  139. </Row>
  140. <Row>
  141. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  142. <Form.TextArea
  143. field={'gemini.supported_imagine_models'}
  144. label={t('支持的图像模型')}
  145. placeholder={t('例如:') + '\n' + JSON.stringify(['gemini-2.0-flash-exp-image-generation'], null, 2)}
  146. onChange={(value) => setInputs({ ...inputs, 'gemini.supported_imagine_models': value })}
  147. />
  148. </Col>
  149. </Row>
  150. </Form.Section>
  151. <Form.Section text={t('Gemini思考适配设置')}>
  152. <Row>
  153. <Col span={16}>
  154. <Text>
  155. {t(
  156. "和Claude不同,默认情况下Gemini的思考模型会自动决定要不要思考,就算不开启适配模型也可以正常使用," +
  157. "-nothinking后缀(BudgetTokens=0,思考关闭)也会返回少量的思考token,这是gemini的特性," +
  158. "如果您需要计费,推荐设置无后缀模型价格按思考价格设置"
  159. )}
  160. </Text>
  161. </Col>
  162. </Row>
  163. <Row>
  164. <Col span={16}>
  165. <Form.Switch
  166. label={t('启用Gemini思考后缀适配')}
  167. field={'gemini.thinking_adapter_enabled'}
  168. extraText={"适配-thinking和-nothinking后缀"}
  169. onChange={(value) =>
  170. setInputs({
  171. ...inputs,
  172. 'gemini.thinking_adapter_enabled': value,
  173. })
  174. }
  175. />
  176. </Col>
  177. </Row>
  178. <Row>
  179. <Col span={16}>
  180. <Text>
  181. {t(
  182. 'Gemini思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比',
  183. )}
  184. </Text>
  185. </Col>
  186. </Row>
  187. <Row>
  188. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  189. <Form.InputNumber
  190. label={t('请求模型带-thinking后缀的BudgetTokens数(超出24576的部分将被忽略)')}
  191. field={'gemini.thinking_adapter_budget_tokens_percentage'}
  192. initValue={''}
  193. extraText={t('0.1-1之间的小数')}
  194. min={0.1}
  195. max={1}
  196. onChange={(value) =>
  197. setInputs({
  198. ...inputs,
  199. 'gemini.thinking_adapter_budget_tokens_percentage': value,
  200. })
  201. }
  202. />
  203. </Col>
  204. </Row>
  205. </Form.Section>
  206. <Row>
  207. <Button size='default' onClick={onSubmit}>
  208. {t('保存')}
  209. </Button>
  210. </Row>
  211. </Form>
  212. </Spin>
  213. </>
  214. );
  215. }