SettingGeminiModel.js 8.0 KB

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