Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
fred-bf committed Feb 7, 2024
2 parents cc67528 + bca7424 commit f6098b2
Show file tree
Hide file tree
Showing 15 changed files with 108 additions and 63 deletions.
29 changes: 16 additions & 13 deletions app/client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,31 +144,34 @@ export function getHeaders() {
const headers: Record<string, string> = {
"Content-Type": "application/json",
"x-requested-with": "XMLHttpRequest",
"Accept": "application/json",
Accept: "application/json",
};
const modelConfig = useChatStore.getState().currentSession().mask.modelConfig;
const isGoogle = modelConfig.model === "gemini-pro";
const isGoogle = modelConfig.model.startsWith("gemini");
const isAzure = accessStore.provider === ServiceProvider.Azure;
const authHeader = isAzure ? "api-key" : "Authorization";
const apiKey = isGoogle
? accessStore.googleApiKey
: isAzure
? accessStore.azureApiKey
: accessStore.openaiApiKey;

const clientConfig = getClientConfig();
const makeBearer = (s: string) => `${isAzure ? "" : "Bearer "}${s.trim()}`;
const validString = (x: string) => x && x.length > 0;

// use user's api key first
if (validString(apiKey)) {
headers[authHeader] = makeBearer(apiKey);
} else if (
accessStore.enabledAccessControl() &&
validString(accessStore.accessCode)
) {
headers[authHeader] = makeBearer(
ACCESS_CODE_PREFIX + accessStore.accessCode,
);
// when using google api in app, not set auth header
if (!(isGoogle && clientConfig?.isApp)) {
// use user's api key first
if (validString(apiKey)) {
headers[authHeader] = makeBearer(apiKey);
} else if (
accessStore.enabledAccessControl() &&
validString(accessStore.accessCode)
) {
headers[authHeader] = makeBearer(
ACCESS_CODE_PREFIX + accessStore.accessCode,
);
}
}

return headers;
Expand Down
32 changes: 18 additions & 14 deletions app/client/platforms/google.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import { Google, REQUEST_TIMEOUT_MS } from "@/app/constant";
import { ChatOptions, getHeaders, LLMApi, LLMModel, LLMUsage } from "../api";
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
import {
EventStreamContentType,
fetchEventSource,
} from "@fortaine/fetch-event-source";
import { prettyObject } from "@/app/utils/format";
import { getClientConfig } from "@/app/config/client";
import Locale from "../../locales";
import { getServerSideConfig } from "@/app/config/server";
import de from "@/app/locales/de";
import { DEFAULT_API_HOST } from "@/app/constant";
export class GeminiProApi implements LLMApi {
extractMessage(res: any) {
console.log("[Response] gemini-pro response: ", res);
Expand All @@ -21,7 +14,7 @@ export class GeminiProApi implements LLMApi {
);
}
async chat(options: ChatOptions): Promise<void> {
const apiClient = this;
// const apiClient = this;
const messages = options.messages.map((v) => ({
role: v.role.replace("assistant", "model").replace("system", "user"),
parts: [{ text: v.content }],
Expand Down Expand Up @@ -79,20 +72,31 @@ export class GeminiProApi implements LLMApi {
],
};

console.log("[Request] google payload: ", requestPayload);
const isApp = !!getClientConfig()?.isApp;

const shouldStream = !!options.config.stream;
const controller = new AbortController();
options.onController?.(controller);
const accessStore = useAccessStore.getState();
try {
const chatPath = this.path(Google.ChatPath);
let chatPath = this.path(Google.ChatPath);

// let baseUrl = accessStore.googleUrl;

chatPath = isApp
? DEFAULT_API_HOST +
"/api/proxy/google/" +
Google.ChatPath +
`?key=${accessStore.googleApiKey}`
: chatPath;

const chatPayload = {
method: "POST",
body: JSON.stringify(requestPayload),
signal: controller.signal,
headers: getHeaders(),
};

console.log("[Request] google chatPath: ", chatPath, isApp);
// make a fetch request
const requestTimeoutId = setTimeout(
() => controller.abort(),
Expand Down Expand Up @@ -134,6 +138,8 @@ export class GeminiProApi implements LLMApi {

// start animaion
animateResponseText();

console.log("[Proxy Endpoint] ", streamChatPath);
fetch(streamChatPath, chatPayload)
.then((response) => {
const reader = response?.body?.getReader();
Expand Down Expand Up @@ -187,9 +193,7 @@ export class GeminiProApi implements LLMApi {
} else {
const res = await fetch(chatPath, chatPayload);
clearTimeout(requestTimeoutId);

const resJson = await res.json();

if (resJson?.promptFeedback?.blockReason) {
// being blocked
options.onError?.(
Expand Down
7 changes: 6 additions & 1 deletion app/client/platforms/openai.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use client";
import {
ApiPath,
DEFAULT_API_HOST,
Expand Down Expand Up @@ -45,7 +46,9 @@ export class ChatGPTApi implements LLMApi {

if (baseUrl.length === 0) {
const isApp = !!getClientConfig()?.isApp;
baseUrl = isApp ? DEFAULT_API_HOST : ApiPath.OpenAI;
baseUrl = isApp
? DEFAULT_API_HOST + "/proxy" + ApiPath.OpenAI
: ApiPath.OpenAI;
}

if (baseUrl.endsWith("/")) {
Expand All @@ -59,6 +62,8 @@ export class ChatGPTApi implements LLMApi {
path = makeAzurePath(path, accessStore.azureApiVersion);
}

console.log("[Proxy Endpoint] ", baseUrl, path);

return [baseUrl, path].join("/");
}

Expand Down
2 changes: 1 addition & 1 deletion app/components/exporter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ export function PreviewActions(props: {
setShouldExport(false);

var api: ClientApi;
if (config.modelConfig.model === "gemini-pro") {
if (config.modelConfig.model.startsWith("gemini")) {
api = new ClientApi(ModelProvider.GeminiPro);
} else {
api = new ClientApi(ModelProvider.GPT);
Expand Down
2 changes: 1 addition & 1 deletion app/components/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export function useLoadData() {
const config = useAppConfig();

var api: ClientApi;
if (config.modelConfig.model === "gemini-pro") {
if (config.modelConfig.model.startsWith("gemini")) {
api = new ClientApi(ModelProvider.GeminiPro);
} else {
api = new ClientApi(ModelProvider.GPT);
Expand Down
2 changes: 1 addition & 1 deletion app/components/model-config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export function ModelConfigList(props: {
></input>
</ListItem>

{props.modelConfig.model === "gemini-pro" ? null : (
{props.modelConfig.model.startsWith("gemini") ? null : (
<>
<ListItem
title={Locale.Settings.PresencePenalty.Title}
Expand Down
4 changes: 3 additions & 1 deletion app/store/access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import { ensure } from "../utils/clone";
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done

const DEFAULT_OPENAI_URL =
getClientConfig()?.buildMode === "export" ? DEFAULT_API_HOST : ApiPath.OpenAI;
getClientConfig()?.buildMode === "export"
? DEFAULT_API_HOST + "/api/proxy/openai"
: ApiPath.OpenAI;

const DEFAULT_ACCESS_STATE = {
accessCode: "",
Expand Down
22 changes: 13 additions & 9 deletions app/store/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,18 @@ function countMessages(msgs: ChatMessage[]) {
}

function fillTemplateWith(input: string, modelConfig: ModelConfig) {
const cutoff = KnowledgeCutOffDate[modelConfig.model] ?? KnowledgeCutOffDate.default;
const cutoff =
KnowledgeCutOffDate[modelConfig.model] ?? KnowledgeCutOffDate.default;
// Find the model in the DEFAULT_MODELS array that matches the modelConfig.model
const modelInfo = DEFAULT_MODELS.find(m => m.name === modelConfig.model);
if (!modelInfo) {
throw new Error(`Model ${modelConfig.model} not found in DEFAULT_MODELS array.`);
const modelInfo = DEFAULT_MODELS.find((m) => m.name === modelConfig.model);

var serviceProvider = "OpenAI";
if (modelInfo) {
// TODO: auto detect the providerName from the modelConfig.model

// Directly use the providerName from the modelInfo
serviceProvider = modelInfo.provider.providerName;
}
// Directly use the providerName from the modelInfo
const serviceProvider = modelInfo.provider.providerName;

const vars = {
ServiceProvider: serviceProvider,
Expand All @@ -119,7 +123,7 @@ function fillTemplateWith(input: string, modelConfig: ModelConfig) {
}

Object.entries(vars).forEach(([name, value]) => {
const regex = new RegExp(`{{${name}}}`, 'g');
const regex = new RegExp(`{{${name}}}`, "g");
output = output.replace(regex, value.toString()); // Ensure value is a string
});

Expand Down Expand Up @@ -312,7 +316,7 @@ export const useChatStore = createPersistStore(
});

var api: ClientApi;
if (modelConfig.model === "gemini-pro") {
if (modelConfig.model.startsWith("gemini")) {
api = new ClientApi(ModelProvider.GeminiPro);
} else {
api = new ClientApi(ModelProvider.GPT);
Expand Down Expand Up @@ -497,7 +501,7 @@ export const useChatStore = createPersistStore(
const modelConfig = session.mask.modelConfig;

var api: ClientApi;
if (modelConfig.model === "gemini-pro") {
if (modelConfig.model.startsWith("gemini")) {
api = new ClientApi(ModelProvider.GeminiPro);
} else {
api = new ClientApi(ModelProvider.GPT);
Expand Down
4 changes: 2 additions & 2 deletions app/utils/cors.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { getClientConfig } from "../config/client";
import { ApiPath, DEFAULT_CORS_HOST } from "../constant";
import { ApiPath, DEFAULT_API_HOST } from "../constant";

export function corsPath(path: string) {
const baseUrl = getClientConfig()?.isApp ? `${DEFAULT_CORS_HOST}` : "";
const baseUrl = getClientConfig()?.isApp ? `${DEFAULT_API_HOST}` : "";

if (!path.startsWith("/")) {
path = "/" + path;
Expand Down
11 changes: 10 additions & 1 deletion next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,17 @@ if (mode !== "export") {

nextConfig.rewrites = async () => {
const ret = [
// adjust for previous verison directly using "/api/proxy/" as proxy base route
{
source: "/api/proxy/:path*",
source: "/api/proxy/v1/:path*",
destination: "https://api.openai.com/v1/:path*",
},
{
source: "/api/proxy/google/:path*",
destination: "https://generativelanguage.googleapis.com/:path*",
},
{
source: "/api/proxy/openai/:path*",
destination: "https://api.openai.com/:path*",
},
{
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "chatgpt-next-web",
"name": "nextchat",
"private": false,
"license": "mit",
"scripts": {
Expand Down Expand Up @@ -64,4 +64,4 @@
"resolutions": {
"lint-staged/yaml": "^2.2.2"
}
}
}
2 changes: 1 addition & 1 deletion scripts/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ if ! command -v node >/dev/null || ! command -v git >/dev/null || ! command -v y
fi

# Clone the repository and install dependencies
git clone https://github.com/Yidadaa/ChatGPT-Next-Web
git clone https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web
cd ChatGPT-Next-Web
yarn install

Expand Down
22 changes: 11 additions & 11 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 22 additions & 4 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[package]
name = "chatgpt-next-web"
name = "nextchat"
version = "0.1.0"
description = "A cross platform app for LLM ChatBot."
authors = ["Yidadaa"]
license = "mit"
repository = ""
default-run = "chatgpt-next-web"
default-run = "nextchat"
edition = "2021"
rust-version = "1.60"

Expand All @@ -17,11 +17,29 @@ tauri-build = { version = "1.3.0", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.3.0", features = ["notification-all", "fs-all", "clipboard-all", "dialog-all", "shell-open", "updater", "window-close", "window-hide", "window-maximize", "window-minimize", "window-set-icon", "window-set-ignore-cursor-events", "window-set-resizable", "window-show", "window-start-dragging", "window-unmaximize", "window-unminimize"] }
tauri = { version = "1.3.0", features = [
"notification-all",
"fs-all",
"clipboard-all",
"dialog-all",
"shell-open",
"updater",
"window-close",
"window-hide",
"window-maximize",
"window-minimize",
"window-set-icon",
"window-set-ignore-cursor-events",
"window-set-resizable",
"window-show",
"window-start-dragging",
"window-unmaximize",
"window-unminimize",
] }
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }

[features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
# DO NOT REMOVE!!
custom-protocol = [ "tauri/custom-protocol" ]
custom-protocol = ["tauri/custom-protocol"]
2 changes: 1 addition & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
"updater": {
"active": true,
"endpoints": [
"https://github.com/Yidadaa/ChatGPT-Next-Web/releases/latest/download/latest.json"
"https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/releases/latest/download/latest.json"
],
"dialog": false,
"windows": {
Expand Down

0 comments on commit f6098b2

Please sign in to comment.