diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index f497f9cbdf..d6ce2d79ff 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -1029,7 +1029,7 @@ "turbo": "Turbo" }, "req_error_no_balance": "Please check the validity of the token", - "req_error_text": "Operation failed. Please try again. Avoid using 'copyrighted' or 'sensitive' words in your prompt.", + "req_error_text": "The server is busy or the prompt contains \"copyrighted\" or \"sensitive\" terms. Please try again.", "req_error_token": "Please check the validity of the token", "required_field": "Required field", "seed": "Seed", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 98b410cc14..20944b1b9e 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -1029,7 +1029,7 @@ "turbo": "高速" }, "req_error_no_balance": "トークンの有効性を確認してください", - "req_error_text": "実行に失敗しました。もう一度お試しください。プロンプトに「著作権用語」や「センシティブな用語」を含めないでください。", + "req_error_text": "サーバーが混雑しているか、プロンプトに「著作権用語」または「敏感な用語」が含まれています。もう一度お試しください。", "req_error_token": "トークンの有効性を確認してください", "required_field": "必須項目", "seed": "シード", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 553deecd60..94be920dee 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -1029,7 +1029,7 @@ "turbo": "Быстро" }, "req_error_no_balance": "Пожалуйста, проверьте действительность токена", - "req_error_text": "Операция не удалась, повторите попытку. Пожалуйста, избегайте защищенных авторским правом терминов и конфиденциальных слов в запросах.", + "req_error_text": "Сервер перегружен или в запросе обнаружены «авторские» либо «чувствительные» слова. Пожалуйста, повторите попытку.", "req_error_token": "Пожалуйста, проверьте действительность токена", "required_field": "Обязательное поле", "seed": "Ключ генерации", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 2fdc939755..2cf1a3f88c 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -1029,7 +1029,7 @@ "turbo": "快速" }, "req_error_no_balance": "请检查令牌有效性", - "req_error_text": "运行失败,请重试。提示词避免 \"版权词\" 和 \"敏感词\" 哦。", + "req_error_text": "服务器繁忙或提示词出现 \"版权词\" 和 \"敏感词\" ,请重试。", "req_error_token": "请检查令牌有效性", "required_field": "必填项", "seed": "随机种子", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 756010e65f..30c746b0de 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -1029,7 +1029,7 @@ "turbo": "快速" }, "req_error_no_balance": "請檢查令牌的有效性", - "req_error_text": "运行失败,请重试。提示词避免 “版权词” 和” 敏感词” 哦。", + "req_error_text": "伺服器繁忙或提示詞中出現「版權詞」或「敏感詞」,請重試。", "req_error_token": "請檢查令牌的有效性", "required_field": "必填欄位", "seed": "隨機種子", diff --git a/src/renderer/src/pages/paintings/DmxapiPage.tsx b/src/renderer/src/pages/paintings/DmxapiPage.tsx index af24801ea9..18547fed0c 100644 --- a/src/renderer/src/pages/paintings/DmxapiPage.tsx +++ b/src/renderer/src/pages/paintings/DmxapiPage.tsx @@ -1,8 +1,7 @@ import { PlusOutlined, RedoOutlined } from '@ant-design/icons' import DMXAPIToImg from '@renderer/assets/images/providers/DMXAPI-to-img.webp' import { Navbar, NavbarCenter, NavbarRight } from '@renderer/components/app/Navbar' -import { VStack } from '@renderer/components/Layout' -import { HStack } from '@renderer/components/Layout' +import { HStack, VStack } from '@renderer/components/Layout' import Scrollbar from '@renderer/components/Scrollbar' import { isMac } from '@renderer/config/constant' import { getProviderLogo } from '@renderer/config/providers' @@ -19,8 +18,7 @@ import { DmxapiPainting } from '@types' import { Avatar, Button, Input, Radio, Segmented, Select, Switch, Tooltip } from 'antd' import TextArea from 'antd/es/input/TextArea' import { Info } from 'lucide-react' -import React, { FC } from 'react' -import { useEffect, useRef, useState } from 'react' +import React, { FC, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' @@ -32,14 +30,13 @@ import Artboard from './components/Artboard' import ImageUploader from './components/ImageUploader' import PaintingsList from './components/PaintingsList' import { + ALL_MODELS, COURSE_URL, DEFAULT_PAINTING, - IMAGE_EDIT_MODELS, - IMAGE_MERGE_MODELS, IMAGE_SIZES, + MODEL_GROUPS, MODEOPTIONS, - STYLE_TYPE_OPTIONS, - TEXT_TO_IMAGES_MODELS + STYLE_TYPE_OPTIONS } from './config/DmxapiConfig' const generateRandomSeed = () => Math.floor(Math.random() * 1000000).toString() @@ -88,24 +85,15 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => { const getModelOptions = (mode: generationModeType) => { if (mode === generationModeType.EDIT) { - return IMAGE_EDIT_MODELS.map((model) => ({ - label: model.name, - value: model.id - })) + return MODEL_GROUPS.IMAGE_EDIT } if (mode === generationModeType.MERGE) { - return IMAGE_MERGE_MODELS.map((model) => ({ - label: model.name, - value: model.id - })) + return MODEL_GROUPS.IMAGE_MERGE } // 默认情况或其它模式下的选项 - return TEXT_TO_IMAGES_MODELS.map((model) => ({ - label: model.name, - value: model.id - })) + return MODEL_GROUPS.TEXT_TO_IMAGES } const [modelOptions, setModelOptions] = useState(() => { @@ -126,13 +114,22 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => { const getNewPainting = (params?: Partial) => { clearImages() const generationMode = params?.generationMode || painting?.generationMode || MODEOPTIONS[0].value - const modelOptionsList = getModelOptions(generationMode as generationModeType) + const modelGroups = getModelOptions(generationMode as generationModeType) + // 获取第一个非空分组的第一个模型 + let firstModel = '' + for (const provider of Object.keys(modelGroups)) { + if (modelGroups[provider].length > 0) { + firstModel = modelGroups[provider][0].id + break + } + } + return { ...DEFAULT_PAINTING, id: uuid(), seed: generateRandomSeed(), generationMode, - model: modelOptionsList[0]?.value, + model: firstModel, ...params } } @@ -148,7 +145,7 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => { } const onSelectModel = (modelId: string) => { - const model = TEXT_TO_IMAGES_MODELS.find((m) => m.id === modelId) + const model = ALL_MODELS.find((m) => m.id === modelId) if (model) { updatePaintingState({ model: modelId }) } @@ -222,9 +219,17 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => { const onGenerationModeChange = (v: generationModeType) => { clearImages() - const newModelOptions = getModelOptions(v) - setModelOptions(newModelOptions) - const firstModel = newModelOptions[0]?.value + const newModelGroups = getModelOptions(v) + setModelOptions(newModelGroups) + + // 获取第一个非空分组的第一个模型 + let firstModel = '' + for (const provider of Object.keys(newModelGroups)) { + if (newModelGroups[provider].length > 0) { + firstModel = newModelGroups[provider][0].id + break + } + } // 如果有urls,创建新的painting if (Array.isArray(painting.urls) && painting.urls.length > 0) { @@ -376,13 +381,25 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => { const data = await response.json() - if ( - painting.generationMode && - [generationModeType.EDIT, generationModeType.MERGE].includes(painting.generationMode) - ) { - return data.data.map((item: { b64_json: string }) => 'data:image/png;base64,' + item.b64_json) - } - return data.data.map((item: { url: string }) => item.url) + // if ( + // painting.generationMode && + // [generationModeType.EDIT, generationModeType.MERGE].includes(painting.generationMode) + // ) { + // return data.data.map((item: { b64_json: string }) => 'data:image/png;base64,' + item.b64_json) + // } + // return data.data.map((item: { url: string }) => item.url) + + return data.data.map((item: { url: string; b64_json: string }) => { + if (item.b64_json) { + return 'data:image/png;base64,' + item.b64_json + } + + if (item.url) { + return item.url + } + + return '' + }) } // 下载图像函数 @@ -603,11 +620,11 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => { return (
- 正在用 OpenAI 官方 gpt-image-1 模型生产, + 正在用使用官方的模型生产,
预计等待2~5分钟效果最好,
- 本次消耗金额请到DMIAPI后台日志查看 + 本次消耗金额请到DMXAPI后台日志查看
) @@ -699,7 +716,20 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => { )} {t('common.model')} - + {Object.entries(modelOptions).map(([provider, models]) => { + if (models.length === 0) return null + return ( + + {models.map((model) => ( + + {model.name} + + ))} + + ) + })} + {painting.generationMode === generationModeType.GENERATION && ( <> diff --git a/src/renderer/src/pages/paintings/config/DmxapiConfig.ts b/src/renderer/src/pages/paintings/config/DmxapiConfig.ts index 2f6efb845c..3b95b9ae67 100644 --- a/src/renderer/src/pages/paintings/config/DmxapiConfig.ts +++ b/src/renderer/src/pages/paintings/config/DmxapiConfig.ts @@ -42,27 +42,64 @@ export const STYLE_TYPE_OPTIONS = [ export const TEXT_TO_IMAGES_MODELS = [ { id: 'seedream-3.0', - provider: 'DMXAPI', + provider: 'doubao', name: ' 即梦 seedream-3.0' + }, + { + id: 'flux-kontext-pro', + provider: 'Black Forest Labs', + name: 'flux-kontext-pro' + }, + { + id: 'flux-kontext-max', + provider: 'Black Forest Labs', + name: 'flux-kontext-max' + }, + { + id: 'imagen4', + provider: 'Google', + name: 'imagen4' } ] export const IMAGE_EDIT_MODELS = [ { id: 'gpt-image-1', - provider: 'DMXAPI', - name: 'OpenAI:gpt-image-1' + provider: 'OpenAI', + name: 'gpt-image-1' + }, + { + id: 'flux-kontext-pro', + provider: 'Black Forest Labs', + name: 'flux-kontext-pro' + }, + { + id: 'flux-kontext-max', + provider: 'Black Forest Labs', + name: 'flux-kontext-max' } ] export const IMAGE_MERGE_MODELS = [ { id: 'gpt-image-1', - provider: 'DMXAPI', - name: 'OpenAI:gpt-image-1' + provider: 'OpenAI', + name: 'gpt-image-1' + }, + { + id: 'flux-kontext-pro', + provider: 'Black Forest Labs', + name: 'flux-kontext-pro' + }, + { + id: 'flux-kontext-max', + provider: 'Black Forest Labs', + name: 'flux-kontext-max' } ] +export const ALL_MODELS = [...TEXT_TO_IMAGES_MODELS, ...IMAGE_EDIT_MODELS, ...IMAGE_MERGE_MODELS] + export const IMAGE_SIZES = [ { label: '1:1', @@ -118,3 +155,25 @@ export const MODEOPTIONS = [ { label: '改图', value: generationModeType.EDIT }, { label: '合并图', value: generationModeType.MERGE } ] + +// 按品牌分组的模型配置 +export const MODEL_GROUPS = { + TEXT_TO_IMAGES: { + Doubao: TEXT_TO_IMAGES_MODELS.filter((model) => model.provider === 'doubao'), + OpenAI: TEXT_TO_IMAGES_MODELS.filter((model) => model.provider === 'OpenAI'), + 'Black Forest Labs': IMAGE_EDIT_MODELS.filter((model) => model.provider === 'Black Forest Labs'), + Google: TEXT_TO_IMAGES_MODELS.filter((model) => model.provider === 'Google') + }, + IMAGE_EDIT: { + Doubao: IMAGE_EDIT_MODELS.filter((model) => model.provider === 'doubao'), + OpenAI: IMAGE_EDIT_MODELS.filter((model) => model.provider === 'OpenAI'), + 'Black Forest Labs': IMAGE_EDIT_MODELS.filter((model) => model.provider === 'Black Forest Labs'), + Google: IMAGE_EDIT_MODELS.filter((model) => model.provider === 'Google') + }, + IMAGE_MERGE: { + Doubao: IMAGE_MERGE_MODELS.filter((model) => model.provider === 'doubao'), + OpenAI: IMAGE_MERGE_MODELS.filter((model) => model.provider === 'OpenAI'), + 'Black Forest Labs': IMAGE_MERGE_MODELS.filter((model) => model.provider === 'Black Forest Labs'), + Google: IMAGE_MERGE_MODELS.filter((model) => model.provider === 'Google') + } +}