Merge remote-tracking branch 'upstream/main'

# Conflicts:
#	app/api/auth.ts
#	app/api/common.ts
#	app/azure.ts
#	app/client/api.ts
#	app/client/platforms/openai.ts
#	app/components/chat.tsx
#	app/components/settings.tsx
#	app/constant.ts
#	app/layout.tsx
#	app/masks/index.ts
#	app/store/chat.ts
#	app/store/config.ts
#	app/utils/hooks.ts
#	app/utils/model.ts
#	package.json
#	yarn.lock
This commit is contained in:
sijinhui
2024-07-11 10:58:00 +08:00
43 changed files with 5654 additions and 5553 deletions

View File

@@ -92,6 +92,7 @@ import {
Path,
REQUEST_TIMEOUT_MS,
UNFINISHED_INPUT,
ServiceProvider,
} from "../constant";
import { Avatar } from "./emoji";
// import { ContextPrompts, MaskAvatar, MaskConfig } from "./mask";
@@ -471,6 +472,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);
@@ -486,6 +490,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 current_day_token = localStorage.getItem("current_day_token") ?? "";
@@ -503,13 +515,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 as ModelType;
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);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [chatStore, currentModel, models]);
@@ -592,7 +608,7 @@ export function ChatActions(props: {
<ChatAction
onClick={() => setShowModelSelector(true)}
text={currentModel}
text={currentModelName}
icon={<RobotIcon />}
/>
@@ -610,21 +626,36 @@ export function ChatActions(props: {
{/*/>*/}
{showModelSelector && (
<ModalSelector
defaultSelectedValue={currentModel}
<Selector
defaultSelectedValue={`${currentModel}@${currentProviderName}`}
items={models.map((m) => ({
title: m.displayName,
title: `${m.displayName}${
m?.provider?.providerName
? "(" + m?.provider?.providerName + ")"
: ""
}`,
subTitle: m.describe,
value: m.name,
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);
}
}}
/>
)}

View File

@@ -36,11 +36,10 @@ import { toBlob, toPng } from "html-to-image";
import { DEFAULT_MASK_AVATAR } from "../store/mask";
import { prettyObject } from "../utils/format";
import { EXPORT_MESSAGE_CLASS_NAME, ModelProvider } from "../constant";
import { EXPORT_MESSAGE_CLASS_NAME } from "../constant";
import { getClientConfig } from "../config/client";
import { ClientApi } from "../client/api";
import { type ClientApi, getClientApi } from "../client/api";
import { getMessageTextContent } from "../utils";
import { identifyDefaultClaudeModel } from "../utils/checkers";
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
loading: () => <LoadingIcon />,
@@ -313,14 +312,7 @@ export function PreviewActions(props: {
const onRenderMsgs = (msgs: ChatMessage[]) => {
setShouldExport(false);
var api: ClientApi;
if (config.modelConfig.model.startsWith("gemini")) {
api = new ClientApi(ModelProvider.GeminiPro);
} else if (identifyDefaultClaudeModel(config.modelConfig.model)) {
api = new ClientApi(ModelProvider.Claude);
} else {
api = new ClientApi(ModelProvider.GPT);
}
const api: ClientApi = getClientApi(config.modelConfig.providerName);
api
.share(msgs)

View File

@@ -12,7 +12,7 @@ import LoadingIcon from "../icons/three-dots.svg";
import { getCSSVar, useMobileScreen } from "../utils";
import dynamic from "next/dynamic";
import { ModelProvider, Path, SlotID } from "../constant";
import { Path, SlotID } from "../constant";
import { ErrorBoundary } from "./error";
import { getISOLang, getLang } from "../locales";
@@ -27,9 +27,8 @@ import { SideBar } from "./sidebar";
import { useAppConfig } from "@/app/store";
import { AuthPage } from "./auth";
import { getClientConfig } from "../config/client";
import { ClientApi } from "../client/api";
import { type ClientApi, getClientApi } from "../client/api";
import { useAccessStore } from "../store";
import { identifyDefaultClaudeModel } from "../utils/checkers";
export function Loading(props: { noLogo?: boolean }) {
return (
@@ -174,14 +173,8 @@ function Screen() {
export function useLoadData() {
const config = useAppConfig();
var api: ClientApi;
if (config.modelConfig.model.startsWith("gemini")) {
api = new ClientApi(ModelProvider.GeminiPro);
} else if (identifyDefaultClaudeModel(config.modelConfig.model)) {
api = new ClientApi(ModelProvider.Claude);
} else {
api = new ClientApi(ModelProvider.GPT);
}
const api: ClientApi = getClientApi(config.modelConfig.providerName);
useEffect(() => {
(async () => {
const models = await api.llm.models();

View File

@@ -1,3 +1,4 @@
import { ServiceProvider } from "@/app/constant";
import { ModalConfigValidator, ModelConfig } from "../store";
import Locale from "../locales";
@@ -10,25 +11,25 @@ export function ModelConfigList(props: {
updateConfig: (updater: (config: ModelConfig) => void) => void;
}) {
const allModels = useAllModels();
const value = `${props.modelConfig.model}@${props.modelConfig?.providerName}`;
return (
<>
<ListItem title={Locale.Settings.Model}>
<Select
value={props.modelConfig.model}
value={value}
onChange={(e) => {
props.updateConfig(
(config) =>
(config.model = ModalConfigValidator.model(
e.currentTarget.value,
)),
);
const [model, providerName] = e.currentTarget.value.split("@");
props.updateConfig((config) => {
config.model = ModalConfigValidator.model(model);
config.providerName = providerName as ServiceProvider;
});
}}
>
{allModels
.filter((v) => v.available)
.map((v, i) => (
<option value={v.name} key={i}>
<option value={`${v.name}@${v.provider?.providerName}`} key={i}>
{v.displayName}({v.provider?.providerName})
</option>
))}
@@ -92,7 +93,7 @@ export function ModelConfigList(props: {
></input>
</ListItem>
{props.modelConfig.model.startsWith("gemini") ? null : (
{props.modelConfig?.providerName == ServiceProvider.Google ? null : (
<>
<ListItem
title={Locale.Settings.PresencePenalty.Title}

View File

@@ -53,6 +53,9 @@ import Link from "next/link";
import {
Anthropic,
Azure,
Baidu,
ByteDance,
Alibaba,
Google,
OPENAI_BASE_URL,
Path,
@@ -1187,6 +1190,156 @@ export function Settings() {
{/* </ListItem>*/}
{/* </>*/}
{/* )}*/}
{/* {accessStore.provider === ServiceProvider.Baidu && (*/}
{/* <>*/}
{/* <ListItem*/}
{/* title={Locale.Settings.Access.Baidu.Endpoint.Title}*/}
{/* subTitle={*/}
{/* Locale.Settings.Access.Anthropic.Endpoint.SubTitle +*/}
{/* Baidu.ExampleEndpoint*/}
{/* }*/}
{/* >*/}
{/* <input*/}
{/* type="text"*/}
{/* value={accessStore.baiduUrl}*/}
{/* placeholder={Baidu.ExampleEndpoint}*/}
{/* onChange={(e) =>*/}
{/* accessStore.update(*/}
{/* (access) =>*/}
{/* (access.baiduUrl = e.currentTarget.value),*/}
{/* )*/}
{/* }*/}
{/* ></input>*/}
{/* </ListItem>*/}
{/* <ListItem*/}
{/* title={Locale.Settings.Access.Baidu.ApiKey.Title}*/}
{/* subTitle={Locale.Settings.Access.Baidu.ApiKey.SubTitle}*/}
{/* >*/}
{/* <PasswordInput*/}
{/* value={accessStore.baiduApiKey}*/}
{/* type="text"*/}
{/* placeholder={*/}
{/* Locale.Settings.Access.Baidu.ApiKey.Placeholder*/}
{/* }*/}
{/* onChange={(e) => {*/}
{/* accessStore.update(*/}
{/* (access) =>*/}
{/* (access.baiduApiKey = e.currentTarget.value),*/}
{/* );*/}
{/* }}*/}
{/* />*/}
{/* </ListItem>*/}
{/* <ListItem*/}
{/* title={Locale.Settings.Access.Baidu.SecretKey.Title}*/}
{/* subTitle={*/}
{/* Locale.Settings.Access.Baidu.SecretKey.SubTitle*/}
{/* }*/}
{/* >*/}
{/* <PasswordInput*/}
{/* value={accessStore.baiduSecretKey}*/}
{/* type="text"*/}
{/* placeholder={*/}
{/* Locale.Settings.Access.Baidu.SecretKey.Placeholder*/}
{/* }*/}
{/* onChange={(e) => {*/}
{/* accessStore.update(*/}
{/* (access) =>*/}
{/* (access.baiduSecretKey = e.currentTarget.value),*/}
{/* );*/}
{/* }}*/}
{/* />*/}
{/* </ListItem>*/}
{/* </>*/}
{/* )}*/}
{/* {accessStore.provider === ServiceProvider.ByteDance && (*/}
{/* <>*/}
{/* <ListItem*/}
{/* title={Locale.Settings.Access.ByteDance.Endpoint.Title}*/}
{/* subTitle={*/}
{/* Locale.Settings.Access.ByteDance.Endpoint.SubTitle +*/}
{/* ByteDance.ExampleEndpoint*/}
{/* }*/}
{/* >*/}
{/* <input*/}
{/* type="text"*/}
{/* value={accessStore.bytedanceUrl}*/}
{/* placeholder={ByteDance.ExampleEndpoint}*/}
{/* onChange={(e) =>*/}
{/* accessStore.update(*/}
{/* (access) =>*/}
{/* (access.bytedanceUrl = e.currentTarget.value),*/}
{/* )*/}
{/* }*/}
{/* ></input>*/}
{/* </ListItem>*/}
{/* <ListItem*/}
{/* title={Locale.Settings.Access.ByteDance.ApiKey.Title}*/}
{/* subTitle={*/}
{/* Locale.Settings.Access.ByteDance.ApiKey.SubTitle*/}
{/* }*/}
{/* >*/}
{/* <PasswordInput*/}
{/* value={accessStore.bytedanceApiKey}*/}
{/* type="text"*/}
{/* placeholder={*/}
{/* Locale.Settings.Access.ByteDance.ApiKey.Placeholder*/}
{/* }*/}
{/* onChange={(e) => {*/}
{/* accessStore.update(*/}
{/* (access) =>*/}
{/* (access.bytedanceApiKey =*/}
{/* e.currentTarget.value),*/}
{/* );*/}
{/* }}*/}
{/* />*/}
{/* </ListItem>*/}
{/* </>*/}
{/* )}*/}
{/* {accessStore.provider === ServiceProvider.Alibaba && (*/}
{/* <>*/}
{/* <ListItem*/}
{/* title={Locale.Settings.Access.Alibaba.Endpoint.Title}*/}
{/* subTitle={*/}
{/* Locale.Settings.Access.Alibaba.Endpoint.SubTitle +*/}
{/* Alibaba.ExampleEndpoint*/}
{/* }*/}
{/* >*/}
{/* <input*/}
{/* type="text"*/}
{/* value={accessStore.alibabaUrl}*/}
{/* placeholder={Alibaba.ExampleEndpoint}*/}
{/* onChange={(e) =>*/}
{/* accessStore.update(*/}
{/* (access) =>*/}
{/* (access.alibabaUrl = e.currentTarget.value),*/}
{/* )*/}
{/* }*/}
{/* ></input>*/}
{/* </ListItem>*/}
{/* <ListItem*/}
{/* title={Locale.Settings.Access.Alibaba.ApiKey.Title}*/}
{/* subTitle={*/}
{/* Locale.Settings.Access.Alibaba.ApiKey.SubTitle*/}
{/* }*/}
{/* >*/}
{/* <PasswordInput*/}
{/* value={accessStore.alibabaApiKey}*/}
{/* type="text"*/}
{/* placeholder={*/}
{/* Locale.Settings.Access.Alibaba.ApiKey.Placeholder*/}
{/* }*/}
{/* onChange={(e) => {*/}
{/* accessStore.update(*/}
{/* (access) =>*/}
{/* (access.alibabaApiKey = e.currentTarget.value),*/}
{/* );*/}
{/* }}*/}
{/* />*/}
{/* </ListItem>*/}
{/* </>*/}
{/* )}*/}
{/* </>*/}
{/* )}*/}
{/* </>*/}