Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat redesign UI #4544

Open
wants to merge 40 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
0a8e5d6
feat: seperate chat page
Apr 12, 2024
3fc9b91
feat: choe
Apr 12, 2024
51a1d9f
feat: chat panel redesigned ui
Apr 16, 2024
b3559f9
feat: chat panel UE done
Apr 18, 2024
3d0a98d
feat: maskpage&newchatpage adapt new ui framework done
Apr 19, 2024
1074fff
feat: clear trash
Apr 19, 2024
37cc875
feat: optiminize message&img display
Apr 19, 2024
4a5465f
feat: chat panel header absolute
Apr 22, 2024
4400392
feat: chat panel header background blur&transparent
Apr 22, 2024
f7074bb
feat: chat panel header add zindex config
Apr 22, 2024
c990864
feat: redesign settings page
Apr 24, 2024
bb7422c
Merge remote-tracking branch 'origin/main' into feat-redesign-ui
Apr 25, 2024
59583e5
feat: light theme mode
Apr 25, 2024
48e8c0a
feat: optiminize
Apr 25, 2024
1a636b0
feat: function delete chat dev done
Apr 26, 2024
9569888
feat: ui fixed
Apr 28, 2024
9f48133
feat: HoverPopover done
Apr 28, 2024
c34b8ab
feat: ui optiminize
Apr 28, 2024
8c28c40
feat: refactor select model
Apr 29, 2024
5ea6206
feat: select model done
Apr 29, 2024
996537d
feat: optiminize modal UE on mobile dev
Apr 30, 2024
c3d91bf
feat: complete the missing UI
Apr 30, 2024
cadd255
chore: update settings width
fred-bf Apr 30, 2024
ecf6cc2
style: add transition
fred-bf Apr 30, 2024
15d6ed2
style: add transition
fred-bf Apr 30, 2024
4d5a947
style: add transition
fred-bf Apr 30, 2024
3d99965
feat: bugfix
Apr 30, 2024
8a14cb1
feat: merge remote
Apr 30, 2024
68f0fa9
complete colors in dark mode
May 1, 2024
32f62d7
feat: bump version
fred-bf May 6, 2024
fa2f8c6
feat: old page dark mode compatible
May 6, 2024
4e44313
feat: old page ui style optiminize
May 7, 2024
240d330
feat: 1)add font source 2)add validator in ListItem 3)settings page…
May 7, 2024
00b1a97
Merge branch 'feat-redesign-ui' into v3
fred-bf May 8, 2024
a0e4a46
feat: model provider refactor done
May 15, 2024
74a6e12
feat: merge main
May 16, 2024
8093d1f
feat: 1) Present 'maxtokens' as properties tied to a single model. 2)…
May 17, 2024
77e321c
Merge branch 'v3' into feat-redesign-ui
fred-bf May 22, 2024
8de8acd
feat: mix handlers of proxy server in providers
May 22, 2024
3fcf051
Merge branch 'feat-redesign-ui' of github.com:ChatGPTNextWeb/ChatGPT-…
May 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 9 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
{
"extends": "next/core-web-vitals",
"plugins": ["prettier"]
"plugins": [
"prettier"
],
"parserOptions": {
"ecmaFeatures": {
"legacyDecorators": true
}
},
"ignorePatterns": ["globals.css"]
}
93 changes: 93 additions & 0 deletions app/api/provider/[...path]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import * as ProviderTemplates from "@/app/client/providers";
import { getServerSideConfig } from "@/app/config/server";
import { NextRequest, NextResponse } from "next/server";
import { cloneDeep } from "lodash-es";
import {
disableSystemApiKey,
makeUrlsUsable,
modelNameRequestHeader,
} from "@/app/client/common";
import { collectModelTable } from "@/app/utils/model";

async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
const [providerName] = params.path;
const { headers } = req;
const serverConfig = getServerSideConfig();
const modelName = headers.get(modelNameRequestHeader);

const ProviderTemplate = Object.values(ProviderTemplates).find(
(t) => t.prototype.name === providerName,
);

if (!ProviderTemplate) {
return NextResponse.json(
{
error: true,
message: "No provider found: " + providerName,
},
{
status: 404,
},
);
}

// #1815 try to refuse gpt4 request
if (modelName && serverConfig.customModels) {
try {
const modelTable = collectModelTable([], serverConfig.customModels);

// not undefined and is false
if (modelTable[modelName]?.available === false) {
return NextResponse.json(
{
error: true,
message: `you are not allowed to use ${modelName} model`,
},
{
status: 403,
},
);
}
} catch (e) {
console.error("models filter", e);
}
}

const config = disableSystemApiKey(
makeUrlsUsable(cloneDeep(serverConfig), [
"anthropicUrl",
"azureUrl",
"googleUrl",
"baseUrl",
]),
["anthropicApiKey", "azureApiKey", "googleApiKey", "apiKey"],
serverConfig.needCode &&
ProviderTemplate !== ProviderTemplates.NextChatProvider, // if it must take a access code in the req, do not provide system-keys for Non-nextchat providers
);

const request = Object.assign({}, req, {
subpath: params.path.join("/"),
});

return new ProviderTemplate().serverSideRequestHandler(request, config);
}

export const GET = handle;
export const POST = handle;
export const PUT = handle;
export const PATCH = handle;
export const DELETE = handle;
export const OPTIONS = handle;

export const runtime = "edge";
export const preferredRegion = Array.from(
new Set(
Object.values(ProviderTemplates).reduce(
(arr, t) => [...arr, ...(t.prototype.preferredRegion ?? [])],
[] as string[],
),
),
);
7 changes: 7 additions & 0 deletions app/client/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * from "./types";

export * from "./locale";

export * from "./utils";

export const modelNameRequestHeader = "x-nextchat-model-name";
19 changes: 19 additions & 0 deletions app/client/common/locale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Lang, getLang } from "@/app/locales";

interface PlainConfig {
[k: string]: PlainConfig | string;
}

export type LocaleMap<
TextPlainConfig extends PlainConfig,
Default extends Lang,
> = Partial<Record<Lang, TextPlainConfig>> & {
[name in Default]: TextPlainConfig;
};

export function getLocaleText<
TextPlainConfig extends PlainConfig,
DefaultLang extends Lang,
>(textMap: LocaleMap<TextPlainConfig, DefaultLang>, defaultLang: DefaultLang) {
return textMap[getLang()] || textMap[defaultLang];
}
211 changes: 211 additions & 0 deletions app/client/common/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
import { RequestMessage } from "../api";
import { getServerSideConfig } from "@/app/config/server";
import { NextRequest, NextResponse } from "next/server";

export { type RequestMessage };

// ===================================== LLM Types start ======================================

export interface ModelConfig {
temperature: number;
top_p: number;
presence_penalty: number;
frequency_penalty: number;
max_tokens: number;
}

export interface ModelSettings extends Omit<ModelConfig, "max_tokens"> {
global_max_tokens: number;
}

export type ModelTemplate = {
name: string; // id of model in a provider
displayName: string;
isVisionModel?: boolean;
isDefaultActive: boolean; // model is initialized to be active
isDefaultSelected?: boolean; // model is initialized to be as default used model
max_tokens?: number;
};

export interface Model extends Omit<ModelTemplate, "isDefaultActive"> {
providerTemplateName: string;
isActive: boolean;
providerName: string;
available: boolean;
customized: boolean; // Only customized model is allowed to be modified
}

export interface ModelInfo extends Pick<ModelTemplate, "name"> {
[k: string]: any;
}

// ===================================== LLM Types end ======================================

// ===================================== Chat Request Types start ======================================

export interface ChatRequestPayload {
messages: RequestMessage[];
context: {
isApp: boolean;
};
}

export interface StandChatRequestPayload extends ChatRequestPayload {
modelConfig: ModelConfig;
model: string;
}

export interface InternalChatRequestPayload<SettingKeys extends string = "">
extends StandChatRequestPayload {
providerConfig: Partial<Record<SettingKeys, string>>;
isVisionModel: Model["isVisionModel"];
stream: boolean;
}

export interface ProviderRequestPayload {
headers: Record<string, string>;
body: string;
url: string;
method: string;
}

export interface InternalChatHandlers {
onProgress: (message: string, chunk: string) => void;
onFinish: (message: string) => void;
onError: (err: Error) => void;
}

export interface ChatHandlers extends InternalChatHandlers {
onProgress: (chunk: string) => void;
onFinish: () => void;
onFlash: (message: string) => void;
}

// ===================================== Chat Request Types end ======================================

// ===================================== Chat Response Types start ======================================

export interface StandChatReponseMessage {
message: string;
}

// ===================================== Chat Request Types end ======================================

// ===================================== Provider Settings Types start ======================================

type NumberRange = [number, number];

export type Validator =
| "required"
| "number"
| "string"
| NumberRange
| NumberRange[]
| ((v: any) => Promise<string | void>);

export type CommonSettingItem<SettingKeys extends string> = {
name: SettingKeys;
title?: string;
description?: string;
validators?: Validator[];
};

export type InputSettingItem = {
type: "input";
placeholder?: string;
} & (
| {
inputType?: "password" | "normal";
defaultValue?: string;
}
| {
inputType?: "number";
defaultValue?: number;
}
);

export type SelectSettingItem = {
type: "select";
options: {
name: string;
value: "number" | "string" | "boolean";
}[];
placeholder?: string;
};

export type RangeSettingItem = {
type: "range";
range: NumberRange;
};

export type SwitchSettingItem = {
type: "switch";
};

export type SettingItem<SettingKeys extends string = ""> =
CommonSettingItem<SettingKeys> &
(
| InputSettingItem
| SelectSettingItem
| RangeSettingItem
| SwitchSettingItem
);

// ===================================== Provider Settings Types end ======================================

// ===================================== Provider Template Types start ======================================

export type ServerConfig = ReturnType<typeof getServerSideConfig>;

export interface IProviderTemplate<
SettingKeys extends string,
NAME extends string,
Meta extends Record<string, any>,
> {
readonly name: NAME;

readonly apiRouteRootName: `/api/provider/${NAME}`;

readonly allowedApiMethods: Array<
"GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS"
>;

readonly metas: Meta;

readonly providerMeta: {
displayName: string;
settingItems: SettingItem<SettingKeys>[];
};
readonly defaultModels: ModelTemplate[];

streamChat(
payload: InternalChatRequestPayload<SettingKeys>,
handlers: ChatHandlers,
fetch: typeof window.fetch,
): AbortController;

chat(
payload: InternalChatRequestPayload<SettingKeys>,
fetch: typeof window.fetch,
): Promise<StandChatReponseMessage>;

getAvailableModels?(
providerConfig: InternalChatRequestPayload<SettingKeys>["providerConfig"],
): Promise<ModelInfo[]>;

readonly runtime: "edge";
readonly preferredRegion: "auto" | "global" | "home" | string | string[];

serverSideRequestHandler(
req: NextRequest & {
subpath: string;
},
serverConfig: ServerConfig,
): Promise<NextResponse>;
}

export type ProviderTemplate = IProviderTemplate<any, any, any>;

export interface Serializable<Snapshot> {
serialize(): Snapshot;
}