import { useState, useEffect, useMemo } from "react"; import Stack from "@mui/material/Stack"; import TextField from "@mui/material/TextField"; import Button from "@mui/material/Button"; import LoadingButton from "@mui/lab/LoadingButton"; import MenuItem from "@mui/material/MenuItem"; import FormControlLabel from "@mui/material/FormControlLabel"; import Switch from "@mui/material/Switch"; import { useI18n } from "../../hooks/I18n"; import Typography from "@mui/material/Typography"; import Accordion from "@mui/material/Accordion"; import AccordionSummary from "@mui/material/AccordionSummary"; import AccordionDetails from "@mui/material/AccordionDetails"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import AddIcon from "@mui/icons-material/Add"; import Alert from "@mui/material/Alert"; import Menu from "@mui/material/Menu"; import Grid from "@mui/material/Grid"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; import { useAlert } from "../../hooks/Alert"; import { useApiList, useApiItem } from "../../hooks/Api"; import { useConfirm } from "../../hooks/Confirm"; import { apiTranslate } from "../../apis"; import Box from "@mui/material/Box"; import { limitNumber, limitFloat } from "../../libs/utils"; import ReusableAutocomplete from "./ReusableAutocomplete"; import ShowMoreButton from "./ShowMoreButton"; import { OPT_TRANS_DEEPLX, OPT_TRANS_OLLAMA, OPT_TRANS_CUSTOMIZE, OPT_TRANS_NIUTRANS, OPT_TRANS_BUILTINAI, DEFAULT_FETCH_LIMIT, DEFAULT_FETCH_INTERVAL, DEFAULT_HTTP_TIMEOUT, DEFAULT_BATCH_INTERVAL, DEFAULT_BATCH_SIZE, DEFAULT_BATCH_LENGTH, DEFAULT_CONTEXT_SIZE, OPT_ALL_TYPES, API_SPE_TYPES, BUILTIN_STONES, BUILTIN_PLACEHOLDERS, BUILTIN_PLACETAGS, } from "../../config"; function TestButton({ apiSlug, api }) { const i18n = useI18n(); const alert = useAlert(); const [loading, setLoading] = useState(false); const handleApiTest = async () => { try { setLoading(true); const [text] = await apiTranslate({ apiSlug, text: "hello world", fromLang: "en", toLang: "zh-CN", apiSetting: api, useCache: false, }); if (!text) { throw new Error("empty result"); } alert.success(i18n("test_success")); } catch (err) { // alert.error(`${i18n("test_failed")}: ${err.message}`); let msg = err.message; try { msg = JSON.stringify(JSON.parse(err.message), null, 2); } catch (err) { // skip } alert.error( <>
{i18n("test_failed")}
{msg === err.message ? (
{msg}
) : (
              {msg}
            
)} ); } finally { setLoading(false); } }; return ( {i18n("click_test")} ); } function ApiFields({ apiSlug, isUserApi, deleteApi }) { const { api, update, reset } = useApiItem(apiSlug); const i18n = useI18n(); const [formData, setFormData] = useState({}); const [isModified, setIsModified] = useState(false); const [showMore, setShowMore] = useState(false); const confirm = useConfirm(); useEffect(() => { if (api) { setFormData(api); } }, [api]); useEffect(() => { if (!api) return; const hasChanged = JSON.stringify(api) !== JSON.stringify(formData); setIsModified(hasChanged); }, [api, formData]); const handleChange = (e) => { let { name, value, type, checked } = e.target; if (type === "checkbox" || type === "switch") { value = checked; } // if (value === "true") value = true; // if (value === "false") value = false; switch (name) { case "fetchLimit": value = limitNumber(value, 1, 100); break; case "fetchInterval": value = limitNumber(value, 0, 5000); break; case "httpTimeout": value = limitNumber(value, 5000, 60000); break; case "temperature": value = limitFloat(value, 0, 2); break; case "maxTokens": value = limitNumber(value, 0, 2 ** 15); break; case "batchInterval": value = limitNumber(value, 100, 10000); break; case "batchSize": value = limitNumber(value, 1, 100); break; case "batchLength": value = limitNumber(value, 1000, 100000); break; case "contextSize": value = limitNumber(value, 1, 20); break; default: } setFormData((prevData) => ({ ...prevData, [name]: value, })); }; const handleSave = () => { // 过滤掉 api 对象中不存在的字段 // const updatedFields = Object.keys(formData).reduce((acc, key) => { // if (api && Object.keys(api).includes(key)) { // acc[key] = formData[key]; // } // return acc; // }, {}); // update(updatedFields); update(formData); }; const handleReset = () => { reset(); }; const handleDelete = async () => { const isConfirmed = await confirm({ confirmText: i18n("delete"), cancelText: i18n("cancel"), }); if (isConfirmed) { deleteApi(apiSlug); } }; const { url = "", key = "", model = "", apiType, systemPrompt = "", // userPrompt = "", customHeader = "", customBody = "", think = false, thinkIgnore = "", fetchLimit = DEFAULT_FETCH_LIMIT, fetchInterval = DEFAULT_FETCH_INTERVAL, httpTimeout = DEFAULT_HTTP_TIMEOUT, dictNo = "", memoryNo = "", reqHook = "", resHook = "", temperature = 0, maxTokens = 256, apiName = "", isDisabled = false, useBatchFetch = false, batchInterval = DEFAULT_BATCH_INTERVAL, batchSize = DEFAULT_BATCH_SIZE, batchLength = DEFAULT_BATCH_LENGTH, useContext = false, contextSize = DEFAULT_CONTEXT_SIZE, tone = "neutral", placeholder = BUILTIN_PLACEHOLDERS[0], placetag = BUILTIN_PLACETAGS[0], // aiTerms = false, } = formData; const keyHelper = useMemo( () => (API_SPE_TYPES.mulkeys.has(apiType) ? i18n("mulkeys_help") : ""), [apiType, i18n] ); return ( {!API_SPE_TYPES.machine.has(apiType) && apiType !== OPT_TRANS_BUILTINAI && ( <> )} {API_SPE_TYPES.ai.has(apiType) && ( <> {/* todo: 改成 ReusableAutocomplete 可选择和填写模型 */} {/* */} )} {apiType === OPT_TRANS_OLLAMA && ( <> {i18n("nothink")} {i18n("think")} )} {apiType === OPT_TRANS_NIUTRANS && ( <> )} {apiType === OPT_TRANS_CUSTOMIZE && ( <> {i18n("request_hook_helper")} } /> {i18n("response_hook_helper")} } /> )} {API_SPE_TYPES.batch.has(api.apiType) && ( {i18n("disable")} {i18n("enable")} )} {API_SPE_TYPES.context.has(api.apiType) && ( <> {" "} {i18n("disable")} {i18n("enable")} {" "} )} {showMore && ( <> {BUILTIN_PLACEHOLDERS.map((item) => ( {item} ))} {BUILTIN_PLACETAGS.map((item) => ( {`<${item}>`} ))} {apiType !== OPT_TRANS_BUILTINAI && ( <> {" "} )} {apiType !== OPT_TRANS_CUSTOMIZE && apiType !== OPT_TRANS_BUILTINAI && ( <> {i18n("request_hook_helper")} } /> {i18n("response_hook_helper")} } /> )} )} {isUserApi && ( )} } label={i18n("is_disabled")} /> {/* {apiType === OPT_TRANS_CUSTOMIZE &&
{i18n("custom_api_help")}
} */}
); } function ApiAccordion({ api, isUserApi, deleteApi }) { const [expanded, setExpanded] = useState(false); const handleChange = (e) => { setExpanded((pre) => !pre); }; return ( }> {`[${api.apiType}] ${api.apiName}`} {expanded && ( )} ); } export default function Apis() { const i18n = useI18n(); const { userApis, builtinApis, addApi, deleteApi } = useApiList(); const apiTypes = useMemo( () => OPT_ALL_TYPES.map((type) => ({ type, label: type, })), [] ); const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); const handleClick = (event) => { setAnchorEl(event.currentTarget); }; const handleClose = () => { setAnchorEl(null); }; const handleMenuItemClick = (apiType) => { addApi(apiType); handleClose(); }; return ( {i18n("about_api")}
{i18n("about_api_2")}
{apiTypes.map((apiOption) => ( handleMenuItemClick(apiOption.type)} > {apiOption.label} ))} {userApis.map((api) => ( ))} {builtinApis.map((api) => ( ))}
); }