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: #813 #118 #513 and fix #829 #832 #839

Merged
merged 5 commits into from
Apr 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 64 additions & 20 deletions app/components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import LightIcon from "../icons/light.svg";
import DarkIcon from "../icons/dark.svg";
import AutoIcon from "../icons/auto.svg";
import BottomIcon from "../icons/bottom.svg";
import StopIcon from "../icons/pause.svg";

import {
Message,
Expand All @@ -38,7 +39,6 @@ import {
isMobileScreen,
selectOrCopy,
autoGrowTextArea,
getCSSVar,
} from "../utils";

import dynamic from "next/dynamic";
Expand Down Expand Up @@ -355,8 +355,8 @@ export function ChatActions(props: {
}) {
const chatStore = useChatStore();

// switch themes
const theme = chatStore.config.theme;

function nextTheme() {
const themes = [Theme.Auto, Theme.Light, Theme.Dark];
const themeIndex = themes.indexOf(theme);
Expand All @@ -365,8 +365,20 @@ export function ChatActions(props: {
chatStore.updateConfig((config) => (config.theme = nextTheme));
}

// stop all responses
const couldStop = ControllerPool.hasPending();
const stopAll = () => ControllerPool.stopAll();

return (
<div className={chatStyle["chat-input-actions"]}>
{couldStop && (
<div
className={`${chatStyle["chat-input-action"]} clickable`}
onClick={stopAll}
>
<StopIcon />
</div>
)}
{!props.hitBottom && (
<div
className={`${chatStyle["chat-input-action"]} clickable`}
Expand Down Expand Up @@ -524,21 +536,45 @@ export function Chat(props: {
}
};

const onResend = (botIndex: number) => {
const findLastUesrIndex = (messageId: number) => {
// find last user input message and resend
for (let i = botIndex; i >= 0; i -= 1) {
if (messages[i].role === "user") {
setIsLoading(true);
chatStore
.onUserInput(messages[i].content)
.then(() => setIsLoading(false));
chatStore.updateCurrentSession((session) =>
session.messages.splice(i, 2),
);
inputRef.current?.focus();
return;
let lastUserMessageIndex: number | null = null;
for (let i = 0; i < session.messages.length; i += 1) {
const message = session.messages[i];
if (message.id === messageId) {
break;
}
if (message.role === "user") {
lastUserMessageIndex = i;
}
}

return lastUserMessageIndex;
};

const deleteMessage = (userIndex: number) => {
chatStore.updateCurrentSession((session) =>
session.messages.splice(userIndex, 2),
);
};

const onDelete = (botMessageId: number) => {
const userIndex = findLastUesrIndex(botMessageId);
if (userIndex === null) return;
deleteMessage(userIndex);
};

const onResend = (botMessageId: number) => {
// find last user input message and resend
const userIndex = findLastUesrIndex(botMessageId);
if (userIndex === null) return;

setIsLoading(true);
chatStore
.onUserInput(session.messages[userIndex].content)
.then(() => setIsLoading(false));
deleteMessage(userIndex);
inputRef.current?.focus();
};

const config = useChatStore((state) => state.config);
Expand Down Expand Up @@ -710,12 +746,20 @@ export function Chat(props: {
{Locale.Chat.Actions.Stop}
</div>
) : (
<div
className={styles["chat-message-top-action"]}
onClick={() => onResend(i)}
>
{Locale.Chat.Actions.Retry}
</div>
<>
<div
className={styles["chat-message-top-action"]}
onClick={() => onDelete(message.id ?? i)}
>
{Locale.Chat.Actions.Delete}
</div>
<div
className={styles["chat-message-top-action"]}
onClick={() => onResend(message.id ?? i)}
>
{Locale.Chat.Actions.Retry}
</div>
</>
)}

<div
Expand Down
2 changes: 1 addition & 1 deletion app/icons/bottom.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/icons/pause.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/locales/cn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const cn = {
Copy: "复制",
Stop: "停止",
Retry: "重试",
Delete: "删除",
},
Rename: "重命名对话",
Typing: "正在输入…",
Expand Down
1 change: 1 addition & 0 deletions app/locales/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const de: LocaleType = {
Copy: "Kopieren",
Stop: "Stop",
Retry: "Wiederholen",
Delete: "Delete",
},
Rename: "Chat umbenennen",
Typing: "Tippen...",
Expand Down
1 change: 1 addition & 0 deletions app/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const en: LocaleType = {
Copy: "Copy",
Stop: "Stop",
Retry: "Retry",
Delete: "Delete",
},
Rename: "Rename Chat",
Typing: "Typing…",
Expand Down
1 change: 1 addition & 0 deletions app/locales/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const es: LocaleType = {
Copy: "Copiar",
Stop: "Detener",
Retry: "Reintentar",
Delete: "Delete",
},
Rename: "Renombrar chat",
Typing: "Escribiendo...",
Expand Down
24 changes: 7 additions & 17 deletions app/locales/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,13 @@ export function getLang(): Lang {

const lang = getLanguage();

if (lang.includes("zh") || lang.includes("cn")) {
return "cn";
} else if (lang.includes("tw")) {
return "tw";
} else if (lang.includes("es")) {
return "es";
} else if (lang.includes("it")) {
return "it";
} else if (lang.includes("tr")) {
return "tr";
} else if (lang.includes("jp")) {
return "jp";
} else if (lang.includes("de")) {
return "de";
} else {
return "en";
for (const option of AllLangs) {
if (lang.includes(option)) {
return option;
}
}

return "en";
}

export function changeLang(lang: Lang) {
Expand All @@ -87,4 +77,4 @@ export default {
tr: TR,
jp: JP,
de: DE,
}[getLang()];
}[getLang()] as typeof CN;
1 change: 1 addition & 0 deletions app/locales/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const it: LocaleType = {
Copy: "Copia",
Stop: "Stop",
Retry: "Riprova",
Delete: "Delete",
},
Rename: "Rinomina Chat",
Typing: "Typing…",
Expand Down
3 changes: 1 addition & 2 deletions app/locales/jp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const jp = {
Copy: "コピー",
Stop: "停止",
Retry: "リトライ",
Delete: "Delete",
},
Rename: "チャットの名前を変更",
Typing: "入力中…",
Expand Down Expand Up @@ -178,6 +179,4 @@ const jp = {
},
};

export type LocaleType = typeof jp;

export default jp;
1 change: 1 addition & 0 deletions app/locales/tr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const tr: LocaleType = {
Copy: "Kopyala",
Stop: "Durdur",
Retry: "Tekrar Dene",
Delete: "Delete",
},
Rename: "Sohbeti Yeniden Adlandır",
Typing: "Yazıyor…",
Expand Down
1 change: 1 addition & 0 deletions app/locales/tw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const tw: LocaleType = {
Copy: "複製",
Stop: "停止",
Retry: "重試",
Delete: "刪除",
},
Rename: "重命名對話",
Typing: "正在輸入…",
Expand Down
15 changes: 11 additions & 4 deletions app/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ChatRequest, ChatResponse } from "./api/openai/typing";
import { Message, ModelConfig, useAccessStore, useChatStore } from "./store";
import { showToast } from "./components/ui-lib";

const TIME_OUT_MS = 30000;
const TIME_OUT_MS = 60000;

const makeRequestParam = (
messages: Message[],
Expand Down Expand Up @@ -167,15 +167,14 @@ export async function requestChatStream(
options?.onController?.(controller);

while (true) {
// handle time out, will stop if no response in 10 secs
const resTimeoutId = setTimeout(() => finish(), TIME_OUT_MS);
const content = await reader?.read();
clearTimeout(resTimeoutId);

if (!content || !content.value) {
break;
}

const text = decoder.decode(content.value, { stream: true });
responseText += text;

Expand Down Expand Up @@ -235,6 +234,14 @@ export const ControllerPool = {
controller?.abort();
},

stopAll() {
Object.values(this.controllers).forEach((v) => v.abort());
},

hasPending() {
return Object.values(this.controllers).length > 0;
},

remove(sessionIndex: number, messageId: number) {
const key = this.key(sessionIndex, messageId);
delete this.controllers[key];
Expand Down
3 changes: 2 additions & 1 deletion app/store/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ export const useChatStore = create<ChatStore>()(
const botMessage: Message = createMessage({
role: "assistant",
streaming: true,
id: userMessage.id! + 1,
});

// get recent messages
Expand Down Expand Up @@ -421,7 +422,7 @@ export const useChatStore = create<ChatStore>()(
onError(error, statusCode) {
if (statusCode === 401) {
botMessage.content = Locale.Error.Unauthorized;
} else {
} else if (!error.message.includes("aborted")) {
botMessage.content += "\n\n" + Locale.Store.Error;
}
botMessage.streaming = false;
Expand Down
7 changes: 3 additions & 4 deletions app/store/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,9 @@ export const usePromptStore = create<PromptStore>()(
})
.concat([...(state?.prompts?.values() ?? [])]);

const allPromptsForSearch = builtinPrompts.reduce(
(pre, cur) => pre.concat(cur),
[],
);
const allPromptsForSearch = builtinPrompts
.reduce((pre, cur) => pre.concat(cur), [])
.filter((v) => !!v.title && !!v.content);
SearchService.count.builtin = res.en.length + res.cn.length;
SearchService.init(allPromptsForSearch);
});
Expand Down
13 changes: 13 additions & 0 deletions middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ export const config = {

const serverConfig = getServerSideConfig();

function getIP(req: NextRequest) {
let ip = req.ip ?? req.headers.get("x-real-ip");
const forwardedFor = req.headers.get("x-forwarded-for");

if (!ip && forwardedFor) {
ip = forwardedFor.split(",").at(0) ?? "";
}

return ip;
}

export function middleware(req: NextRequest) {
const accessCode = req.headers.get("access-code");
const token = req.headers.get("token");
Expand All @@ -16,6 +27,8 @@ export function middleware(req: NextRequest) {
console.log("[Auth] allowed hashed codes: ", [...serverConfig.codes]);
console.log("[Auth] got access code:", accessCode);
console.log("[Auth] hashed access code:", hashedCode);
console.log("[User IP] ", getIP(req));
console.log("[Time] ", new Date().toLocaleString());

if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) {
return NextResponse.json(
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"start": "next start",
"lint": "next lint",
"fetch": "node ./scripts/fetch-prompts.mjs",
"prepare": "husky install"
"prepare": "husky install",
"proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev"
},
"dependencies": {
"@hello-pangea/dnd": "^16.2.0",
Expand All @@ -18,7 +19,7 @@
"emoji-picker-react": "^4.4.7",
"eventsource-parser": "^0.1.0",
"fuse.js": "^6.6.2",
"next": "^13.2.3",
"next": "^13.3.0",
"node-fetch": "^3.3.1",
"openai": "^3.2.1",
"react": "^18.2.0",
Expand Down
1 change: 1 addition & 0 deletions scripts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
proxychains.conf
5 changes: 5 additions & 0 deletions scripts/init-proxy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
dir="$(dirname "$0")"
config=$dir/proxychains.conf
host_ip=$(grep nameserver /etc/resolv.conf | sed 's/nameserver //')
cp $dir/proxychains.template.conf $config
sed -i "\$s/.*/http $host_ip 7890/" $config
12 changes: 12 additions & 0 deletions scripts/proxychains.template.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
strict_chain
proxy_dns

remote_dns_subnet 224

tcp_read_time_out 15000
tcp_connect_time_out 8000

localnet 127.0.0.0/255.0.0.0

[ProxyList]
socks4 127.0.0.1 9050
Loading