EditChannel.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. import React, { useEffect, useState } from 'react';
  2. import { Button, Form, Header, Message, Segment } from 'semantic-ui-react';
  3. import { useParams } from 'react-router-dom';
  4. import { API, showError, showInfo, showSuccess } from '../../helpers';
  5. import { CHANNEL_OPTIONS } from '../../constants';
  6. const EditChannel = () => {
  7. const params = useParams();
  8. const channelId = params.id;
  9. const isEdit = channelId !== undefined;
  10. const [loading, setLoading] = useState(isEdit);
  11. const originInputs = {
  12. name: '',
  13. type: 1,
  14. key: '',
  15. base_url: '',
  16. other: '',
  17. group: 'default',
  18. models: [],
  19. };
  20. const [batch, setBatch] = useState(false);
  21. const [inputs, setInputs] = useState(originInputs);
  22. const [modelOptions, setModelOptions] = useState([]);
  23. const [basicModels, setBasicModels] = useState([]);
  24. const [fullModels, setFullModels] = useState([]);
  25. const handleInputChange = (e, { name, value }) => {
  26. setInputs((inputs) => ({ ...inputs, [name]: value }));
  27. };
  28. const loadChannel = async () => {
  29. let res = await API.get(`/api/channel/${channelId}`);
  30. const { success, message, data } = res.data;
  31. if (success) {
  32. if (data.models === "") {
  33. data.models = []
  34. } else {
  35. data.models = data.models.split(",")
  36. }
  37. setInputs(data);
  38. } else {
  39. showError(message);
  40. }
  41. setLoading(false);
  42. };
  43. const fetchModels = async () => {
  44. try {
  45. let res = await API.get(`/api/channel/models`);
  46. setModelOptions(res.data.data.map((model) => ({
  47. key: model.id,
  48. text: model.id,
  49. value: model.id,
  50. })));
  51. setFullModels(res.data.data.map((model) => model.id));
  52. setBasicModels(res.data.data.filter((model) => !model.id.startsWith("gpt-4")).map((model) => model.id));
  53. } catch (error) {
  54. showError(error.message);
  55. }
  56. };
  57. useEffect(() => {
  58. if (isEdit) {
  59. loadChannel().then();
  60. }
  61. fetchModels().then();
  62. }, []);
  63. const submit = async () => {
  64. if (!isEdit && (inputs.name === '' || inputs.key === '')) {
  65. showInfo('请填写渠道名称和渠道密钥!');
  66. return;
  67. }
  68. let localInputs = inputs;
  69. if (localInputs.base_url.endsWith('/')) {
  70. localInputs.base_url = localInputs.base_url.slice(0, localInputs.base_url.length - 1);
  71. }
  72. if (localInputs.type === 3 && localInputs.other === '') {
  73. localInputs.other = '2023-03-15-preview';
  74. }
  75. let res;
  76. localInputs.models = localInputs.models.join(",")
  77. if (isEdit) {
  78. res = await API.put(`/api/channel/`, { ...localInputs, id: parseInt(channelId) });
  79. } else {
  80. res = await API.post(`/api/channel/`, localInputs);
  81. }
  82. const { success, message } = res.data;
  83. if (success) {
  84. if (isEdit) {
  85. showSuccess('渠道更新成功!');
  86. } else {
  87. showSuccess('渠道创建成功!');
  88. setInputs(originInputs);
  89. }
  90. } else {
  91. showError(message);
  92. }
  93. };
  94. return (
  95. <>
  96. <Segment loading={loading}>
  97. <Header as='h3'>{isEdit ? '更新渠道信息' : '创建新的渠道'}</Header>
  98. <Form autoComplete='new-password'>
  99. <Form.Field>
  100. <Form.Select
  101. label='类型'
  102. name='type'
  103. options={CHANNEL_OPTIONS}
  104. value={inputs.type}
  105. onChange={handleInputChange}
  106. />
  107. </Form.Field>
  108. {
  109. inputs.type === 3 && (
  110. <>
  111. <Message>
  112. 注意,<strong>模型部署名称必须和模型名称保持一致</strong>,因为 One API 会把请求体中的 model
  113. 参数替换为你的部署名称(模型名称中的点会被剔除),<a target='_blank'
  114. href='https://github.com/songquanpeng/one-api/issues/133?notification_referrer_id=NT_kwDOAmJSYrM2NjIwMzI3NDgyOjM5OTk4MDUw#issuecomment-1571602271'>图片演示</a>。
  115. </Message>
  116. <Form.Field>
  117. <Form.Input
  118. label='AZURE_OPENAI_ENDPOINT'
  119. name='base_url'
  120. placeholder={'请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com'}
  121. onChange={handleInputChange}
  122. value={inputs.base_url}
  123. autoComplete='new-password'
  124. />
  125. </Form.Field>
  126. <Form.Field>
  127. <Form.Input
  128. label='默认 API 版本'
  129. name='other'
  130. placeholder={'请输入默认 API 版本,例如:2023-03-15-preview,该配置可以被实际的请求查询参数所覆盖'}
  131. onChange={handleInputChange}
  132. value={inputs.other}
  133. autoComplete='new-password'
  134. />
  135. </Form.Field>
  136. </>
  137. )
  138. }
  139. {
  140. inputs.type === 8 && (
  141. <Form.Field>
  142. <Form.Input
  143. label='Base URL'
  144. name='base_url'
  145. placeholder={'请输入自定义渠道的 Base URL,例如:https://openai.justsong.cn'}
  146. onChange={handleInputChange}
  147. value={inputs.base_url}
  148. autoComplete='new-password'
  149. />
  150. </Form.Field>
  151. )
  152. }
  153. <Form.Field>
  154. <Form.Input
  155. label='名称'
  156. name='name'
  157. placeholder={'请输入名称'}
  158. onChange={handleInputChange}
  159. value={inputs.name}
  160. autoComplete='new-password'
  161. />
  162. </Form.Field>
  163. <Form.Field>
  164. <Form.Input
  165. label='分组'
  166. name='group'
  167. placeholder={'请输入分组'}
  168. onChange={handleInputChange}
  169. value={inputs.group}
  170. autoComplete='new-password'
  171. />
  172. </Form.Field>
  173. <Form.Field>
  174. <Form.Dropdown
  175. label='模型'
  176. placeholder={'请选择该通道所支持的模型'}
  177. name='models'
  178. fluid
  179. multiple
  180. selection
  181. onChange={handleInputChange}
  182. value={inputs.models}
  183. autoComplete='new-password'
  184. options={modelOptions}
  185. />
  186. </Form.Field>
  187. <div style={{ lineHeight: '40px', marginBottom: '12px'}}>
  188. <Button type={'button'} onClick={() => {
  189. handleInputChange(null, { name: 'models', value: basicModels });
  190. }}>填入基础模型</Button>
  191. <Button type={'button'} onClick={() => {
  192. handleInputChange(null, { name: 'models', value: fullModels });
  193. }}>填入所有模型</Button>
  194. </div>
  195. {
  196. batch ? <Form.Field>
  197. <Form.TextArea
  198. label='密钥'
  199. name='key'
  200. placeholder={'请输入密钥,一行一个'}
  201. onChange={handleInputChange}
  202. value={inputs.key}
  203. style={{ minHeight: 150, fontFamily: 'JetBrains Mono, Consolas' }}
  204. autoComplete='new-password'
  205. />
  206. </Form.Field> : <Form.Field>
  207. <Form.Input
  208. label='密钥'
  209. name='key'
  210. placeholder={'请输入密钥'}
  211. onChange={handleInputChange}
  212. value={inputs.key}
  213. autoComplete='new-password'
  214. />
  215. </Form.Field>
  216. }
  217. {
  218. !isEdit && (
  219. <Form.Checkbox
  220. checked={batch}
  221. label='批量创建'
  222. name='batch'
  223. onChange={() => setBatch(!batch)}
  224. />
  225. )
  226. }
  227. <Button positive onClick={submit}>提交</Button>
  228. </Form>
  229. </Segment>
  230. </>
  231. );
  232. };
  233. export default EditChannel;