diff --git a/app/api/openai.ts b/app/api/openai.ts
index 0059ff8b4..7dfd84e17 100644
--- a/app/api/openai.ts
+++ b/app/api/openai.ts
@@ -13,7 +13,9 @@ function getModels(remoteModelRes: OpenAIListModelResponse) {
if (config.disableGPT4) {
remoteModelRes.data = remoteModelRes.data.filter(
- (m) => !m.id.startsWith("gpt-4") || m.id.startsWith("gpt-4o-mini"),
+ (m) =>
+ !(m.id.startsWith("gpt-4") || m.id.startsWith("chatgpt-4o")) ||
+ m.id.startsWith("gpt-4o-mini"),
);
}
diff --git a/app/client/platforms/anthropic.ts b/app/client/platforms/anthropic.ts
index fce675a16..7dd39c9cd 100644
--- a/app/client/platforms/anthropic.ts
+++ b/app/client/platforms/anthropic.ts
@@ -203,7 +203,7 @@ export class ClaudeApi implements LLMApi {
const [tools, funcs] = usePluginStore
.getState()
.getAsTools(
- useChatStore.getState().currentSession().mask?.plugin as string[],
+ useChatStore.getState().currentSession().mask?.plugin || [],
);
return stream(
path,
diff --git a/app/client/platforms/moonshot.ts b/app/client/platforms/moonshot.ts
index c38d3317b..cd10d2f6c 100644
--- a/app/client/platforms/moonshot.ts
+++ b/app/client/platforms/moonshot.ts
@@ -125,7 +125,7 @@ export class MoonshotApi implements LLMApi {
const [tools, funcs] = usePluginStore
.getState()
.getAsTools(
- useChatStore.getState().currentSession().mask?.plugin as string[],
+ useChatStore.getState().currentSession().mask?.plugin || [],
);
return stream(
chatPath,
diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts
index b3b306d1d..4d6f28845 100644
--- a/app/client/platforms/openai.ts
+++ b/app/client/platforms/openai.ts
@@ -244,7 +244,7 @@ export class ChatGPTApi implements LLMApi {
const [tools, funcs] = usePluginStore
.getState()
.getAsTools(
- useChatStore.getState().currentSession().mask?.plugin as string[],
+ useChatStore.getState().currentSession().mask?.plugin || [],
);
// console.log("getAsTools", tools, funcs);
stream(
@@ -407,7 +407,9 @@ export class ChatGPTApi implements LLMApi {
});
const resJson = (await res.json()) as OpenAIListModelResponse;
- const chatModels = resJson.data?.filter((m) => m.id.startsWith("gpt-"));
+ const chatModels = resJson.data?.filter(
+ (m) => m.id.startsWith("gpt-") || m.id.startsWith("chatgpt-"),
+ );
console.log("[Models]", chatModels);
if (!chatModels) {
diff --git a/app/components/chat.tsx b/app/components/chat.tsx
index 1bd843f90..09edd8f31 100644
--- a/app/components/chat.tsx
+++ b/app/components/chat.tsx
@@ -68,6 +68,7 @@ import {
isDalle3,
removeOutdatedEntries,
showPlugins,
+ safeLocalStorage,
} from "../utils";
import { uploadImage as uploadImageRemote } from "@/app/utils/chat";
@@ -110,6 +111,8 @@ import { getClientConfig } from "../config/client";
import { useAllModels } from "../utils/hooks";
import { MultimodalContent } from "../client/api";
+const localStorage = safeLocalStorage();
+
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
loading: () =>
(
- () => localStorage.getItem("Mask-language") as Lang | undefined,
- );
- useEffect(() => {
- if (filterLang) {
- localStorage.setItem("Mask-language", filterLang);
- } else {
- localStorage.removeItem("Mask-language");
- }
- }, [filterLang]);
+ const filterLang = maskStore.language;
const allMasks = maskStore
.getAll()
@@ -542,9 +533,9 @@ export function MaskPage() {
onChange={(e) => {
const value = e.currentTarget.value;
if (value === Locale.Settings.Lang.All) {
- setFilterLang(undefined);
+ maskStore.setLanguage(undefined);
} else {
- setFilterLang(value as Lang);
+ maskStore.setLanguage(value as Lang);
}
}}
>
diff --git a/app/config/server.ts b/app/config/server.ts
index e953af369..676b0174f 100644
--- a/app/config/server.ts
+++ b/app/config/server.ts
@@ -120,12 +120,15 @@ export const getServerSideConfig = () => {
if (disableGPT4) {
if (customModels) customModels += ",";
customModels += DEFAULT_MODELS.filter(
- (m) => m.name.startsWith("gpt-4") && !m.name.startsWith("gpt-4o-mini"),
+ (m) =>
+ (m.name.startsWith("gpt-4") || m.name.startsWith("chatgpt-4o")) &&
+ !m.name.startsWith("gpt-4o-mini"),
)
.map((m) => "-" + m.name)
.join(",");
if (
- defaultModel.startsWith("gpt-4") &&
+ (defaultModel.startsWith("gpt-4") ||
+ defaultModel.startsWith("chatgpt-4o")) &&
!defaultModel.startsWith("gpt-4o-mini")
)
defaultModel = "";
diff --git a/app/constant.ts b/app/constant.ts
index 90557c16c..eb82d3c66 100644
--- a/app/constant.ts
+++ b/app/constant.ts
@@ -246,6 +246,7 @@ export const KnowledgeCutOffDate: Record = {
"gpt-4o": "2023-10",
"gpt-4o-2024-05-13": "2023-10",
"gpt-4o-2024-08-06": "2023-10",
+ "chatgpt-4o-latest": "2023-10",
"gpt-4o-mini": "2023-10",
"gpt-4o-mini-2024-07-18": "2023-10",
"gpt-4-vision-preview": "2023-04",
@@ -268,6 +269,7 @@ const openaiModels = [
"gpt-4o",
"gpt-4o-2024-05-13",
"gpt-4o-2024-08-06",
+ "chatgpt-4o-latest",
"gpt-4o-mini",
"gpt-4o-mini-2024-07-18",
"gpt-4-vision-preview",
diff --git a/app/layout.tsx b/app/layout.tsx
index abefd69c1..7d14cb88d 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -41,7 +41,11 @@ export default function RootLayout({
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
-
+
diff --git a/app/locales/index.ts b/app/locales/index.ts
index acdb3e878..ff7e3a262 100644
--- a/app/locales/index.ts
+++ b/app/locales/index.ts
@@ -18,10 +18,13 @@ import ar from "./ar";
import bn from "./bn";
import sk from "./sk";
import { merge } from "../utils/merge";
+import { safeLocalStorage } from "@/app/utils";
import type { LocaleType } from "./cn";
export type { LocaleType, PartialLocaleType } from "./cn";
+const localStorage = safeLocalStorage();
+
const ALL_LANGS = {
cn,
en,
@@ -82,17 +85,11 @@ merge(fallbackLang, targetLang);
export default fallbackLang as LocaleType;
function getItem(key: string) {
- try {
- return localStorage.getItem(key);
- } catch {
- return null;
- }
+ return localStorage.getItem(key);
}
function setItem(key: string, value: string) {
- try {
- localStorage.setItem(key, value);
- } catch {}
+ localStorage.setItem(key, value);
}
function getLanguage() {
diff --git a/app/store/chat.ts b/app/store/chat.ts
index 69ad88caf..bbb914710 100644
--- a/app/store/chat.ts
+++ b/app/store/chat.ts
@@ -30,10 +30,12 @@ import { nanoid } from "nanoid";
import { createPersistStore } from "../utils/store";
import { collectModelsWithDefaultModel } from "../utils/model";
import { useAccessStore } from "./access";
+import { isDalle3, safeLocalStorage } from "../utils";
import { useSyncStore } from "./sync";
-import { isDalle3 } from "../utils";
import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
+const localStorage = safeLocalStorage();
+
export type ChatMessageTool = {
id: string;
index?: number;
@@ -113,7 +115,7 @@ function createEmptySession(): ChatSession {
function getSummarizeModel(currentModel: string) {
// if it is using gpt-* models, force to use 4o-mini to summarize
- if (currentModel.startsWith("gpt")) {
+ if (currentModel.startsWith("gpt") || currentModel.startsWith("chatgpt")) {
const configStore = useAppConfig.getState();
const accessStore = useAccessStore.getState();
const allModel = collectModelsWithDefaultModel(
@@ -196,6 +198,7 @@ const DEFAULT_CHAT_STATE = {
sessions: [createEmptySession()],
currentSessionIndex: 0,
deletedSessionIds: {} as Record,
+ lastInput: "",
};
export const useChatStore = createPersistStore(
@@ -551,7 +554,8 @@ export const useChatStore = createPersistStore(
// system prompts, to get close to OpenAI Web ChatGPT
const shouldInjectSystemPrompts =
modelConfig.enableInjectSystemPrompts &&
- session.mask.modelConfig.model.startsWith("gpt-");
+ (session.mask.modelConfig.model.startsWith("gpt-") ||
+ session.mask.modelConfig.model.startsWith("chatgpt-"));
var systemPrompts: ChatMessage[] = [];
systemPrompts = shouldInjectSystemPrompts
@@ -775,6 +779,11 @@ export const useChatStore = createPersistStore(
localStorage.clear();
location.reload();
},
+ setLastInput(lastInput: string) {
+ set({
+ lastInput,
+ });
+ },
};
return methods;
diff --git a/app/store/mask.ts b/app/store/mask.ts
index 083121b65..0c74a892e 100644
--- a/app/store/mask.ts
+++ b/app/store/mask.ts
@@ -23,9 +23,12 @@ export type Mask = {
export const DEFAULT_MASK_STATE = {
masks: {} as Record,
+ language: undefined as Lang | undefined,
};
-export type MaskState = typeof DEFAULT_MASK_STATE;
+export type MaskState = typeof DEFAULT_MASK_STATE & {
+ language?: Lang | undefined;
+};
export const DEFAULT_MASK_AVATAR = "gpt-bot";
export const createEmptyMask = () =>
@@ -102,6 +105,11 @@ export const useMaskStore = createPersistStore(
search(text: string) {
return Object.values(get().masks);
},
+ setLanguage(language: Lang | undefined) {
+ set({
+ language,
+ });
+ },
}),
{
name: StoreKey.Mask,
diff --git a/app/store/plugin.ts b/app/store/plugin.ts
index 74f0fbe17..2356c6db0 100644
--- a/app/store/plugin.ts
+++ b/app/store/plugin.ts
@@ -199,7 +199,7 @@ export const usePluginStore = createPersistStore(
getAsTools(ids: string[]) {
const plugins = get().plugins;
- const selected = ids
+ const selected = (ids || [])
.map((id) => plugins[id])
.filter((i) => i)
.map((p) => FunctionToolService.add(p));
diff --git a/app/utils.ts b/app/utils.ts
index 5ce1f8708..ef0f4963a 100644
--- a/app/utils.ts
+++ b/app/utils.ts
@@ -331,3 +331,63 @@ export function adapter(config: Record) {
: path;
return fetch(fetchUrl as string, { ...rest, responseType: "text" });
}
+
+export function safeLocalStorage(): {
+ getItem: (key: string) => string | null;
+ setItem: (key: string, value: string) => void;
+ removeItem: (key: string) => void;
+ clear: () => void;
+} {
+ let storage: Storage | null;
+
+ try {
+ if (typeof window !== "undefined" && window.localStorage) {
+ storage = window.localStorage;
+ } else {
+ storage = null;
+ }
+ } catch (e) {
+ console.error("localStorage is not available:", e);
+ storage = null;
+ }
+
+ return {
+ getItem(key: string): string | null {
+ if (storage) {
+ return storage.getItem(key);
+ } else {
+ console.warn(
+ `Attempted to get item "${key}" from localStorage, but localStorage is not available.`,
+ );
+ return null;
+ }
+ },
+ setItem(key: string, value: string): void {
+ if (storage) {
+ storage.setItem(key, value);
+ } else {
+ console.warn(
+ `Attempted to set item "${key}" in localStorage, but localStorage is not available.`,
+ );
+ }
+ },
+ removeItem(key: string): void {
+ if (storage) {
+ storage.removeItem(key);
+ } else {
+ console.warn(
+ `Attempted to remove item "${key}" from localStorage, but localStorage is not available.`,
+ );
+ }
+ },
+ clear(): void {
+ if (storage) {
+ storage.clear();
+ } else {
+ console.warn(
+ "Attempted to clear localStorage, but localStorage is not available.",
+ );
+ }
+ },
+ };
+}
diff --git a/app/utils/indexedDB-storage.ts b/app/utils/indexedDB-storage.ts
index da3094550..51417e9f3 100644
--- a/app/utils/indexedDB-storage.ts
+++ b/app/utils/indexedDB-storage.ts
@@ -1,5 +1,8 @@
import { StateStorage } from "zustand/middleware";
import { get, set, del, clear } from "idb-keyval";
+import { safeLocalStorage } from "@/app/utils";
+
+const localStorage = safeLocalStorage();
class IndexedDBStorage implements StateStorage {
public async getItem(name: string): Promise {
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 78807a2c5..78835d24d 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -9,7 +9,7 @@
},
"package": {
"productName": "NextChat",
- "version": "2.14.2"
+ "version": "2.15.1"
},
"tauri": {
"allowlist": {