Compare commits

..

1 Commits

Author SHA1 Message Date
Dogtiti
a5ac2386d8 Merge pull request #5470 from ChatGPTNextWeb/add_tip_top
add fanyi add top tip
2024-09-25 15:46:21 +08:00
60 changed files with 328 additions and 735 deletions

View File

@@ -66,4 +66,4 @@ ANTHROPIC_API_VERSION=
ANTHROPIC_URL= ANTHROPIC_URL=
### (optional) ### (optional)
WHITE_WEBDAV_ENDPOINTS= WHITE_WEBDEV_ENDPOINTS=

View File

@@ -12,18 +12,15 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4
一键免费部署你的跨平台私人 ChatGPT 应用, 支持 GPT3, GPT4 & Gemini Pro 模型。 一键免费部署你的跨平台私人 ChatGPT 应用, 支持 GPT3, GPT4 & Gemini Pro 模型。
[![Saas][Saas-image]][saas-url]
[![Web][Web-image]][web-url] [![Web][Web-image]][web-url]
[![Windows][Windows-image]][download-url] [![Windows][Windows-image]][download-url]
[![MacOS][MacOS-image]][download-url] [![MacOS][MacOS-image]][download-url]
[![Linux][Linux-image]][download-url] [![Linux][Linux-image]][download-url]
[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [Web App](https://app.nextchat.dev) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Enterprise Edition](#enterprise-edition) / [Twitter](https://twitter.com/NextChatDev) [Web App](https://app.nextchat.dev/) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Enterprise Edition](#enterprise-edition) / [Twitter](https://twitter.com/NextChatDev)
[NextChatAI](https://nextchat.dev/chat) / [网页版](https://app.nextchat.dev) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) [网页版](https://app.nextchat.dev/) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues)
[saas-url]: https://nextchat.dev/chat?utm_source=readme
[saas-image]: https://img.shields.io/badge/NextChat-Saas-green?logo=microsoftedge
[web-url]: https://app.nextchat.dev/ [web-url]: https://app.nextchat.dev/
[download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases [download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases
[Web-image]: https://img.shields.io/badge/Web-PWA-orange?logo=microsoftedge [Web-image]: https://img.shields.io/badge/Web-PWA-orange?logo=microsoftedge
@@ -63,7 +60,7 @@ For enterprise inquiries, please contact: **business@nextchat.dev**
企业版咨询: **business@nextchat.dev** 企业版咨询: **business@nextchat.dev**
<img width="300" src="https://github.com/user-attachments/assets/3d4305ac-6e95-489e-884b-51d51db5f692"> <img width="300" src="https://github.com/user-attachments/assets/3daeb7b6-ab63-4542-9141-2e4a12c80601">
## Features ## Features
@@ -100,7 +97,6 @@ For enterprise inquiries, please contact: **business@nextchat.dev**
## What's New ## What's New
- 🚀 v2.15.4 The Application supports using Tauri fetch LLM API, MORE SECURITY! [#5379](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5379)
- 🚀 v2.15.0 Now supports Plugins! Read this: [NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins) - 🚀 v2.15.0 Now supports Plugins! Read this: [NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins)
- 🚀 v2.14.0 Now supports Artifacts & SD - 🚀 v2.14.0 Now supports Artifacts & SD
- 🚀 v2.10.1 support Google Gemini Pro model. - 🚀 v2.10.1 support Google Gemini Pro model.
@@ -138,7 +134,6 @@ For enterprise inquiries, please contact: **business@nextchat.dev**
## 最新动态 ## 最新动态
- 🚀 v2.15.4 客户端支持Tauri本地直接调用大模型API更安全[#5379](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5379)
- 🚀 v2.15.0 现在支持插件功能了!了解更多:[NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins) - 🚀 v2.15.0 现在支持插件功能了!了解更多:[NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins)
- 🚀 v2.14.0 现在支持 Artifacts & SD 了。 - 🚀 v2.14.0 现在支持 Artifacts & SD 了。
- 🚀 v2.10.1 现在支持 Gemini Pro 模型。 - 🚀 v2.10.1 现在支持 Gemini Pro 模型。
@@ -177,7 +172,7 @@ We recommend that you follow the steps below to re-deploy:
### Enable Automatic Updates ### Enable Automatic Updates
> If you encounter a failure of Upstream Sync execution, please [manually update code](./README.md#manually-updating-code). > If you encounter a failure of Upstream Sync execution, please manually sync fork once.
After forking the project, due to the limitations imposed by GitHub, you need to manually enable Workflows and Upstream Sync Action on the Actions page of the forked project. Once enabled, automatic updates will be scheduled every hour: After forking the project, due to the limitations imposed by GitHub, you need to manually enable Workflows and Upstream Sync Action on the Actions page of the forked project. Once enabled, automatic updates will be scheduled every hour:
@@ -334,9 +329,9 @@ 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.
For Azure: use `modelName@Azure=deploymentName` to customize model name and deployment name. For Azure: use `modelName@azure=deploymentName` to customize model name and deployment name.
> Example: `+gpt-3.5-turbo@Azure=gpt35` will show option `gpt35(Azure)` in model list. > Example: `+gpt-3.5-turbo@azure=gpt35` will show option `gpt35(Azure)` in model list.
> If you only can use Azure model, `-all,+gpt-3.5-turbo@Azure=gpt35` will `gpt35(Azure)` the only option in model list. > If you only can use Azure model, `-all,+gpt-3.5-turbo@azure=gpt35` will `gpt35(Azure)` the only option in model list.
For ByteDance: use `modelName@bytedance=deploymentName` to customize model name and deployment name. For ByteDance: use `modelName@bytedance=deploymentName` to customize model name and deployment name.
> Example: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx` will show option `Doubao-lite-4k(ByteDance)` in model list. > Example: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx` will show option `Doubao-lite-4k(ByteDance)` in model list.
@@ -345,7 +340,7 @@ For ByteDance: use `modelName@bytedance=deploymentName` to customize model name
Change default model Change default model
### `WHITE_WEBDAV_ENDPOINTS` (optional) ### `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

View File

@@ -8,7 +8,7 @@
一键免费部署你的私人 ChatGPT 网页应用,支持 GPT3, GPT4 & Gemini Pro 模型。 一键免费部署你的私人 ChatGPT 网页应用,支持 GPT3, GPT4 & Gemini Pro 模型。
[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) /[演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N)
[<img src="https://vercel.com/button" alt="Deploy on Zeabur" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) [<img src="https://vercel.com/button" alt="Deploy on Zeabur" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
@@ -54,7 +54,7 @@
### 打开自动更新 ### 打开自动更新
> 如果你遇到了 Upstream Sync 执行错误,请[手动 Sync Fork 一次](./README_CN.md#手动更新代码) > 如果你遇到了 Upstream Sync 执行错误,请手动 Sync Fork 一次!
当你 fork 项目之后,由于 Github 的限制,需要手动去你 fork 后的项目的 Actions 页面启用 Workflows并启用 Upstream Sync Action启用之后即可开启每小时定时自动更新 当你 fork 项目之后,由于 Github 的限制,需要手动去你 fork 后的项目的 Actions 页面启用 Workflows并启用 Upstream Sync Action启用之后即可开启每小时定时自动更新
@@ -202,7 +202,7 @@ ByteDance Api Url.
如果你想禁用从链接解析预制设置,将此环境变量设置为 1 即可。 如果你想禁用从链接解析预制设置,将此环境变量设置为 1 即可。
### `WHITE_WEBDAV_ENDPOINTS` (可选) ### `WHITE_WEBDEV_ENDPOINTS` (可选)
如果你想增加允许访问的webdav服务地址可以使用该选项格式要求 如果你想增加允许访问的webdav服务地址可以使用该选项格式要求
- 每一个地址必须是一个完整的 endpoint - 每一个地址必须是一个完整的 endpoint
@@ -216,9 +216,9 @@ ByteDance Api Url.
用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。 用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。
在Azure的模式下支持使用`modelName@Azure=deploymentName`的方式配置模型名称和部署名称(deploy-name) 在Azure的模式下支持使用`modelName@azure=deploymentName`的方式配置模型名称和部署名称(deploy-name)
> 示例:`+gpt-3.5-turbo@Azure=gpt35`这个配置会在模型列表显示一个`gpt35(Azure)`的选项。 > 示例:`+gpt-3.5-turbo@azure=gpt35`这个配置会在模型列表显示一个`gpt35(Azure)`的选项。
> 如果你只能使用Azure模式那么设置 `-all,+gpt-3.5-turbo@Azure=gpt35` 则可以让对话的默认使用 `gpt35(Azure)` > 如果你只能使用Azure模式那么设置 `-all,+gpt-3.5-turbo@azure=gpt35` 则可以让对话的默认使用 `gpt35(Azure)`
在ByteDance的模式下支持使用`modelName@bytedance=deploymentName`的方式配置模型名称和部署名称(deploy-name) 在ByteDance的模式下支持使用`modelName@bytedance=deploymentName`的方式配置模型名称和部署名称(deploy-name)
> 示例: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx`这个配置会在模型列表显示一个`Doubao-lite-4k(ByteDance)`的选项 > 示例: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx`这个配置会在模型列表显示一个`Doubao-lite-4k(ByteDance)`的选项

View File

@@ -5,7 +5,7 @@
ワンクリックで無料であなた専用の ChatGPT ウェブアプリをデプロイ。GPT3、GPT4 & Gemini Pro モデルをサポート。 ワンクリックで無料であなた専用の ChatGPT ウェブアプリをデプロイ。GPT3、GPT4 & Gemini Pro モデルをサポート。
[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N) [企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N)
[<img src="https://vercel.com/button" alt="Zeaburでデプロイ" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Zeaburでデプロイ" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Gitpodで開く" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) [<img src="https://vercel.com/button" alt="Zeaburでデプロイ" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Zeaburでデプロイ" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Gitpodで開く" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
@@ -54,7 +54,7 @@
### 自動更新を開く ### 自動更新を開く
> Upstream Sync の実行エラーが発生した場合は、[手動で Sync Fork](./README_JA.md#手動でコードを更新する) してください! > Upstream Sync の実行エラーが発生した場合は、手動で Sync Fork してください!
プロジェクトを fork した後、GitHub の制限により、fork 後のプロジェクトの Actions ページで Workflows を手動で有効にし、Upstream Sync Action を有効にする必要があります。有効化後、毎時の定期自動更新が可能になります: プロジェクトを fork した後、GitHub の制限により、fork 後のプロジェクトの Actions ページで Workflows を手動で有効にし、Upstream Sync Action を有効にする必要があります。有効化後、毎時の定期自動更新が可能になります:
@@ -193,7 +193,7 @@ ByteDance API の URL。
リンクからのプリセット設定解析を無効にしたい場合は、この環境変数を 1 に設定します。 リンクからのプリセット設定解析を無効にしたい場合は、この環境変数を 1 に設定します。
### `WHITE_WEBDAV_ENDPOINTS` (オプション) ### `WHITE_WEBDEV_ENDPOINTS` (オプション)
アクセス許可を与える WebDAV サービスのアドレスを追加したい場合、このオプションを使用します。フォーマット要件: アクセス許可を与える WebDAV サービスのアドレスを追加したい場合、このオプションを使用します。フォーマット要件:
- 各アドレスは完全なエンドポイントでなければなりません。 - 各アドレスは完全なエンドポイントでなければなりません。
@@ -207,8 +207,8 @@ ByteDance API の URL。
モデルリストを管理します。`+` でモデルを追加し、`-` でモデルを非表示にし、`モデル名=表示名` でモデルの表示名をカスタマイズし、カンマで区切ります。 モデルリストを管理します。`+` でモデルを追加し、`-` でモデルを非表示にし、`モデル名=表示名` でモデルの表示名をカスタマイズし、カンマで区切ります。
Azure モードでは、`modelName@Azure=deploymentName` 形式でモデル名とデプロイ名deploy-nameを設定できます。 Azure モードでは、`modelName@azure=deploymentName` 形式でモデル名とデプロイ名deploy-nameを設定できます。
> 例:`+gpt-3.5-turbo@Azure=gpt35` この設定でモデルリストに `gpt35(Azure)` のオプションが表示されます。 > 例:`+gpt-3.5-turbo@azure=gpt35` この設定でモデルリストに `gpt35(Azure)` のオプションが表示されます。
ByteDance モードでは、`modelName@bytedance=deploymentName` 形式でモデル名とデプロイ名deploy-nameを設定できます。 ByteDance モードでは、`modelName@bytedance=deploymentName` 形式でモデル名とデプロイ名deploy-nameを設定できます。
> 例: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx` この設定でモデルリストに `Doubao-lite-4k(ByteDance)` のオプションが表示されます。 > 例: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx` この設定でモデルリストに `Doubao-lite-4k(ByteDance)` のオプションが表示されます。

View File

@@ -23,8 +23,7 @@ export async function handle(
}); });
} }
const bearToken = const bearToken = req.headers.get("Authorization") ?? "";
req.headers.get("x-goog-api-key") || req.headers.get("Authorization") || "";
const token = bearToken.trim().replaceAll("Bearer ", "").trim(); const token = bearToken.trim().replaceAll("Bearer ", "").trim();
const apiKey = token ? token : serverConfig.googleApiKey; const apiKey = token ? token : serverConfig.googleApiKey;
@@ -92,8 +91,8 @@ async function request(req: NextRequest, apiKey: string) {
}, },
10 * 60 * 1000, 10 * 60 * 1000,
); );
const fetchUrl = `${baseUrl}${path}${ const fetchUrl = `${baseUrl}${path}?key=${apiKey}${
req?.nextUrl?.searchParams?.get("alt") === "sse" ? "?alt=sse" : "" req?.nextUrl?.searchParams?.get("alt") === "sse" ? "&alt=sse" : ""
}`; }`;
console.log("[Fetch Url] ", fetchUrl); console.log("[Fetch Url] ", fetchUrl);
@@ -101,9 +100,6 @@ async function request(req: NextRequest, apiKey: string) {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
"Cache-Control": "no-store", "Cache-Control": "no-store",
"x-goog-api-key":
req.headers.get("x-goog-api-key") ||
(req.headers.get("Authorization") ?? "").replace("Bearer ", ""),
}, },
method: req.method, method: req.method,
body: req.body, body: req.body,

View File

@@ -6,7 +6,7 @@ import { NextRequest, NextResponse } from "next/server";
import { auth } from "./auth"; import { auth } from "./auth";
import { requestOpenai } from "./common"; import { requestOpenai } from "./common";
const ALLOWED_PATH = new Set(Object.values(OpenaiPath)); const ALLOWD_PATH = new Set(Object.values(OpenaiPath));
function getModels(remoteModelRes: OpenAIListModelResponse) { function getModels(remoteModelRes: OpenAIListModelResponse) {
const config = getServerSideConfig(); const config = getServerSideConfig();
@@ -34,7 +34,7 @@ export async function handle(
const subpath = params.path.join("/"); const subpath = params.path.join("/");
if (!ALLOWED_PATH.has(subpath)) { if (!ALLOWD_PATH.has(subpath)) {
console.log("[OpenAI Route] forbidden path ", subpath); console.log("[OpenAI Route] forbidden path ", subpath);
return NextResponse.json( return NextResponse.json(
{ {

View File

@@ -6,7 +6,7 @@ const config = getServerSideConfig();
const mergedAllowedWebDavEndpoints = [ const mergedAllowedWebDavEndpoints = [
...internalAllowedWebDavEndpoints, ...internalAllowedWebDavEndpoints,
...config.allowedWebDavEndpoints, ...config.allowedWebDevEndpoints,
].filter((domain) => Boolean(domain.trim())); ].filter((domain) => Boolean(domain.trim()));
const normalizeUrl = (url: string) => { const normalizeUrl = (url: string) => {

View File

@@ -231,7 +231,7 @@ export function getHeaders(ignoreHeaders: boolean = false) {
function getConfig() { function getConfig() {
const modelConfig = chatStore.currentSession().mask.modelConfig; const modelConfig = chatStore.currentSession().mask.modelConfig;
const isGoogle = modelConfig.providerName === ServiceProvider.Google; const isGoogle = modelConfig.providerName == ServiceProvider.Google;
const isAzure = modelConfig.providerName === ServiceProvider.Azure; const isAzure = modelConfig.providerName === ServiceProvider.Azure;
const isAnthropic = modelConfig.providerName === ServiceProvider.Anthropic; const isAnthropic = modelConfig.providerName === ServiceProvider.Anthropic;
const isBaidu = modelConfig.providerName == ServiceProvider.Baidu; const isBaidu = modelConfig.providerName == ServiceProvider.Baidu;
@@ -272,13 +272,7 @@ export function getHeaders(ignoreHeaders: boolean = false) {
} }
function getAuthHeader(): string { function getAuthHeader(): string {
return isAzure return isAzure ? "api-key" : isAnthropic ? "x-api-key" : "Authorization";
? "api-key"
: isAnthropic
? "x-api-key"
: isGoogle
? "x-goog-api-key"
: "Authorization";
} }
const { const {
@@ -289,15 +283,14 @@ export function getHeaders(ignoreHeaders: boolean = false) {
apiKey, apiKey,
isEnabledAccessControl, isEnabledAccessControl,
} = getConfig(); } = getConfig();
// when using google api in app, not set auth header
if (isGoogle && clientConfig?.isApp) return headers;
// when using baidu api in app, not set auth header // when using baidu api in app, not set auth header
if (isBaidu && clientConfig?.isApp) return headers; if (isBaidu && clientConfig?.isApp) return headers;
const authHeader = getAuthHeader(); const authHeader = getAuthHeader();
const bearerToken = getBearerToken( const bearerToken = getBearerToken(apiKey, isAzure || isAnthropic);
apiKey,
isAzure || isAnthropic || isGoogle,
);
if (bearerToken) { if (bearerToken) {
headers[authHeader] = bearerToken; headers[authHeader] = bearerToken;

View File

@@ -23,7 +23,6 @@ import {
import { prettyObject } from "@/app/utils/format"; import { prettyObject } from "@/app/utils/format";
import { getClientConfig } from "@/app/config/client"; import { getClientConfig } from "@/app/config/client";
import { getMessageTextContent } from "@/app/utils"; import { getMessageTextContent } from "@/app/utils";
import { fetch } from "@/app/utils/stream";
export interface OpenAIListModelResponse { export interface OpenAIListModelResponse {
object: string; object: string;
@@ -179,7 +178,6 @@ export class QwenApi implements LLMApi {
controller.signal.onabort = finish; controller.signal.onabort = finish;
fetchEventSource(chatPath, { fetchEventSource(chatPath, {
fetch: fetch as any,
...chatPayload, ...chatPayload,
async onopen(res) { async onopen(res) {
clearTimeout(requestTimeoutId); clearTimeout(requestTimeoutId);

View File

@@ -8,7 +8,7 @@ import {
ChatMessageTool, ChatMessageTool,
} from "@/app/store"; } from "@/app/store";
import { getClientConfig } from "@/app/config/client"; import { getClientConfig } from "@/app/config/client";
import { ANTHROPIC_BASE_URL } from "@/app/constant"; import { DEFAULT_API_HOST } from "@/app/constant";
import { getMessageTextContent, isVisionModel } from "@/app/utils"; import { getMessageTextContent, isVisionModel } from "@/app/utils";
import { preProcessImageContent, stream } from "@/app/utils/chat"; import { preProcessImageContent, stream } from "@/app/utils/chat";
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare"; import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
@@ -388,7 +388,9 @@ export class ClaudeApi implements LLMApi {
if (baseUrl.trim().length === 0) { if (baseUrl.trim().length === 0) {
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
baseUrl = isApp ? ANTHROPIC_BASE_URL : ApiPath.Anthropic; baseUrl = isApp
? DEFAULT_API_HOST + "/api/proxy/anthropic"
: ApiPath.Anthropic;
} }
if (!baseUrl.startsWith("http") && !baseUrl.startsWith("/api")) { if (!baseUrl.startsWith("http") && !baseUrl.startsWith("/api")) {

View File

@@ -24,7 +24,6 @@ import {
import { prettyObject } from "@/app/utils/format"; import { prettyObject } from "@/app/utils/format";
import { getClientConfig } from "@/app/config/client"; import { getClientConfig } from "@/app/config/client";
import { getMessageTextContent } from "@/app/utils"; import { getMessageTextContent } from "@/app/utils";
import { fetch } from "@/app/utils/stream";
export interface OpenAIListModelResponse { export interface OpenAIListModelResponse {
object: string; object: string;
@@ -198,7 +197,6 @@ export class ErnieApi implements LLMApi {
controller.signal.onabort = finish; controller.signal.onabort = finish;
fetchEventSource(chatPath, { fetchEventSource(chatPath, {
fetch: fetch as any,
...chatPayload, ...chatPayload,
async onopen(res) { async onopen(res) {
clearTimeout(requestTimeoutId); clearTimeout(requestTimeoutId);

View File

@@ -23,7 +23,6 @@ import {
import { prettyObject } from "@/app/utils/format"; import { prettyObject } from "@/app/utils/format";
import { getClientConfig } from "@/app/config/client"; import { getClientConfig } from "@/app/config/client";
import { getMessageTextContent } from "@/app/utils"; import { getMessageTextContent } from "@/app/utils";
import { fetch } from "@/app/utils/stream";
export interface OpenAIListModelResponse { export interface OpenAIListModelResponse {
object: string; object: string;
@@ -166,7 +165,6 @@ export class DoubaoApi implements LLMApi {
controller.signal.onabort = finish; controller.signal.onabort = finish;
fetchEventSource(chatPath, { fetchEventSource(chatPath, {
fetch: fetch as any,
...chatPayload, ...chatPayload,
async onopen(res) { async onopen(res) {
clearTimeout(requestTimeoutId); clearTimeout(requestTimeoutId);

View File

@@ -9,7 +9,7 @@ import {
} from "../api"; } from "../api";
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
import { getClientConfig } from "@/app/config/client"; import { getClientConfig } from "@/app/config/client";
import { GEMINI_BASE_URL } from "@/app/constant"; import { DEFAULT_API_HOST } from "@/app/constant";
import Locale from "../../locales"; import Locale from "../../locales";
import { import {
EventStreamContentType, EventStreamContentType,
@@ -22,7 +22,6 @@ import {
isVisionModel, isVisionModel,
} from "@/app/utils"; } from "@/app/utils";
import { preProcessImageContent } from "@/app/utils/chat"; import { preProcessImageContent } from "@/app/utils/chat";
import { fetch } from "@/app/utils/stream";
export class GeminiProApi implements LLMApi { export class GeminiProApi implements LLMApi {
path(path: string): string { path(path: string): string {
@@ -35,7 +34,7 @@ export class GeminiProApi implements LLMApi {
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
if (baseUrl.length === 0) { if (baseUrl.length === 0) {
baseUrl = isApp ? GEMINI_BASE_URL : ApiPath.Google; baseUrl = isApp ? DEFAULT_API_HOST + `/api/proxy/google` : ApiPath.Google;
} }
if (baseUrl.endsWith("/")) { if (baseUrl.endsWith("/")) {
baseUrl = baseUrl.slice(0, baseUrl.length - 1); baseUrl = baseUrl.slice(0, baseUrl.length - 1);
@@ -49,6 +48,10 @@ export class GeminiProApi implements LLMApi {
let chatPath = [baseUrl, path].join("/"); let chatPath = [baseUrl, path].join("/");
chatPath += chatPath.includes("?") ? "&alt=sse" : "?alt=sse"; chatPath += chatPath.includes("?") ? "&alt=sse" : "?alt=sse";
// if chatPath.startsWith('http') then add key in query string
if (chatPath.startsWith("http") && accessStore.googleApiKey) {
chatPath += `&key=${accessStore.googleApiKey}`;
}
return chatPath; return chatPath;
} }
extractMessage(res: any) { extractMessage(res: any) {
@@ -214,7 +217,6 @@ export class GeminiProApi implements LLMApi {
controller.signal.onabort = finish; controller.signal.onabort = finish;
fetchEventSource(chatPath, { fetchEventSource(chatPath, {
fetch: fetch as any,
...chatPayload, ...chatPayload,
async onopen(res) { async onopen(res) {
clearTimeout(requestTimeoutId); clearTimeout(requestTimeoutId);

View File

@@ -1,7 +1,7 @@
"use client"; "use client";
import { import {
ApiPath, ApiPath,
IFLYTEK_BASE_URL, DEFAULT_API_HOST,
Iflytek, Iflytek,
REQUEST_TIMEOUT_MS, REQUEST_TIMEOUT_MS,
} from "@/app/constant"; } from "@/app/constant";
@@ -22,7 +22,6 @@ import {
import { prettyObject } from "@/app/utils/format"; import { prettyObject } from "@/app/utils/format";
import { getClientConfig } from "@/app/config/client"; import { getClientConfig } from "@/app/config/client";
import { getMessageTextContent } from "@/app/utils"; import { getMessageTextContent } from "@/app/utils";
import { fetch } from "@/app/utils/stream";
import { RequestPayload } from "./openai"; import { RequestPayload } from "./openai";
@@ -41,7 +40,7 @@ export class SparkApi implements LLMApi {
if (baseUrl.length === 0) { if (baseUrl.length === 0) {
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const apiPath = ApiPath.Iflytek; const apiPath = ApiPath.Iflytek;
baseUrl = isApp ? IFLYTEK_BASE_URL : apiPath; baseUrl = isApp ? DEFAULT_API_HOST + "/proxy" + apiPath : apiPath;
} }
if (baseUrl.endsWith("/")) { if (baseUrl.endsWith("/")) {
@@ -150,7 +149,6 @@ export class SparkApi implements LLMApi {
controller.signal.onabort = finish; controller.signal.onabort = finish;
fetchEventSource(chatPath, { fetchEventSource(chatPath, {
fetch: fetch as any,
...chatPayload, ...chatPayload,
async onopen(res) { async onopen(res) {
clearTimeout(requestTimeoutId); clearTimeout(requestTimeoutId);

View File

@@ -2,7 +2,7 @@
// azure and openai, using same models. so using same LLMApi. // azure and openai, using same models. so using same LLMApi.
import { import {
ApiPath, ApiPath,
MOONSHOT_BASE_URL, DEFAULT_API_HOST,
Moonshot, Moonshot,
REQUEST_TIMEOUT_MS, REQUEST_TIMEOUT_MS,
} from "@/app/constant"; } from "@/app/constant";
@@ -40,7 +40,7 @@ export class MoonshotApi implements LLMApi {
if (baseUrl.length === 0) { if (baseUrl.length === 0) {
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const apiPath = ApiPath.Moonshot; const apiPath = ApiPath.Moonshot;
baseUrl = isApp ? MOONSHOT_BASE_URL : apiPath; baseUrl = isApp ? DEFAULT_API_HOST + "/proxy" + apiPath : apiPath;
} }
if (baseUrl.endsWith("/")) { if (baseUrl.endsWith("/")) {

View File

@@ -2,7 +2,7 @@
// azure and openai, using same models. so using same LLMApi. // azure and openai, using same models. so using same LLMApi.
import { import {
ApiPath, ApiPath,
OPENAI_BASE_URL, DEFAULT_API_HOST,
DEFAULT_MODELS, DEFAULT_MODELS,
OpenaiPath, OpenaiPath,
Azure, Azure,
@@ -98,7 +98,7 @@ export class ChatGPTApi implements LLMApi {
if (baseUrl.length === 0) { if (baseUrl.length === 0) {
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const apiPath = isAzure ? ApiPath.Azure : ApiPath.OpenAI; const apiPath = isAzure ? ApiPath.Azure : ApiPath.OpenAI;
baseUrl = isApp ? OPENAI_BASE_URL : apiPath; baseUrl = isApp ? DEFAULT_API_HOST + "/proxy" + apiPath : apiPath;
} }
if (baseUrl.endsWith("/")) { if (baseUrl.endsWith("/")) {
@@ -277,7 +277,6 @@ export class ChatGPTApi implements LLMApi {
); );
} }
if (shouldStream) { if (shouldStream) {
let index = -1;
const [tools, funcs] = usePluginStore const [tools, funcs] = usePluginStore
.getState() .getState()
.getAsTools( .getAsTools(
@@ -303,10 +302,10 @@ export class ChatGPTApi implements LLMApi {
}>; }>;
const tool_calls = choices[0]?.delta?.tool_calls; const tool_calls = choices[0]?.delta?.tool_calls;
if (tool_calls?.length > 0) { if (tool_calls?.length > 0) {
const index = tool_calls[0]?.index;
const id = tool_calls[0]?.id; const id = tool_calls[0]?.id;
const args = tool_calls[0]?.function?.arguments; const args = tool_calls[0]?.function?.arguments;
if (id) { if (id) {
index += 1;
runTools.push({ runTools.push({
id, id,
type: tool_calls[0]?.type, type: tool_calls[0]?.type,
@@ -328,8 +327,6 @@ export class ChatGPTApi implements LLMApi {
toolCallMessage: any, toolCallMessage: any,
toolCallResult: any[], toolCallResult: any[],
) => { ) => {
// reset index value
index = -1;
// @ts-ignore // @ts-ignore
requestPayload?.messages?.splice( requestPayload?.messages?.splice(
// @ts-ignore // @ts-ignore

View File

@@ -1,5 +1,5 @@
"use client"; "use client";
import { ApiPath, TENCENT_BASE_URL, REQUEST_TIMEOUT_MS } from "@/app/constant"; import { ApiPath, DEFAULT_API_HOST, REQUEST_TIMEOUT_MS } from "@/app/constant";
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store"; import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
import { import {
@@ -22,7 +22,6 @@ import mapKeys from "lodash-es/mapKeys";
import mapValues from "lodash-es/mapValues"; import mapValues from "lodash-es/mapValues";
import isArray from "lodash-es/isArray"; import isArray from "lodash-es/isArray";
import isObject from "lodash-es/isObject"; import isObject from "lodash-es/isObject";
import { fetch } from "@/app/utils/stream";
export interface OpenAIListModelResponse { export interface OpenAIListModelResponse {
object: string; object: string;
@@ -71,7 +70,9 @@ export class HunyuanApi implements LLMApi {
if (baseUrl.length === 0) { if (baseUrl.length === 0) {
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
baseUrl = isApp ? TENCENT_BASE_URL : ApiPath.Tencent; baseUrl = isApp
? DEFAULT_API_HOST + "/api/proxy/tencent"
: ApiPath.Tencent;
} }
if (baseUrl.endsWith("/")) { if (baseUrl.endsWith("/")) {
@@ -178,7 +179,6 @@ export class HunyuanApi implements LLMApi {
controller.signal.onabort = finish; controller.signal.onabort = finish;
fetchEventSource(chatPath, { fetchEventSource(chatPath, {
fetch: fetch as any,
...chatPayload, ...chatPayload,
async onopen(res) { async onopen(res) {
clearTimeout(requestTimeoutId); clearTimeout(requestTimeoutId);

View File

@@ -1815,7 +1815,6 @@ function _Chat() {
{message?.tools?.map((tool) => ( {message?.tools?.map((tool) => (
<div <div
key={tool.id} key={tool.id}
title={tool?.errorMsg}
className={styles["chat-message-tool"]} className={styles["chat-message-tool"]}
> >
{tool.isError === false ? ( {tool.isError === false ? (

View File

@@ -21,9 +21,6 @@ import {
} from "./artifacts"; } from "./artifacts";
import { useChatStore } from "../store"; import { useChatStore } from "../store";
import { IconButton } from "./button"; import { IconButton } from "./button";
import { useAppConfig } from "../store/config";
export function Mermaid(props: { code: string }) { export function Mermaid(props: { code: string }) {
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const [hasError, setHasError] = useState(false); const [hasError, setHasError] = useState(false);
@@ -94,9 +91,7 @@ export function PreCode(props: { children: any }) {
} }
}, 600); }, 600);
const config = useAppConfig(); const enableArtifacts = session.mask?.enableArtifacts !== false;
const enableArtifacts =
session.mask?.enableArtifacts !== false && config.enableArtifacts;
//Wrap the paragraph for plain-text //Wrap the paragraph for plain-text
useEffect(() => { useEffect(() => {
@@ -132,9 +127,8 @@ export function PreCode(props: { children: any }) {
className="copy-code-button" className="copy-code-button"
onClick={() => { onClick={() => {
if (ref.current) { if (ref.current) {
copyToClipboard( const code = ref.current.innerText;
ref.current.querySelector("code")?.innerText ?? "", copyToClipboard(code);
);
} }
}} }}
></span> ></span>
@@ -207,6 +201,23 @@ function CustomCode(props: { children: any; className?: string }) {
); );
} }
function escapeDollarNumber(text: string) {
let escapedText = "";
for (let i = 0; i < text.length; i += 1) {
let char = text[i];
const nextChar = text[i + 1] || " ";
if (char === "$" && nextChar >= "0" && nextChar <= "9") {
char = "\\$";
}
escapedText += char;
}
return escapedText;
}
function escapeBrackets(text: string) { function escapeBrackets(text: string) {
const pattern = const pattern =
/(```[\s\S]*?```|`.*?`)|\\\[([\s\S]*?[^\\])\\\]|\\\((.*?)\\\)/g; /(```[\s\S]*?```|`.*?`)|\\\[([\s\S]*?[^\\])\\\]|\\\((.*?)\\\)/g;
@@ -235,7 +246,7 @@ function tryWrapHtmlCode(text: string) {
}, },
) )
.replace( .replace(
/(<\/body>)([\r\n\s]*?)(<\/html>)([\n\r]*)([`]*)([\n\r]*?)/g, /(<\/body>)([\r\n\s]*?)(<\/html>)([\n\r]*?)([`]*?)([\n\r]*?)/g,
(match, bodyEnd, space, htmlEnd, newLine, quoteEnd) => { (match, bodyEnd, space, htmlEnd, newLine, quoteEnd) => {
return !quoteEnd ? bodyEnd + space + htmlEnd + "\n```\n" : match; return !quoteEnd ? bodyEnd + space + htmlEnd + "\n```\n" : match;
}, },
@@ -244,7 +255,7 @@ function tryWrapHtmlCode(text: string) {
function _MarkDownContent(props: { content: string }) { function _MarkDownContent(props: { content: string }) {
const escapedContent = useMemo(() => { const escapedContent = useMemo(() => {
return tryWrapHtmlCode(escapeBrackets(props.content)); return tryWrapHtmlCode(escapeBrackets(escapeDollarNumber(props.content)));
}, [props.content]); }, [props.content]);
return ( return (
@@ -266,20 +277,6 @@ function _MarkDownContent(props: { content: string }) {
p: (pProps) => <p {...pProps} dir="auto" />, p: (pProps) => <p {...pProps} dir="auto" />,
a: (aProps) => { a: (aProps) => {
const href = aProps.href || ""; const href = aProps.href || "";
if (/\.(aac|mp3|opus|wav)$/.test(href)) {
return (
<figure>
<audio controls src={href}></audio>
</figure>
);
}
if (/\.(3gp|3g2|webm|ogv|mpeg|mp4|avi)$/.test(href)) {
return (
<video controls width="99.9%">
<source src={href} />
</video>
);
}
const isInternal = /^\/#/i.test(href); const isInternal = /^\/#/i.test(href);
const target = isInternal ? "_self" : aProps.target ?? "_blank"; const target = isInternal ? "_self" : aProps.target ?? "_blank";
return <a {...aProps} target={target} />; return <a {...aProps} target={target} />;

View File

@@ -166,7 +166,6 @@ export function MaskConfig(props: {
></input> ></input>
</ListItem> </ListItem>
{globalConfig.enableArtifacts && (
<ListItem <ListItem
title={Locale.Mask.Config.Artifacts.Title} title={Locale.Mask.Config.Artifacts.Title}
subTitle={Locale.Mask.Config.Artifacts.SubTitle} subTitle={Locale.Mask.Config.Artifacts.SubTitle}
@@ -182,7 +181,6 @@ export function MaskConfig(props: {
}} }}
></input> ></input>
</ListItem> </ListItem>
)}
{!props.shouldSyncFromGlobal ? ( {!props.shouldSyncFromGlobal ? (
<ListItem <ListItem

View File

@@ -10,29 +10,7 @@
max-height: 240px; max-height: 240px;
overflow-y: auto; overflow-y: auto;
white-space: pre-wrap; white-space: pre-wrap;
min-width: 280px; min-width: 300px;
} }
} }
.plugin-schema {
display: flex;
justify-content: flex-end;
flex-direction: row;
input {
margin-right: 20px;
@media screen and (max-width: 600px) {
margin-right: 0px;
}
}
@media screen and (max-width: 600px) {
flex-direction: column;
gap: 5px;
button {
padding: 10px;
}
}
}

View File

@@ -12,6 +12,7 @@ import EditIcon from "../icons/edit.svg";
import AddIcon from "../icons/add.svg"; import AddIcon from "../icons/add.svg";
import CloseIcon from "../icons/close.svg"; import CloseIcon from "../icons/close.svg";
import DeleteIcon from "../icons/delete.svg"; import DeleteIcon from "../icons/delete.svg";
import EyeIcon from "../icons/eye.svg";
import ConfirmIcon from "../icons/confirm.svg"; import ConfirmIcon from "../icons/confirm.svg";
import ReloadIcon from "../icons/reload.svg"; import ReloadIcon from "../icons/reload.svg";
import GithubIcon from "../icons/github.svg"; import GithubIcon from "../icons/github.svg";
@@ -28,6 +29,7 @@ import {
import Locale from "../locales"; import Locale from "../locales";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useState } from "react"; import { useState } from "react";
import { getClientConfig } from "../config/client";
export function PluginPage() { export function PluginPage() {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -207,11 +209,19 @@ export function PluginPage() {
</div> </div>
</div> </div>
<div className={styles["mask-actions"]}> <div className={styles["mask-actions"]}>
{m.builtin ? (
<IconButton
icon={<EyeIcon />}
text={Locale.Plugin.Item.View}
onClick={() => setEditingPluginId(m.id)}
/>
) : (
<IconButton <IconButton
icon={<EditIcon />} icon={<EditIcon />}
text={Locale.Plugin.Item.Edit} text={Locale.Plugin.Item.Edit}
onClick={() => setEditingPluginId(m.id)} onClick={() => setEditingPluginId(m.id)}
/> />
)}
{!m.builtin && ( {!m.builtin && (
<IconButton <IconButton
icon={<DeleteIcon />} icon={<DeleteIcon />}
@@ -315,13 +325,30 @@ export function PluginPage() {
></PasswordInput> ></PasswordInput>
</ListItem> </ListItem>
)} )}
{!getClientConfig()?.isApp && (
<ListItem
title={Locale.Plugin.Auth.Proxy}
subTitle={Locale.Plugin.Auth.ProxyDescription}
>
<input
type="checkbox"
checked={editingPlugin?.usingProxy}
style={{ minWidth: 16 }}
onChange={(e) => {
pluginStore.updatePlugin(editingPlugin.id, (plugin) => {
plugin.usingProxy = e.currentTarget.checked;
});
}}
></input>
</ListItem>
)}
</List> </List>
<List> <List>
<ListItem title={Locale.Plugin.EditModal.Content}> <ListItem title={Locale.Plugin.EditModal.Content}>
<div className={pluginStyles["plugin-schema"]}> <div style={{ display: "flex", justifyContent: "flex-end" }}>
<input <input
type="text" type="text"
style={{ minWidth: 200 }} style={{ minWidth: 200, marginRight: 20 }}
onInput={(e) => setLoadUrl(e.currentTarget.value)} onInput={(e) => setLoadUrl(e.currentTarget.value)}
></input> ></input>
<IconButton <IconButton

View File

@@ -1492,23 +1492,6 @@ export function Settings() {
} }
></input> ></input>
</ListItem> </ListItem>
<ListItem
title={Locale.Mask.Config.Artifacts.Title}
subTitle={Locale.Mask.Config.Artifacts.SubTitle}
>
<input
aria-label={Locale.Mask.Config.Artifacts.Title}
type="checkbox"
checked={config.enableArtifacts}
onChange={(e) =>
updateConfig(
(config) =>
(config.enableArtifacts = e.currentTarget.checked),
)
}
></input>
</ListItem>
</List> </List>
<SyncItems /> <SyncItems />

View File

@@ -154,8 +154,8 @@ export const getServerSideConfig = () => {
// `[Server Config] using ${randomIndex + 1} of ${apiKeys.length} api key`, // `[Server Config] using ${randomIndex + 1} of ${apiKeys.length} api key`,
// ); // );
const allowedWebDavEndpoints = ( const allowedWebDevEndpoints = (
process.env.WHITE_WEBDAV_ENDPOINTS ?? "" process.env.WHITE_WEBDEV_ENDPOINTS ?? ""
).split(","); ).split(",");
return { return {
@@ -229,6 +229,6 @@ export const getServerSideConfig = () => {
disableFastLink: !!process.env.DISABLE_FAST_LINK, disableFastLink: !!process.env.DISABLE_FAST_LINK,
customModels, customModels,
defaultModel, defaultModel,
allowedWebDavEndpoints, allowedWebDevEndpoints,
}; };
}; };

View File

@@ -11,6 +11,7 @@ export const RUNTIME_CONFIG_DOM = "danger-runtime-config";
export const STABILITY_BASE_URL = "https://api.stability.ai"; export const STABILITY_BASE_URL = "https://api.stability.ai";
export const DEFAULT_API_HOST = "https://api.nextchat.dev";
export const OPENAI_BASE_URL = "https://api.openai.com"; export const OPENAI_BASE_URL = "https://api.openai.com";
export const ANTHROPIC_BASE_URL = "https://api.anthropic.com"; export const ANTHROPIC_BASE_URL = "https://api.anthropic.com";
@@ -502,4 +503,3 @@ export const PLUGINS = [
]; ];
export const SAAS_CHAT_URL = "https://nextchat.dev/chat"; export const SAAS_CHAT_URL = "https://nextchat.dev/chat";
export const SAAS_CHAT_UTM_URL = "https://nextchat.dev/chat?utm=github";

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const ar: PartialLocaleType = { const ar: PartialLocaleType = {
@@ -9,10 +9,10 @@ const ar: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق: ? `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق:
\\ 1⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_URL})
\\ 2⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️` \\ 2⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️`
: `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق: : `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق:
\ 1⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_URL})
\ 2⃣ إذا كنت تستخدم إصدار النشر الخاص، انقر [هنا](/#/auth) لإدخال مفتاح الوصول 🔑 \ 2⃣ إذا كنت تستخدم إصدار النشر الخاص، انقر [هنا](/#/auth) لإدخال مفتاح الوصول 🔑
\ 3⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️ \ 3⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const bn: PartialLocaleType = { const bn: PartialLocaleType = {
@@ -9,10 +9,10 @@ const bn: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই: ? `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই:
\\ 1⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_URL})
\\ 2⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️` \\ 2⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️`
: `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই: : `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই:
\ 1⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_URL})
\ 2⃣ যদি আপনি একটি প্রাইভেট ডেপ্লয়মেন্ট সংস্করণ ব্যবহার করেন, তাহলে [এখানে ক্লিক করুন](/#/auth) প্রবেশাধিকার কীগুলি প্রবেশ করতে 🔑 \ 2⃣ যদি আপনি একটি প্রাইভেট ডেপ্লয়মেন্ট সংস্করণ ব্যবহার করেন, তাহলে [এখানে ক্লিক করুন](/#/auth) প্রবেশাধিকার কীগুলি প্রবেশ করতে 🔑
\ 3⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️ \ 3⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️
`, `,

View File

@@ -1,7 +1,8 @@
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const SAAS_CHAT_URL_WITH_PARAM = `${SAAS_CHAT_URL}?data=title`;
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const cn = { const cn = {
@@ -9,10 +10,10 @@ const cn = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 对话遇到了一些问题,不用慌: ? `😆 对话遇到了一些问题,不用慌:
\\ 1⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL_WITH_PARAM})
\\ 2⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️` \\ 2⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️`
: `😆 对话遇到了一些问题,不用慌: : `😆 对话遇到了一些问题,不用慌:
\ 1⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_URL_WITH_PARAM})
\ 2⃣ 如果你正在使用私有部署版本,点击[这里](/#/auth)输入访问秘钥 🔑 \ 2⃣ 如果你正在使用私有部署版本,点击[这里](/#/auth)输入访问秘钥 🔑
\ 3⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️ \ 3⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const cs: PartialLocaleType = { const cs: PartialLocaleType = {
@@ -9,10 +9,10 @@ const cs: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 Rozhovor narazil na nějaké problémy, nebojte se: ? `😆 Rozhovor narazil na nějaké problémy, nebojte se:
\\ 1⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_URL})
\\ 2⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️` \\ 2⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️`
: `😆 Rozhovor narazil na nějaké problémy, nebojte se: : `😆 Rozhovor narazil na nějaké problémy, nebojte se:
\ 1⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_URL})
\ 2⃣ Pokud používáte verzi soukromého nasazení, klikněte [sem](/#/auth) a zadejte přístupový klíč 🔑 \ 2⃣ Pokud používáte verzi soukromého nasazení, klikněte [sem](/#/auth) a zadejte přístupový klíč 🔑
\ 3⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️ \ 3⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const de: PartialLocaleType = { const de: PartialLocaleType = {
@@ -9,10 +9,10 @@ const de: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 Das Gespräch hatte einige Probleme, keine Sorge: ? `😆 Das Gespräch hatte einige Probleme, keine Sorge:
\\ 1⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_URL})
\\ 2⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️` \\ 2⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️`
: `😆 Das Gespräch hatte einige Probleme, keine Sorge: : `😆 Das Gespräch hatte einige Probleme, keine Sorge:
\ 1⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_URL})
\ 2⃣ Wenn du eine private Bereitstellung verwendest, klicke [hier](/#/auth), um den Zugriffsschlüssel einzugeben 🔑 \ 2⃣ Wenn du eine private Bereitstellung verwendest, klicke [hier](/#/auth), um den Zugriffsschlüssel einzugeben 🔑
\ 3⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️ \ 3⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import { LocaleType } from "./index"; import { LocaleType } from "./index";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
// if you are adding a new translation, please use PartialLocaleType instead of LocaleType // if you are adding a new translation, please use PartialLocaleType instead of LocaleType
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
@@ -10,10 +10,10 @@ const en: LocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 Oops, there's an issue. No worries: ? `😆 Oops, there's an issue. No worries:
\\ 1⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_URL})
\\ 2⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️` \\ 2⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️`
: `😆 Oops, there's an issue. Let's fix it: : `😆 Oops, there's an issue. Let's fix it:
\ 1⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_URL})
\ 2⃣ Using a private setup? [Click here](/#/auth) to enter your key 🔑 \ 2⃣ Using a private setup? [Click here](/#/auth) to enter your key 🔑
\ 3⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️ \ 3⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const es: PartialLocaleType = { const es: PartialLocaleType = {
@@ -9,10 +9,10 @@ const es: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 La conversación encontró algunos problemas, no te preocupes: ? `😆 La conversación encontró algunos problemas, no te preocupes:
\\ 1⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_URL})
\\ 2⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️` \\ 2⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️`
: `😆 La conversación encontró algunos problemas, no te preocupes: : `😆 La conversación encontró algunos problemas, no te preocupes:
\ 1⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_URL})
\ 2⃣ Si estás utilizando una versión de implementación privada, haz clic [aquí](/#/auth) para ingresar la clave de acceso 🔑 \ 2⃣ Si estás utilizando una versión de implementación privada, haz clic [aquí](/#/auth) para ingresar la clave de acceso 🔑
\ 3⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️ \ 3⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const fr: PartialLocaleType = { const fr: PartialLocaleType = {
@@ -9,10 +9,10 @@ const fr: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 La conversation a rencontré quelques problèmes, pas de panique : ? `😆 La conversation a rencontré quelques problèmes, pas de panique :
\\ 1⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_URL})
\\ 2⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️` \\ 2⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️`
: `😆 La conversation a rencontré quelques problèmes, pas de panique : : `😆 La conversation a rencontré quelques problèmes, pas de panique :
\ 1⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_URL})
\ 2⃣ Si vous utilisez une version déployée privée, cliquez [ici](/#/auth) pour entrer la clé d'accès 🔑 \ 2⃣ Si vous utilisez une version déployée privée, cliquez [ici](/#/auth) pour entrer la clé d'accès 🔑
\ 3⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️ \ 3⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const id: PartialLocaleType = { const id: PartialLocaleType = {
@@ -9,10 +9,10 @@ const id: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir: ? `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir:
\\ 1⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_URL})
\\ 2⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️` \\ 2⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️`
: `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir: : `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir:
\ 1⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_URL})
\ 2⃣ Jika Anda menggunakan versi penyebaran pribadi, klik [di sini](/#/auth) untuk memasukkan kunci akses 🔑 \ 2⃣ Jika Anda menggunakan versi penyebaran pribadi, klik [di sini](/#/auth) untuk memasukkan kunci akses 🔑
\ 3⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️ \ 3⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const it: PartialLocaleType = { const it: PartialLocaleType = {
@@ -9,10 +9,10 @@ const it: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti: ? `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti:
\\ 1⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_URL})
\\ 2⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️` \\ 2⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️`
: `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti: : `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti:
\ 1⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_URL})
\ 2⃣ Se stai utilizzando una versione di distribuzione privata, clicca [qui](/#/auth) per inserire la chiave di accesso 🔑 \ 2⃣ Se stai utilizzando una versione di distribuzione privata, clicca [qui](/#/auth) per inserire la chiave di accesso 🔑
\ 3⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️ \ 3⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const jp: PartialLocaleType = { const jp: PartialLocaleType = {
@@ -9,10 +9,10 @@ const jp: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 会話中に問題が発生しましたが、心配しないでください: ? `😆 会話中に問題が発生しましたが、心配しないでください:
\\ 1⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_URL})
\\ 2⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️` \\ 2⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️`
: `😆 会話中に問題が発生しましたが、心配しないでください: : `😆 会話中に問題が発生しましたが、心配しないでください:
\ 1⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_URL})
\ 2⃣ プライベートデプロイ版を使用している場合は、[ここをクリックして](/#/auth)アクセストークンを入力してください 🔑 \ 2⃣ プライベートデプロイ版を使用している場合は、[ここをクリックして](/#/auth)アクセストークンを入力してください 🔑
\ 3⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️ \ 3⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const ko: PartialLocaleType = { const ko: PartialLocaleType = {
@@ -9,10 +9,10 @@ const ko: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요: ? `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요:
\\ 1⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_URL})
\\ 2⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️` \\ 2⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️`
: `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요: : `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요:
\ 1⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_URL})
\ 2⃣ 개인 배포 버전을 사용하고 있다면, [여기를 클릭하여](/#/auth) 접근 키를 입력하세요 🔑 \ 2⃣ 개인 배포 버전을 사용하고 있다면, [여기를 클릭하여](/#/auth) 접근 키를 입력하세요 🔑
\ 3⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️ \ 3⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const no: PartialLocaleType = { const no: PartialLocaleType = {
@@ -9,10 +9,10 @@ const no: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 Samtalen har støtt på noen problemer, ikke bekymre deg: ? `😆 Samtalen har støtt på noen problemer, ikke bekymre deg:
\\ 1⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_URL})
\\ 2⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️` \\ 2⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️`
: `😆 Samtalen har støtt på noen problemer, ikke bekymre deg: : `😆 Samtalen har støtt på noen problemer, ikke bekymre deg:
\ 1⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_URL})
\ 2⃣ Hvis du bruker en privat distribusjonsversjon, klikk [her](/#/auth) for å skrive inn tilgangsnøkkelen 🔑 \ 2⃣ Hvis du bruker en privat distribusjonsversjon, klikk [her](/#/auth) for å skrive inn tilgangsnøkkelen 🔑
\ 3⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️ \ 3⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import { PartialLocaleType } from "../locales/index"; import { PartialLocaleType } from "../locales/index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const pt: PartialLocaleType = { const pt: PartialLocaleType = {
@@ -9,10 +9,10 @@ const pt: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 A conversa encontrou alguns problemas, não se preocupe: ? `😆 A conversa encontrou alguns problemas, não se preocupe:
\\ 1⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_URL})
\\ 2⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️` \\ 2⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️`
: `😆 A conversa encontrou alguns problemas, não se preocupe: : `😆 A conversa encontrou alguns problemas, não se preocupe:
\ 1⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_URL})
\ 2⃣ Se você estiver usando uma versão de implantação privada, clique [aqui](/#/auth) para inserir a chave de acesso 🔑 \ 2⃣ Se você estiver usando uma versão de implantação privada, clique [aqui](/#/auth) para inserir a chave de acesso 🔑
\ 3⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️ \ 3⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import { PartialLocaleType } from "../locales/index"; import { PartialLocaleType } from "../locales/index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const ru: PartialLocaleType = { const ru: PartialLocaleType = {
@@ -9,10 +9,10 @@ const ru: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 В разговоре возникли некоторые проблемы, не переживайте: ? `😆 В разговоре возникли некоторые проблемы, не переживайте:
\\ 1⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_URL})
\\ 2⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️` \\ 2⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️`
: `😆 В разговоре возникли некоторые проблемы, не переживайте: : `😆 В разговоре возникли некоторые проблемы, не переживайте:
\ 1⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_URL})
\ 2⃣ Если вы используете частную версию развертывания, нажмите [здесь](/#/auth), чтобы ввести ключ доступа 🔑 \ 2⃣ Если вы используете частную версию развертывания, нажмите [здесь](/#/auth), чтобы ввести ключ доступа 🔑
\ 3⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️ \ 3⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
// if you are adding a new translation, please use PartialLocaleType instead of LocaleType // if you are adding a new translation, please use PartialLocaleType instead of LocaleType
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
@@ -10,10 +10,10 @@ const sk: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 Rozhovor narazil na nejaké problémy, nebojte sa: ? `😆 Rozhovor narazil na nejaké problémy, nebojte sa:
\\ 1⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_URL})
\\ 2⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️` \\ 2⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️`
: `😆 Rozhovor narazil na nejaké problémy, nebojte sa: : `😆 Rozhovor narazil na nejaké problémy, nebojte sa:
\ 1⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_URL})
\ 2⃣ Ak používate verziu súkromného nasadenia, kliknite [sem](/#/auth), aby ste zadali prístupový kľúč 🔑 \ 2⃣ Ak používate verziu súkromného nasadenia, kliknite [sem](/#/auth), aby ste zadali prístupový kľúč 🔑
\ 3⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️ \ 3⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️
`, `,

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const tr: PartialLocaleType = { const tr: PartialLocaleType = {
@@ -9,10 +9,10 @@ const tr: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin: ? `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin:
\\ 1⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_URL})
\\ 2⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️` \\ 2⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️`
: `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin: : `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin:
\ 1⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_URL})
\ 2⃣ Eğer özel dağıtım sürümü kullanıyorsanız, [buraya tıklayarak](/#/auth) erişim anahtarını girin 🔑 \ 2⃣ Eğer özel dağıtım sürümü kullanıyorsanız, [buraya tıklayarak](/#/auth) erişim anahtarını girin 🔑
\ 3⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️ \ 3⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️
`, `,

View File

@@ -1,6 +1,6 @@
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const tw = { const tw = {
@@ -8,12 +8,12 @@ const tw = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 對話遇到了一些問題,不用慌: ? `😆 對話遇到了一些問題,不用慌:
\\ 1⃣ 想要無須設定開箱即用,[點這裡立刻開啟對話 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ 想要零配置開箱即用,[點這裡立刻開啟對話 🚀](${SAAS_CHAT_URL})
\\ 2⃣ 如果你想消耗自己的 OpenAI 資源,點[這裡](/#/settings)修改設定 ⚙️` \\ 2⃣ 如果你想消耗自己的 OpenAI 資源,點[這裡](/#/settings)修改設定 ⚙️`
: `😆 對話遇到了一些問題,不用慌: : `😆 對話遇到了一些問題,不用慌:
\ 1⃣ 想要無須設定開箱即用,[點這裡立刻開啟對話 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ 想要零配置開箱即用,[點這裡立刻開啟對話 🚀](${SAAS_CHAT_URL})
\ 2⃣ 如果你正在使用私有部署版本,點[這裡](/#/auth)輸入存取金鑰 🔑 \ 2⃣ 如果你正在使用私有部署版本,點[這裡](/#/auth)輸入訪問秘鑰 🔑
\ 3⃣ 如果你想消耗自己的 OpenAI 資源,點[這裡](/#/settings)修改設定 ⚙️ \ 3⃣ 如果你想消耗自己的 OpenAI 資源,點[這裡](/#/settings)修改設定 ⚙️
`, `,
}, },
@@ -25,9 +25,9 @@ const tw = {
Confirm: "確認", Confirm: "確認",
Later: "稍候再說", Later: "稍候再說",
Return: "返回", Return: "返回",
SaasTips: "設定太麻煩,想要立即使用", SaasTips: "配置太麻煩,想要立即使用",
TopTips: TopTips:
"🥳 NextChat AI 首發優惠,立刻解鎖 OpenAI o1, GPT-4o, Claude-3.5 等最新的大型語言模型", "🥳 NextChat AI 首發優惠,立刻解鎖 OpenAI o1, GPT-4o, Claude-3.5 等最新模型",
}, },
ChatItem: { ChatItem: {
ChatItemCount: (count: number) => `${count} 則對話`, ChatItemCount: (count: number) => `${count} 則對話`,
@@ -53,8 +53,8 @@ const tw = {
PinToastAction: "檢視", PinToastAction: "檢視",
Delete: "刪除", Delete: "刪除",
Edit: "編輯", Edit: "編輯",
RefreshTitle: "重新整理標題", RefreshTitle: "刷新標題",
RefreshToast: "已傳送重新整理標題請求", RefreshToast: "已發送刷新標題請求",
}, },
Commands: { Commands: {
new: "新建聊天", new: "新建聊天",
@@ -95,10 +95,10 @@ const tw = {
IsContext: "預設提示詞", IsContext: "預設提示詞",
ShortcutKey: { ShortcutKey: {
Title: "鍵盤快捷方式", Title: "鍵盤快捷方式",
newChat: "開新聊天", newChat: "開新聊天",
focusInput: "聚焦輸入框", focusInput: "聚焦輸入框",
copyLastMessage: "複製最後一個回覆", copyLastMessage: "複製最後一個回覆",
copyLastCode: "複製最後一個程式碼區塊", copyLastCode: "複製最後一個代碼塊",
showShortcutKey: "顯示快捷方式", showShortcutKey: "顯示快捷方式",
}, },
}, },
@@ -174,9 +174,9 @@ const tw = {
SubTitle: "聊天內容的字型大小", SubTitle: "聊天內容的字型大小",
}, },
FontFamily: { FontFamily: {
Title: "聊天字", Title: "聊天字",
SubTitle: "聊天內容的字,若空則用全域預設字型", SubTitle: "聊天內容的字,若空則用全局默認字體",
Placeholder: "字名稱", Placeholder: "字名稱",
}, },
InjectSystemPrompts: { InjectSystemPrompts: {
Title: "匯入系統提示", Title: "匯入系統提示",
@@ -301,8 +301,8 @@ const tw = {
Title: "使用 NextChat AI", Title: "使用 NextChat AI",
Label: "(性價比最高的方案)", Label: "(性價比最高的方案)",
SubTitle: SubTitle:
"由 NextChat 官方維護,無須設定開箱即用,支 OpenAI o1、GPT-4o、Claude-3.5 等最新的大型語言模型", "由 NextChat 官方維護,零配置開箱即用,支 OpenAI o1、GPT-4o、Claude-3.5 等最新模型",
ChatNow: "立刻開始對話", ChatNow: "立刻對話",
}, },
AccessCode: { AccessCode: {
@@ -485,18 +485,18 @@ const tw = {
}, },
}, },
SearchChat: { SearchChat: {
Name: "搜", Name: "搜",
Page: { Page: {
Title: "搜聊天記錄", Title: "搜聊天記錄",
Search: "輸入搜關鍵詞", Search: "輸入搜關鍵詞",
NoResult: "沒有找到結果", NoResult: "沒有找到結果",
NoData: "沒有資料", NoData: "沒有數據",
Loading: "載中", Loading: "載中",
SubTitle: (count: number) => `找到 ${count} 條結果`, SubTitle: (count: number) => `找到 ${count} 條結果`,
}, },
Item: { Item: {
View: "檢視", View: "查看",
}, },
}, },
NewChat: { NewChat: {

View File

@@ -1,7 +1,7 @@
import { SubmitKey } from "../store/config"; import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index"; import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { SAAS_CHAT_UTM_URL } from "@/app/constant"; import { SAAS_CHAT_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
const vi: PartialLocaleType = { const vi: PartialLocaleType = {
@@ -9,10 +9,10 @@ const vi: PartialLocaleType = {
Error: { Error: {
Unauthorized: isApp Unauthorized: isApp
? `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng: ? `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng:
\\ 1⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_UTM_URL}) \\ 1⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_URL})
\\ 2⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️` \\ 2⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️`
: `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng: : `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng:
\ 1⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_UTM_URL}) \ 1⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_URL})
\ 2⃣ Nếu bạn đang sử dụng phiên bản triển khai riêng, hãy nhấp [vào đây](/#/auth) để nhập khóa truy cập 🔑 \ 2⃣ Nếu bạn đang sử dụng phiên bản triển khai riêng, hãy nhấp [vào đây](/#/auth) để nhập khóa truy cập 🔑
\ 3⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️ \ 3⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️
`, `,

View File

@@ -1,18 +1,9 @@
import { import {
ApiPath,
DEFAULT_API_HOST,
GoogleSafetySettingsThreshold, GoogleSafetySettingsThreshold,
ServiceProvider, ServiceProvider,
StoreKey, StoreKey,
ApiPath,
OPENAI_BASE_URL,
ANTHROPIC_BASE_URL,
GEMINI_BASE_URL,
BAIDU_BASE_URL,
BYTEDANCE_BASE_URL,
ALIBABA_BASE_URL,
TENCENT_BASE_URL,
MOONSHOT_BASE_URL,
STABILITY_BASE_URL,
IFLYTEK_BASE_URL,
} from "../constant"; } from "../constant";
import { getHeaders } from "../client/api"; import { getHeaders } from "../client/api";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
@@ -24,25 +15,45 @@ let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
const isApp = getClientConfig()?.buildMode === "export"; const isApp = getClientConfig()?.buildMode === "export";
const DEFAULT_OPENAI_URL = isApp ? OPENAI_BASE_URL : ApiPath.OpenAI; const DEFAULT_OPENAI_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/openai"
: ApiPath.OpenAI;
const DEFAULT_GOOGLE_URL = isApp ? GEMINI_BASE_URL : ApiPath.Google; const DEFAULT_GOOGLE_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/google"
: ApiPath.Google;
const DEFAULT_ANTHROPIC_URL = isApp ? ANTHROPIC_BASE_URL : ApiPath.Anthropic; const DEFAULT_ANTHROPIC_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/anthropic"
: ApiPath.Anthropic;
const DEFAULT_BAIDU_URL = isApp ? BAIDU_BASE_URL : ApiPath.Baidu; const DEFAULT_BAIDU_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/baidu"
: ApiPath.Baidu;
const DEFAULT_BYTEDANCE_URL = isApp ? BYTEDANCE_BASE_URL : ApiPath.ByteDance; const DEFAULT_BYTEDANCE_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/bytedance"
: ApiPath.ByteDance;
const DEFAULT_ALIBABA_URL = isApp ? ALIBABA_BASE_URL : ApiPath.Alibaba; const DEFAULT_ALIBABA_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/alibaba"
: ApiPath.Alibaba;
const DEFAULT_TENCENT_URL = isApp ? TENCENT_BASE_URL : ApiPath.Tencent; const DEFAULT_TENCENT_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/tencent"
: ApiPath.Tencent;
const DEFAULT_MOONSHOT_URL = isApp ? MOONSHOT_BASE_URL : ApiPath.Moonshot; const DEFAULT_MOONSHOT_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/moonshot"
: ApiPath.Moonshot;
const DEFAULT_STABILITY_URL = isApp ? STABILITY_BASE_URL : ApiPath.Stability; const DEFAULT_STABILITY_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/stability"
: ApiPath.Stability;
const DEFAULT_IFLYTEK_URL = isApp ? IFLYTEK_BASE_URL : ApiPath.Iflytek; const DEFAULT_IFLYTEK_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/iflytek"
: ApiPath.Iflytek;
const DEFAULT_ACCESS_STATE = { const DEFAULT_ACCESS_STATE = {
accessCode: "", accessCode: "",
@@ -200,13 +211,10 @@ export const useAccessStore = createPersistStore(
}) })
.then((res) => res.json()) .then((res) => res.json())
.then((res) => { .then((res) => {
const defaultModel = res.defaultModel ?? ""; // Set default model from env request
if (defaultModel !== "") { let defaultModel = res.defaultModel ?? "";
const [model, providerName] = defaultModel.split("@"); if (defaultModel !== "")
DEFAULT_CONFIG.modelConfig.model = model; DEFAULT_CONFIG.modelConfig.model = defaultModel;
DEFAULT_CONFIG.modelConfig.providerName = providerName;
}
return res; return res;
}) })
.then((res: DangerConfig) => { .then((res: DangerConfig) => {

View File

@@ -16,9 +16,6 @@ import {
DEFAULT_SYSTEM_TEMPLATE, DEFAULT_SYSTEM_TEMPLATE,
KnowledgeCutOffDate, KnowledgeCutOffDate,
StoreKey, StoreKey,
SUMMARIZE_MODEL,
GEMINI_SUMMARIZE_MODEL,
ServiceProvider,
} from "../constant"; } from "../constant";
import Locale, { getLang } from "../locales"; import Locale, { getLang } from "../locales";
import { isDalle3, safeLocalStorage } from "../utils"; import { isDalle3, safeLocalStorage } from "../utils";
@@ -26,8 +23,6 @@ import { prettyObject } from "../utils/format";
import { createPersistStore } from "../utils/store"; import { createPersistStore } from "../utils/store";
import { estimateTokenLength } from "../utils/token"; import { estimateTokenLength } from "../utils/token";
import { ModelConfig, ModelType, useAppConfig } from "./config"; import { ModelConfig, ModelType, useAppConfig } from "./config";
import { useAccessStore } from "./access";
import { collectModelsWithDefaultModel } from "../utils/model";
import { createEmptyMask, Mask } from "./mask"; import { createEmptyMask, Mask } from "./mask";
const localStorage = safeLocalStorage(); const localStorage = safeLocalStorage();
@@ -42,7 +37,6 @@ export type ChatMessageTool = {
}; };
content?: string; content?: string;
isError?: boolean; isError?: boolean;
errorMsg?: string;
}; };
export type ChatMessage = RequestMessage & { export type ChatMessage = RequestMessage & {
@@ -108,35 +102,6 @@ function createEmptySession(): ChatSession {
}; };
} }
function getSummarizeModel(
currentModel: string,
providerName: string,
): string[] {
// if it is using gpt-* models, force to use 4o-mini to summarize
if (currentModel.startsWith("gpt") || currentModel.startsWith("chatgpt")) {
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,
);
if (summarizeModel) {
return [
summarizeModel.name,
summarizeModel.provider?.providerName as string,
];
}
}
if (currentModel.startsWith("gemini")) {
return [GEMINI_SUMMARIZE_MODEL, ServiceProvider.Google];
}
return [currentModel, providerName];
}
function countMessages(msgs: ChatMessage[]) { function countMessages(msgs: ChatMessage[]) {
return msgs.reduce( return msgs.reduce(
(pre, cur) => pre + estimateTokenLength(getMessageTextContent(cur)), (pre, cur) => pre + estimateTokenLength(getMessageTextContent(cur)),
@@ -613,14 +578,8 @@ export const useChatStore = createPersistStore(
return; return;
} }
// if not config compressModel, then using getSummarizeModel const providerName = modelConfig.compressProviderName;
const [model, providerName] = modelConfig.compressModel const api: ClientApi = getClientApi(providerName);
? [modelConfig.compressModel, modelConfig.compressProviderName]
: getSummarizeModel(
session.mask.modelConfig.model,
session.mask.modelConfig.providerName,
);
const api: ClientApi = getClientApi(providerName as ServiceProvider);
// remove error messages if any // remove error messages if any
const messages = session.messages; const messages = session.messages;
@@ -651,12 +610,11 @@ export const useChatStore = createPersistStore(
api.llm.chat({ api.llm.chat({
messages: topicMessages, messages: topicMessages,
config: { config: {
model, model: modelConfig.compressModel,
stream: false, stream: false,
providerName, providerName,
}, },
onFinish(message) { onFinish(message) {
if (!isValidMessage(message)) return;
get().updateCurrentSession( get().updateCurrentSession(
(session) => (session) =>
(session.topic = (session.topic =
@@ -715,8 +673,7 @@ export const useChatStore = createPersistStore(
config: { config: {
...modelcfg, ...modelcfg,
stream: true, stream: true,
model, model: modelConfig.compressModel,
providerName,
}, },
onUpdate(message) { onUpdate(message) {
session.memoryPrompt = message; session.memoryPrompt = message;
@@ -733,10 +690,6 @@ export const useChatStore = createPersistStore(
}, },
}); });
} }
function isValidMessage(message: any): boolean {
return typeof message === "string" && !message.startsWith("```json");
}
}, },
updateStat(message: ChatMessage) { updateStat(message: ChatMessage) {
@@ -769,7 +722,7 @@ export const useChatStore = createPersistStore(
}, },
{ {
name: StoreKey.Chat, name: StoreKey.Chat,
version: 3.3, version: 3.2,
migrate(persistedState, version) { migrate(persistedState, version) {
const state = persistedState as any; const state = persistedState as any;
const newState = JSON.parse( const newState = JSON.parse(
@@ -825,14 +778,6 @@ export const useChatStore = createPersistStore(
config.modelConfig.compressProviderName; config.modelConfig.compressProviderName;
}); });
} }
// revert default summarize model for every session
if (version < 3.3) {
newState.sessions.forEach((s) => {
const config = useAppConfig.getState();
s.mask.modelConfig.compressModel = "";
s.mask.modelConfig.compressProviderName = "";
});
}
return newState as any; return newState as any;
}, },

View File

@@ -50,8 +50,6 @@ export const DEFAULT_CONFIG = {
enableAutoGenerateTitle: true, enableAutoGenerateTitle: true,
sidebarWidth: DEFAULT_SIDEBAR_WIDTH, sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
enableArtifacts: true, // show artifacts config
disablePromptHint: false, disablePromptHint: false,
dontShowMaskSplashScreen: false, // dont show splash screen when create chat dontShowMaskSplashScreen: false, // dont show splash screen when create chat
@@ -71,8 +69,8 @@ export const DEFAULT_CONFIG = {
sendMemory: true, sendMemory: true,
historyMessageCount: 4, historyMessageCount: 4,
compressMessageLengthThreshold: 1000, compressMessageLengthThreshold: 1000,
compressModel: "", compressModel: "gpt-4o-mini" as ModelType,
compressProviderName: "", compressProviderName: "OpenAI" as ServiceProvider,
enableInjectSystemPrompts: true, enableInjectSystemPrompts: true,
template: config?.template ?? DEFAULT_INPUT_TEMPLATE, template: config?.template ?? DEFAULT_INPUT_TEMPLATE,
size: "1024x1024" as DalleSize, size: "1024x1024" as DalleSize,
@@ -178,7 +176,7 @@ export const useAppConfig = createPersistStore(
}), }),
{ {
name: StoreKey.Config, name: StoreKey.Config,
version: 4.1, version: 4,
merge(persistedState, currentState) { merge(persistedState, currentState) {
const state = persistedState as ChatConfig | undefined; const state = persistedState as ChatConfig | undefined;
@@ -231,7 +229,7 @@ export const useAppConfig = createPersistStore(
: config?.template ?? DEFAULT_INPUT_TEMPLATE; : config?.template ?? DEFAULT_INPUT_TEMPLATE;
} }
if (version < 4.1) { if (version < 4) {
state.modelConfig.compressModel = state.modelConfig.compressModel =
DEFAULT_CONFIG.modelConfig.compressModel; DEFAULT_CONFIG.modelConfig.compressModel;
state.modelConfig.compressProviderName = state.modelConfig.compressProviderName =

View File

@@ -2,12 +2,8 @@ import OpenAPIClientAxios from "openapi-client-axios";
import { StoreKey } from "../constant"; import { StoreKey } from "../constant";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { createPersistStore } from "../utils/store"; import { createPersistStore } from "../utils/store";
import { getClientConfig } from "../config/client";
import yaml from "js-yaml"; import yaml from "js-yaml";
import { adapter, getOperationId } from "../utils"; import { adapter } from "../utils";
import { useAccessStore } from "./access";
const isApp = getClientConfig()?.isApp !== false;
export type Plugin = { export type Plugin = {
id: string; id: string;
@@ -20,6 +16,7 @@ export type Plugin = {
authLocation?: string; authLocation?: string;
authHeader?: string; authHeader?: string;
authToken?: string; authToken?: string;
usingProxy?: boolean;
}; };
export type FunctionToolItem = { export type FunctionToolItem = {
@@ -49,25 +46,18 @@ export const FunctionToolService = {
plugin?.authType == "basic" plugin?.authType == "basic"
? `Basic ${plugin?.authToken}` ? `Basic ${plugin?.authToken}`
: plugin?.authType == "bearer" : plugin?.authType == "bearer"
? `Bearer ${plugin?.authToken}` ? ` Bearer ${plugin?.authToken}`
: plugin?.authToken; : plugin?.authToken;
const authLocation = plugin?.authLocation || "header"; const authLocation = plugin?.authLocation || "header";
const definition = yaml.load(plugin.content) as any; const definition = yaml.load(plugin.content) as any;
const serverURL = definition?.servers?.[0]?.url; const serverURL = definition?.servers?.[0]?.url;
const baseURL = !isApp ? "/api/proxy" : serverURL; const baseURL = !!plugin?.usingProxy ? "/api/proxy" : serverURL;
const headers: Record<string, string | undefined> = { const headers: Record<string, string | undefined> = {
"X-Base-URL": !isApp ? serverURL : undefined, "X-Base-URL": !!plugin?.usingProxy ? serverURL : undefined,
}; };
if (authLocation == "header") { if (authLocation == "header") {
headers[headerName] = tokenValue; headers[headerName] = tokenValue;
} }
// try using openaiApiKey for Dalle3 Plugin.
if (!tokenValue && plugin.id === "dalle3") {
const openaiApiKey = useAccessStore.getState().openaiApiKey;
if (openaiApiKey) {
headers[headerName] = `Bearer ${openaiApiKey}`;
}
}
const api = new OpenAPIClientAxios({ const api = new OpenAPIClientAxios({
definition: yaml.load(plugin.content) as any, definition: yaml.load(plugin.content) as any,
axiosConfigDefaults: { axiosConfigDefaults: {
@@ -116,7 +106,7 @@ export const FunctionToolService = {
return { return {
type: "function", type: "function",
function: { function: {
name: getOperationId(o), name: o.operationId,
description: o.description || o.summary, description: o.description || o.summary,
parameters: parameters, parameters: parameters,
}, },
@@ -124,7 +114,7 @@ export const FunctionToolService = {
}), }),
funcs: operations.reduce((s, o) => { funcs: operations.reduce((s, o) => {
// @ts-ignore // @ts-ignore
s[getOperationId(o)] = function (args) { s[o.operationId] = function (args) {
const parameters: Record<string, any> = {}; const parameters: Record<string, any> = {};
if (o.parameters instanceof Array) { if (o.parameters instanceof Array) {
o.parameters.forEach((p) => { o.parameters.forEach((p) => {
@@ -139,8 +129,8 @@ export const FunctionToolService = {
} else if (authLocation == "body") { } else if (authLocation == "body") {
args[headerName] = tokenValue; args[headerName] = tokenValue;
} }
// @ts-ignore if o.operationId is null, then using o.path and o.method // @ts-ignore
return api.client.paths[o.path][o.method]( return api.client[o.operationId](
parameters, parameters,
args, args,
api.axiosConfigDefaults, api.axiosConfigDefaults,
@@ -175,7 +165,7 @@ export const usePluginStore = createPersistStore(
(set, get) => ({ (set, get) => ({
create(plugin?: Partial<Plugin>) { create(plugin?: Partial<Plugin>) {
const plugins = get().plugins; const plugins = get().plugins;
const id = plugin?.id || nanoid(); const id = nanoid();
plugins[id] = { plugins[id] = {
...createEmptyPlugin(), ...createEmptyPlugin(),
...plugin, ...plugin,
@@ -230,42 +220,5 @@ export const usePluginStore = createPersistStore(
{ {
name: StoreKey.Plugin, name: StoreKey.Plugin,
version: 1, version: 1,
onRehydrateStorage(state) {
// Skip store rehydration on server side
if (typeof window === "undefined") {
return;
}
fetch("./plugins.json")
.then((res) => res.json())
.then((res) => {
Promise.all(
res.map((item: any) =>
// skip get schema
state.get(item.id)
? item
: fetch(item.schema)
.then((res) => res.text())
.then((content) => ({
...item,
content,
}))
.catch((e) => item),
),
).then((builtinPlugins: any) => {
builtinPlugins
.filter((item: any) => item?.content)
.forEach((item: any) => {
const plugin = state.create(item);
state.updatePlugin(plugin.id, (plugin) => {
const tool = FunctionToolService.add(plugin, true);
plugin.title = tool.api.definition.info.title;
plugin.version = tool.api.definition.info.version;
plugin.builtin = true;
});
});
});
});
},
}, },
); );

View File

@@ -1,7 +1,7 @@
import Fuse from "fuse.js"; import Fuse from "fuse.js";
import { nanoid } from "nanoid";
import { StoreKey } from "../constant";
import { getLang } from "../locales"; import { getLang } from "../locales";
import { StoreKey } from "../constant";
import { nanoid } from "nanoid";
import { createPersistStore } from "../utils/store"; import { createPersistStore } from "../utils/store";
export interface Prompt { export interface Prompt {
@@ -147,11 +147,6 @@ export const usePromptStore = createPersistStore(
}, },
onRehydrateStorage(state) { onRehydrateStorage(state) {
// Skip store rehydration on server side
if (typeof window === "undefined") {
return;
}
const PROMPT_URL = "./prompts.json"; const PROMPT_URL = "./prompts.json";
type PromptList = Array<[string, string]>; type PromptList = Array<[string, string]>;

View File

@@ -12,6 +12,7 @@ import { downloadAs, readFromFile } from "../utils";
import { showToast } from "../components/ui-lib"; import { showToast } from "../components/ui-lib";
import Locale from "../locales"; import Locale from "../locales";
import { createSyncClient, ProviderType } from "../utils/cloud"; import { createSyncClient, ProviderType } from "../utils/cloud";
import { corsPath } from "../utils/cors";
export interface WebDavConfig { export interface WebDavConfig {
server: string; server: string;
@@ -25,7 +26,7 @@ export type SyncStore = GetStoreState<typeof useSyncStore>;
const DEFAULT_SYNC_STATE = { const DEFAULT_SYNC_STATE = {
provider: ProviderType.WebDAV, provider: ProviderType.WebDAV,
useProxy: true, useProxy: true,
proxyUrl: ApiPath.Cors as string, proxyUrl: corsPath(ApiPath.Cors),
webdav: { webdav: {
endpoint: "", endpoint: "",

View File

@@ -2,9 +2,8 @@ import { useEffect, useState } from "react";
import { showToast } from "./components/ui-lib"; import { showToast } from "./components/ui-lib";
import Locale from "./locales"; import Locale from "./locales";
import { RequestMessage } from "./client/api"; import { RequestMessage } from "./client/api";
import { ServiceProvider } from "./constant"; import { ServiceProvider, REQUEST_TIMEOUT_MS } from "./constant";
// import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http";
import { fetch as tauriStreamFetch } from "./utils/stream";
export function trimTopic(topic: string) { export function trimTopic(topic: string) {
// Fix an issue where double quotes still show in the Indonesian language // Fix an issue where double quotes still show in the Indonesian language
@@ -293,23 +292,30 @@ export function fetch(
options?: Record<string, unknown>, options?: Record<string, unknown>,
): Promise<any> { ): Promise<any> {
if (window.__TAURI__) { if (window.__TAURI__) {
return tauriStreamFetch(url, options); const payload = options?.body || options?.data;
return tauriFetch(url, {
...options,
body:
payload &&
({
type: "Text",
payload,
} as any),
timeout: ((options?.timeout as number) || REQUEST_TIMEOUT_MS) / 1000,
responseType:
options?.responseType == "text" ? ResponseType.Text : ResponseType.JSON,
} as any);
} }
return window.fetch(url, options); return window.fetch(url, options);
} }
export function adapter(config: Record<string, unknown>) { export function adapter(config: Record<string, unknown>) {
const { baseURL, url, params, data: body, ...rest } = config; const { baseURL, url, params, ...rest } = config;
const path = baseURL ? `${baseURL}${url}` : url; const path = baseURL ? `${baseURL}${url}` : url;
const fetchUrl = params const fetchUrl = params
? `${path}?${new URLSearchParams(params as any).toString()}` ? `${path}?${new URLSearchParams(params as any).toString()}`
: path; : path;
return fetch(fetchUrl as string, { ...rest, body }).then((res) => { return fetch(fetchUrl as string, { ...rest, responseType: "text" });
const { status, headers, statusText } = res;
return res
.text()
.then((data: string) => ({ status, statusText, headers, data }));
});
} }
export function safeLocalStorage(): { export function safeLocalStorage(): {
@@ -371,15 +377,3 @@ export function safeLocalStorage(): {
}, },
}; };
} }
export function getOperationId(operation: {
operationId?: string;
method: string;
path: string;
}) {
// pattern '^[a-zA-Z0-9_-]+$'
return (
operation?.operationId ||
`${operation.method.toUpperCase()}${operation.path.replaceAll("/", "_")}`
);
}

View File

@@ -10,7 +10,6 @@ import {
fetchEventSource, fetchEventSource,
} from "@fortaine/fetch-event-source"; } from "@fortaine/fetch-event-source";
import { prettyObject } from "./format"; import { prettyObject } from "./format";
import { fetch as tauriFetch } from "./stream";
export function compressImage(file: Blob, maxSize: number): Promise<string> { export function compressImage(file: Blob, maxSize: number): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -222,12 +221,7 @@ export function stream(
), ),
) )
.then((res) => { .then((res) => {
let content = res.data || res?.statusText; const content = JSON.stringify(res.data);
// hotfix #5614
content =
typeof content === "string"
? content
: JSON.stringify(content);
if (res.status >= 300) { if (res.status >= 300) {
return Promise.reject(content); return Promise.reject(content);
} }
@@ -242,11 +236,7 @@ export function stream(
return content; return content;
}) })
.catch((e) => { .catch((e) => {
options?.onAfterTool?.({ options?.onAfterTool?.({ ...tool, isError: true });
...tool,
isError: true,
errorMsg: e.toString(),
});
return e.toString(); return e.toString();
}) })
.then((content) => ({ .then((content) => ({
@@ -297,7 +287,6 @@ export function stream(
REQUEST_TIMEOUT_MS, REQUEST_TIMEOUT_MS,
); );
fetchEventSource(chatPath, { fetchEventSource(chatPath, {
fetch: tauriFetch as any,
...chatPayload, ...chatPayload,
async onopen(res) { async onopen(res) {
clearTimeout(requestTimeoutId); clearTimeout(requestTimeoutId);

19
app/utils/cors.ts Normal file
View File

@@ -0,0 +1,19 @@
import { getClientConfig } from "../config/client";
import { DEFAULT_API_HOST } from "../constant";
export function corsPath(path: string) {
const baseUrl = getClientConfig()?.isApp ? `${DEFAULT_API_HOST}` : "";
if (baseUrl === "" && path === "") {
return "";
}
if (!path.startsWith("/")) {
path = "/" + path;
}
if (!path.endsWith("/")) {
path += "/";
}
return `${baseUrl}${path}`;
}

View File

@@ -1,107 +0,0 @@
// using tauri command to send request
// see src-tauri/src/stream.rs, and src-tauri/src/main.rs
// 1. invoke('stream_fetch', {url, method, headers, body}), get response with headers.
// 2. listen event: `stream-response` multi times to get body
type ResponseEvent = {
id: number;
payload: {
request_id: number;
status?: number;
chunk?: number[];
};
};
type StreamResponse = {
request_id: number;
status: number;
status_text: string;
headers: Record<string, string>;
};
export function fetch(url: string, options?: RequestInit): Promise<any> {
if (window.__TAURI__) {
const {
signal,
method = "GET",
headers: _headers = {},
body = [],
} = options || {};
let unlisten: Function | undefined;
let setRequestId: Function | undefined;
const requestIdPromise = new Promise((resolve) => (setRequestId = resolve));
const ts = new TransformStream();
const writer = ts.writable.getWriter();
let closed = false;
const close = () => {
if (closed) return;
closed = true;
unlisten && unlisten();
writer.ready.then(() => {
writer.close().catch((e) => console.error(e));
});
};
if (signal) {
signal.addEventListener("abort", () => close());
}
// @ts-ignore 2. listen response multi times, and write to Response.body
window.__TAURI__.event
.listen("stream-response", (e: ResponseEvent) =>
requestIdPromise.then((request_id) => {
const { request_id: rid, chunk, status } = e?.payload || {};
if (request_id != rid) {
return;
}
if (chunk) {
writer.ready.then(() => {
writer.write(new Uint8Array(chunk));
});
} else if (status === 0) {
// end of body
close();
}
}),
)
.then((u: Function) => (unlisten = u));
const headers: Record<string, string> = {
Accept: "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
"User-Agent": navigator.userAgent,
};
for (const item of new Headers(_headers || {})) {
headers[item[0]] = item[1];
}
return window.__TAURI__
.invoke("stream_fetch", {
method: method.toUpperCase(),
url,
headers,
// TODO FormData
body:
typeof body === "string"
? Array.from(new TextEncoder().encode(body))
: [],
})
.then((res: StreamResponse) => {
const { request_id, status, status_text: statusText, headers } = res;
setRequestId?.(request_id);
const response = new Response(ts.readable, {
status,
statusText,
headers,
});
if (status >= 300) {
setTimeout(close, 100);
}
return response;
})
.catch((e) => {
console.error("stream error", e);
throw e;
});
}
return window.fetch(url, options);
}

View File

@@ -1,17 +0,0 @@
[
{
"id": "dalle3",
"name": "Dalle3",
"schema": "https://ghp.ci/https://raw.githubusercontent.com/ChatGPTNextWeb/NextChat-Awesome-Plugins/main/plugins/dalle/openapi.json"
},
{
"id": "arxivsearch",
"name": "ArxivSearch",
"schema": "https://ghp.ci/https://raw.githubusercontent.com/ChatGPTNextWeb/NextChat-Awesome-Plugins/main/plugins/arxivsearch/openapi.json"
},
{
"id": "duckduckgolite",
"name": "DuckDuckGoLiteSearch",
"schema": "https://ghp.ci/https://raw.githubusercontent.com/ChatGPTNextWeb/NextChat-Awesome-Plugins/main/plugins/duckduckgolite/openapi.json"
}
]

52
src-tauri/Cargo.lock generated
View File

@@ -348,9 +348,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.7.2" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@@ -942,9 +942,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.1" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [ dependencies = [
"percent-encoding", "percent-encoding",
] ]
@@ -970,9 +970,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.30" version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
[[package]] [[package]]
name = "futures-executor" name = "futures-executor"
@@ -987,9 +987,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-io" name = "futures-io"
version = "0.3.30" version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
[[package]] [[package]]
name = "futures-lite" name = "futures-lite"
@@ -1008,9 +1008,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-macro" name = "futures-macro"
version = "0.3.30" version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1019,21 +1019,21 @@ dependencies = [
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.30" version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.30" version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.30" version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-io", "futures-io",
@@ -1555,9 +1555,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.5.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [ dependencies = [
"unicode-bidi", "unicode-bidi",
"unicode-normalization", "unicode-normalization",
@@ -1986,10 +1986,6 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
name = "nextchat" name = "nextchat"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bytes",
"futures-util",
"percent-encoding",
"reqwest",
"serde", "serde",
"serde_json", "serde_json",
"tauri", "tauri",
@@ -2285,9 +2281,9 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.1" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]] [[package]]
name = "phf" name = "phf"
@@ -2549,9 +2545,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.86" version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -3893,9 +3889,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.2" version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna", "idna",

View File

@@ -37,10 +37,6 @@ tauri = { version = "1.5.4", features = [ "http-all",
"window-unminimize", "window-unminimize",
] } ] }
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
percent-encoding = "2.3.1"
reqwest = "0.11.18"
futures-util = "0.3.30"
bytes = "1.7.2"
[features] [features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.

View File

@@ -1,11 +1,8 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!! // Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
mod stream;
fn main() { fn main() {
tauri::Builder::default() tauri::Builder::default()
.invoke_handler(tauri::generate_handler![stream::stream_fetch])
.plugin(tauri_plugin_window_state::Builder::default().build()) .plugin(tauri_plugin_window_state::Builder::default().build())
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");

View File

@@ -1,134 +0,0 @@
//
//
use std::time::Duration;
use std::error::Error;
use std::sync::atomic::{AtomicU32, Ordering};
use std::collections::HashMap;
use futures_util::{StreamExt};
use reqwest::Client;
use reqwest::header::{HeaderName, HeaderMap};
static REQUEST_COUNTER: AtomicU32 = AtomicU32::new(0);
#[derive(Debug, Clone, serde::Serialize)]
pub struct StreamResponse {
request_id: u32,
status: u16,
status_text: String,
headers: HashMap<String, String>
}
#[derive(Clone, serde::Serialize)]
pub struct EndPayload {
request_id: u32,
status: u16,
}
#[derive(Clone, serde::Serialize)]
pub struct ChunkPayload {
request_id: u32,
chunk: bytes::Bytes,
}
#[tauri::command]
pub async fn stream_fetch(
window: tauri::Window,
method: String,
url: String,
headers: HashMap<String, String>,
body: Vec<u8>,
) -> Result<StreamResponse, String> {
let event_name = "stream-response";
let request_id = REQUEST_COUNTER.fetch_add(1, Ordering::SeqCst);
let mut _headers = HeaderMap::new();
for (key, value) in &headers {
_headers.insert(key.parse::<HeaderName>().unwrap(), value.parse().unwrap());
}
// println!("method: {:?}", method);
// println!("url: {:?}", url);
// println!("headers: {:?}", headers);
// println!("headers: {:?}", _headers);
let method = method.parse::<reqwest::Method>().map_err(|err| format!("failed to parse method: {}", err))?;
let client = Client::builder()
.default_headers(_headers)
.redirect(reqwest::redirect::Policy::limited(3))
.connect_timeout(Duration::new(3, 0))
.build()
.map_err(|err| format!("failed to generate client: {}", err))?;
let mut request = client.request(
method.clone(),
url.parse::<reqwest::Url>().map_err(|err| format!("failed to parse url: {}", err))?
);
if method == reqwest::Method::POST || method == reqwest::Method::PUT || method == reqwest::Method::PATCH {
let body = bytes::Bytes::from(body);
// println!("body: {:?}", body);
request = request.body(body);
}
// println!("client: {:?}", client);
// println!("request: {:?}", request);
let response_future = request.send();
let res = response_future.await;
let response = match res {
Ok(res) => {
// get response and emit to client
let mut headers = HashMap::new();
for (name, value) in res.headers() {
headers.insert(
name.as_str().to_string(),
std::str::from_utf8(value.as_bytes()).unwrap().to_string()
);
}
let status = res.status().as_u16();
tauri::async_runtime::spawn(async move {
let mut stream = res.bytes_stream();
while let Some(chunk) = stream.next().await {
match chunk {
Ok(bytes) => {
// println!("chunk: {:?}", bytes);
if let Err(e) = window.emit(event_name, ChunkPayload{ request_id, chunk: bytes }) {
println!("Failed to emit chunk payload: {:?}", e);
}
}
Err(err) => {
println!("Error chunk: {:?}", err);
}
}
}
if let Err(e) = window.emit(event_name, EndPayload{ request_id, status: 0 }) {
println!("Failed to emit end payload: {:?}", e);
}
});
StreamResponse {
request_id,
status,
status_text: "OK".to_string(),
headers,
}
}
Err(err) => {
println!("Error response: {:?}", err.source().expect("REASON").to_string());
StreamResponse {
request_id,
status: 599,
status_text: err.source().expect("REASON").to_string(),
headers: HashMap::new(),
}
}
};
// println!("Response: {:?}", response);
Ok(response)
}

View File

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