Skip to content
This repository was archived by the owner on Sep 7, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions public/logo/minimax.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/i18n/locales/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,15 @@
"prompt_description": "By customizing the Prompt, you can customize the behavior of OpenAI. $text $from $to $detect will be replaced with the text to be translated, source language, target language and detected language.",
"add": "Add Prompt"
},
"minimax": {
"title": "MiniMax",
"request_path": "Request Path",
"api_key": "Api Key",
"model": "Model",
"stream": "Stream Mode",
"prompt_description": "By customizing the Prompt, you can customize the behavior of MiniMax. $text $from $to $detect will be replaced with the text to be translated, source language, target language and detected language.",
"add": "Add Prompt"
},
"geminipro": {
"title": "Gemini Pro",
"request_path": "Request Path",
Expand Down
9 changes: 9 additions & 0 deletions src/i18n/locales/zh_CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,15 @@
"prompt_description": "通过自定义Prompt自定义OpenAI行为, $text $from $to $detect 将会被替换为 待翻译文本,源语言,目标语言和检测到的语言。",
"add": "添加 Prompt"
},
"minimax": {
"title": "MiniMax",
"request_path": "请求地址",
"api_key": "Api Key",
"model": "模型",
"stream": "流式输出",
"prompt_description": "通过自定义Prompt自定义MiniMax行为, $text $from $to $detect 将会被替换为 待翻译文本,源语言,目标语言和检测到的语言。",
"add": "添加 Prompt"
},
"geminipro": {
"title": "Gemini Pro",
"request_path": "请求地址",
Expand Down
2 changes: 2 additions & 0 deletions src/services/translate/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as _deepl from './deepl';
import * as _bing from './bing';
import * as _yandex from './yandex';
import * as _openai from './openai';
import * as _minimax from './minimax';
import * as _google from './google';
import * as _transmart from './transmart';
import * as _alibaba from './alibaba';
Expand All @@ -24,6 +25,7 @@ export const deepl = _deepl;
export const bing = _bing;
export const yandex = _yandex;
export const openai = _openai;
export const minimax = _minimax;
export const google = _google;
export const transmart = _transmart;
export const alibaba = _alibaba;
Expand Down
290 changes: 290 additions & 0 deletions src/services/translate/minimax/Config.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
import { Input, Button, Switch, Textarea } from '@nextui-org/react';
import { DropdownTrigger } from '@nextui-org/react';
import { MdDeleteOutline } from 'react-icons/md';
import { DropdownMenu } from '@nextui-org/react';
import { DropdownItem } from '@nextui-org/react';
import toast, { Toaster } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import { Dropdown } from '@nextui-org/react';
import { open } from '@tauri-apps/api/shell';
import React, { useState } from 'react';

import { useConfig } from '../../../hooks/useConfig';
import { useToastStyle } from '../../../hooks';
import { translate } from './index';
import { Language } from './index';
import { INSTANCE_NAME_CONFIG_KEY } from '../../../utils/service_instance';

// https://platform.minimax.io/docs/api-reference/text-openai-api
const availableModels = ['MiniMax-M3'];

// MiniMax requires temperature in (0, 1]; 0 is not allowed.
export const defaultRequestArguments = JSON.stringify({
temperature: 0.8,
top_p: 0.95,
});

export function Config(props) {
const { instanceKey, updateServiceList, onClose } = props;
const { t } = useTranslation();
const [serviceConfig, setServiceConfig] = useConfig(
instanceKey,
{
[INSTANCE_NAME_CONFIG_KEY]: t('services.translate.minimax.title'),
requestPath: 'https://api.minimax.io/v1/chat/completions',
model: 'MiniMax-M3',
apiKey: '',
stream: false,
promptList: [
{
role: 'system',
content:
'You are a professional translation engine, please translate the text into a colloquial, professional, elegant and fluent content, without the style of machine translation. You must only translate the text content, never interpret it.',
},
{ role: 'user', content: `Translate into $to:\n"""\n$text\n"""` },
],
requestArguments: defaultRequestArguments,
},
{ sync: false }
);

const [isLoading, setIsLoading] = useState(false);

const toastStyle = useToastStyle();

return (
serviceConfig !== null && (
<form
onSubmit={(e) => {
e.preventDefault();
setIsLoading(true);
translate('hello', Language.auto, Language.zh_cn, { config: serviceConfig }).then(
() => {
setIsLoading(false);
setServiceConfig(serviceConfig, true);
updateServiceList(instanceKey);
onClose();
},
(e) => {
setIsLoading(false);
toast.error(t('config.service.test_failed') + e.toString(), { style: toastStyle });
}
);
}}
>
<Toaster />
<div className='config-item'>
<Input
label={t('services.instance_name')}
labelPlacement='outside-left'
value={serviceConfig[INSTANCE_NAME_CONFIG_KEY]}
variant='bordered'
classNames={{
base: 'justify-between',
label: 'text-[length:--nextui-font-size-medium]',
mainWrapper: 'max-w-[50%]',
}}
onValueChange={(value) => {
setServiceConfig({
...serviceConfig,
[INSTANCE_NAME_CONFIG_KEY]: value,
});
}}
/>
</div>
<div className='config-item'>
<h3 className='my-auto'>{t('services.help')}</h3>
<Button
onPress={() => {
open('https://platform.minimax.io/docs/api-reference/text-openai-api');
}}
>
{t('services.help')}
</Button>
</div>
<div className='config-item'>
<Switch
isSelected={serviceConfig['stream']}
onValueChange={(value) => {
setServiceConfig({
...serviceConfig,
stream: value,
});
}}
classNames={{
base: 'flex flex-row-reverse justify-between w-full max-w-full',
}}
>
{t('services.translate.minimax.stream')}
</Switch>
</div>
<div className='config-item'>
<Input
label={t('services.translate.minimax.request_path')}
labelPlacement='outside-left'
value={serviceConfig['requestPath']}
variant='bordered'
classNames={{
base: 'justify-between',
label: 'text-[length:--nextui-font-size-medium]',
mainWrapper: 'max-w-[50%]',
}}
onValueChange={(value) => {
setServiceConfig({
...serviceConfig,
requestPath: value,
});
}}
/>
</div>
<div className='config-item'>
<Input
label={t('services.translate.minimax.api_key')}
labelPlacement='outside-left'
type='password'
value={serviceConfig['apiKey']}
variant='bordered'
classNames={{
base: 'justify-between',
label: 'text-[length:--nextui-font-size-medium]',
mainWrapper: 'max-w-[50%]',
}}
onValueChange={(value) => {
setServiceConfig({
...serviceConfig,
apiKey: value,
});
}}
/>
</div>
<div className='config-item'>
<h3 className='my-auto'>{t('services.translate.minimax.model')}</h3>
<Dropdown>
<DropdownTrigger>
<Button variant='bordered'>{serviceConfig.model}</Button>
</DropdownTrigger>
<DropdownMenu
autoFocus='first'
aria-label='model'
onAction={(key) => {
setServiceConfig({
...serviceConfig,
model: key,
});
}}
>
{availableModels.map((it) => (
<DropdownItem key={it}>{it}</DropdownItem>
))}
</DropdownMenu>
</Dropdown>
</div>

<h3 className='my-auto'>Prompt List</h3>
<p className='text-[10px] text-default-700'>{t('services.translate.minimax.prompt_description')}</p>

<div className='bg-content2 rounded-[10px] p-3'>
{serviceConfig.promptList &&
serviceConfig.promptList.map((prompt, index) => {
return (
<div className='config-item'>
<Textarea
label={prompt.role}
labelPlacement='outside'
variant='faded'
value={prompt.content}
placeholder={`Input Some ${prompt.role} Prompt`}
onValueChange={(value) => {
setServiceConfig({
...serviceConfig,
promptList: serviceConfig.promptList.map((p, i) => {
if (i === index) {
if (i === 0) {
return {
role: 'system',
content: value,
};
} else {
return {
role: index % 2 !== 0 ? 'user' : 'assistant',
content: value,
};
}
} else {
return p;
}
}),
});
}}
/>
<Button
isIconOnly
color='danger'
className='my-auto mx-1'
variant='flat'
onPress={() => {
setServiceConfig({
...serviceConfig,
promptList: serviceConfig.promptList.filter((_, i) => i !== index),
});
}}
>
<MdDeleteOutline className='text-[18px]' />
</Button>
</div>
);
})}
<Button
fullWidth
onPress={() => {
setServiceConfig({
...serviceConfig,
promptList: [
...serviceConfig.promptList,
{
role:
serviceConfig.promptList.length === 0
? 'system'
: serviceConfig.promptList.length % 2 === 0
? 'assistant'
: 'user',
content: '',
},
],
});
}}
>
{t('services.translate.minimax.add')}
</Button>
</div>
<br />

<h3 className='my-auto'>Request Arguments</h3>
<div className='config-item'>
<Textarea
label=''
labelPlacement='outside'
variant='faded'
value={serviceConfig['requestArguments']}
placeholder={`Input API Request Arguments`}
onValueChange={(value) => {
setServiceConfig({
...serviceConfig,
requestArguments: value,
});
}}
/>
</div>
<br />
<Button
type='submit'
isLoading={isLoading}
fullWidth
color='primary'
>
{t('common.save')}
</Button>
</form>
)
);
}
Loading