mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-11-14 13:03:49 +08:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
@@ -71,7 +71,7 @@ import {
|
||||
isSupportRAGModel,
|
||||
} from "../utils";
|
||||
|
||||
import { compressImage } from "@/app/utils/chat";
|
||||
import { uploadImage as uploadImageRemote } from "@/app/utils/chat";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
@@ -102,6 +102,7 @@ import {
|
||||
Path,
|
||||
REQUEST_TIMEOUT_MS,
|
||||
UNFINISHED_INPUT,
|
||||
ServiceProvider,
|
||||
} from "../constant";
|
||||
import { Avatar } from "./emoji";
|
||||
import { ContextPrompts, MaskAvatar, MaskConfig } from "./mask";
|
||||
@@ -269,11 +270,11 @@ function useSubmitHandler() {
|
||||
};
|
||||
}
|
||||
|
||||
export type RenderPompt = Pick<Prompt, "title" | "content">;
|
||||
export type RenderPrompt = Pick<Prompt, "title" | "content">;
|
||||
|
||||
export function PromptHints(props: {
|
||||
prompts: RenderPompt[];
|
||||
onPromptSelect: (prompt: RenderPompt) => void;
|
||||
prompts: RenderPrompt[];
|
||||
onPromptSelect: (prompt: RenderPrompt) => void;
|
||||
}) {
|
||||
const noPrompts = props.prompts.length === 0;
|
||||
const [selectIndex, setSelectIndex] = useState(0);
|
||||
@@ -502,6 +503,9 @@ export function ChatActions(props: {
|
||||
|
||||
// switch model
|
||||
const currentModel = chatStore.currentSession().mask.modelConfig.model;
|
||||
const currentProviderName =
|
||||
chatStore.currentSession().mask.modelConfig?.providerName ||
|
||||
ServiceProvider.OpenAI;
|
||||
const allModels = useAllModels();
|
||||
const models = useMemo(() => {
|
||||
const filteredModels = allModels.filter((m) => m.available);
|
||||
@@ -517,6 +521,14 @@ export function ChatActions(props: {
|
||||
return filteredModels;
|
||||
}
|
||||
}, [allModels]);
|
||||
const currentModelName = useMemo(() => {
|
||||
const model = models.find(
|
||||
(m) =>
|
||||
m.name == currentModel &&
|
||||
m?.provider?.providerName == currentProviderName,
|
||||
);
|
||||
return model?.displayName ?? "";
|
||||
}, [models, currentModel, currentProviderName]);
|
||||
const [showModelSelector, setShowModelSelector] = useState(false);
|
||||
const [showUploadImage, setShowUploadImage] = useState(false);
|
||||
const [showUploadFile, setShowUploadFile] = useState(false);
|
||||
@@ -542,13 +554,17 @@ export function ChatActions(props: {
|
||||
const isUnavaliableModel = !models.some((m) => m.name === currentModel);
|
||||
if (isUnavaliableModel && models.length > 0) {
|
||||
// show next model to default model if exist
|
||||
let nextModel: ModelType = (
|
||||
models.find((model) => model.isDefault) || models[0]
|
||||
).name;
|
||||
chatStore.updateCurrentSession(
|
||||
(session) => (session.mask.modelConfig.model = nextModel),
|
||||
let nextModel = models.find((model) => model.isDefault) || models[0];
|
||||
chatStore.updateCurrentSession((session) => {
|
||||
session.mask.modelConfig.model = nextModel.name;
|
||||
session.mask.modelConfig.providerName = nextModel?.provider
|
||||
?.providerName as ServiceProvider;
|
||||
});
|
||||
showToast(
|
||||
nextModel?.provider?.providerName == "ByteDance"
|
||||
? nextModel.displayName
|
||||
: nextModel.name,
|
||||
);
|
||||
showToast(nextModel);
|
||||
}
|
||||
}, [chatStore, currentModel, models]);
|
||||
|
||||
@@ -584,7 +600,6 @@ export function ChatActions(props: {
|
||||
icon={props.uploading ? <LoadingButtonIcon /> : <ImageIcon />}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showUploadFile && (
|
||||
<ChatAction
|
||||
onClick={props.uploadFile}
|
||||
@@ -607,6 +622,7 @@ export function ChatActions(props: {
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
<ChatAction
|
||||
onClick={props.showPromptHints}
|
||||
text={Locale.Chat.InputActions.Prompt}
|
||||
@@ -620,13 +636,6 @@ export function ChatActions(props: {
|
||||
text={Locale.Chat.InputActions.Masks}
|
||||
icon={<MaskIcon />}
|
||||
/>
|
||||
|
||||
<ChatAction
|
||||
onClick={() => setShowModelSelector(true)}
|
||||
text={currentModel}
|
||||
icon={<RobotIcon />}
|
||||
/>
|
||||
|
||||
{config.pluginConfig.enable &&
|
||||
/^gpt(?!.*03\d{2}$).*$/.test(currentModel) &&
|
||||
currentModel != "gpt-4-vision-preview" && (
|
||||
@@ -641,21 +650,43 @@ export function ChatActions(props: {
|
||||
/>
|
||||
)}
|
||||
|
||||
<ChatAction
|
||||
onClick={() => setShowModelSelector(true)}
|
||||
text={currentModelName}
|
||||
icon={<RobotIcon />}
|
||||
/>
|
||||
|
||||
{showModelSelector && (
|
||||
<Selector
|
||||
defaultSelectedValue={currentModel}
|
||||
defaultSelectedValue={`${currentModel}@${currentProviderName}`}
|
||||
items={models.map((m) => ({
|
||||
title: m.displayName,
|
||||
value: m.name,
|
||||
title: `${m.displayName}${
|
||||
m?.provider?.providerName
|
||||
? "(" + m?.provider?.providerName + ")"
|
||||
: ""
|
||||
}`,
|
||||
value: `${m.name}@${m?.provider?.providerName}`,
|
||||
}))}
|
||||
onClose={() => setShowModelSelector(false)}
|
||||
onSelection={(s) => {
|
||||
if (s.length === 0) return;
|
||||
const [model, providerName] = s[0].split("@");
|
||||
chatStore.updateCurrentSession((session) => {
|
||||
session.mask.modelConfig.model = s[0] as ModelType;
|
||||
session.mask.modelConfig.model = model as ModelType;
|
||||
session.mask.modelConfig.providerName =
|
||||
providerName as ServiceProvider;
|
||||
session.mask.syncGlobalConfig = false;
|
||||
});
|
||||
showToast(s[0]);
|
||||
if (providerName == "ByteDance") {
|
||||
const selectedModel = models.find(
|
||||
(m) =>
|
||||
m.name == model &&
|
||||
m?.provider?.providerName == providerName,
|
||||
);
|
||||
showToast(selectedModel?.displayName ?? "");
|
||||
} else {
|
||||
showToast(model);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -792,7 +823,7 @@ function _Chat() {
|
||||
|
||||
// prompt hints
|
||||
const promptStore = usePromptStore();
|
||||
const [promptHints, setPromptHints] = useState<RenderPompt[]>([]);
|
||||
const [promptHints, setPromptHints] = useState<RenderPrompt[]>([]);
|
||||
const onSearch = useDebouncedCallback(
|
||||
(text: string) => {
|
||||
const matchedPrompts = promptStore.search(text);
|
||||
@@ -905,7 +936,7 @@ function _Chat() {
|
||||
setAutoScroll(true);
|
||||
};
|
||||
|
||||
const onPromptSelect = (prompt: RenderPompt) => {
|
||||
const onPromptSelect = (prompt: RenderPrompt) => {
|
||||
setTimeout(() => {
|
||||
setPromptHints([]);
|
||||
|
||||
@@ -1319,7 +1350,7 @@ function _Chat() {
|
||||
...(await new Promise<string[]>((res, rej) => {
|
||||
setUploading(true);
|
||||
const imagesData: string[] = [];
|
||||
compressImage(file, 256 * 1024)
|
||||
uploadImageRemote(file)
|
||||
.then((dataUrl) => {
|
||||
imagesData.push(dataUrl);
|
||||
setUploading(false);
|
||||
@@ -1361,7 +1392,7 @@ function _Chat() {
|
||||
const imagesData: string[] = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = event.target.files[i];
|
||||
compressImage(file, 256 * 1024)
|
||||
uploadImageRemote(file)
|
||||
.then((dataUrl) => {
|
||||
imagesData.push(dataUrl);
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user