Merge branch 'main' into main

This commit is contained in:
一只会飞的旺旺 2024-05-19 15:03:36 +08:00 committed by GitHub
commit ae101fc610
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 446 additions and 462 deletions

View File

@ -245,13 +245,17 @@ To control custom models, use `+` to add a custom model, use `-` to hide a model
User `-all` to disable all default models, `+all` to enable all default models. User `-all` to disable all default models, `+all` to enable all default models.
### `WHITE_WEBDEV_ENDPOINTS` (可选) ### `WHITE_WEBDEV_ENDPOINTS` (optional)
You can use this option if you want to increase the number of webdav service addresses you are allowed to access, as required by the format You can use this option if you want to increase the number of webdav service addresses you are allowed to access, as required by the format
- Each address must be a complete endpoint - Each address must be a complete endpoint
> `https://xxxx/yyy` > `https://xxxx/yyy`
- Multiple addresses are connected by ', ' - Multiple addresses are connected by ', '
### `DEFAULT_INPUT_TEMPLATE` (optional)
Customize the default template used to initialize the User Input Preprocessing configuration item in Settings.
## Requirements ## Requirements
NodeJS >= 18, Docker >= 20 NodeJS >= 18, Docker >= 20

View File

@ -156,6 +156,9 @@ anthropic claude Api Url.
用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。 用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。
### `DEFAULT_INPUT_TEMPLATE` (可选)
自定义默认的 template用于初始化『设置』中的『用户输入预处理』配置项
## 开发 ## 开发
点击下方按钮,开始二次开发: 点击下方按钮,开始二次开发:

View File

@ -13,6 +13,7 @@ const DANGER_CONFIG = {
hideBalanceQuery: serverConfig.hideBalanceQuery, hideBalanceQuery: serverConfig.hideBalanceQuery,
disableFastLink: serverConfig.disableFastLink, disableFastLink: serverConfig.disableFastLink,
customModels: serverConfig.customModels, customModels: serverConfig.customModels,
defaultModel: serverConfig.defaultModel,
}; };
declare global { declare global {

View File

@ -1,12 +1,12 @@
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import { STORAGE_KEY, internalWhiteWebDavEndpoints } from "../../../constant"; import { STORAGE_KEY, internalAllowedWebDavEndpoints } from "../../../constant";
import { getServerSideConfig } from "@/app/config/server"; import { getServerSideConfig } from "@/app/config/server";
const config = getServerSideConfig(); const config = getServerSideConfig();
const mergedWhiteWebDavEndpoints = [ const mergedAllowedWebDavEndpoints = [
...internalWhiteWebDavEndpoints, ...internalAllowedWebDavEndpoints,
...config.whiteWebDevEndpoints, ...config.allowedWebDevEndpoints,
].filter((domain) => Boolean(domain.trim())); ].filter((domain) => Boolean(domain.trim()));
async function handle( async function handle(
@ -24,7 +24,9 @@ async function handle(
// Validate the endpoint to prevent potential SSRF attacks // Validate the endpoint to prevent potential SSRF attacks
if ( if (
!mergedWhiteWebDavEndpoints.some((white) => endpoint?.startsWith(white)) !mergedAllowedWebDavEndpoints.some(
(allowedEndpoint) => endpoint?.startsWith(allowedEndpoint),
)
) { ) {
return NextResponse.json( return NextResponse.json(
{ {

View File

@ -161,6 +161,13 @@ export class ClaudeApi implements LLMApi {
}; };
}); });
if (prompt[0]?.role === "assistant") {
prompt.unshift({
role: "user",
content: ";",
});
}
const requestBody: AnthropicChatRequest = { const requestBody: AnthropicChatRequest = {
messages: prompt, messages: prompt,
stream: shouldStream, stream: shouldStream,
@ -348,7 +355,11 @@ export class ClaudeApi implements LLMApi {
path(path: string): string { path(path: string): string {
const accessStore = useAccessStore.getState(); const accessStore = useAccessStore.getState();
let baseUrl: string = accessStore.anthropicUrl; let baseUrl: string = "";
if (accessStore.useCustomConfig) {
baseUrl = accessStore.anthropicUrl;
}
// if endpoint is empty, use default endpoint // if endpoint is empty, use default endpoint
if (baseUrl.trim().length === 0) { if (baseUrl.trim().length === 0) {

View File

@ -21,11 +21,10 @@ export class GeminiProApi implements LLMApi {
} }
async chat(options: ChatOptions): Promise<void> { async chat(options: ChatOptions): Promise<void> {
// const apiClient = this; // const apiClient = this;
const visionModel = isVisionModel(options.config.model);
let multimodal = false; let multimodal = false;
const messages = options.messages.map((v) => { const messages = options.messages.map((v) => {
let parts: any[] = [{ text: getMessageTextContent(v) }]; let parts: any[] = [{ text: getMessageTextContent(v) }];
if (visionModel) { if (isVisionModel(options.config.model)) {
const images = getMessageImages(v); const images = getMessageImages(v);
if (images.length > 0) { if (images.length > 0) {
multimodal = true; multimodal = true;
@ -104,24 +103,25 @@ export class GeminiProApi implements LLMApi {
}; };
const accessStore = useAccessStore.getState(); const accessStore = useAccessStore.getState();
let baseUrl = accessStore.googleUrl;
let baseUrl = "";
if (accessStore.useCustomConfig) {
baseUrl = accessStore.googleUrl;
}
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
let shouldStream = !!options.config.stream; let shouldStream = !!options.config.stream;
const controller = new AbortController(); const controller = new AbortController();
options.onController?.(controller); options.onController?.(controller);
try { try {
let googleChatPath = visionModel
? Google.VisionChatPath
: Google.ChatPath;
let chatPath = this.path(googleChatPath);
// let baseUrl = accessStore.googleUrl; // let baseUrl = accessStore.googleUrl;
if (!baseUrl) { if (!baseUrl) {
baseUrl = isApp baseUrl = isApp
? DEFAULT_API_HOST + "/api/proxy/google/" + googleChatPath ? DEFAULT_API_HOST + "/api/proxy/google/" + Google.ChatPath(modelConfig.model)
: chatPath; : this.path(Google.ChatPath(modelConfig.model));
} }
if (isApp) { if (isApp) {
@ -139,6 +139,7 @@ export class GeminiProApi implements LLMApi {
() => controller.abort(), () => controller.abort(),
REQUEST_TIMEOUT_MS, REQUEST_TIMEOUT_MS,
); );
if (shouldStream) { if (shouldStream) {
let responseText = ""; let responseText = "";
let remainText = ""; let remainText = "";

View File

@ -60,6 +60,9 @@ export class ChatGPTApi implements LLMApi {
path(path: string): string { path(path: string): string {
const accessStore = useAccessStore.getState(); const accessStore = useAccessStore.getState();
let baseUrl = "";
if (accessStore.useCustomConfig) {
const isAzure = accessStore.provider === ServiceProvider.Azure; const isAzure = accessStore.provider === ServiceProvider.Azure;
if (isAzure && !accessStore.isValidAzure()) { if (isAzure && !accessStore.isValidAzure()) {
@ -68,7 +71,12 @@ export class ChatGPTApi implements LLMApi {
); );
} }
let baseUrl = isAzure ? accessStore.azureUrl : accessStore.openaiUrl; if (isAzure) {
path = makeAzurePath(path, accessStore.azureApiVersion);
}
baseUrl = isAzure ? accessStore.azureUrl : accessStore.openaiUrl;
}
if (baseUrl.length === 0) { if (baseUrl.length === 0) {
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
@ -84,10 +92,6 @@ export class ChatGPTApi implements LLMApi {
baseUrl = "https://" + baseUrl; baseUrl = "https://" + baseUrl;
} }
if (isAzure) {
path = makeAzurePath(path, accessStore.azureApiVersion);
}
console.log("[Proxy Endpoint] ", baseUrl, path); console.log("[Proxy Endpoint] ", baseUrl, path);
return [baseUrl, path].join("/"); return [baseUrl, path].join("/");
@ -125,7 +129,7 @@ export class ChatGPTApi implements LLMApi {
}; };
// add max_tokens to vision model // add max_tokens to vision model
if (visionModel) { if (visionModel && modelConfig.model.includes("preview")) {
requestPayload["max_tokens"] = Math.max(modelConfig.max_tokens, 4000); requestPayload["max_tokens"] = Math.max(modelConfig.max_tokens, 4000);
} }

View File

@ -59,9 +59,10 @@ import {
getMessageTextContent, getMessageTextContent,
getMessageImages, getMessageImages,
isVisionModel, isVisionModel,
compressImage,
} from "../utils"; } from "../utils";
import { compressImage } from "@/app/utils/chat";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { ChatControllerPool } from "../client/controller"; import { ChatControllerPool } from "../client/controller";
@ -448,10 +449,20 @@ export function ChatActions(props: {
// switch model // switch model
const currentModel = chatStore.currentSession().mask.modelConfig.model; const currentModel = chatStore.currentSession().mask.modelConfig.model;
const allModels = useAllModels(); const allModels = useAllModels();
const models = useMemo( const models = useMemo(() => {
() => allModels.filter((m) => m.available), const filteredModels = allModels.filter((m) => m.available);
[allModels], const defaultModel = filteredModels.find((m) => m.isDefault);
);
if (defaultModel) {
const arr = [
defaultModel,
...filteredModels.filter((m) => m !== defaultModel),
];
return arr;
} else {
return filteredModels;
}
}, [allModels]);
const [showModelSelector, setShowModelSelector] = useState(false); const [showModelSelector, setShowModelSelector] = useState(false);
const [showUploadImage, setShowUploadImage] = useState(false); const [showUploadImage, setShowUploadImage] = useState(false);
@ -467,7 +478,10 @@ export function ChatActions(props: {
// switch to first available model // switch to first available model
const isUnavaliableModel = !models.some((m) => m.name === currentModel); const isUnavaliableModel = !models.some((m) => m.name === currentModel);
if (isUnavaliableModel && models.length > 0) { if (isUnavaliableModel && models.length > 0) {
const nextModel = models[0].name as ModelType; // show next model to default model if exist
let nextModel: ModelType = (
models.find((model) => model.isDefault) || models[0]
).name;
chatStore.updateCurrentSession( chatStore.updateCurrentSession(
(session) => (session.mask.modelConfig.model = nextModel), (session) => (session.mask.modelConfig.model = nextModel),
); );
@ -1075,6 +1089,7 @@ function _Chat() {
if (payload.url) { if (payload.url) {
accessStore.update((access) => (access.openaiUrl = payload.url!)); accessStore.update((access) => (access.openaiUrl = payload.url!));
} }
accessStore.update((access) => (access.useCustomConfig = true));
}); });
} }
} catch { } catch {
@ -1106,7 +1121,9 @@ function _Chat() {
const handlePaste = useCallback( const handlePaste = useCallback(
async (event: React.ClipboardEvent<HTMLTextAreaElement>) => { async (event: React.ClipboardEvent<HTMLTextAreaElement>) => {
const currentModel = chatStore.currentSession().mask.modelConfig.model; const currentModel = chatStore.currentSession().mask.modelConfig.model;
if(!isVisionModel(currentModel)){return;} if (!isVisionModel(currentModel)) {
return;
}
const items = (event.clipboardData || window.clipboardData).items; const items = (event.clipboardData || window.clipboardData).items;
for (const item of items) { for (const item of items) {
if (item.kind === "file" && item.type.startsWith("image/")) { if (item.kind === "file" && item.type.startsWith("image/")) {

View File

@ -1,4 +1,5 @@
import tauriConfig from "../../src-tauri/tauri.conf.json"; import tauriConfig from "../../src-tauri/tauri.conf.json";
import { DEFAULT_INPUT_TEMPLATE } from "../constant";
export const getBuildConfig = () => { export const getBuildConfig = () => {
if (typeof process === "undefined") { if (typeof process === "undefined") {
@ -38,6 +39,7 @@ export const getBuildConfig = () => {
...commitInfo, ...commitInfo,
buildMode, buildMode,
isApp, isApp,
template: process.env.DEFAULT_INPUT_TEMPLATE ?? DEFAULT_INPUT_TEMPLATE,
}; };
}; };

View File

@ -21,6 +21,7 @@ declare global {
ENABLE_BALANCE_QUERY?: string; // allow user to query balance or not ENABLE_BALANCE_QUERY?: string; // allow user to query balance or not
DISABLE_FAST_LINK?: string; // disallow parse settings from url or not DISABLE_FAST_LINK?: string; // disallow parse settings from url or not
CUSTOM_MODELS?: string; // to control custom models CUSTOM_MODELS?: string; // to control custom models
DEFAULT_MODEL?: string; // to cnntrol default model in every new chat window
// azure only // azure only
AZURE_URL?: string; // https://{azure-url}/openai/deployments/{deploy-name} AZURE_URL?: string; // https://{azure-url}/openai/deployments/{deploy-name}
@ -33,6 +34,9 @@ declare global {
// google tag manager // google tag manager
GTM_ID?: string; GTM_ID?: string;
// custom template for preprocessing user input
DEFAULT_INPUT_TEMPLATE?: string;
} }
} }
} }
@ -50,6 +54,22 @@ const ACCESS_CODES = (function getAccessCodes(): Set<string> {
} }
})(); })();
function getApiKey(keys?: string) {
const apiKeyEnvVar = keys ?? "";
const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim());
const randomIndex = Math.floor(Math.random() * apiKeys.length);
const apiKey = apiKeys[randomIndex];
if (apiKey) {
console.log(
`[Server Config] using ${randomIndex + 1} of ${
apiKeys.length
} api key - ${apiKey}`,
);
}
return apiKey;
}
export const getServerSideConfig = () => { export const getServerSideConfig = () => {
if (typeof process === "undefined") { if (typeof process === "undefined") {
throw Error( throw Error(
@ -59,46 +79,48 @@ export const getServerSideConfig = () => {
const disableGPT4 = !!process.env.DISABLE_GPT4; const disableGPT4 = !!process.env.DISABLE_GPT4;
let customModels = process.env.CUSTOM_MODELS ?? ""; let customModels = process.env.CUSTOM_MODELS ?? "";
let defaultModel = process.env.DEFAULT_MODEL ?? "";
if (disableGPT4) { if (disableGPT4) {
if (customModels) customModels += ","; if (customModels) customModels += ",";
customModels += DEFAULT_MODELS.filter((m) => m.name.startsWith("gpt-4")) customModels += DEFAULT_MODELS.filter((m) => m.name.startsWith("gpt-4"))
.map((m) => "-" + m.name) .map((m) => "-" + m.name)
.join(","); .join(",");
if (defaultModel.startsWith("gpt-4")) defaultModel = "";
} }
const isAzure = !!process.env.AZURE_URL; const isAzure = !!process.env.AZURE_URL;
const isGoogle = !!process.env.GOOGLE_API_KEY; const isGoogle = !!process.env.GOOGLE_API_KEY;
const isAnthropic = !!process.env.ANTHROPIC_API_KEY; const isAnthropic = !!process.env.ANTHROPIC_API_KEY;
const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? ""; // const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? "";
const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim()); // const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim());
const randomIndex = Math.floor(Math.random() * apiKeys.length); // const randomIndex = Math.floor(Math.random() * apiKeys.length);
const apiKey = apiKeys[randomIndex]; // const apiKey = apiKeys[randomIndex];
console.log( // console.log(
`[Server Config] using ${randomIndex + 1} of ${apiKeys.length} api key`, // `[Server Config] using ${randomIndex + 1} of ${apiKeys.length} api key`,
); // );
const whiteWebDevEndpoints = (process.env.WHITE_WEBDEV_ENDPOINTS ?? "").split( const allowedWebDevEndpoints = (
",", process.env.WHITE_WEBDEV_ENDPOINTS ?? ""
); ).split(",");
return { return {
baseUrl: process.env.BASE_URL, baseUrl: process.env.BASE_URL,
apiKey, apiKey: getApiKey(process.env.OPENAI_API_KEY),
openaiOrgId: process.env.OPENAI_ORG_ID, openaiOrgId: process.env.OPENAI_ORG_ID,
isAzure, isAzure,
azureUrl: process.env.AZURE_URL, azureUrl: process.env.AZURE_URL,
azureApiKey: process.env.AZURE_API_KEY, azureApiKey: getApiKey(process.env.AZURE_API_KEY),
azureApiVersion: process.env.AZURE_API_VERSION, azureApiVersion: process.env.AZURE_API_VERSION,
isGoogle, isGoogle,
googleApiKey: process.env.GOOGLE_API_KEY, googleApiKey: getApiKey(process.env.GOOGLE_API_KEY),
googleUrl: process.env.GOOGLE_URL, googleUrl: process.env.GOOGLE_URL,
isAnthropic, isAnthropic,
anthropicApiKey: process.env.ANTHROPIC_API_KEY, anthropicApiKey: getApiKey(process.env.ANTHROPIC_API_KEY),
anthropicApiVersion: process.env.ANTHROPIC_API_VERSION, anthropicApiVersion: process.env.ANTHROPIC_API_VERSION,
anthropicUrl: process.env.ANTHROPIC_URL, anthropicUrl: process.env.ANTHROPIC_URL,
@ -116,6 +138,7 @@ export const getServerSideConfig = () => {
hideBalanceQuery: !process.env.ENABLE_BALANCE_QUERY, hideBalanceQuery: !process.env.ENABLE_BALANCE_QUERY,
disableFastLink: !!process.env.DISABLE_FAST_LINK, disableFastLink: !!process.env.DISABLE_FAST_LINK,
customModels, customModels,
whiteWebDevEndpoints, defaultModel,
allowedWebDevEndpoints,
}; };
}; };

View File

@ -98,10 +98,8 @@ export const Azure = {
export const Google = { export const Google = {
ExampleEndpoint: "https://generativelanguage.googleapis.com/", ExampleEndpoint: "https://generativelanguage.googleapis.com/",
ChatPath: "v1beta/models/gemini-1.5-pro-latest:generateContent", ChatPath: (modelName: string) => `v1beta/models/${modelName}:generateContent`,
VisionChatPath: "v1beta/models/gemini-1.5-pro-latest:generateContent",
// /api/openai/v1/chat/completions
}; };
export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang
@ -130,265 +128,82 @@ export const KnowledgeCutOffDate: Record<string, string> = {
"gpt-4-turbo": "2023-12", "gpt-4-turbo": "2023-12",
"gpt-4-turbo-2024-04-09": "2023-12", "gpt-4-turbo-2024-04-09": "2023-12",
"gpt-4-turbo-preview": "2023-12", "gpt-4-turbo-preview": "2023-12",
"gpt-4-1106-preview": "2023-04", "gpt-4o": "2023-10",
"gpt-4-0125-preview": "2023-12", "gpt-4o-2024-05-13": "2023-10",
"gpt-4-vision-preview": "2023-04", "gpt-4-vision-preview": "2023-04",
// After improvements, // After improvements,
// it's now easier to add "KnowledgeCutOffDate" instead of stupid hardcoding it, as was done previously. // it's now easier to add "KnowledgeCutOffDate" instead of stupid hardcoding it, as was done previously.
"gemini-pro": "2023-12", "gemini-pro": "2023-12",
"gemini-pro-vision": "2023-12",
}; };
const openaiModels = [
"gpt-3.5-turbo",
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
"gpt-4",
"gpt-4-0613",
"gpt-4-32k",
"gpt-4-32k-0613",
"gpt-4-turbo",
"gpt-4-turbo-preview",
"gpt-4o",
"gpt-4o-2024-05-13",
"gpt-4-vision-preview",
"gpt-4-turbo-2024-04-09"
];
const googleModels = [
"gemini-1.0-pro",
"gemini-1.5-pro-latest",
"gemini-1.5-flash-latest",
"gemini-pro-vision",
];
const anthropicModels = [
"claude-instant-1.2",
"claude-2.0",
"claude-2.1",
"claude-3-sonnet-20240229",
"claude-3-opus-20240229",
"claude-3-haiku-20240307",
];
export const DEFAULT_MODELS = [ export const DEFAULT_MODELS = [
{ ...openaiModels.map((name) => ({
name: "gpt-4", name,
available: true, available: true,
provider: { provider: {
id: "openai", id: "openai",
providerName: "OpenAI", providerName: "OpenAI",
providerType: "openai", providerType: "openai",
}, },
}, })),
{ ...googleModels.map((name) => ({
name: "gpt-4-0314", name,
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-0613",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-32k",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-32k-0314",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-32k-0613",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-turbo",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-turbo-2024-04-09",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-turbo-preview",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-1106-preview",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-0125-preview",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-vision-preview",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo-0125",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo-0301",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo-0613",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo-1106",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo-16k",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo-16k-0613",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gemini-pro",
available: true, available: true,
provider: { provider: {
id: "google", id: "google",
providerName: "Google", providerName: "Google",
providerType: "google", providerType: "google",
}, },
}, })),
{ ...anthropicModels.map((name) => ({
name: "gemini-pro-vision", name,
available: true,
provider: {
id: "google",
providerName: "Google",
providerType: "google",
},
},
{
name: "claude-instant-1.2",
available: true, available: true,
provider: { provider: {
id: "anthropic", id: "anthropic",
providerName: "Anthropic", providerName: "Anthropic",
providerType: "anthropic", providerType: "anthropic",
}, },
}, })),
{
name: "claude-2.0",
available: true,
provider: {
id: "anthropic",
providerName: "Anthropic",
providerType: "anthropic",
},
},
{
name: "claude-2.1",
available: true,
provider: {
id: "anthropic",
providerName: "Anthropic",
providerType: "anthropic",
},
},
{
name: "claude-3-opus-20240229",
available: true,
provider: {
id: "anthropic",
providerName: "Anthropic",
providerType: "anthropic",
},
},
{
name: "claude-3-sonnet-20240229",
available: true,
provider: {
id: "anthropic",
providerName: "Anthropic",
providerType: "anthropic",
},
},
{
name: "claude-3-haiku-20240307",
available: true,
provider: {
id: "anthropic",
providerName: "Anthropic",
providerType: "anthropic",
},
},
] as const; ] as const;
export const CHAT_PAGE_SIZE = 15; export const CHAT_PAGE_SIZE = 15;
export const MAX_RENDER_MSG_COUNT = 45; export const MAX_RENDER_MSG_COUNT = 45;
// some famous webdav endpoints // some famous webdav endpoints
export const internalWhiteWebDavEndpoints = [ export const internalAllowedWebDavEndpoints = [
"https://dav.jianguoyun.com/dav/", "https://dav.jianguoyun.com/dav/",
"https://dav.dropdav.com/", "https://dav.dropdav.com/",
"https://dav.box.com/dav", "https://dav.box.com/dav",

View File

@ -296,7 +296,7 @@ const en: LocaleType = {
Endpoint: { Endpoint: {
Title: "OpenAI Endpoint", Title: "OpenAI Endpoint",
SubTitle: "Must starts with http(s):// or use /api/openai as default", SubTitle: "Must start with http(s):// or use /api/openai as default",
}, },
}, },
Azure: { Azure: {

View File

@ -8,6 +8,7 @@ import { getHeaders } from "../client/api";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { createPersistStore } from "../utils/store"; import { createPersistStore } from "../utils/store";
import { ensure } from "../utils/clone"; import { ensure } from "../utils/clone";
import { DEFAULT_CONFIG } from "./config";
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
@ -48,6 +49,7 @@ const DEFAULT_ACCESS_STATE = {
disableGPT4: false, disableGPT4: false,
disableFastLink: false, disableFastLink: false,
customModels: "", customModels: "",
defaultModel: "",
}; };
export const useAccessStore = createPersistStore( export const useAccessStore = createPersistStore(
@ -100,6 +102,13 @@ export const useAccessStore = createPersistStore(
}, },
}) })
.then((res) => res.json()) .then((res) => res.json())
.then((res) => {
// Set default model from env request
let defaultModel = res.defaultModel ?? "";
DEFAULT_CONFIG.modelConfig.model =
defaultModel !== "" ? defaultModel : "gpt-3.5-turbo";
return res;
})
.then((res: DangerConfig) => { .then((res: DangerConfig) => {
console.log("[Config] got config from server", res); console.log("[Config] got config from server", res);
set(() => ({ ...res })); set(() => ({ ...res }));

View File

@ -21,6 +21,8 @@ import { estimateTokenLength } from "../utils/token";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { createPersistStore } from "../utils/store"; import { createPersistStore } from "../utils/store";
import { identifyDefaultClaudeModel } from "../utils/checkers"; import { identifyDefaultClaudeModel } from "../utils/checkers";
import { collectModelsWithDefaultModel } from "../utils/model";
import { useAccessStore } from "./access";
export type ChatMessage = RequestMessage & { export type ChatMessage = RequestMessage & {
date: string; date: string;
@ -87,9 +89,19 @@ function createEmptySession(): ChatSession {
function getSummarizeModel(currentModel: string) { function getSummarizeModel(currentModel: string) {
// if it is using gpt-* models, force to use 3.5 to summarize // if it is using gpt-* models, force to use 3.5 to summarize
if (currentModel.startsWith("gpt")) { if (currentModel.startsWith("gpt")) {
return SUMMARIZE_MODEL; const configStore = useAppConfig.getState();
const accessStore = useAccessStore.getState();
const allModel = collectModelsWithDefaultModel(
configStore.models,
[configStore.customModels, accessStore.customModels].join(","),
accessStore.defaultModel,
);
const summarizeModel = allModel.find(
(m) => m.name === SUMMARIZE_MODEL && m.available,
);
return summarizeModel?.name ?? currentModel;
} }
if (currentModel.startsWith("gemini-pro")) { if (currentModel.startsWith("gemini")) {
return GEMINI_SUMMARIZE_MODEL; return GEMINI_SUMMARIZE_MODEL;
} }
return currentModel; return currentModel;
@ -416,14 +428,13 @@ export const useChatStore = createPersistStore(
getMemoryPrompt() { getMemoryPrompt() {
const session = get().currentSession(); const session = get().currentSession();
if (session.memoryPrompt.length) {
return { return {
role: "system", role: "system",
content: content: Locale.Store.Prompt.History(session.memoryPrompt),
session.memoryPrompt.length > 0
? Locale.Store.Prompt.History(session.memoryPrompt)
: "",
date: "", date: "",
} as ChatMessage; } as ChatMessage;
}
}, },
getMessagesWithMemory() { getMessagesWithMemory() {
@ -459,16 +470,15 @@ export const useChatStore = createPersistStore(
systemPrompts.at(0)?.content ?? "empty", systemPrompts.at(0)?.content ?? "empty",
); );
} }
const memoryPrompt = get().getMemoryPrompt();
// long term memory // long term memory
const shouldSendLongTermMemory = const shouldSendLongTermMemory =
modelConfig.sendMemory && modelConfig.sendMemory &&
session.memoryPrompt && session.memoryPrompt &&
session.memoryPrompt.length > 0 && session.memoryPrompt.length > 0 &&
session.lastSummarizeIndex > clearContextIndex; session.lastSummarizeIndex > clearContextIndex;
const longTermMemoryPrompts = shouldSendLongTermMemory const longTermMemoryPrompts =
? [get().getMemoryPrompt()] shouldSendLongTermMemory && memoryPrompt ? [memoryPrompt] : [];
: [];
const longTermMemoryStartIndex = session.lastSummarizeIndex; const longTermMemoryStartIndex = session.lastSummarizeIndex;
// short term memory // short term memory
@ -593,9 +603,11 @@ export const useChatStore = createPersistStore(
Math.max(0, n - modelConfig.historyMessageCount), Math.max(0, n - modelConfig.historyMessageCount),
); );
} }
const memoryPrompt = get().getMemoryPrompt();
if (memoryPrompt) {
// add memory prompt // add memory prompt
toBeSummarizedMsgs.unshift(get().getMemoryPrompt()); toBeSummarizedMsgs.unshift(memoryPrompt);
}
const lastSummarizeIndex = session.messages.length; const lastSummarizeIndex = session.messages.length;

View File

@ -1,5 +1,4 @@
import { LLMModel } from "../client/api"; import { LLMModel } from "../client/api";
import { isMacOS } from "../utils";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { import {
DEFAULT_INPUT_TEMPLATE, DEFAULT_INPUT_TEMPLATE,
@ -25,6 +24,8 @@ export enum Theme {
Light = "light", Light = "light",
} }
const config = getClientConfig();
export const DEFAULT_CONFIG = { export const DEFAULT_CONFIG = {
lastUpdate: Date.now(), // timestamp, to merge state lastUpdate: Date.now(), // timestamp, to merge state
@ -32,7 +33,7 @@ export const DEFAULT_CONFIG = {
avatar: "1f603", avatar: "1f603",
fontSize: 14, fontSize: 14,
theme: Theme.Auto as Theme, theme: Theme.Auto as Theme,
tightBorder: !!getClientConfig()?.isApp, tightBorder: !!config?.isApp,
sendPreviewBubble: true, sendPreviewBubble: true,
enableAutoGenerateTitle: true, enableAutoGenerateTitle: true,
sidebarWidth: DEFAULT_SIDEBAR_WIDTH, sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
@ -56,7 +57,7 @@ export const DEFAULT_CONFIG = {
historyMessageCount: 4, historyMessageCount: 4,
compressMessageLengthThreshold: 1000, compressMessageLengthThreshold: 1000,
enableInjectSystemPrompts: true, enableInjectSystemPrompts: true,
template: DEFAULT_INPUT_TEMPLATE, template: config?.template ?? DEFAULT_INPUT_TEMPLATE,
}, },
}; };
@ -132,7 +133,7 @@ export const useAppConfig = createPersistStore(
}), }),
{ {
name: StoreKey.Config, name: StoreKey.Config,
version: 3.8, version: 3.9,
migrate(persistedState, version) { migrate(persistedState, version) {
const state = persistedState as ChatConfig; const state = persistedState as ChatConfig;
@ -163,6 +164,13 @@ export const useAppConfig = createPersistStore(
state.lastUpdate = Date.now(); state.lastUpdate = Date.now();
} }
if (version < 3.9) {
state.modelConfig.template =
state.modelConfig.template !== DEFAULT_INPUT_TEMPLATE
? state.modelConfig.template
: config?.template ?? DEFAULT_INPUT_TEMPLATE;
}
return state as any; return state as any;
}, },
}, },

View File

@ -97,11 +97,18 @@ export const useSyncStore = createPersistStore(
const client = this.getClient(); const client = this.getClient();
try { try {
const remoteState = JSON.parse( const remoteState = await client.get(config.username);
if (!remoteState || remoteState === "") {
await client.set(config.username, JSON.stringify(localState));
console.log("[Sync] Remote state is empty, using local state instead.");
return
} else {
const parsedRemoteState = JSON.parse(
await client.get(config.username), await client.get(config.username),
) as AppState; ) as AppState;
mergeAppState(localState, remoteState); mergeAppState(localState, parsedRemoteState);
setLocalAppState(localState); setLocalAppState(localState);
}
} catch (e) { } catch (e) {
console.log("[Sync] failed to get remote state", e); console.log("[Sync] failed to get remote state", e);
throw e; throw e;

View File

@ -86,6 +86,7 @@
@include dark; @include dark;
} }
} }
html { html {
height: var(--full-height); height: var(--full-height);
@ -110,6 +111,10 @@ body {
@media only screen and (max-width: 600px) { @media only screen and (max-width: 600px) {
background-color: var(--second); background-color: var(--second);
} }
*:focus-visible {
outline: none;
}
} }
::-webkit-scrollbar { ::-webkit-scrollbar {

View File

@ -83,48 +83,6 @@ export async function downloadAs(text: string, filename: string) {
} }
} }
export function compressImage(file: File, maxSize: number): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (readerEvent: any) => {
const image = new Image();
image.onload = () => {
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
let width = image.width;
let height = image.height;
let quality = 0.9;
let dataUrl;
do {
canvas.width = width;
canvas.height = height;
ctx?.clearRect(0, 0, canvas.width, canvas.height);
ctx?.drawImage(image, 0, 0, width, height);
dataUrl = canvas.toDataURL("image/jpeg", quality);
if (dataUrl.length < maxSize) break;
if (quality > 0.5) {
// Prioritize quality reduction
quality -= 0.1;
} else {
// Then reduce the size
width *= 0.9;
height *= 0.9;
}
} while (dataUrl.length > maxSize);
resolve(dataUrl);
};
image.onerror = reject;
image.src = readerEvent.target.result;
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
export function readFromFile() { export function readFromFile() {
return new Promise<string>((res, rej) => { return new Promise<string>((res, rej) => {
const fileInput = document.createElement("input"); const fileInput = document.createElement("input");
@ -290,8 +248,19 @@ export function getMessageImages(message: RequestMessage): string[] {
} }
export function isVisionModel(model: string) { export function isVisionModel(model: string) {
const visionKeywords = ["vision", "claude-3"]; // Note: This is a better way using the TypeScript feature instead of `&&` or `||` (ts v5.5.0-dev.20240314 I've been using)
const isGpt4Turbo = model.includes("gpt-4-turbo") && !model.includes("preview");
return visionKeywords.some((keyword) => model.includes(keyword)) || isGpt4Turbo; const visionKeywords = [
"vision",
"claude-3",
"gemini-1.5-pro",
"gemini-1.5-flash",
"gpt-4o",
];
const isGpt4Turbo =
model.includes("gpt-4-turbo") && !model.includes("preview");
return (
visionKeywords.some((keyword) => model.includes(keyword)) || isGpt4Turbo
);
} }

54
app/utils/chat.ts Normal file
View File

@ -0,0 +1,54 @@
import heic2any from "heic2any";
export function compressImage(file: File, maxSize: number): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (readerEvent: any) => {
const image = new Image();
image.onload = () => {
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
let width = image.width;
let height = image.height;
let quality = 0.9;
let dataUrl;
do {
canvas.width = width;
canvas.height = height;
ctx?.clearRect(0, 0, canvas.width, canvas.height);
ctx?.drawImage(image, 0, 0, width, height);
dataUrl = canvas.toDataURL("image/jpeg", quality);
if (dataUrl.length < maxSize) break;
if (quality > 0.5) {
// Prioritize quality reduction
quality -= 0.1;
} else {
// Then reduce the size
width *= 0.9;
height *= 0.9;
}
} while (dataUrl.length > maxSize);
resolve(dataUrl);
};
image.onerror = reject;
image.src = readerEvent.target.result;
};
reader.onerror = reject;
if (file.type.includes("heic")) {
heic2any({ blob: file, toType: "image/jpeg" })
.then((blob) => {
reader.readAsDataURL(blob as Blob);
})
.catch((e) => {
reject(e);
});
}
reader.readAsDataURL(file);
});
}

View File

@ -93,14 +93,17 @@ export function createUpstashClient(store: SyncStore) {
} }
let url; let url;
if (proxyUrl.length > 0 || proxyUrl === "/") { const pathPrefix = "/api/upstash/";
let u = new URL(proxyUrl + "/api/upstash/" + path);
try {
let u = new URL(proxyUrl + pathPrefix + path);
// add query params // add query params
u.searchParams.append("endpoint", config.endpoint); u.searchParams.append("endpoint", config.endpoint);
url = u.toString(); url = u.toString();
} else { } catch (e) {
url = "/api/upstash/" + path + "?endpoint=" + config.endpoint; url = pathPrefix + path + "?endpoint=" + config.endpoint;
} }
return url; return url;
}, },
}; };

View File

@ -63,26 +63,26 @@ export function createWebDavClient(store: SyncStore) {
}; };
}, },
path(path: string, proxyUrl: string = "") { path(path: string, proxyUrl: string = "") {
// if (!path.endsWith("/")) {
// path += "/";
// }
if (path.startsWith("/")) { if (path.startsWith("/")) {
path = path.slice(1); path = path.slice(1);
} }
if (proxyUrl.length > 0 && !proxyUrl.endsWith("/")) { if (proxyUrl.endsWith("/")) {
proxyUrl += "/"; proxyUrl = proxyUrl.slice(0, -1);
} }
let url; let url;
if (proxyUrl.length > 0 || proxyUrl === "/") { const pathPrefix = "/api/webdav/";
let u = new URL(proxyUrl + "api/webdav/" + path);
try {
let u = new URL(proxyUrl + pathPrefix + path);
// add query params // add query params
u.searchParams.append("endpoint", config.endpoint); u.searchParams.append("endpoint", config.endpoint);
url = u.toString(); url = u.toString();
} else { } catch (e) {
url = "/api/upstash/" + path + "?endpoint=" + config.endpoint; url = pathPrefix + path + "?endpoint=" + config.endpoint;
} }
return url; return url;
}, },
}; };

View File

@ -1,14 +1,15 @@
import { useMemo } from "react"; import { useMemo } from "react";
import { useAccessStore, useAppConfig } from "../store"; import { useAccessStore, useAppConfig } from "../store";
import { collectModels } from "./model"; import { collectModels, collectModelsWithDefaultModel } from "./model";
export function useAllModels() { export function useAllModels() {
const accessStore = useAccessStore(); const accessStore = useAccessStore();
const configStore = useAppConfig(); const configStore = useAppConfig();
const models = useMemo(() => { const models = useMemo(() => {
return collectModels( return collectModelsWithDefaultModel(
configStore.models, configStore.models,
[configStore.customModels, accessStore.customModels].join(","), [configStore.customModels, accessStore.customModels].join(","),
accessStore.defaultModel,
); );
}, [accessStore.customModels, configStore.customModels, configStore.models]); }, [accessStore.customModels, configStore.customModels, configStore.models]);

View File

@ -1,5 +1,11 @@
import { LLMModel } from "../client/api"; import { LLMModel } from "../client/api";
const customProvider = (modelName: string) => ({
id: modelName,
providerName: "",
providerType: "custom",
});
export function collectModelTable( export function collectModelTable(
models: readonly LLMModel[], models: readonly LLMModel[],
customModels: string, customModels: string,
@ -11,6 +17,7 @@ export function collectModelTable(
name: string; name: string;
displayName: string; displayName: string;
provider?: LLMModel["provider"]; // Marked as optional provider?: LLMModel["provider"]; // Marked as optional
isDefault?: boolean;
} }
> = {}; > = {};
@ -22,12 +29,6 @@ export function collectModelTable(
}; };
}); });
const customProvider = (modelName: string) => ({
id: modelName,
providerName: "",
providerType: "custom",
});
// server custom models // server custom models
customModels customModels
.split(",") .split(",")
@ -52,6 +53,24 @@ export function collectModelTable(
}; };
} }
}); });
return modelTable;
}
export function collectModelTableWithDefaultModel(
models: readonly LLMModel[],
customModels: string,
defaultModel: string,
) {
let modelTable = collectModelTable(models, customModels);
if (defaultModel && defaultModel !== "") {
modelTable[defaultModel] = {
...modelTable[defaultModel],
name: defaultModel,
available: true,
isDefault: true,
};
}
return modelTable; return modelTable;
} }
@ -67,3 +86,17 @@ export function collectModels(
return allModels; return allModels;
} }
export function collectModelsWithDefaultModel(
models: readonly LLMModel[],
customModels: string,
defaultModel: string,
) {
const modelTable = collectModelTableWithDefaultModel(
models,
customModels,
defaultModel,
);
const allModels = Object.values(modelTable);
return allModels;
}

View File

@ -24,10 +24,11 @@
"@vercel/speed-insights": "^1.0.2", "@vercel/speed-insights": "^1.0.2",
"emoji-picker-react": "^4.9.2", "emoji-picker-react": "^4.9.2",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"heic2any": "^0.0.4",
"html-to-image": "^1.11.11", "html-to-image": "^1.11.11",
"mermaid": "^10.6.1", "mermaid": "^10.6.1",
"nanoid": "^5.0.3", "nanoid": "^5.0.3",
"next": "^13.4.9", "next": "^14.1.1",
"node-fetch": "^3.3.1", "node-fetch": "^3.3.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

View File

@ -9,7 +9,7 @@
}, },
"package": { "package": {
"productName": "NextChat", "productName": "NextChat",
"version": "2.11.3" "version": "2.12.3"
}, },
"tauri": { "tauri": {
"allowlist": { "allowlist": {

165
yarn.lock
View File

@ -1218,10 +1218,10 @@
"@jridgewell/resolve-uri" "3.1.0" "@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14" "@jridgewell/sourcemap-codec" "1.4.14"
"@next/env@13.4.9": "@next/env@14.1.1":
version "13.4.9" version "14.1.1"
resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.9.tgz#b77759514dd56bfa9791770755a2482f4d6ca93e" resolved "https://registry.yarnpkg.com/@next/env/-/env-14.1.1.tgz#80150a8440eb0022a73ba353c6088d419b908bac"
integrity sha512-vuDRK05BOKfmoBYLNi2cujG2jrYbEod/ubSSyqgmEx9n/W3eZaJQdRNhTfumO+qmq/QTzLurW487n/PM/fHOkw== integrity sha512-7CnQyD5G8shHxQIIg3c7/pSeYFeMhsNbpU/bmvH7ZnDql7mNRgg8O2JZrhrc/soFnfBnKP4/xXNiiSIPn2w8gA==
"@next/eslint-plugin-next@13.4.19": "@next/eslint-plugin-next@13.4.19":
version "13.4.19" version "13.4.19"
@ -1230,50 +1230,50 @@
dependencies: dependencies:
glob "7.1.7" glob "7.1.7"
"@next/swc-darwin-arm64@13.4.9": "@next/swc-darwin-arm64@14.1.1":
version "13.4.9" version "14.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.9.tgz#0ed408d444bbc6b0a20f3506a9b4222684585677" resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.1.tgz#b74ba7c14af7d05fa2848bdeb8ee87716c939b64"
integrity sha512-TVzGHpZoVBk3iDsTOQA/R6MGmFp0+17SWXMEWd6zG30AfuELmSSMe2SdPqxwXU0gbpWkJL1KgfLzy5ReN0crqQ== integrity sha512-yDjSFKQKTIjyT7cFv+DqQfW5jsD+tVxXTckSe1KIouKk75t1qZmj/mV3wzdmFb0XHVGtyRjDMulfVG8uCKemOQ==
"@next/swc-darwin-x64@13.4.9": "@next/swc-darwin-x64@14.1.1":
version "13.4.9" version "14.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.9.tgz#a08fccdee68201522fe6618ec81f832084b222f8" resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.1.tgz#82c3e67775e40094c66e76845d1a36cc29c9e78b"
integrity sha512-aSfF1fhv28N2e7vrDZ6zOQ+IIthocfaxuMWGReB5GDriF0caTqtHttAvzOMgJgXQtQx6XhyaJMozLTSEXeNN+A== integrity sha512-KCQmBL0CmFmN8D64FHIZVD9I4ugQsDBBEJKiblXGgwn7wBCSe8N4Dx47sdzl4JAg39IkSN5NNrr8AniXLMb3aw==
"@next/swc-linux-arm64-gnu@13.4.9": "@next/swc-linux-arm64-gnu@14.1.1":
version "13.4.9" version "14.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.9.tgz#1798c2341bb841e96521433eed00892fb24abbd1" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.1.tgz#4f4134457b90adc5c3d167d07dfb713c632c0caa"
integrity sha512-JhKoX5ECzYoTVyIy/7KykeO4Z2lVKq7HGQqvAH+Ip9UFn1MOJkOnkPRB7v4nmzqAoY+Je05Aj5wNABR1N18DMg== integrity sha512-YDQfbWyW0JMKhJf/T4eyFr4b3tceTorQ5w2n7I0mNVTFOvu6CGEzfwT3RSAQGTi/FFMTFcuspPec/7dFHuP7Eg==
"@next/swc-linux-arm64-musl@13.4.9": "@next/swc-linux-arm64-musl@14.1.1":
version "13.4.9" version "14.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.9.tgz#cee04c51610eddd3638ce2499205083656531ea0" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.1.tgz#594bedafaeba4a56db23a48ffed2cef7cd09c31a"
integrity sha512-OOn6zZBIVkm/4j5gkPdGn4yqQt+gmXaLaSjRSO434WplV8vo2YaBNbSHaTM9wJpZTHVDYyjzuIYVEzy9/5RVZw== integrity sha512-fiuN/OG6sNGRN/bRFxRvV5LyzLB8gaL8cbDH5o3mEiVwfcMzyE5T//ilMmaTrnA8HLMS6hoz4cHOu6Qcp9vxgQ==
"@next/swc-linux-x64-gnu@13.4.9": "@next/swc-linux-x64-gnu@14.1.1":
version "13.4.9" version "14.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.9.tgz#1932d0367916adbc6844b244cda1d4182bd11f7a" resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.1.tgz#cb4e75f1ff2b9bcadf2a50684605928ddfc58528"
integrity sha512-iA+fJXFPpW0SwGmx/pivVU+2t4zQHNOOAr5T378PfxPHY6JtjV6/0s1vlAJUdIHeVpX98CLp9k5VuKgxiRHUpg== integrity sha512-rv6AAdEXoezjbdfp3ouMuVqeLjE1Bin0AuE6qxE6V9g3Giz5/R3xpocHoAi7CufRR+lnkuUjRBn05SYJ83oKNQ==
"@next/swc-linux-x64-musl@13.4.9": "@next/swc-linux-x64-musl@14.1.1":
version "13.4.9" version "14.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.9.tgz#a66aa8c1383b16299b72482f6360facd5cde3c7a" resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.1.tgz#15f26800df941b94d06327f674819ab64b272e25"
integrity sha512-rlNf2WUtMM+GAQrZ9gMNdSapkVi3koSW3a+dmBVp42lfugWVvnyzca/xJlN48/7AGx8qu62WyO0ya1ikgOxh6A== integrity sha512-YAZLGsaNeChSrpz/G7MxO3TIBLaMN8QWMr3X8bt6rCvKovwU7GqQlDu99WdvF33kI8ZahvcdbFsy4jAFzFX7og==
"@next/swc-win32-arm64-msvc@13.4.9": "@next/swc-win32-arm64-msvc@14.1.1":
version "13.4.9" version "14.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.9.tgz#39482ee856c867177a612a30b6861c75e0736a4a" resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.1.tgz#060c134fa7fa843666e3e8574972b2b723773dd9"
integrity sha512-5T9ybSugXP77nw03vlgKZxD99AFTHaX8eT1ayKYYnGO9nmYhJjRPxcjU5FyYI+TdkQgEpIcH7p/guPLPR0EbKA== integrity sha512-1L4mUYPBMvVDMZg1inUYyPvFSduot0g73hgfD9CODgbr4xiTYe0VOMTZzaRqYJYBA9mana0x4eaAaypmWo1r5A==
"@next/swc-win32-ia32-msvc@13.4.9": "@next/swc-win32-ia32-msvc@14.1.1":
version "13.4.9" version "14.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.9.tgz#29db85e34b597ade1a918235d16a760a9213c190" resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.1.tgz#5c06889352b1f77e3807834a0d0afd7e2d2d1da2"
integrity sha512-ojZTCt1lP2ucgpoiFgrFj07uq4CZsq4crVXpLGgQfoFq00jPKRPgesuGPaz8lg1yLfvafkU3Jd1i8snKwYR3LA== integrity sha512-jvIE9tsuj9vpbbXlR5YxrghRfMuG0Qm/nZ/1KDHc+y6FpnZ/apsgh+G6t15vefU0zp3WSpTMIdXRUsNl/7RSuw==
"@next/swc-win32-x64-msvc@13.4.9": "@next/swc-win32-x64-msvc@14.1.1":
version "13.4.9" version "14.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.9.tgz#0c2758164cccd61bc5a1c6cd8284fe66173e4a2b" resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.1.tgz#d38c63a8f9b7f36c1470872797d3735b4a9c5c52"
integrity sha512-QbT03FXRNdpuL+e9pLnu+XajZdm/TtIXVYY4lA9t+9l0fLZbHXDYEKitAqxrOj37o3Vx5ufxiRAniaIebYDCgw== integrity sha512-S6K6EHDU5+1KrBDLko7/c1MNy/Ya73pIAmvKeFwsF4RmBFJSO7/7YeD4FnZ4iBdzE69PpQ4sOMU9ORKeNuxe8A==
"@next/third-parties@^14.1.0": "@next/third-parties@^14.1.0":
version "14.1.0" version "14.1.0"
@ -1424,10 +1424,10 @@
"@svgr/plugin-jsx" "^6.5.1" "@svgr/plugin-jsx" "^6.5.1"
"@svgr/plugin-svgo" "^6.5.1" "@svgr/plugin-svgo" "^6.5.1"
"@swc/helpers@0.5.1": "@swc/helpers@0.5.2":
version "0.5.1" version "0.5.2"
resolved "https://registry.npmmirror.com/@swc/helpers/-/helpers-0.5.1.tgz#e9031491aa3f26bfcc974a67f48bd456c8a5357a" resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.2.tgz#85ea0c76450b61ad7d10a37050289eded783c27d"
integrity sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg== integrity sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==
dependencies: dependencies:
tslib "^2.4.0" tslib "^2.4.0"
@ -2130,10 +2130,10 @@ camelcase@^6.2.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001503: caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001503, caniuse-lite@^1.0.30001579:
version "1.0.30001509" version "1.0.30001617"
resolved "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001509.tgz#2b7ad5265392d6d2de25cd8776d1ab3899570d14" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz#809bc25f3f5027ceb33142a7d6c40759d7a901eb"
integrity sha512-2uDDk+TRiTX5hMcUYT/7CSyzMZxjfGu0vAUjS2g0LSD8UoXOv0LtpH4LxGMemsiPq6LCVIUjNwVM0erkOkGCDA== integrity sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==
ccount@^2.0.0: ccount@^2.0.0:
version "2.0.1" version "2.0.1"
@ -3525,7 +3525,7 @@ gopd@^1.0.1:
dependencies: dependencies:
get-intrinsic "^1.1.3" get-intrinsic "^1.1.3"
graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
version "4.2.11" version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
@ -3669,6 +3669,11 @@ heap@^0.2.6:
resolved "https://registry.npmmirror.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" resolved "https://registry.npmmirror.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc"
integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==
heic2any@^0.0.4:
version "0.0.4"
resolved "https://registry.npmmirror.com/heic2any/-/heic2any-0.0.4.tgz#eddb8e6fec53c8583a6e18b65069bb5e8d19028a"
integrity sha512-3lLnZiDELfabVH87htnRolZ2iehX9zwpRyGNz22GKXIu0fznlblf0/ftppXKNqS26dqFSeqfIBhAmAj/uSp0cA==
highlight.js@~11.7.0: highlight.js@~11.7.0:
version "11.7.0" version "11.7.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.7.0.tgz#3ff0165bc843f8c9bce1fd89e2fda9143d24b11e" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.7.0.tgz#3ff0165bc843f8c9bce1fd89e2fda9143d24b11e"
@ -4753,10 +4758,10 @@ ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
nanoid@^3.3.4: nanoid@^3.3.6:
version "3.3.6" version "3.3.7"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
nanoid@^5.0.3: nanoid@^5.0.3:
version "5.0.3" version "5.0.3"
@ -4773,29 +4778,28 @@ neo-async@^2.6.2:
resolved "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" resolved "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
next@^13.4.9: next@^14.1.1:
version "13.4.9" version "14.1.1"
resolved "https://registry.yarnpkg.com/next/-/next-13.4.9.tgz#473de5997cb4c5d7a4fb195f566952a1cbffbeba" resolved "https://registry.yarnpkg.com/next/-/next-14.1.1.tgz#92bd603996c050422a738e90362dff758459a171"
integrity sha512-vtefFm/BWIi/eWOqf1GsmKG3cjKw1k3LjuefKRcL3iiLl3zWzFdPG3as6xtxrGO6gwTzzaO1ktL4oiHt/uvTjA== integrity sha512-McrGJqlGSHeaz2yTRPkEucxQKe5Zq7uPwyeHNmJaZNY4wx9E9QdxmTp310agFRoMuIYgQrCrT3petg13fSVOww==
dependencies: dependencies:
"@next/env" "13.4.9" "@next/env" "14.1.1"
"@swc/helpers" "0.5.1" "@swc/helpers" "0.5.2"
busboy "1.6.0" busboy "1.6.0"
caniuse-lite "^1.0.30001406" caniuse-lite "^1.0.30001579"
postcss "8.4.14" graceful-fs "^4.2.11"
postcss "8.4.31"
styled-jsx "5.1.1" styled-jsx "5.1.1"
watchpack "2.4.0"
zod "3.21.4"
optionalDependencies: optionalDependencies:
"@next/swc-darwin-arm64" "13.4.9" "@next/swc-darwin-arm64" "14.1.1"
"@next/swc-darwin-x64" "13.4.9" "@next/swc-darwin-x64" "14.1.1"
"@next/swc-linux-arm64-gnu" "13.4.9" "@next/swc-linux-arm64-gnu" "14.1.1"
"@next/swc-linux-arm64-musl" "13.4.9" "@next/swc-linux-arm64-musl" "14.1.1"
"@next/swc-linux-x64-gnu" "13.4.9" "@next/swc-linux-x64-gnu" "14.1.1"
"@next/swc-linux-x64-musl" "13.4.9" "@next/swc-linux-x64-musl" "14.1.1"
"@next/swc-win32-arm64-msvc" "13.4.9" "@next/swc-win32-arm64-msvc" "14.1.1"
"@next/swc-win32-ia32-msvc" "13.4.9" "@next/swc-win32-ia32-msvc" "14.1.1"
"@next/swc-win32-x64-msvc" "13.4.9" "@next/swc-win32-x64-msvc" "14.1.1"
node-domexception@^1.0.0: node-domexception@^1.0.0:
version "1.0.0" version "1.0.0"
@ -5036,12 +5040,12 @@ pidtree@^0.6.0:
resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c"
integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==
postcss@8.4.14: postcss@8.4.31:
version "8.4.14" version "8.4.31"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d"
integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==
dependencies: dependencies:
nanoid "^3.3.4" nanoid "^3.3.6"
picocolors "^1.0.0" picocolors "^1.0.0"
source-map-js "^1.0.2" source-map-js "^1.0.2"
@ -6039,7 +6043,7 @@ vfile@^5.0.0:
unist-util-stringify-position "^3.0.0" unist-util-stringify-position "^3.0.0"
vfile-message "^3.0.0" vfile-message "^3.0.0"
watchpack@2.4.0, watchpack@^2.4.0: watchpack@^2.4.0:
version "2.4.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
@ -6185,11 +6189,6 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
zod@3.21.4:
version "3.21.4"
resolved "https://registry.npmmirror.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db"
integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==
zustand@^4.3.8: zustand@^4.3.8:
version "4.3.8" version "4.3.8"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.8.tgz#37113df8e9e1421b0be1b2dca02b49b76210e7c4" resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.8.tgz#37113df8e9e1421b0be1b2dca02b49b76210e7c4"