Compare commits

...

10 Commits

Author SHA1 Message Date
Dakai
868bfca161 Merge c3b50a9c93 into 3809375694 2025-04-21 10:11:49 +08:00
RiverRay
3809375694 Merge pull request #6457 from ACTOR-ALCHEMIST/main
Some checks failed
Run Tests / test (push) Has been cancelled
Support OpenAI o3 and o4-mini
2025-04-19 16:00:41 +08:00
RiverRay
1b0de25986 Update README.md 2025-04-19 15:59:31 +08:00
RiverRay
865c45dd29 Update README.md 2025-04-19 15:56:53 +08:00
RiverRay
1f5d8e6d9c Merge pull request #6458 from ChatGPTNextWeb/Leizhenpeng-patch-7
Update README.md
2025-04-19 15:50:48 +08:00
Jasper Hu
2d7229d2b8 feat: 支持 OpenAI 新模型 o3 与 o4-mini,并适配新参数 2025-04-18 20:36:07 +01:00
dakai
c3b50a9c93 refactor: optimize playSpeech function in TTSConfigList component 2024-10-27 12:42:16 +08:00
dakai
ab4bf3ba67 remove redundancy code 2024-10-16 11:34:20 +08:00
dakai
67192a7946 follow the rabbit to fix potential issues 2024-10-14 03:46:51 +08:00
dakai
bcd50b89c8 feat: add voice audio preview button in tts-config option 2024-10-14 03:25:44 +08:00
4 changed files with 102 additions and 20 deletions

View File

@@ -22,7 +22,7 @@ English / [简体中文](./README_CN.md)
[![MacOS][MacOS-image]][download-url] [![MacOS][MacOS-image]][download-url]
[![Linux][Linux-image]][download-url] [![Linux][Linux-image]][download-url]
[NextChatAI](https://nextchat.club?utm_source=readme) / [Web App Demo](https://app.nextchat.dev) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Enterprise Edition](#enterprise-edition) / [Twitter](https://twitter.com/NextChatDev) [NextChatAI](https://nextchat.club?utm_source=readme) / [iOS APP](https://apps.apple.com/us/app/nextchat-ai/id6743085599) / [Web App Demo](https://app.nextchat.dev) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Enterprise Edition](#enterprise-edition)
[saas-url]: https://nextchat.club?utm_source=readme [saas-url]: https://nextchat.club?utm_source=readme
@@ -41,7 +41,9 @@ English / [简体中文](./README_CN.md)
</div> </div>
## 🥳 Cheer for NextChat iOS Version Online! ## 🥳 Cheer for NextChat iOS Version Online!
> [ 👉 Click Here Install Now](https://apps.apple.com/us/app/nextchat-ai/id6743085599) > [👉 Click Here to Install Now](https://apps.apple.com/us/app/nextchat-ai/id6743085599)
> [❤️ Source Code Coming Soon](https://github.com/ChatGPTNextWeb/NextChat-iOS)
![Github iOS Image](https://github.com/user-attachments/assets/e0aa334f-4c13-4dc9-8310-e3b09fa4b9f3) ![Github iOS Image](https://github.com/user-attachments/assets/e0aa334f-4c13-4dc9-8310-e3b09fa4b9f3)

View File

@@ -198,7 +198,8 @@ export class ChatGPTApi implements LLMApi {
const isDalle3 = _isDalle3(options.config.model); const isDalle3 = _isDalle3(options.config.model);
const isO1OrO3 = const isO1OrO3 =
options.config.model.startsWith("o1") || options.config.model.startsWith("o1") ||
options.config.model.startsWith("o3"); options.config.model.startsWith("o3") ||
options.config.model.startsWith("o4-mini");
if (isDalle3) { if (isDalle3) {
const prompt = getMessageTextContent( const prompt = getMessageTextContent(
options.messages.slice(-1)?.pop() as any, options.messages.slice(-1)?.pop() as any,
@@ -243,7 +244,7 @@ export class ChatGPTApi implements LLMApi {
} }
// add max_tokens to vision model // add max_tokens to vision model
if (visionModel) { if (visionModel && !isO1OrO3) {
requestPayload["max_tokens"] = Math.max(modelConfig.max_tokens, 4000); requestPayload["max_tokens"] = Math.max(modelConfig.max_tokens, 4000);
} }
} }

View File

@@ -1,19 +1,76 @@
import { TTSConfig, TTSConfigValidator } from "../store"; import { TTSConfig, TTSConfigValidator } from "../store";
import React, { useState } from "react";
import Locale from "../locales"; import Locale from "../locales";
import { ListItem, Select } from "./ui-lib"; import { ListItem, Select } from "./ui-lib";
import { import {
ModelProvider,
DEFAULT_TTS_ENGINE, DEFAULT_TTS_ENGINE,
DEFAULT_TTS_ENGINES, DEFAULT_TTS_ENGINES,
DEFAULT_TTS_MODELS, DEFAULT_TTS_MODELS,
DEFAULT_TTS_VOICES, DEFAULT_TTS_VOICES,
} from "../constant"; } from "../constant";
import { InputRange } from "./input-range"; import { InputRange } from "./input-range";
import { IconButton } from "./button";
import SpeakIcon from "../icons/speak.svg";
import SpeakStopIcon from "../icons/speak-stop.svg";
import { createTTSPlayer } from "../utils/audio";
import { useAppConfig } from "../store";
import { ClientApi } from "../client/api";
import { showToast } from "../components/ui-lib";
const ttsPlayer = createTTSPlayer();
export function TTSConfigList(props: { export function TTSConfigList(props: {
ttsConfig: TTSConfig; ttsConfig: TTSConfig;
updateConfig: (updater: (config: TTSConfig) => void) => void; updateConfig: (updater: (config: TTSConfig) => void) => void;
}) { }) {
const [speechLoading, setSpeechLoading] = useState(false);
const [speechStatus, setSpeechStatus] = useState(false);
const config = useAppConfig.getState();
function stopSpeech() {
ttsPlayer.stop();
setSpeechStatus(false);
}
async function playSpeech(text: string, ttsConfig: TTSConfig) {
try {
const api = new ClientApi(ModelProvider.GPT);
setSpeechLoading(true);
ttsPlayer.init();
const audioBuffer = await api.llm.speech({
model: ttsConfig.model,
input: text,
voice: ttsConfig.voice,
speed: ttsConfig.speed,
});
setSpeechStatus(true);
await ttsPlayer.play(audioBuffer, () => {
setSpeechStatus(false);
});
} catch (error) {
console.error("[OpenAI Speech]", error);
setSpeechStatus(false);
// Implement user-facing error notification here
if (typeof (error as Error).message === "string") {
showToast((error as Error).message);
}
} finally {
setSpeechLoading(false);
}
}
async function openaiSpeech(text: string) {
if (speechStatus) {
stopSpeech();
} else {
await playSpeech(text, config.ttsConfig);
}
}
return ( return (
<> <>
<ListItem <ListItem
@@ -88,23 +145,41 @@ export function TTSConfigList(props: {
title={Locale.Settings.TTS.Voice.Title} title={Locale.Settings.TTS.Voice.Title}
subTitle={Locale.Settings.TTS.Voice.SubTitle} subTitle={Locale.Settings.TTS.Voice.SubTitle}
> >
<Select <div style={{ display: "flex", gap: "10px" }}>
value={props.ttsConfig.voice} <IconButton
onChange={(e) => { aria={Locale.Chat.Actions.Speech}
props.updateConfig( icon={speechStatus ? <SpeakStopIcon /> : <SpeakIcon />}
(config) => text={
(config.voice = TTSConfigValidator.voice( speechLoading
? "Loading..."
: speechStatus
? Locale.Chat.Actions.Stop
: Locale.Chat.Actions.Speech
}
onClick={() => {
openaiSpeech(
"NextChat,Unleash your imagination, experience the future of AI conversation.",
);
}}
/>
<Select
value={props.ttsConfig.voice}
onChange={(e) => {
props.updateConfig((config) => {
config.voice = TTSConfigValidator.voice(
e.currentTarget.value, e.currentTarget.value,
)), );
); });
}} }}
> >
{DEFAULT_TTS_VOICES.map((v, i) => ( {DEFAULT_TTS_VOICES.map((v, i) => (
<option value={v} key={i}> <option value={v} key={i}>
{v} {v}
</option> </option>
))} ))}
</Select> </Select>
</div>
</ListItem> </ListItem>
<ListItem <ListItem
title={Locale.Settings.TTS.Speed.Title} title={Locale.Settings.TTS.Speed.Title}

View File

@@ -478,6 +478,8 @@ export const VISION_MODEL_REGEXES = [
/^dall-e-3$/, // Matches exactly "dall-e-3" /^dall-e-3$/, // Matches exactly "dall-e-3"
/glm-4v/, /glm-4v/,
/vl/i, /vl/i,
/o3/,
/o4-mini/,
]; ];
export const EXCLUDE_VISION_MODEL_REGEXES = [/claude-3-5-haiku-20241022/]; export const EXCLUDE_VISION_MODEL_REGEXES = [/claude-3-5-haiku-20241022/];
@@ -516,6 +518,8 @@ const openaiModels = [
"o1-mini", "o1-mini",
"o1-preview", "o1-preview",
"o3-mini", "o3-mini",
"o3",
"o4-mini",
]; ];
const googleModels = [ const googleModels = [