Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
da29a94714
|
@ -0,0 +1,8 @@
|
|||
# local env files
|
||||
.env*.local
|
||||
|
||||
# docker-compose env files
|
||||
.env
|
||||
|
||||
*.key
|
||||
*.key.pub
|
|
@ -8,6 +8,16 @@ CODE=your-password
|
|||
# You can start service behind a proxy
|
||||
PROXY_URL=http://localhost:7890
|
||||
|
||||
# (optional)
|
||||
# Default: Empty
|
||||
# Googel Gemini Pro API key, set if you want to use Google Gemini Pro API.
|
||||
GOOGLE_API_KEY=
|
||||
|
||||
# (optional)
|
||||
# Default: https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent
|
||||
# Googel Gemini Pro API url, set if you want to customize Google Gemini Pro API url.
|
||||
GOOGLE_URL=
|
||||
|
||||
# Override openai api request base url. (optional)
|
||||
# Default: https://api.openai.com
|
||||
# Examples: http://your-openai-proxy.com
|
||||
|
|
|
@ -16,6 +16,7 @@ FROM base AS builder
|
|||
RUN apk update && apk add --no-cache git
|
||||
|
||||
ENV OPENAI_API_KEY=""
|
||||
ENV GOOGLE_API_KEY=""
|
||||
ENV CODE=""
|
||||
ENV NEXT_PUBLIC_ENABLE_NODEJS_PLUGIN=1
|
||||
|
||||
|
@ -32,6 +33,7 @@ RUN apk add proxychains-ng
|
|||
|
||||
ENV PROXY_URL=""
|
||||
ENV OPENAI_API_KEY=""
|
||||
ENV GOOGLE_API_KEY=""
|
||||
ENV CODE=""
|
||||
|
||||
COPY --from=builder /app/public ./public
|
||||
|
|
15
README.md
15
README.md
|
@ -3,7 +3,7 @@
|
|||
|
||||
<h1 align="center">ChatGPT Next Web LangChain</h1>
|
||||
|
||||
一键免费部署你的跨平台私人 ChatGPT 应用(基于 LangChain 实现插件功能)。
|
||||
一键免费部署你的跨平台私人 ChatGPT 应用, 支持 GPT3, GPT4 & Gemini Pro 模型。(基于 LangChain 实现插件功能)
|
||||
|
||||
[![Web][Web-image]][web-url]
|
||||
[![Windows][Windows-image]][download-url]
|
||||
|
@ -60,13 +60,10 @@
|
|||
- 使用本插件需要一定的专业知识,Stable Diffusion 本身的相关问题不在本项目的解答范围内,如果您确定要使用本插件请参考 [Stable Diffusion 插件配置指南](./docs/stable-diffusion-plugin-cn.md) 文档进行配置
|
||||
- StableDiffusion 插件需要配置对象存储服务,请参考 [对象存储服务配置指南](./docs/s3-oss.md) 配置
|
||||
- Arxiv
|
||||
- ⚠ 实验性支持 Gemini-Pro 模型
|
||||
- 支持 Gemini-Pro 模型(同步上游仓库并修改接口为流式传输)
|
||||
- 以下功能目前还不支持
|
||||
- `temperature` 等参数配置
|
||||
- **面具**和**系统提示词**功能(google 的相关接口不支持,后续支持会跟进)
|
||||
- **插件功能**
|
||||
- 如何启用
|
||||
- 配置 `CUSTOM_MODELS` 添加 `gemini-pro` 模型,如:`CUSTOM_MODELS=gemini-pro`
|
||||
- 配置密钥 `GOOGLE_API_KEY` ,key 可以在这里获取:https://ai.google.dev/tutorials/setup
|
||||
- 配置自定义接口地址(可选) `GOOGLE_BASE_URL`,可以使用我的这个项目搭建一个基于 vercel 的代理服务:[google-gemini-vercel-proxy](https://github.com/Hk-Gosuto/google-gemini-vercel-proxy)
|
||||
- 常见问题参考:[Gemini Prompting FAQs](https://js.langchain.com/docs/integrations/chat/google_generativeai#gemini-prompting-faqs)
|
||||
|
@ -194,6 +191,14 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填
|
|||
|
||||
如果你不想让用户查询余额,将此环境变量设置为 1 即可。
|
||||
|
||||
### `GOOGLE_API_KEY` (可选)
|
||||
|
||||
Google Gemini Pro Api Key.
|
||||
|
||||
### `GOOGLE_BASE_URL` (可选)
|
||||
|
||||
Google Gemini Pro Api Url.
|
||||
|
||||
## 部署
|
||||
|
||||
### 容器部署 (推荐)
|
||||
|
|
16
README_CN.md
16
README_CN.md
|
@ -1,14 +1,16 @@
|
|||
<div align="center">
|
||||
<img src="./docs/images/icon.svg" alt="预览"/>
|
||||
|
||||
<h1 align="center">ChatGPT Next Web</h1>
|
||||
<h1 align="center">NextChat</h1>
|
||||
|
||||
一键免费部署你的私人 ChatGPT 网页应用。
|
||||
一键免费部署你的私人 ChatGPT 网页应用,支持 GPT3, GPT4 & Gemini Pro 模型。
|
||||
|
||||
[演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) / [QQ 群](https://user-images.githubusercontent.com/16968934/228190818-7dd00845-e9b9-4363-97e5-44c507ac76da.jpeg) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) / [Donate](#捐赠-donate-usdt)
|
||||
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)
|
||||
|
||||
[](https://zeabur.com/templates/ZBUEFA)
|
||||
|
||||
[](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
|
||||
|
||||

|
||||
|
@ -19,7 +21,7 @@
|
|||
|
||||
1. 准备好你的 [OpenAI API Key](https://platform.openai.com/account/api-keys);
|
||||
2. 点击右侧按钮开始部署:
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web),直接使用 Github 账号登录即可,记得在环境变量页填入 API Key 和[页面访问密码](#配置页面访问密码) CODE;
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&env=GOOGLE_API_KEY&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web),直接使用 Github 账号登录即可,记得在环境变量页填入 API Key 和[页面访问密码](#配置页面访问密码) CODE;
|
||||
3. 部署完毕后,即可开始使用;
|
||||
4. (可选)[绑定自定义域名](https://vercel.com/docs/concepts/projects/domains/add-a-domain):Vercel 分配的域名 DNS 在某些区域被污染了,绑定自定义域名即可直连。
|
||||
|
||||
|
@ -104,6 +106,14 @@ Azure 密钥。
|
|||
|
||||
Azure Api 版本,你可以在这里找到:[Azure 文档](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#chat-completions)。
|
||||
|
||||
### `GOOGLE_API_KEY` (optional)
|
||||
|
||||
Google Gemini Pro 密钥.
|
||||
|
||||
### `GOOGLE_URL` (optional)
|
||||
|
||||
Google Gemini Pro Api Url.
|
||||
|
||||
### `HIDE_USER_API_KEY` (可选)
|
||||
|
||||
如果你不想让用户自行填入 API Key,将此环境变量设置为 1 即可。
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { NextRequest } from "next/server";
|
||||
import { getServerSideConfig } from "../config/server";
|
||||
import md5 from "spark-md5";
|
||||
import { ACCESS_CODE_PREFIX } from "../constant";
|
||||
import { ACCESS_CODE_PREFIX, ModelProvider } from "../constant";
|
||||
|
||||
function getIP(req: NextRequest) {
|
||||
let ip = req.ip ?? req.headers.get("x-real-ip");
|
||||
|
@ -16,15 +16,15 @@ function getIP(req: NextRequest) {
|
|||
|
||||
function parseApiKey(bearToken: string) {
|
||||
const token = bearToken.trim().replaceAll("Bearer ", "").trim();
|
||||
const isOpenAiKey = !token.startsWith(ACCESS_CODE_PREFIX);
|
||||
const isApiKey = !token.startsWith(ACCESS_CODE_PREFIX);
|
||||
|
||||
return {
|
||||
accessCode: isOpenAiKey ? "" : token.slice(ACCESS_CODE_PREFIX.length),
|
||||
apiKey: isOpenAiKey ? token : "",
|
||||
accessCode: isApiKey ? "" : token.slice(ACCESS_CODE_PREFIX.length),
|
||||
apiKey: isApiKey ? token : "",
|
||||
};
|
||||
}
|
||||
|
||||
export function auth(req: NextRequest) {
|
||||
export function auth(req: NextRequest, modelProvider: ModelProvider) {
|
||||
const authToken = req.headers.get("Authorization") ?? "";
|
||||
|
||||
// check if it is openai api key or user token
|
||||
|
@ -49,22 +49,23 @@ export function auth(req: NextRequest) {
|
|||
if (serverConfig.hideUserApiKey && !!apiKey) {
|
||||
return {
|
||||
error: true,
|
||||
msg: "you are not allowed to access openai with your own api key",
|
||||
msg: "you are not allowed to access with your own api key",
|
||||
};
|
||||
}
|
||||
|
||||
// if user does not provide an api key, inject system api key
|
||||
if (!apiKey) {
|
||||
const serverApiKey = serverConfig.isAzure
|
||||
const serverConfig = getServerSideConfig();
|
||||
|
||||
const systemApiKey =
|
||||
modelProvider === ModelProvider.GeminiPro
|
||||
? serverConfig.googleApiKey
|
||||
: serverConfig.isAzure
|
||||
? serverConfig.azureApiKey
|
||||
: serverConfig.apiKey;
|
||||
|
||||
if (serverApiKey) {
|
||||
if (systemApiKey) {
|
||||
console.log("[Auth] use system api key");
|
||||
req.headers.set(
|
||||
"Authorization",
|
||||
`${serverConfig.isAzure ? "" : "Bearer "}${serverApiKey}`,
|
||||
);
|
||||
req.headers.set("Authorization", `Bearer ${systemApiKey}`);
|
||||
} else {
|
||||
console.log("[Auth] admin did not provide an api key");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getServerSideConfig } from "../config/server";
|
||||
import { DEFAULT_MODELS, GOOGLE_BASE_URL, OPENAI_BASE_URL } from "../constant";
|
||||
import { DEFAULT_MODELS, OPENAI_BASE_URL, GEMINI_BASE_URL } from "../constant";
|
||||
import { collectModelTable } from "../utils/model";
|
||||
import { makeAzurePath } from "../azure";
|
||||
|
||||
|
@ -9,7 +9,16 @@ const serverConfig = getServerSideConfig();
|
|||
export async function requestOpenai(req: NextRequest) {
|
||||
const controller = new AbortController();
|
||||
|
||||
if (serverConfig.isAzure) {
|
||||
const authValue =
|
||||
req.headers
|
||||
.get("Authorization")
|
||||
?.trim()
|
||||
.replaceAll("Bearer ", "")
|
||||
.trim() ?? "";
|
||||
} else {
|
||||
const authValue = req.headers.get("Authorization") ?? "";
|
||||
}
|
||||
const authHeaderName = serverConfig.isAzure ? "api-key" : "Authorization";
|
||||
|
||||
let path = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll(
|
||||
|
@ -118,73 +127,3 @@ export async function requestOpenai(req: NextRequest) {
|
|||
clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
|
||||
export async function requestGoogleGemini(req: NextRequest) {
|
||||
const controller = new AbortController();
|
||||
|
||||
const authValue =
|
||||
req.headers.get("Authorization")?.replace("Bearer ", "") ?? "";
|
||||
const authHeaderName = "x-goog-api-key";
|
||||
|
||||
console.log(req.nextUrl);
|
||||
|
||||
let path = `${req.nextUrl.pathname}`.replaceAll("/api/google/", "");
|
||||
|
||||
let baseUrl = serverConfig.googleBaseUrl || GOOGLE_BASE_URL;
|
||||
|
||||
if (!baseUrl.startsWith("http")) {
|
||||
baseUrl = `https://${baseUrl}`;
|
||||
}
|
||||
|
||||
if (baseUrl.endsWith("/")) {
|
||||
baseUrl = baseUrl.slice(0, -1);
|
||||
}
|
||||
|
||||
console.log("[Proxy] ", path);
|
||||
console.log("[Google Base Url]", baseUrl);
|
||||
// this fix [Org ID] undefined in server side if not using custom point
|
||||
if (serverConfig.openaiOrgId !== undefined) {
|
||||
console.log("[Org ID]", serverConfig.openaiOrgId);
|
||||
}
|
||||
|
||||
const timeoutId = setTimeout(
|
||||
() => {
|
||||
controller.abort();
|
||||
},
|
||||
10 * 60 * 1000,
|
||||
);
|
||||
|
||||
const fetchUrl = `${baseUrl}/${path}?alt=sse`;
|
||||
const fetchOptions: RequestInit = {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Cache-Control": "no-store",
|
||||
[authHeaderName]: authValue,
|
||||
},
|
||||
method: req.method,
|
||||
body: req.body,
|
||||
// to fix #2485: https://stackoverflow.com/questions/55920957/cloudflare-worker-typeerror-one-time-use-body
|
||||
redirect: "manual",
|
||||
// @ts-ignore
|
||||
duplex: "half",
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch(fetchUrl, fetchOptions);
|
||||
|
||||
// to prevent browser prompt for credentials
|
||||
const newHeaders = new Headers(res.headers);
|
||||
newHeaders.delete("www-authenticate");
|
||||
// to disable nginx buffering
|
||||
newHeaders.set("X-Accel-Buffering", "no");
|
||||
|
||||
return new Response(res.body, {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
headers: newHeaders,
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,46 +1,98 @@
|
|||
import { prettyObject } from "@/app/utils/format";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { auth, googleAuth } from "../../auth";
|
||||
import { requestGoogleGemini } from "../../common";
|
||||
import { auth } from "../../auth";
|
||||
import { getServerSideConfig } from "@/app/config/server";
|
||||
import { GEMINI_BASE_URL, Google, ModelProvider } from "@/app/constant";
|
||||
|
||||
async function handle(
|
||||
req: NextRequest,
|
||||
{ params }: { params: { path: string[] } },
|
||||
) {
|
||||
console.log("[OpenAI Route] params ", params);
|
||||
console.log("[Google Route] params ", params);
|
||||
|
||||
if (req.method === "OPTIONS") {
|
||||
return NextResponse.json({ body: "OK" }, { status: 200 });
|
||||
}
|
||||
|
||||
const subpath = params.path.join("/");
|
||||
const controller = new AbortController();
|
||||
|
||||
// if (!ALLOWD_PATH.has(subpath)) {
|
||||
// console.log("[OpenAI Route] forbidden path ", subpath);
|
||||
// return NextResponse.json(
|
||||
// {
|
||||
// error: true,
|
||||
// msg: "you are not allowed to request " + subpath,
|
||||
// },
|
||||
// {
|
||||
// status: 403,
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
const serverConfig = getServerSideConfig();
|
||||
|
||||
const authResult = googleAuth(req);
|
||||
let baseUrl = serverConfig.googleUrl || GEMINI_BASE_URL;
|
||||
|
||||
if (!baseUrl.startsWith("http")) {
|
||||
baseUrl = `https://${baseUrl}`;
|
||||
}
|
||||
|
||||
if (baseUrl.endsWith("/")) {
|
||||
baseUrl = baseUrl.slice(0, -1);
|
||||
}
|
||||
|
||||
let path = `${req.nextUrl.pathname}`.replaceAll("/api/google/", "");
|
||||
|
||||
console.log("[Proxy] ", path);
|
||||
console.log("[Base Url]", baseUrl);
|
||||
|
||||
const timeoutId = setTimeout(
|
||||
() => {
|
||||
controller.abort();
|
||||
},
|
||||
10 * 60 * 1000,
|
||||
);
|
||||
|
||||
const authResult = auth(req, ModelProvider.GeminiPro);
|
||||
if (authResult.error) {
|
||||
return NextResponse.json(authResult, {
|
||||
status: 401,
|
||||
});
|
||||
}
|
||||
|
||||
const bearToken = req.headers.get("Authorization") ?? "";
|
||||
const token = bearToken.trim().replaceAll("Bearer ", "").trim();
|
||||
|
||||
const key = token ? token : serverConfig.googleApiKey;
|
||||
|
||||
if (!key) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: `missing GOOGLE_API_KEY in server env vars`,
|
||||
},
|
||||
{
|
||||
status: 401,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const fetchUrl = `${baseUrl}/${path}?key=${key}`;
|
||||
const fetchOptions: RequestInit = {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Cache-Control": "no-store",
|
||||
},
|
||||
method: req.method,
|
||||
body: req.body,
|
||||
// to fix #2485: https://stackoverflow.com/questions/55920957/cloudflare-worker-typeerror-one-time-use-body
|
||||
redirect: "manual",
|
||||
// @ts-ignore
|
||||
duplex: "half",
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await requestGoogleGemini(req);
|
||||
return response;
|
||||
} catch (e) {
|
||||
console.error("[OpenAI] ", e);
|
||||
return NextResponse.json(prettyObject(e));
|
||||
const res = await fetch(fetchUrl, fetchOptions);
|
||||
// to prevent browser prompt for credentials
|
||||
const newHeaders = new Headers(res.headers);
|
||||
newHeaders.delete("www-authenticate");
|
||||
// to disable nginx buffering
|
||||
newHeaders.set("X-Accel-Buffering", "no");
|
||||
|
||||
return new Response(res.body, {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
headers: newHeaders,
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { type OpenAIListModelResponse } from "@/app/client/platforms/openai";
|
||||
import { getServerSideConfig } from "@/app/config/server";
|
||||
import { OpenaiPath } from "@/app/constant";
|
||||
import { ModelProvider, OpenaiPath } from "@/app/constant";
|
||||
import { prettyObject } from "@/app/utils/format";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { auth } from "../../auth";
|
||||
|
@ -45,7 +45,7 @@ async function handle(
|
|||
);
|
||||
}
|
||||
|
||||
const authResult = auth(req);
|
||||
const authResult = auth(req, ModelProvider.GPT);
|
||||
if (authResult.error) {
|
||||
return NextResponse.json(authResult, {
|
||||
status: 401,
|
||||
|
@ -75,4 +75,22 @@ export const GET = handle;
|
|||
export const POST = handle;
|
||||
|
||||
export const runtime = "edge";
|
||||
export const preferredRegion = ['arn1', 'bom1', 'cdg1', 'cle1', 'cpt1', 'dub1', 'fra1', 'gru1', 'hnd1', 'iad1', 'icn1', 'kix1', 'lhr1', 'pdx1', 'sfo1', 'sin1', 'syd1'];
|
||||
export const preferredRegion = [
|
||||
"arn1",
|
||||
"bom1",
|
||||
"cdg1",
|
||||
"cle1",
|
||||
"cpt1",
|
||||
"dub1",
|
||||
"fra1",
|
||||
"gru1",
|
||||
"hnd1",
|
||||
"iad1",
|
||||
"icn1",
|
||||
"kix1",
|
||||
"lhr1",
|
||||
"pdx1",
|
||||
"sfo1",
|
||||
"sin1",
|
||||
"syd1",
|
||||
];
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
import { getClientConfig } from "../config/client";
|
||||
import { ACCESS_CODE_PREFIX, Azure, ServiceProvider } from "../constant";
|
||||
import { ChatMessage, ModelType, useAccessStore } from "../store";
|
||||
import {
|
||||
ACCESS_CODE_PREFIX,
|
||||
Azure,
|
||||
ModelProvider,
|
||||
ServiceProvider,
|
||||
} from "../constant";
|
||||
import { ChatMessage, ModelType, useAccessStore, useChatStore } from "../store";
|
||||
import { ChatGPTApi } from "./platforms/openai";
|
||||
import { GeminiApi } from "./platforms/google";
|
||||
import { FileApi } from "./platforms/utils";
|
||||
|
||||
import { GeminiProApi } from "./platforms/google";
|
||||
export const ROLES = ["system", "user", "assistant"] as const;
|
||||
export type MessageRole = (typeof ROLES)[number];
|
||||
|
||||
|
@ -61,6 +65,13 @@ export interface LLMUsage {
|
|||
export interface LLMModel {
|
||||
name: string;
|
||||
available: boolean;
|
||||
provider: LLMModelProvider;
|
||||
}
|
||||
|
||||
export interface LLMModelProvider {
|
||||
id: string;
|
||||
providerName: string;
|
||||
providerType: string;
|
||||
}
|
||||
|
||||
export abstract class LLMApi {
|
||||
|
@ -101,16 +112,15 @@ export class ClientApi {
|
|||
public llm: LLMApi;
|
||||
public file: FileApi;
|
||||
|
||||
constructor() {
|
||||
constructor(provider: ModelProvider = ModelProvider.GPT) {
|
||||
if (provider === ModelProvider.GeminiPro) {
|
||||
this.llm = new GeminiProApi();
|
||||
return;
|
||||
}
|
||||
this.llm = new ChatGPTApi();
|
||||
this.file = new FileApi();
|
||||
}
|
||||
|
||||
switch(model: string) {
|
||||
if (model.startsWith("gemini")) this.llm = new GeminiApi();
|
||||
else this.llm = new ChatGPTApi();
|
||||
}
|
||||
|
||||
config() {}
|
||||
|
||||
prompts() {}
|
||||
|
@ -127,7 +137,7 @@ export class ClientApi {
|
|||
{
|
||||
from: "human",
|
||||
value:
|
||||
"Share from [ChatGPT Next Web]: https://github.com/Yidadaa/ChatGPT-Next-Web",
|
||||
"Share from [NextChat]: https://github.com/Yidadaa/ChatGPT-Next-Web",
|
||||
},
|
||||
]);
|
||||
// 敬告二开开发者们,为了开源大模型的发展,请不要修改上述消息,此消息用于后续数据清洗使用
|
||||
|
@ -157,44 +167,21 @@ export class ClientApi {
|
|||
}
|
||||
}
|
||||
|
||||
export const api = new ClientApi();
|
||||
|
||||
export function getAuthHeaders() {
|
||||
const accessStore = useAccessStore.getState();
|
||||
const headers: Record<string, string> = {};
|
||||
|
||||
const isAzure = accessStore.provider === ServiceProvider.Azure;
|
||||
const authHeader = isAzure ? "api-key" : "Authorization";
|
||||
const apiKey = isAzure ? accessStore.azureApiKey : accessStore.openaiApiKey;
|
||||
|
||||
const makeBearer = (s: string) => `${isAzure ? "" : "Bearer "}${s.trim()}`;
|
||||
const validString = (x: string) => x && x.length > 0;
|
||||
|
||||
// use user's api key first
|
||||
if (validString(apiKey)) {
|
||||
headers[authHeader] = makeBearer(apiKey);
|
||||
} else if (
|
||||
accessStore.enabledAccessControl() &&
|
||||
validString(accessStore.accessCode)
|
||||
) {
|
||||
headers[authHeader] = makeBearer(
|
||||
ACCESS_CODE_PREFIX + accessStore.accessCode,
|
||||
);
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
export function getHeaders() {
|
||||
const accessStore = useAccessStore.getState();
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
"x-requested-with": "XMLHttpRequest",
|
||||
};
|
||||
|
||||
const modelConfig = useChatStore.getState().currentSession().mask.modelConfig;
|
||||
const isGoogle = modelConfig.model === "gemini-pro";
|
||||
const isAzure = accessStore.provider === ServiceProvider.Azure;
|
||||
const authHeader = isAzure ? "api-key" : "Authorization";
|
||||
const apiKey = isAzure ? accessStore.azureApiKey : accessStore.openaiApiKey;
|
||||
const apiKey = isGoogle
|
||||
? accessStore.googleApiKey
|
||||
: isAzure
|
||||
? accessStore.azureApiKey
|
||||
: accessStore.openaiApiKey;
|
||||
|
||||
const makeBearer = (s: string) => `${isAzure ? "" : "Bearer "}${s.trim()}`;
|
||||
const validString = (x: string) => x && x.length > 0;
|
||||
|
@ -213,31 +200,3 @@ export function getHeaders() {
|
|||
|
||||
return headers;
|
||||
}
|
||||
|
||||
export function getGeminiHeaders() {
|
||||
const accessStore = useAccessStore.getState();
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
"x-requested-with": "XMLHttpRequest",
|
||||
};
|
||||
|
||||
const authHeader = "Authorization";
|
||||
const apiKey = accessStore.googleApiKey;
|
||||
|
||||
const makeBearer = (s: string) => `${"Bearer "}${s.trim()}`;
|
||||
const validString = (x: string) => x && x.length > 0;
|
||||
|
||||
// use user's api key first
|
||||
if (validString(apiKey)) {
|
||||
headers[authHeader] = makeBearer(apiKey);
|
||||
} else if (
|
||||
accessStore.enabledAccessControl() &&
|
||||
validString(accessStore.accessCode)
|
||||
) {
|
||||
headers[authHeader] = makeBearer(
|
||||
ACCESS_CODE_PREFIX + accessStore.accessCode,
|
||||
);
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
|
|
@ -1,120 +1,77 @@
|
|||
import {
|
||||
ApiPath,
|
||||
DEFAULT_API_HOST,
|
||||
DEFAULT_MODELS,
|
||||
GooglePath,
|
||||
OpenaiPath,
|
||||
REQUEST_TIMEOUT_MS,
|
||||
ServiceProvider,
|
||||
} from "@/app/constant";
|
||||
import { Google, REQUEST_TIMEOUT_MS } from "@/app/constant";
|
||||
import { ChatOptions, getHeaders, LLMApi, LLMModel, LLMUsage } from "../api";
|
||||
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
|
||||
|
||||
import {
|
||||
AgentChatOptions,
|
||||
ChatOptions,
|
||||
getGeminiHeaders,
|
||||
getHeaders,
|
||||
LLMApi,
|
||||
LLMModel,
|
||||
LLMUsage,
|
||||
} from "../api";
|
||||
import Locale from "../../locales";
|
||||
import {
|
||||
EventStreamContentType,
|
||||
fetchEventSource,
|
||||
} from "@fortaine/fetch-event-source";
|
||||
import { prettyObject } from "@/app/utils/format";
|
||||
|
||||
export interface OpenAIListModelResponse {
|
||||
object: string;
|
||||
data: Array<{
|
||||
id: string;
|
||||
object: string;
|
||||
root: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export class GeminiApi implements LLMApi {
|
||||
private disableListModels = true;
|
||||
|
||||
path(path: string): string {
|
||||
const accessStore = useAccessStore.getState();
|
||||
let baseUrl = ApiPath.GoogleAI;
|
||||
// if (baseUrl.length === 0) {
|
||||
// const isApp = !!getClientConfig()?.isApp;
|
||||
// baseUrl = isApp ? DEFAULT_API_HOST : ApiPath.OpenAI;
|
||||
// }
|
||||
|
||||
// if (baseUrl.endsWith("/")) {
|
||||
// baseUrl = baseUrl.slice(0, baseUrl.length - 1);
|
||||
// }
|
||||
// if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.OpenAI)) {
|
||||
// baseUrl = "https://" + baseUrl;
|
||||
// }
|
||||
|
||||
// if (isAzure) {
|
||||
// path = makeAzurePath(path, accessStore.azureApiVersion);
|
||||
// }
|
||||
|
||||
return [baseUrl, path].join("/");
|
||||
}
|
||||
|
||||
import { getClientConfig } from "@/app/config/client";
|
||||
import Locale from "../../locales";
|
||||
import { getServerSideConfig } from "@/app/config/server";
|
||||
export class GeminiProApi implements LLMApi {
|
||||
extractMessage(res: any) {
|
||||
return res.choices?.at(0)?.message?.content ?? "";
|
||||
console.log("[Response] gemini-pro response: ", res);
|
||||
|
||||
return (
|
||||
res?.candidates?.at(0)?.content?.parts.at(0)?.text ||
|
||||
res?.error?.message ||
|
||||
""
|
||||
);
|
||||
}
|
||||
async chat(options: ChatOptions): Promise<void> {
|
||||
const messages = options.messages.map((v) => ({
|
||||
role: v.role.replace("assistant", "model").replace("system", "user"),
|
||||
parts: [{ text: v.content }],
|
||||
}));
|
||||
|
||||
// google requires that role in neighboring messages must not be the same
|
||||
for (let i = 0; i < messages.length - 1; ) {
|
||||
// Check if current and next item both have the role "model"
|
||||
if (messages[i].role === messages[i + 1].role) {
|
||||
// Concatenate the 'parts' of the current and next item
|
||||
messages[i].parts = messages[i].parts.concat(messages[i + 1].parts);
|
||||
// Remove the next item
|
||||
messages.splice(i + 1, 1);
|
||||
} else {
|
||||
// Move to the next item
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
async chat(options: ChatOptions) {
|
||||
const messages: any[] = [];
|
||||
console.log(options.messages);
|
||||
let systemPrompt = "";
|
||||
for (const v of options.messages) {
|
||||
if (v.role === "system") {
|
||||
// systemPrompt = v.content;
|
||||
continue;
|
||||
}
|
||||
let content = v.content;
|
||||
if (systemPrompt !== "") {
|
||||
content = `${systemPrompt}\n${content}`;
|
||||
systemPrompt = "";
|
||||
}
|
||||
let message: {
|
||||
role: string;
|
||||
parts: { text: string }[];
|
||||
} = {
|
||||
role: v.role === "assistant" ? "model" : "user",
|
||||
parts: [],
|
||||
const modelConfig = {
|
||||
...useAppConfig.getState().modelConfig,
|
||||
...useChatStore.getState().currentSession().mask.modelConfig,
|
||||
...{
|
||||
model: options.config.model,
|
||||
},
|
||||
};
|
||||
message.parts.push({
|
||||
text: content,
|
||||
});
|
||||
messages.push(message);
|
||||
}
|
||||
|
||||
const requestPayload = {
|
||||
contents: messages,
|
||||
generationConfig: {
|
||||
temperature: 1.0,
|
||||
maxOutputTokens: 8000,
|
||||
topP: 0.8,
|
||||
topK: 10,
|
||||
// stopSequences: [
|
||||
// "Title"
|
||||
// ],
|
||||
temperature: modelConfig.temperature,
|
||||
maxOutputTokens: modelConfig.max_tokens,
|
||||
topP: modelConfig.top_p,
|
||||
// "topK": modelConfig.top_k,
|
||||
},
|
||||
};
|
||||
|
||||
console.log("[Request] gemini payload: ", requestPayload);
|
||||
console.log("[Request] google payload: ", requestPayload);
|
||||
|
||||
const shouldStream = true;
|
||||
// todo: support stream later
|
||||
const shouldStream = false;
|
||||
const controller = new AbortController();
|
||||
options.onController?.(controller);
|
||||
|
||||
try {
|
||||
const chatPath = this.path(
|
||||
GooglePath.ChatPath.replace("{{model}}", options.config.model),
|
||||
);
|
||||
const chatPath = this.path(Google.ChatPath);
|
||||
const chatPayload = {
|
||||
method: "POST",
|
||||
body: JSON.stringify(requestPayload),
|
||||
signal: controller.signal,
|
||||
headers: getGeminiHeaders(),
|
||||
headers: getHeaders(),
|
||||
};
|
||||
|
||||
// make a fetch request
|
||||
|
@ -122,7 +79,6 @@ export class GeminiApi implements LLMApi {
|
|||
() => controller.abort(),
|
||||
REQUEST_TIMEOUT_MS,
|
||||
);
|
||||
|
||||
if (shouldStream) {
|
||||
let responseText = "";
|
||||
let remainText = "";
|
||||
|
@ -158,13 +114,14 @@ export class GeminiApi implements LLMApi {
|
|||
};
|
||||
|
||||
controller.signal.onabort = finish;
|
||||
|
||||
fetchEventSource(chatPath, {
|
||||
...chatPayload,
|
||||
async onopen(res) {
|
||||
clearTimeout(requestTimeoutId);
|
||||
const contentType = res.headers.get("content-type");
|
||||
console.log(
|
||||
"[Google] request response content type: ",
|
||||
"[OpenAI] request response content type: ",
|
||||
contentType,
|
||||
);
|
||||
|
||||
|
@ -207,15 +164,13 @@ export class GeminiApi implements LLMApi {
|
|||
const text = msg.data;
|
||||
try {
|
||||
const json = JSON.parse(text) as {
|
||||
candidates: Array<{
|
||||
content: {
|
||||
parts: Array<{
|
||||
text: string;
|
||||
}>;
|
||||
choices: Array<{
|
||||
delta: {
|
||||
content: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
const delta = json.candidates[0]?.content?.parts[0]?.text;
|
||||
const delta = json.choices[0]?.delta?.content;
|
||||
if (delta) {
|
||||
remainText += delta;
|
||||
}
|
||||
|
@ -237,6 +192,16 @@ export class GeminiApi implements LLMApi {
|
|||
clearTimeout(requestTimeoutId);
|
||||
|
||||
const resJson = await res.json();
|
||||
|
||||
if (resJson?.promptFeedback?.blockReason) {
|
||||
// being blocked
|
||||
options.onError?.(
|
||||
new Error(
|
||||
"Message is being blocked for reason: " +
|
||||
resJson.promptFeedback.blockReason,
|
||||
),
|
||||
);
|
||||
}
|
||||
const message = this.extractMessage(resJson);
|
||||
options.onFinish(message);
|
||||
}
|
||||
|
@ -245,249 +210,13 @@ export class GeminiApi implements LLMApi {
|
|||
options.onError?.(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
async toolAgentChat(options: AgentChatOptions) {
|
||||
const messages = options.messages.map((v) => ({
|
||||
role: v.role,
|
||||
content: v.content,
|
||||
}));
|
||||
|
||||
const modelConfig = {
|
||||
...useAppConfig.getState().modelConfig,
|
||||
...useChatStore.getState().currentSession().mask.modelConfig,
|
||||
...{
|
||||
model: options.config.model,
|
||||
},
|
||||
};
|
||||
|
||||
const requestPayload = {
|
||||
messages,
|
||||
stream: options.config.stream,
|
||||
model: modelConfig.model,
|
||||
temperature: modelConfig.temperature,
|
||||
presence_penalty: modelConfig.presence_penalty,
|
||||
frequency_penalty: modelConfig.frequency_penalty,
|
||||
top_p: modelConfig.top_p,
|
||||
baseUrl: useAccessStore.getState().openaiUrl,
|
||||
maxIterations: options.agentConfig.maxIterations,
|
||||
returnIntermediateSteps: options.agentConfig.returnIntermediateSteps,
|
||||
useTools: options.agentConfig.useTools,
|
||||
};
|
||||
|
||||
console.log("[Request] openai payload: ", requestPayload);
|
||||
|
||||
const shouldStream = true;
|
||||
const controller = new AbortController();
|
||||
options.onController?.(controller);
|
||||
|
||||
try {
|
||||
let path = "/api/langchain/tool/agent/";
|
||||
const enableNodeJSPlugin = !!process.env.NEXT_PUBLIC_ENABLE_NODEJS_PLUGIN;
|
||||
path = enableNodeJSPlugin ? path + "nodejs" : path + "edge";
|
||||
const chatPayload = {
|
||||
method: "POST",
|
||||
body: JSON.stringify(requestPayload),
|
||||
signal: controller.signal,
|
||||
headers: getHeaders(),
|
||||
};
|
||||
|
||||
// make a fetch request
|
||||
const requestTimeoutId = setTimeout(
|
||||
() => controller.abort(),
|
||||
REQUEST_TIMEOUT_MS,
|
||||
);
|
||||
console.log("shouldStream", shouldStream);
|
||||
|
||||
if (shouldStream) {
|
||||
let responseText = "";
|
||||
let finished = false;
|
||||
|
||||
const finish = () => {
|
||||
if (!finished) {
|
||||
options.onFinish(responseText);
|
||||
finished = true;
|
||||
usage(): Promise<LLMUsage> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
};
|
||||
|
||||
controller.signal.onabort = finish;
|
||||
|
||||
fetchEventSource(path, {
|
||||
...chatPayload,
|
||||
async onopen(res) {
|
||||
clearTimeout(requestTimeoutId);
|
||||
const contentType = res.headers.get("content-type");
|
||||
console.log(
|
||||
"[OpenAI] request response content type: ",
|
||||
contentType,
|
||||
);
|
||||
|
||||
if (contentType?.startsWith("text/plain")) {
|
||||
responseText = await res.clone().text();
|
||||
return finish();
|
||||
}
|
||||
|
||||
if (
|
||||
!res.ok ||
|
||||
!res.headers
|
||||
.get("content-type")
|
||||
?.startsWith(EventStreamContentType) ||
|
||||
res.status !== 200
|
||||
) {
|
||||
const responseTexts = [responseText];
|
||||
let extraInfo = await res.clone().text();
|
||||
console.warn(`extraInfo: ${extraInfo}`);
|
||||
// try {
|
||||
// const resJson = await res.clone().json();
|
||||
// extraInfo = prettyObject(resJson);
|
||||
// } catch { }
|
||||
|
||||
if (res.status === 401) {
|
||||
responseTexts.push(Locale.Error.Unauthorized);
|
||||
}
|
||||
|
||||
if (extraInfo) {
|
||||
responseTexts.push(extraInfo);
|
||||
}
|
||||
|
||||
responseText = responseTexts.join("\n\n");
|
||||
|
||||
return finish();
|
||||
}
|
||||
},
|
||||
onmessage(msg) {
|
||||
let response = JSON.parse(msg.data);
|
||||
if (!response.isSuccess) {
|
||||
console.error("[Request]", msg.data);
|
||||
responseText = msg.data;
|
||||
throw Error(response.message);
|
||||
}
|
||||
if (msg.data === "[DONE]" || finished) {
|
||||
return finish();
|
||||
}
|
||||
try {
|
||||
if (response && !response.isToolMessage) {
|
||||
responseText += response.message;
|
||||
options.onUpdate?.(responseText, response.message);
|
||||
} else {
|
||||
options.onToolUpdate?.(response.toolName!, response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("[Request] parse error", response, msg);
|
||||
}
|
||||
},
|
||||
onclose() {
|
||||
finish();
|
||||
},
|
||||
onerror(e) {
|
||||
options.onError?.(e);
|
||||
throw e;
|
||||
},
|
||||
openWhenHidden: true,
|
||||
});
|
||||
} else {
|
||||
const res = await fetch(path, chatPayload);
|
||||
clearTimeout(requestTimeoutId);
|
||||
|
||||
const resJson = await res.json();
|
||||
const message = this.extractMessage(resJson);
|
||||
options.onFinish(message);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("[Request] failed to make a chat reqeust", e);
|
||||
options.onError?.(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
async usage() {
|
||||
const formatDate = (d: Date) =>
|
||||
`${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, "0")}-${d
|
||||
.getDate()
|
||||
.toString()
|
||||
.padStart(2, "0")}`;
|
||||
const ONE_DAY = 1 * 24 * 60 * 60 * 1000;
|
||||
const now = new Date();
|
||||
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
const startDate = formatDate(startOfMonth);
|
||||
const endDate = formatDate(new Date(Date.now() + ONE_DAY));
|
||||
|
||||
const [used, subs] = await Promise.all([
|
||||
fetch(
|
||||
this.path(
|
||||
`${OpenaiPath.UsagePath}?start_date=${startDate}&end_date=${endDate}`,
|
||||
),
|
||||
{
|
||||
method: "GET",
|
||||
headers: getHeaders(),
|
||||
},
|
||||
),
|
||||
fetch(this.path(OpenaiPath.SubsPath), {
|
||||
method: "GET",
|
||||
headers: getHeaders(),
|
||||
}),
|
||||
]);
|
||||
|
||||
if (used.status === 401) {
|
||||
throw new Error(Locale.Error.Unauthorized);
|
||||
}
|
||||
|
||||
if (!used.ok || !subs.ok) {
|
||||
throw new Error("Failed to query usage from openai");
|
||||
}
|
||||
|
||||
const response = (await used.json()) as {
|
||||
total_usage?: number;
|
||||
error?: {
|
||||
type: string;
|
||||
message: string;
|
||||
};
|
||||
};
|
||||
|
||||
const total = (await subs.json()) as {
|
||||
hard_limit_usd?: number;
|
||||
};
|
||||
|
||||
if (response.error && response.error.type) {
|
||||
throw Error(response.error.message);
|
||||
}
|
||||
|
||||
if (response.total_usage) {
|
||||
response.total_usage = Math.round(response.total_usage) / 100;
|
||||
}
|
||||
|
||||
if (total.hard_limit_usd) {
|
||||
total.hard_limit_usd = Math.round(total.hard_limit_usd * 100) / 100;
|
||||
}
|
||||
|
||||
return {
|
||||
used: response.total_usage,
|
||||
total: total.hard_limit_usd,
|
||||
} as LLMUsage;
|
||||
}
|
||||
|
||||
async models(): Promise<LLMModel[]> {
|
||||
if (this.disableListModels) {
|
||||
return DEFAULT_MODELS.slice();
|
||||
}
|
||||
|
||||
const res = await fetch(this.path(OpenaiPath.ListModelPath), {
|
||||
method: "GET",
|
||||
headers: {
|
||||
...getHeaders(),
|
||||
},
|
||||
});
|
||||
|
||||
const resJson = (await res.json()) as OpenAIListModelResponse;
|
||||
const chatModels = resJson.data?.filter((m) => m.id.startsWith("gpt-"));
|
||||
console.log("[Models]", chatModels);
|
||||
|
||||
if (!chatModels) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return chatModels.map((m) => ({
|
||||
name: m.id,
|
||||
available: true,
|
||||
}));
|
||||
path(path: string): string {
|
||||
return "/api/google/" + path;
|
||||
}
|
||||
}
|
||||
export { OpenaiPath };
|
||||
|
|
|
@ -512,6 +512,11 @@ export class ChatGPTApi implements LLMApi {
|
|||
return chatModels.map((m) => ({
|
||||
name: m.id,
|
||||
available: true,
|
||||
provider: {
|
||||
id: "openai",
|
||||
providerName: "OpenAI",
|
||||
providerType: "openai",
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,17 @@ export function AuthPage() {
|
|||
);
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
className={styles["auth-input"]}
|
||||
type="password"
|
||||
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
|
||||
value={accessStore.googleApiKey}
|
||||
onChange={(e) => {
|
||||
accessStore.update(
|
||||
(access) => (access.googleApiKey = e.currentTarget.value),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
|
|
|
@ -29,10 +29,11 @@ import NextImage from "next/image";
|
|||
|
||||
import { toBlob, toPng } from "html-to-image";
|
||||
import { DEFAULT_MASK_AVATAR } from "../store/mask";
|
||||
import { api } from "../client/api";
|
||||
|
||||
import { prettyObject } from "../utils/format";
|
||||
import { EXPORT_MESSAGE_CLASS_NAME } from "../constant";
|
||||
import { EXPORT_MESSAGE_CLASS_NAME, ModelProvider } from "../constant";
|
||||
import { getClientConfig } from "../config/client";
|
||||
import { ClientApi } from "../client/api";
|
||||
|
||||
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
|
||||
loading: () => <LoadingIcon />,
|
||||
|
@ -301,10 +302,17 @@ export function PreviewActions(props: {
|
|||
}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [shouldExport, setShouldExport] = useState(false);
|
||||
|
||||
const config = useAppConfig();
|
||||
const onRenderMsgs = (msgs: ChatMessage[]) => {
|
||||
setShouldExport(false);
|
||||
|
||||
var api: ClientApi;
|
||||
if (config.modelConfig.model === "gemini-pro") {
|
||||
api = new ClientApi(ModelProvider.GeminiPro);
|
||||
} else {
|
||||
api = new ClientApi(ModelProvider.GPT);
|
||||
}
|
||||
|
||||
api
|
||||
.share(msgs)
|
||||
.then((res) => {
|
||||
|
@ -530,7 +538,7 @@ export function ImagePreviewer(props: {
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<div className={styles["main-title"]}>ChatGPT Next Web</div>
|
||||
<div className={styles["main-title"]}>NextChat</div>
|
||||
<div className={styles["sub-title"]}>
|
||||
github.com/Yidadaa/ChatGPT-Next-Web
|
||||
</div>
|
||||
|
|
|
@ -12,7 +12,7 @@ import LoadingIcon from "../icons/three-dots.svg";
|
|||
import { getCSSVar, useMobileScreen } from "../utils";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { Path, SlotID } from "../constant";
|
||||
import { ModelProvider, Path, SlotID } from "../constant";
|
||||
import { ErrorBoundary } from "./error";
|
||||
|
||||
import { getISOLang, getLang } from "../locales";
|
||||
|
@ -27,7 +27,7 @@ import { SideBar } from "./sidebar";
|
|||
import { useAppConfig } from "../store/config";
|
||||
import { AuthPage } from "./auth";
|
||||
import { getClientConfig } from "../config/client";
|
||||
import { api } from "../client/api";
|
||||
import { ClientApi } from "../client/api";
|
||||
import { useAccessStore } from "../store";
|
||||
|
||||
export function Loading(props: { noLogo?: boolean }) {
|
||||
|
@ -132,7 +132,8 @@ function Screen() {
|
|||
const isHome = location.pathname === Path.Home;
|
||||
const isAuth = location.pathname === Path.Auth;
|
||||
const isMobileScreen = useMobileScreen();
|
||||
const shouldTightBorder = getClientConfig()?.isApp || (config.tightBorder && !isMobileScreen);
|
||||
const shouldTightBorder =
|
||||
getClientConfig()?.isApp || (config.tightBorder && !isMobileScreen);
|
||||
|
||||
useEffect(() => {
|
||||
loadAsyncGoogleFont();
|
||||
|
@ -174,6 +175,12 @@ function Screen() {
|
|||
export function useLoadData() {
|
||||
const config = useAppConfig();
|
||||
|
||||
var api: ClientApi;
|
||||
if (config.modelConfig.model === "gemini-pro") {
|
||||
api = new ClientApi(ModelProvider.GeminiPro);
|
||||
} else {
|
||||
api = new ClientApi(ModelProvider.GPT);
|
||||
}
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const models = await api.llm.models();
|
||||
|
|
|
@ -29,7 +29,7 @@ export function ModelConfigList(props: {
|
|||
.filter((v) => v.available)
|
||||
.map((v, i) => (
|
||||
<option value={v.name} key={i}>
|
||||
{v.displayName}
|
||||
{v.displayName}({v.provider?.providerName})
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
|
@ -91,6 +91,9 @@ export function ModelConfigList(props: {
|
|||
}
|
||||
></input>
|
||||
</ListItem>
|
||||
|
||||
{props.modelConfig.model === "gemini-pro" ? null : (
|
||||
<>
|
||||
<ListItem
|
||||
title={Locale.Settings.PresencePenalty.Title}
|
||||
subTitle={Locale.Settings.PresencePenalty.SubTitle}
|
||||
|
@ -143,7 +146,8 @@ export function ModelConfigList(props: {
|
|||
onChange={(e) =>
|
||||
props.updateConfig(
|
||||
(config) =>
|
||||
(config.enableInjectSystemPrompts = e.currentTarget.checked),
|
||||
(config.enableInjectSystemPrompts =
|
||||
e.currentTarget.checked),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
|
@ -163,7 +167,8 @@ export function ModelConfigList(props: {
|
|||
}
|
||||
></input>
|
||||
</ListItem>
|
||||
|
||||
</>
|
||||
)}
|
||||
<ListItem
|
||||
title={Locale.Settings.HistoryCount.Title}
|
||||
subTitle={Locale.Settings.HistoryCount.SubTitle}
|
||||
|
|
|
@ -52,6 +52,7 @@ import { copyToClipboard } from "../utils";
|
|||
import Link from "next/link";
|
||||
import {
|
||||
Azure,
|
||||
Google,
|
||||
OPENAI_BASE_URL,
|
||||
Path,
|
||||
RELEASE_URL,
|
||||
|
@ -584,6 +585,7 @@ export function Settings() {
|
|||
const accessStore = useAccessStore();
|
||||
const shouldHideBalanceQuery = useMemo(() => {
|
||||
const isOpenAiUrl = accessStore.openaiUrl.includes(OPENAI_BASE_URL);
|
||||
|
||||
return (
|
||||
accessStore.hideBalanceQuery ||
|
||||
isOpenAiUrl ||
|
||||
|
@ -999,7 +1001,7 @@ export function Settings() {
|
|||
/>
|
||||
</ListItem>
|
||||
</>
|
||||
) : (
|
||||
) : accessStore.provider === "Azure" ? (
|
||||
<>
|
||||
<ListItem
|
||||
title={Locale.Settings.Access.Azure.Endpoint.Title}
|
||||
|
@ -1058,7 +1060,66 @@ export function Settings() {
|
|||
></input>
|
||||
</ListItem>
|
||||
</>
|
||||
)}
|
||||
) : accessStore.provider === "Google" ? (
|
||||
<>
|
||||
<ListItem
|
||||
title={Locale.Settings.Access.Google.Endpoint.Title}
|
||||
subTitle={
|
||||
Locale.Settings.Access.Google.Endpoint.SubTitle +
|
||||
Google.ExampleEndpoint
|
||||
}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value={accessStore.googleUrl}
|
||||
placeholder={Google.ExampleEndpoint}
|
||||
onChange={(e) =>
|
||||
accessStore.update(
|
||||
(access) =>
|
||||
(access.googleUrl = e.currentTarget.value),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
title={Locale.Settings.Access.Azure.ApiKey.Title}
|
||||
subTitle={Locale.Settings.Access.Azure.ApiKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
value={accessStore.googleApiKey}
|
||||
type="text"
|
||||
placeholder={
|
||||
Locale.Settings.Access.Google.ApiKey.Placeholder
|
||||
}
|
||||
onChange={(e) => {
|
||||
accessStore.update(
|
||||
(access) =>
|
||||
(access.googleApiKey = e.currentTarget.value),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
title={Locale.Settings.Access.Google.ApiVerion.Title}
|
||||
subTitle={
|
||||
Locale.Settings.Access.Google.ApiVerion.SubTitle
|
||||
}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value={accessStore.googleApiVersion}
|
||||
placeholder="2023-08-01-preview"
|
||||
onChange={(e) =>
|
||||
accessStore.update(
|
||||
(access) =>
|
||||
(access.googleApiVersion =
|
||||
e.currentTarget.value),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</ListItem>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -155,7 +155,7 @@ export function SideBar(props: { className?: string }) {
|
|||
>
|
||||
<div className={styles["sidebar-header"]} data-tauri-drag-region>
|
||||
<div className={styles["sidebar-title"]} data-tauri-drag-region>
|
||||
ChatGPT Next
|
||||
NextChat
|
||||
</div>
|
||||
<div className={styles["sidebar-sub-title"]}>
|
||||
Build your own AI assistant.
|
||||
|
|
|
@ -26,6 +26,10 @@ declare global {
|
|||
AZURE_URL?: string; // https://{azure-url}/openai/deployments/{deploy-name}
|
||||
AZURE_API_KEY?: string;
|
||||
AZURE_API_VERSION?: string;
|
||||
|
||||
// google only
|
||||
GOOGLE_API_KEY?: string;
|
||||
GOOGLE_URL?: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +65,7 @@ export const getServerSideConfig = () => {
|
|||
}
|
||||
|
||||
const isAzure = !!process.env.AZURE_URL;
|
||||
const isGoogle = !!process.env.GOOGLE_API_KEY;
|
||||
|
||||
const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? "";
|
||||
const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim());
|
||||
|
@ -80,8 +85,9 @@ export const getServerSideConfig = () => {
|
|||
azureApiKey: process.env.AZURE_API_KEY,
|
||||
azureApiVersion: process.env.AZURE_API_VERSION,
|
||||
|
||||
googleApiKey: process.env.GOOGLE_API_KEY ?? "",
|
||||
googleBaseUrl: process.env.GOOGLE_BASE_URL,
|
||||
isGoogle,
|
||||
googleApiKey: process.env.GOOGLE_API_KEY,
|
||||
googleUrl: process.env.GOOGLE_URL,
|
||||
|
||||
needCode: ACCESS_CODES.size > 0,
|
||||
code: process.env.CODE,
|
||||
|
|
|
@ -13,6 +13,8 @@ export const DEFAULT_API_HOST = `${DEFAULT_CORS_HOST}/api/proxy`;
|
|||
export const OPENAI_BASE_URL = "https://api.openai.com";
|
||||
export const GOOGLE_BASE_URL = "https://generativelanguage.googleapis.com";
|
||||
|
||||
export const GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/";
|
||||
|
||||
export enum Path {
|
||||
Home = "/",
|
||||
Chat = "/chat",
|
||||
|
@ -71,6 +73,12 @@ export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown";
|
|||
export enum ServiceProvider {
|
||||
OpenAI = "OpenAI",
|
||||
Azure = "Azure",
|
||||
Google = "Google",
|
||||
}
|
||||
|
||||
export enum ModelProvider {
|
||||
GPT = "GPT",
|
||||
GeminiPro = "GeminiPro",
|
||||
}
|
||||
|
||||
export const OpenaiPath = {
|
||||
|
@ -89,6 +97,14 @@ export const Azure = {
|
|||
ExampleEndpoint: "https://{resource-url}/openai/deployments/{deploy-id}",
|
||||
};
|
||||
|
||||
export const Google = {
|
||||
ExampleEndpoint:
|
||||
"https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent",
|
||||
ChatPath: "v1beta/models/gemini-pro:generateContent",
|
||||
|
||||
// /api/openai/v1/chat/completions
|
||||
};
|
||||
|
||||
export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang
|
||||
export const DEFAULT_SYSTEM_TEMPLATE = `
|
||||
You are ChatGPT, a large language model trained by OpenAI.
|
||||
|
@ -111,46 +127,110 @@ export const DEFAULT_MODELS = [
|
|||
{
|
||||
name: "gpt-4",
|
||||
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-0613",
|
||||
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-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-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,
|
||||
provider: {
|
||||
id: "google",
|
||||
providerName: "Google",
|
||||
providerType: "google",
|
||||
},
|
||||
},
|
||||
] as const;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { getClientConfig } from "./config/client";
|
|||
import { type Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "ChatGPT Next Web",
|
||||
title: "NextChat",
|
||||
description: "Your personal ChatGPT Chat Bot.",
|
||||
viewport: {
|
||||
width: "device-width",
|
||||
|
@ -18,7 +18,7 @@ export const metadata: Metadata = {
|
|||
{ media: "(prefers-color-scheme: dark)", color: "#151515" },
|
||||
],
|
||||
appleWebApp: {
|
||||
title: "ChatGPT Next Web",
|
||||
title: "NextChat",
|
||||
statusBarStyle: "default",
|
||||
},
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ const cn = {
|
|||
Auth: {
|
||||
Title: "需要密码",
|
||||
Tips: "管理员开启了密码验证,请在下方填入访问码",
|
||||
SubTips: "或者输入你的 OpenAI API 密钥",
|
||||
SubTips: "或者输入你的 OpenAI 或 Google API 密钥",
|
||||
Input: "在此处填写访问码",
|
||||
Confirm: "确认",
|
||||
Later: "稍后再说",
|
||||
|
@ -314,6 +314,23 @@ const cn = {
|
|||
SubTitle: "选择指定的部分版本",
|
||||
},
|
||||
},
|
||||
Google: {
|
||||
ApiKey: {
|
||||
Title: "接口密钥",
|
||||
SubTitle: "使用自定义 Google AI Studio API Key 绕过密码访问限制",
|
||||
Placeholder: "Google AI Studio API Key",
|
||||
},
|
||||
|
||||
Endpoint: {
|
||||
Title: "接口地址",
|
||||
SubTitle: "样例:",
|
||||
},
|
||||
|
||||
ApiVerion: {
|
||||
Title: "接口版本 (gemini-pro api version)",
|
||||
SubTitle: "选择指定的部分版本",
|
||||
},
|
||||
},
|
||||
CustomModel: {
|
||||
Title: "自定义模型名",
|
||||
SubTitle: "增加自定义模型可选项,使用英文逗号隔开",
|
||||
|
@ -363,7 +380,7 @@ const cn = {
|
|||
Prompt: {
|
||||
History: (content: string) => "这是历史聊天总结作为前情提要:" + content,
|
||||
Topic:
|
||||
"使用四到五个字直接返回这句话的简要主题,不要解释、不要标点、不要语气词、不要多余文本,如果没有主题,请直接返回“闲聊”",
|
||||
"使用四到五个字直接返回这句话的简要主题,不要解释、不要标点、不要语气词、不要多余文本,不要加粗,如果没有主题,请直接返回“闲聊”",
|
||||
Summarize:
|
||||
"简要总结一下对话内容,用作后续的上下文提示 prompt,控制在 200 字以内",
|
||||
},
|
||||
|
|
|
@ -15,7 +15,7 @@ const en: LocaleType = {
|
|||
Auth: {
|
||||
Title: "Need Access Code",
|
||||
Tips: "Please enter access code below",
|
||||
SubTips: "Or enter your OpenAI API Key",
|
||||
SubTips: "Or enter your OpenAI or Google API Key",
|
||||
Input: "access code",
|
||||
Confirm: "Confirm",
|
||||
Later: "Later",
|
||||
|
@ -321,6 +321,24 @@ const en: LocaleType = {
|
|||
Title: "Custom Models",
|
||||
SubTitle: "Custom model options, seperated by comma",
|
||||
},
|
||||
Google: {
|
||||
ApiKey: {
|
||||
Title: "API Key",
|
||||
SubTitle:
|
||||
"Bypass password access restrictions using a custom Google AI Studio API Key",
|
||||
Placeholder: "Google AI Studio API Key",
|
||||
},
|
||||
|
||||
Endpoint: {
|
||||
Title: "Endpoint Address",
|
||||
SubTitle: "Example:",
|
||||
},
|
||||
|
||||
ApiVerion: {
|
||||
Title: "API Version (gemini-pro api version)",
|
||||
SubTitle: "Select a specific part version",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Model: "Model",
|
||||
|
@ -369,7 +387,7 @@ const en: LocaleType = {
|
|||
History: (content: string) =>
|
||||
"This is a summary of the chat history as a recap: " + content,
|
||||
Topic:
|
||||
"Please generate a four to five word title summarizing our conversation without any lead-in, punctuation, quotation marks, periods, symbols, or additional text. Remove enclosing quotation marks.",
|
||||
"Please generate a four to five word title summarizing our conversation without any lead-in, punctuation, quotation marks, periods, symbols, bold text, or additional text. Remove enclosing quotation marks.",
|
||||
Summarize:
|
||||
"Summarize the discussion briefly in 200 words or less to use as a prompt for future context.",
|
||||
},
|
||||
|
|
|
@ -29,8 +29,10 @@ const DEFAULT_ACCESS_STATE = {
|
|||
azureApiKey: "",
|
||||
azureApiVersion: "2023-08-01-preview",
|
||||
|
||||
// google
|
||||
// google ai studio
|
||||
googleUrl: "",
|
||||
googleApiKey: "",
|
||||
googleApiVersion: "v1",
|
||||
|
||||
// server config
|
||||
needCode: true,
|
||||
|
@ -59,6 +61,10 @@ export const useAccessStore = createPersistStore(
|
|||
return ensure(get(), ["azureUrl", "azureApiKey", "azureApiVersion"]);
|
||||
},
|
||||
|
||||
isValidGoogle() {
|
||||
return ensure(get(), ["googleApiKey"]);
|
||||
},
|
||||
|
||||
isAuthorized() {
|
||||
this.fetch();
|
||||
|
||||
|
@ -66,6 +72,7 @@ export const useAccessStore = createPersistStore(
|
|||
return (
|
||||
this.isValidOpenAI() ||
|
||||
this.isValidAzure() ||
|
||||
this.isValidGoogle() ||
|
||||
!this.enabledAccessControl() ||
|
||||
(this.enabledAccessControl() && ensure(get(), ["accessCode"]))
|
||||
);
|
||||
|
|
|
@ -8,10 +8,11 @@ import {
|
|||
DEFAULT_INPUT_TEMPLATE,
|
||||
DEFAULT_SYSTEM_TEMPLATE,
|
||||
KnowledgeCutOffDate,
|
||||
ModelProvider,
|
||||
StoreKey,
|
||||
SUMMARIZE_MODEL,
|
||||
} from "../constant";
|
||||
import { api, RequestMessage } from "../client/api";
|
||||
import { ClientApi, RequestMessage } from "../client/api";
|
||||
import { ChatControllerPool } from "../client/controller";
|
||||
import { prettyObject } from "../utils/format";
|
||||
import { estimateTokenLength } from "../utils/token";
|
||||
|
@ -319,6 +320,8 @@ export const useChatStore = createPersistStore(
|
|||
session.messages.push(savedUserMessage);
|
||||
session.messages.push(botMessage);
|
||||
});
|
||||
var api: ClientApi;
|
||||
api = new ClientApi(ModelProvider.GPT);
|
||||
if (
|
||||
config.pluginConfig.enable &&
|
||||
session.mask.usePlugins &&
|
||||
|
@ -392,8 +395,10 @@ export const useChatStore = createPersistStore(
|
|||
},
|
||||
});
|
||||
} else {
|
||||
if (modelConfig.model === "gemini-pro") {
|
||||
api = new ClientApi(ModelProvider.GeminiPro);
|
||||
}
|
||||
// make request
|
||||
api.switch(modelConfig.model);
|
||||
api.llm.chat({
|
||||
messages: sendMessages,
|
||||
config: { ...modelConfig, stream: true },
|
||||
|
@ -472,7 +477,9 @@ export const useChatStore = createPersistStore(
|
|||
|
||||
// system prompts, to get close to OpenAI Web ChatGPT
|
||||
const shouldInjectSystemPrompts = modelConfig.enableInjectSystemPrompts;
|
||||
const systemPrompts = shouldInjectSystemPrompts
|
||||
|
||||
var systemPrompts: ChatMessage[] = [];
|
||||
systemPrompts = shouldInjectSystemPrompts
|
||||
? [
|
||||
createMessage({
|
||||
role: "system",
|
||||
|
@ -566,6 +573,14 @@ export const useChatStore = createPersistStore(
|
|||
summarizeSession() {
|
||||
const config = useAppConfig.getState();
|
||||
const session = get().currentSession();
|
||||
const modelConfig = session.mask.modelConfig;
|
||||
|
||||
var api: ClientApi;
|
||||
if (modelConfig.model === "gemini-pro") {
|
||||
api = new ClientApi(ModelProvider.GeminiPro);
|
||||
} else {
|
||||
api = new ClientApi(ModelProvider.GPT);
|
||||
}
|
||||
|
||||
// remove error messages if any
|
||||
const messages = session.messages;
|
||||
|
@ -583,7 +598,6 @@ export const useChatStore = createPersistStore(
|
|||
content: Locale.Store.Prompt.Topic,
|
||||
}),
|
||||
);
|
||||
api.switch(session.mask.modelConfig.model);
|
||||
api.llm.chat({
|
||||
messages: topicMessages,
|
||||
config: {
|
||||
|
@ -598,8 +612,6 @@ export const useChatStore = createPersistStore(
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
const modelConfig = session.mask.modelConfig;
|
||||
const summarizeIndex = Math.max(
|
||||
session.lastSummarizeIndex,
|
||||
session.clearContextIndex ?? 0,
|
||||
|
@ -633,7 +645,6 @@ export const useChatStore = createPersistStore(
|
|||
historyMsgLength > modelConfig.compressMessageLengthThreshold &&
|
||||
modelConfig.sendMemory
|
||||
) {
|
||||
api.switch(modelConfig.model);
|
||||
api.llm.chat({
|
||||
messages: toBeSummarizedMsgs.concat(
|
||||
createMessage({
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
import { FETCH_COMMIT_URL, FETCH_TAG_URL, StoreKey } from "../constant";
|
||||
import { api } from "../client/api";
|
||||
import {
|
||||
FETCH_COMMIT_URL,
|
||||
FETCH_TAG_URL,
|
||||
ModelProvider,
|
||||
StoreKey,
|
||||
} from "../constant";
|
||||
import { getClientConfig } from "../config/client";
|
||||
import { createPersistStore } from "../utils/store";
|
||||
import ChatGptIcon from "../icons/chatgpt.png";
|
||||
import Locale from "../locales";
|
||||
import { use } from "react";
|
||||
import { useAppConfig } from ".";
|
||||
import { ClientApi } from "../client/api";
|
||||
|
||||
const ONE_MINUTE = 60 * 1000;
|
||||
const isApp = !!getClientConfig()?.isApp;
|
||||
|
@ -85,29 +92,34 @@ export const useUpdateStore = createPersistStore(
|
|||
}));
|
||||
if (window.__TAURI__?.notification && isApp) {
|
||||
// Check if notification permission is granted
|
||||
await window.__TAURI__?.notification.isPermissionGranted().then((granted) => {
|
||||
await window.__TAURI__?.notification
|
||||
.isPermissionGranted()
|
||||
.then((granted) => {
|
||||
if (!granted) {
|
||||
return;
|
||||
} else {
|
||||
// Request permission to show notifications
|
||||
window.__TAURI__?.notification.requestPermission().then((permission) => {
|
||||
if (permission === 'granted') {
|
||||
window.__TAURI__?.notification
|
||||
.requestPermission()
|
||||
.then((permission) => {
|
||||
if (permission === "granted") {
|
||||
if (version === remoteId) {
|
||||
// Show a notification using Tauri
|
||||
window.__TAURI__?.notification.sendNotification({
|
||||
title: "ChatGPT Next Web",
|
||||
title: "NextChat",
|
||||
body: `${Locale.Settings.Update.IsLatest}`,
|
||||
icon: `${ChatGptIcon.src}`,
|
||||
sound: "Default"
|
||||
sound: "Default",
|
||||
});
|
||||
} else {
|
||||
const updateMessage = Locale.Settings.Update.FoundUpdate(`${remoteId}`);
|
||||
const updateMessage =
|
||||
Locale.Settings.Update.FoundUpdate(`${remoteId}`);
|
||||
// Show a notification for the new version using Tauri
|
||||
window.__TAURI__?.notification.sendNotification({
|
||||
title: "ChatGPT Next Web",
|
||||
title: "NextChat",
|
||||
body: updateMessage,
|
||||
icon: `${ChatGptIcon.src}`,
|
||||
sound: "Default"
|
||||
sound: "Default",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +134,7 @@ export const useUpdateStore = createPersistStore(
|
|||
},
|
||||
|
||||
async updateUsage(force = false) {
|
||||
// only support openai for now
|
||||
const overOneMinute = Date.now() - get().lastUpdateUsage >= ONE_MINUTE;
|
||||
if (!overOneMinute && !force) return;
|
||||
|
||||
|
@ -130,6 +143,7 @@ export const useUpdateStore = createPersistStore(
|
|||
}));
|
||||
|
||||
try {
|
||||
const api = new ClientApi(ModelProvider.GPT);
|
||||
const usage = await api.llm.usage();
|
||||
|
||||
if (usage) {
|
||||
|
|
|
@ -6,23 +6,27 @@ export function collectModelTable(
|
|||
) {
|
||||
const modelTable: Record<
|
||||
string,
|
||||
{ available: boolean; name: string; displayName: string }
|
||||
{
|
||||
available: boolean;
|
||||
name: string;
|
||||
displayName: string;
|
||||
provider?: LLMModel["provider"]; // Marked as optional
|
||||
}
|
||||
> = {};
|
||||
|
||||
// default models
|
||||
models.forEach(
|
||||
(m) =>
|
||||
(modelTable[m.name] = {
|
||||
models.forEach((m) => {
|
||||
modelTable[m.name] = {
|
||||
...m,
|
||||
displayName: m.name,
|
||||
}),
|
||||
);
|
||||
displayName: m.name, // 'provider' is copied over if it exists
|
||||
};
|
||||
});
|
||||
|
||||
// server custom models
|
||||
customModels
|
||||
.split(",")
|
||||
.filter((v) => !!v && v.length > 0)
|
||||
.map((m) => {
|
||||
.forEach((m) => {
|
||||
const available = !m.startsWith("-");
|
||||
const nameConfig =
|
||||
m.startsWith("+") || m.startsWith("-") ? m.slice(1) : m;
|
||||
|
@ -30,14 +34,17 @@ export function collectModelTable(
|
|||
|
||||
// enable or disable all models
|
||||
if (name === "all") {
|
||||
Object.values(modelTable).forEach((m) => (m.available = available));
|
||||
}
|
||||
|
||||
Object.values(modelTable).forEach(
|
||||
(model) => (model.available = available),
|
||||
);
|
||||
} else {
|
||||
modelTable[name] = {
|
||||
name,
|
||||
displayName: displayName || name,
|
||||
available,
|
||||
provider: modelTable[name]?.provider, // Use optional chaining
|
||||
};
|
||||
}
|
||||
});
|
||||
return modelTable;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
version: "3.9"
|
||||
services:
|
||||
chatgpt-next-web:
|
||||
profiles: ["no-proxy"]
|
||||
profiles: [ "no-proxy" ]
|
||||
container_name: chatgpt-next-web
|
||||
image: gosuto/chatgpt-next-web-langchain
|
||||
ports:
|
||||
- 3000:3000
|
||||
environment:
|
||||
- OPENAI_API_KEY=$OPENAI_API_KEY
|
||||
- GOOGLE_API_KEY=$GOOGLE_API_KEY
|
||||
- CODE=$CODE
|
||||
- BASE_URL=$BASE_URL
|
||||
- OPENAI_ORG_ID=$OPENAI_ORG_ID
|
||||
|
@ -18,13 +19,14 @@ services:
|
|||
- OPENAI_SB=$OPENAI_SB
|
||||
|
||||
chatgpt-next-web-proxy:
|
||||
profiles: ["proxy"]
|
||||
profiles: [ "proxy" ]
|
||||
container_name: chatgpt-next-web-proxy
|
||||
image: gosuto/chatgpt-next-web-langchain
|
||||
ports:
|
||||
- 3000:3000
|
||||
environment:
|
||||
- OPENAI_API_KEY=$OPENAI_API_KEY
|
||||
- GOOGLE_API_KEY=$GOOGLE_API_KEY
|
||||
- CODE=$CODE
|
||||
- PROXY_URL=$PROXY_URL
|
||||
- BASE_URL=$BASE_URL
|
||||
|
|
|
@ -23,7 +23,7 @@ Docker 版本相当于稳定版,latest Docker 总是与 latest release version
|
|||
## 如何修改 Vercel 环境变量
|
||||
|
||||
- 进入 vercel 的控制台页面;
|
||||
- 选中你的 chatgpt next web 项目;
|
||||
- 选中你的 NextChat 项目;
|
||||
- 点击页面头部的 Settings 选项;
|
||||
- 找到侧边栏的 Environment Variables 选项;
|
||||
- 修改对应的值即可。
|
||||
|
|
|
@ -23,7 +23,7 @@ Docker 버전은 사실상 안정된 버전과 같습니다. latest Docker는
|
|||
## Vercel 환경 변수를 어떻게 수정하나요?
|
||||
|
||||
- Vercel의 제어판 페이지로 이동합니다.
|
||||
- chatgpt next web 프로젝트를 선택합니다.
|
||||
- NextChat 프로젝트를 선택합니다.
|
||||
- 페이지 상단의 Settings 옵션을 클릭합니다.
|
||||
- 사이드바의 Environment Variables 옵션을 찾습니다.
|
||||
- 해당 값을 수정합니다.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> No english version yet, please read this doc with ChatGPT or other translation tools.
|
||||
|
||||
本文档用于解释 ChatGPT Next Web 的部分功能介绍和设计原则。
|
||||
本文档用于解释 NextChat 的部分功能介绍和设计原则。
|
||||
|
||||
## 面具 (Mask)
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
|||
|
||||
编辑步骤如下:
|
||||
|
||||
1. 在 ChatGPT Next Web 中配置好一个面具;
|
||||
1. 在 NextChat 中配置好一个面具;
|
||||
2. 使用面具编辑页面的下载按钮,将面具保存为 JSON 格式;
|
||||
3. 让 ChatGPT 帮你将 json 文件格式化为对应的 ts 代码;
|
||||
4. 放入对应的 .ts 文件。
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"@aws-sdk/client-s3": "^3.414.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.414.0",
|
||||
"@fortaine/fetch-event-source": "^3.0.6",
|
||||
"@hello-pangea/dnd": "^16.3.0",
|
||||
"@hello-pangea/dnd": "^16.5.0",
|
||||
"@svgr/webpack": "^6.5.1",
|
||||
"@vercel/analytics": "^0.1.11",
|
||||
"axios": "^1.4.0",
|
||||
|
@ -27,8 +27,8 @@
|
|||
"duck-duck-scrape": "^2.2.4",
|
||||
"emoji-picker-react": "^4.5.15",
|
||||
"encoding": "^0.1.13",
|
||||
"fuse.js": "^6.6.2",
|
||||
"html-entities": "^2.4.0",
|
||||
"fuse.js": "^7.0.0",
|
||||
"html-to-image": "^1.11.11",
|
||||
"html-to-text": "^9.0.5",
|
||||
"https-proxy-agent": "^7.0.2",
|
||||
|
@ -54,8 +54,8 @@
|
|||
"zustand": "^4.3.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^1.4.0",
|
||||
"@types/html-to-text": "^9.0.1",
|
||||
"@tauri-apps/cli": "^1.5.8",
|
||||
"@types/node": "^20.9.0",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ChatGPT Next Web",
|
||||
"short_name": "ChatGPT",
|
||||
"name": "NextChat",
|
||||
"short_name": "NextChat",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
|
@ -17,5 +17,4 @@
|
|||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
"withGlobalTauri": true
|
||||
},
|
||||
"package": {
|
||||
"productName": "ChatGPT Next Web",
|
||||
"productName": "NextChat",
|
||||
"version": "2.9.13"
|
||||
},
|
||||
"tauri": {
|
||||
|
@ -68,7 +68,7 @@
|
|||
"icons/icon.ico"
|
||||
],
|
||||
"identifier": "com.yida.chatgpt.next.web",
|
||||
"longDescription": "ChatGPT Next Web is a cross-platform ChatGPT client, including Web/Win/Linux/OSX/PWA.",
|
||||
"longDescription": "NextChat is a cross-platform ChatGPT client, including Web/Win/Linux/OSX/PWA.",
|
||||
"macOS": {
|
||||
"entitlements": null,
|
||||
"exceptionDomain": "",
|
||||
|
@ -77,7 +77,7 @@
|
|||
"signingIdentity": null
|
||||
},
|
||||
"resources": [],
|
||||
"shortDescription": "ChatGPT Next Web App",
|
||||
"shortDescription": "NextChat App",
|
||||
"targets": "all",
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
|
@ -104,7 +104,7 @@
|
|||
"fullscreen": false,
|
||||
"height": 600,
|
||||
"resizable": true,
|
||||
"title": "ChatGPT Next Web",
|
||||
"title": "NextChat",
|
||||
"width": 960,
|
||||
"hiddenTitle": true,
|
||||
"titleBarStyle": "Overlay"
|
||||
|
|
267
yarn.lock
267
yarn.lock
|
@ -617,6 +617,14 @@
|
|||
dependencies:
|
||||
"@babel/highlight" "^7.18.6"
|
||||
|
||||
"@babel/code-frame@^7.22.13":
|
||||
version "7.22.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e"
|
||||
integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.22.13"
|
||||
chalk "^2.4.2"
|
||||
|
||||
"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5":
|
||||
version "7.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298"
|
||||
|
@ -653,6 +661,16 @@
|
|||
"@jridgewell/trace-mapping" "^0.3.17"
|
||||
jsesc "^2.5.1"
|
||||
|
||||
"@babel/generator@^7.23.0":
|
||||
version "7.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420"
|
||||
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
|
||||
dependencies:
|
||||
"@babel/types" "^7.23.0"
|
||||
"@jridgewell/gen-mapping" "^0.3.2"
|
||||
"@jridgewell/trace-mapping" "^0.3.17"
|
||||
jsesc "^2.5.1"
|
||||
|
||||
"@babel/helper-annotate-as-pure@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb"
|
||||
|
@ -718,6 +736,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
|
||||
integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
|
||||
|
||||
"@babel/helper-environment-visitor@^7.22.20":
|
||||
version "7.22.20"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
|
||||
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
|
||||
|
||||
"@babel/helper-explode-assignable-expression@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096"
|
||||
|
@ -733,6 +756,14 @@
|
|||
"@babel/template" "^7.20.7"
|
||||
"@babel/types" "^7.21.0"
|
||||
|
||||
"@babel/helper-function-name@^7.23.0":
|
||||
version "7.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
|
||||
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
|
||||
dependencies:
|
||||
"@babel/template" "^7.22.15"
|
||||
"@babel/types" "^7.23.0"
|
||||
|
||||
"@babel/helper-hoist-variables@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
|
||||
|
@ -740,6 +771,13 @@
|
|||
dependencies:
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/helper-hoist-variables@^7.22.5":
|
||||
version "7.22.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb"
|
||||
integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
|
||||
dependencies:
|
||||
"@babel/types" "^7.22.5"
|
||||
|
||||
"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0":
|
||||
version "7.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5"
|
||||
|
@ -823,16 +861,33 @@
|
|||
dependencies:
|
||||
"@babel/types" "^7.18.6"
|
||||
|
||||
"@babel/helper-split-export-declaration@^7.22.6":
|
||||
version "7.22.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c"
|
||||
integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
|
||||
dependencies:
|
||||
"@babel/types" "^7.22.5"
|
||||
|
||||
"@babel/helper-string-parser@^7.19.4":
|
||||
version "7.19.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63"
|
||||
integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==
|
||||
|
||||
"@babel/helper-string-parser@^7.22.5":
|
||||
version "7.22.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f"
|
||||
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1":
|
||||
version "7.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
|
||||
integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.22.20":
|
||||
version "7.22.20"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
|
||||
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
|
||||
|
||||
"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0":
|
||||
version "7.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180"
|
||||
|
@ -866,11 +921,25 @@
|
|||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/highlight@^7.22.13":
|
||||
version "7.22.20"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54"
|
||||
integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.22.20"
|
||||
chalk "^2.4.2"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/parser@^7.20.7", "@babel/parser@^7.21.3":
|
||||
version "7.21.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3"
|
||||
integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==
|
||||
|
||||
"@babel/parser@^7.22.15", "@babel/parser@^7.23.0":
|
||||
version "7.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
|
||||
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
|
||||
|
||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2"
|
||||
|
@ -1554,12 +1623,12 @@
|
|||
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
|
||||
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
||||
|
||||
"@babel/runtime@^7.12.1", "@babel/runtime@^7.20.7", "@babel/runtime@^7.22.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
version "7.22.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec"
|
||||
integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==
|
||||
"@babel/runtime@^7.12.1", "@babel/runtime@^7.20.7", "@babel/runtime@^7.23.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
version "7.23.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.6.tgz#c05e610dc228855dc92ef1b53d07389ed8ab521d"
|
||||
integrity sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.11"
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@babel/template@^7.18.10", "@babel/template@^7.20.7":
|
||||
version "7.20.7"
|
||||
|
@ -1570,19 +1639,28 @@
|
|||
"@babel/parser" "^7.20.7"
|
||||
"@babel/types" "^7.20.7"
|
||||
|
||||
"@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3":
|
||||
version "7.21.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67"
|
||||
integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==
|
||||
"@babel/template@^7.22.15":
|
||||
version "7.22.15"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
|
||||
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.18.6"
|
||||
"@babel/generator" "^7.21.3"
|
||||
"@babel/helper-environment-visitor" "^7.18.9"
|
||||
"@babel/helper-function-name" "^7.21.0"
|
||||
"@babel/helper-hoist-variables" "^7.18.6"
|
||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
||||
"@babel/parser" "^7.21.3"
|
||||
"@babel/types" "^7.21.3"
|
||||
"@babel/code-frame" "^7.22.13"
|
||||
"@babel/parser" "^7.22.15"
|
||||
"@babel/types" "^7.22.15"
|
||||
|
||||
"@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3":
|
||||
version "7.23.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
|
||||
integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.22.13"
|
||||
"@babel/generator" "^7.23.0"
|
||||
"@babel/helper-environment-visitor" "^7.22.20"
|
||||
"@babel/helper-function-name" "^7.23.0"
|
||||
"@babel/helper-hoist-variables" "^7.22.5"
|
||||
"@babel/helper-split-export-declaration" "^7.22.6"
|
||||
"@babel/parser" "^7.23.0"
|
||||
"@babel/types" "^7.23.0"
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
|
@ -1595,6 +1673,15 @@
|
|||
"@babel/helper-validator-identifier" "^7.19.1"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0":
|
||||
version "7.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb"
|
||||
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
|
||||
dependencies:
|
||||
"@babel/helper-string-parser" "^7.22.5"
|
||||
"@babel/helper-validator-identifier" "^7.22.20"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@braintree/sanitize-url@^6.0.1":
|
||||
version "6.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz#923ca57e173c6b232bbbb07347b1be982f03e783"
|
||||
|
@ -1637,16 +1724,16 @@
|
|||
resolved "https://registry.npmmirror.com/@fortaine/fetch-event-source/-/fetch-event-source-3.0.6.tgz#b8552a2ca2c5202f5699b93a92be0188d422b06e"
|
||||
integrity sha512-621GAuLMvKtyZQ3IA6nlDWhV1V/7PGOTNIGLUifxt0KzM+dZIweJ6F3XvQF3QnqeNfS1N7WQ0Kil1Di/lhChEw==
|
||||
|
||||
"@hello-pangea/dnd@^16.3.0":
|
||||
version "16.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@hello-pangea/dnd/-/dnd-16.3.0.tgz#3776212f812df4e8e69c42831ec8ab7ff3a087d6"
|
||||
integrity sha512-RYQ/K8shtJoyNPvFWz0gfXIK7HF3P3mL9UZFGMuHB0ljRSXVgMjVFI/FxcZmakMzw6tO7NflWLriwTNBow/4vw==
|
||||
"@hello-pangea/dnd@^16.5.0":
|
||||
version "16.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@hello-pangea/dnd/-/dnd-16.5.0.tgz#f323ff9f813204818bc67648a383e8715f47c59c"
|
||||
integrity sha512-n+am6O32jo/CFXciCysz83lPM3I3F58FJw4uS44TceieymcyxQSfzK5OhzPAKrVBZktmuOI6Zim9WABTMtXv4A==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.22.5"
|
||||
"@babel/runtime" "^7.23.2"
|
||||
css-box-model "^1.2.1"
|
||||
memoize-one "^6.0.0"
|
||||
raf-schd "^4.0.3"
|
||||
react-redux "^8.1.1"
|
||||
react-redux "^8.1.3"
|
||||
redux "^4.2.1"
|
||||
use-memo-one "^1.1.3"
|
||||
|
||||
|
@ -2422,71 +2509,71 @@
|
|||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@tauri-apps/cli-darwin-arm64@1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.4.0.tgz#e76bb8515ae31f03f2cbd440c1a09b237a79b3ac"
|
||||
integrity sha512-nA/ml0SfUt6/CYLVbHmT500Y+ijqsuv5+s9EBnVXYSLVg9kbPUZJJHluEYK+xKuOj6xzyuT/+rZFMRapmJD3jQ==
|
||||
"@tauri-apps/cli-darwin-arm64@1.5.8":
|
||||
version "1.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.5.8.tgz#28ca810b910979260dd77c92951d16340fcaa711"
|
||||
integrity sha512-/AksDWfAt3NUSt8Rq2a3gTLASChKzldPVUjmJhcbtsuzFg2nx5g+hhOHxfBYzss2Te1K5mzlu+73LAMy1Sb9Gw==
|
||||
|
||||
"@tauri-apps/cli-darwin-x64@1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.4.0.tgz#dd1472460550d0aa0ec6e699b073be2d77e5b962"
|
||||
integrity sha512-ov/F6Zr+dg9B0PtRu65stFo2G0ow2TUlneqYYrkj+vA3n+moWDHfVty0raDjMLQbQt3rv3uayFMXGPMgble9OA==
|
||||
"@tauri-apps/cli-darwin-x64@1.5.8":
|
||||
version "1.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.5.8.tgz#4060fb0ffcc8312cf48701df51e0e9b665f18382"
|
||||
integrity sha512-gcfSh+BFRDdbIGpggZ1+5R5SgToz2A9LthH8P4ak3OHagDzDvI6ov6zy2UQE3XDWJKdnlna2rSR1dIuRZ0T9bA==
|
||||
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf@1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.4.0.tgz#325e90e47d260ba71a499850ce769b5a6bdfd48d"
|
||||
integrity sha512-zwjbiMncycXDV7doovymyKD7sCg53ouAmfgpUqEBOTY3vgBi9TwijyPhJOqoG5vUVWhouNBC08akGmE4dja15g==
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf@1.5.8":
|
||||
version "1.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.5.8.tgz#00256432520edf04004962caa92cd84fbcc8b63f"
|
||||
integrity sha512-ZHQYuOBGvZubPnh5n8bNaN2VMxPBZWs26960FGQWamm9569UV/TNDHb6mD0Jjk9o0f9P+f98qNhuu5Y37P+vfQ==
|
||||
|
||||
"@tauri-apps/cli-linux-arm64-gnu@1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.4.0.tgz#b5d8f5cba3f8f7c7d44d071681f0ab0a37f2c46e"
|
||||
integrity sha512-5MCBcziqXC72mMXnkZU68mutXIR6zavDxopArE2gQtK841IlE06bIgtLi0kUUhlFJk2nhPRgiDgdLbrPlyt7fw==
|
||||
"@tauri-apps/cli-linux-arm64-gnu@1.5.8":
|
||||
version "1.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.5.8.tgz#7869571b06e8b36a072f2e0e7bb49baab9d3c868"
|
||||
integrity sha512-FFs28Ew3R2EFPYKuyAIouTbp6YnR+shAmJGFNnVy7ibKHL0wxamVKqv1N5N9gUUr+EhbZu2syMBRfG9XQ5mgng==
|
||||
|
||||
"@tauri-apps/cli-linux-arm64-musl@1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.4.0.tgz#f805ab2ee415875900f4b456f17dc4900d2a7911"
|
||||
integrity sha512-7J3pRB6n6uNYgIfCeKt2Oz8J7oSaz2s8GGFRRH2HPxuTHrBNCinzVYm68UhVpJrL3bnGkU0ziVZLsW/iaOGfUg==
|
||||
"@tauri-apps/cli-linux-arm64-musl@1.5.8":
|
||||
version "1.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.5.8.tgz#7cbe0395cbd09d4b49c945e36c2de99478c50a51"
|
||||
integrity sha512-dEYvNyLMmWD0jb30FNfVPXmBq6OGg6is3km+4RlGg8tZU5Zvq78ClUZtaZuER+N/hv27+Uc6UHl9X3hin8cGGw==
|
||||
|
||||
"@tauri-apps/cli-linux-x64-gnu@1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.4.0.tgz#d3f5e69c22420c7ac9e4021b7a94bce2e48cb45d"
|
||||
integrity sha512-Zh5gfAJxOv5AVWxcwuueaQ2vIAhlg0d6nZui6nMyfIJ8dbf3aZQ5ZzP38sYow5h/fbvgL+3GSQxZRBIa3c2E1w==
|
||||
"@tauri-apps/cli-linux-x64-gnu@1.5.8":
|
||||
version "1.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.5.8.tgz#d03ba73f1ac68bf6bace7bf45b50e6b12ce4468b"
|
||||
integrity sha512-ut3TDbtLXmZhz6Q4wim57PV02wG+AfuLSWRPhTL9MsPsg/E7Y6sJhv0bIMAq6SwC59RCH52ZGft6RH7samV2NQ==
|
||||
|
||||
"@tauri-apps/cli-linux-x64-musl@1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.4.0.tgz#2e7f718272ffdd9ace80f57a35023ba0c74767ad"
|
||||
integrity sha512-OLAYoICU3FaYiTdBsI+lQTKnDHeMmFMXIApN0M+xGiOkoIOQcV9CConMPjgmJQ867+NHRNgUGlvBEAh9CiJodQ==
|
||||
"@tauri-apps/cli-linux-x64-musl@1.5.8":
|
||||
version "1.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.5.8.tgz#4ce560aa102e9031d4c51c7bc853263cf3ab9616"
|
||||
integrity sha512-k6ei7ETXVZlNpFOhl/8Cnj709UbEr+VuY9xKK/HgwvNfjA5f8HQ9TSKk/Um7oeT1Y61/eEcvcgF/hDURhFJDPQ==
|
||||
|
||||
"@tauri-apps/cli-win32-arm64-msvc@1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-1.4.0.tgz#85cdb52a06feb92da785def4d02512099464525e"
|
||||
integrity sha512-gZ05GENFbI6CB5MlOUsLlU0kZ9UtHn9riYtSXKT6MYs8HSPRffPHaHSL0WxsJweWh9nR5Hgh/TUU8uW3sYCzCg==
|
||||
"@tauri-apps/cli-win32-arm64-msvc@1.5.8":
|
||||
version "1.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-1.5.8.tgz#df83af81c6d89d4a505f2e96b3d443dd411c1a4a"
|
||||
integrity sha512-l6zm31x1inkS2K5e7otUZ90XBoK+xr2KJObFCZbzmluBE+LM0fgIXCrj7xwH/f0RCUX3VY9HHx4EIo7eLGBXKQ==
|
||||
|
||||
"@tauri-apps/cli-win32-ia32-msvc@1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.4.0.tgz#0b7c921204058215aec9a5a00f735e73909bd330"
|
||||
integrity sha512-JsetT/lTx/Zq98eo8T5CiRyF1nKeX04RO8JlJrI3ZOYsZpp/A5RJvMd/szQ17iOzwiHdge+tx7k2jHysR6oBlQ==
|
||||
"@tauri-apps/cli-win32-ia32-msvc@1.5.8":
|
||||
version "1.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.5.8.tgz#92e5acc4dcd44aec88099059a04bb5ad3b4e59ff"
|
||||
integrity sha512-0k3YpWl6PKV4Qp2N52Sb45egXafSgQXcBaO7TIJG4EDfaEf5f6StN+hYSzdnrq9idrK5x9DDCPuebZTuJ+Q8EA==
|
||||
|
||||
"@tauri-apps/cli-win32-x64-msvc@1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.4.0.tgz#23abe3f08c0df89111c29602f91c21a23577b908"
|
||||
integrity sha512-z8Olcnwp5aYhzqUAarFjqF+oELCjuYWnB2HAJHlfsYNfDCAORY5kct3Fklz8PSsubC3U2EugWn8n42DwnThurg==
|
||||
"@tauri-apps/cli-win32-x64-msvc@1.5.8":
|
||||
version "1.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.5.8.tgz#a0c363969cf5a21c95c235e5bf6a94a410130761"
|
||||
integrity sha512-XjBg8VMswmD9JAHKlb10NRPfBVAZoiOJBbPRte+GP1BUQtqDnbIYcOLSnUCmNZoy3fUBJuKJUBT9tDCbkMr5fQ==
|
||||
|
||||
"@tauri-apps/cli@^1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-1.4.0.tgz#72732ae61e6b7d097e44a8a2ef5f211b2d01d98b"
|
||||
integrity sha512-VXYr2i2iVFl98etQSQsqLzXgX96bnWiNZd1YADgatqwy/qecbd6Kl5ZAPB5R4ynsgE8A1gU7Fbzh7dCEQYFfmA==
|
||||
"@tauri-apps/cli@^1.5.8":
|
||||
version "1.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-1.5.8.tgz#feaf055af370cb192b24ea4c51edf0e577269fb2"
|
||||
integrity sha512-c/mzk5vjjfxtH5uNXSc9h1eiprsolnoBcUwAa4/SZ3gxJ176CwrUKODz3cZBOnzs8omwagwgSN/j7K8NrdFL9g==
|
||||
optionalDependencies:
|
||||
"@tauri-apps/cli-darwin-arm64" "1.4.0"
|
||||
"@tauri-apps/cli-darwin-x64" "1.4.0"
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf" "1.4.0"
|
||||
"@tauri-apps/cli-linux-arm64-gnu" "1.4.0"
|
||||
"@tauri-apps/cli-linux-arm64-musl" "1.4.0"
|
||||
"@tauri-apps/cli-linux-x64-gnu" "1.4.0"
|
||||
"@tauri-apps/cli-linux-x64-musl" "1.4.0"
|
||||
"@tauri-apps/cli-win32-arm64-msvc" "1.4.0"
|
||||
"@tauri-apps/cli-win32-ia32-msvc" "1.4.0"
|
||||
"@tauri-apps/cli-win32-x64-msvc" "1.4.0"
|
||||
"@tauri-apps/cli-darwin-arm64" "1.5.8"
|
||||
"@tauri-apps/cli-darwin-x64" "1.5.8"
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf" "1.5.8"
|
||||
"@tauri-apps/cli-linux-arm64-gnu" "1.5.8"
|
||||
"@tauri-apps/cli-linux-arm64-musl" "1.5.8"
|
||||
"@tauri-apps/cli-linux-x64-gnu" "1.5.8"
|
||||
"@tauri-apps/cli-linux-x64-musl" "1.5.8"
|
||||
"@tauri-apps/cli-win32-arm64-msvc" "1.5.8"
|
||||
"@tauri-apps/cli-win32-ia32-msvc" "1.5.8"
|
||||
"@tauri-apps/cli-win32-x64-msvc" "1.5.8"
|
||||
|
||||
"@trysound/sax@0.2.0":
|
||||
version "0.2.0"
|
||||
|
@ -3228,7 +3315,7 @@ chalk@5.2.0:
|
|||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3"
|
||||
integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==
|
||||
|
||||
chalk@^2.0.0:
|
||||
chalk@^2.0.0, chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
|
@ -4651,10 +4738,10 @@ functions-have-names@^1.2.2:
|
|||
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
|
||||
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
||||
|
||||
fuse.js@^6.6.2:
|
||||
version "6.6.2"
|
||||
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-6.6.2.tgz#fe463fed4b98c0226ac3da2856a415576dc9a111"
|
||||
integrity sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==
|
||||
fuse.js@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-7.0.0.tgz#6573c9fcd4c8268e403b4fc7d7131ffcf99a9eb2"
|
||||
integrity sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==
|
||||
|
||||
gensync@^1.0.0-beta.2:
|
||||
version "1.0.0-beta.2"
|
||||
|
@ -6698,10 +6785,10 @@ react-markdown@^8.0.7:
|
|||
unist-util-visit "^4.0.0"
|
||||
vfile "^5.0.0"
|
||||
|
||||
react-redux@^8.1.1:
|
||||
version "8.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.1.tgz#8e740f3fd864a4cd0de5ba9cdc8ad39cc9e7c81a"
|
||||
integrity sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==
|
||||
react-redux@^8.1.3:
|
||||
version "8.1.3"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.3.tgz#4fdc0462d0acb59af29a13c27ffef6f49ab4df46"
|
||||
integrity sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.1"
|
||||
"@types/hoist-non-react-statics" "^3.3.1"
|
||||
|
@ -6758,10 +6845,10 @@ regenerate@^1.4.2:
|
|||
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
|
||||
integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
|
||||
|
||||
regenerator-runtime@^0.13.11:
|
||||
version "0.13.11"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
|
||||
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
|
||||
regenerator-runtime@^0.14.0:
|
||||
version "0.14.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
|
||||
integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
|
||||
|
||||
regenerator-transform@^0.15.1:
|
||||
version "0.15.1"
|
||||
|
|
Loading…
Reference in New Issue