Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions committed Nov 21, 2023
2 parents dcd1b11 + f248593 commit 5654600
Show file tree
Hide file tree
Showing 34 changed files with 751 additions and 143 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: setup node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: get version
run: echo "PACKAGE_VERSION=$(node -p "require('./src-tauri/tauri.conf.json').package.version")" >> $GITHUB_ENV
- name: create release
Expand Down Expand Up @@ -57,7 +57,7 @@ jobs:
- name: setup node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ Access password, separated by comma.

### `OPENAI_API_KEY` (required)

Your openai api key.
Your openai api key, join multiple api keys with comma.

### `BASE_URL` (optional)

Expand Down Expand Up @@ -218,9 +218,11 @@ If you want to disable parse settings from url, set this to 1.
### `CUSTOM_MODELS` (optional)

> Default: Empty
> Example: `+llama,+claude-2,-gpt-3.5-turbo` means add `llama, claude-2` to model list, and remove `gpt-3.5-turbo` from list.
> Example: `+llama,+claude-2,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo` means add `llama, claude-2` to model list, and remove `gpt-3.5-turbo` from list, and display `gpt-4-1106-preview` as `gpt-4-turbo`.
To control custom models, use `+` to add a custom model, use `-` to hide a model, separated by comma.
To control custom models, use `+` to add a custom model, use `-` to hide a model, use `name=displayName` to customize model name, separated by comma.

User `-all` to disable all default models, `+all` to enable all default models.

## Requirements

Expand Down Expand Up @@ -345,6 +347,7 @@ If you want to add a new translation, read this [document](./docs/translation.md
[@synwith](https://github.com/synwith)
[@piksonGit](https://github.com/piksonGit)
[@ouyangzhiping](https://github.com/ouyangzhiping)
[@wenjiavv](https://github.com/wenjiavv)

### Contributor

Expand Down
9 changes: 5 additions & 4 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ code1,code2,code3
### `OPENAI_API_KEY` (必填项)

OpanAI 密钥,你在 openai 账户页面申请的 api key。
OpanAI 密钥,你在 openai 账户页面申请的 api key,使用英文逗号隔开多个 key,这样可以随机轮询这些 key

### `CODE` (可选)

Expand Down Expand Up @@ -122,9 +122,10 @@ Azure Api 版本,你可以在这里找到:[Azure 文档](https://learn.micro

### `CUSTOM_MODELS` (可选)

> 示例:`+qwen-7b-chat,+glm-6b,-gpt-3.5-turbo` 表示增加 `qwen-7b-chat``glm-6b` 到模型列表,而从列表中删除 `gpt-3.5-turbo`
> 示例:`+qwen-7b-chat,+glm-6b,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo` 表示增加 `qwen-7b-chat``glm-6b` 到模型列表,而从列表中删除 `gpt-3.5-turbo`,并将 `gpt-4-1106-preview` 模型名字展示为 `gpt-4-turbo`
> 如果你想先禁用所有模型,再启用指定模型,可以使用 `-all,+gpt-3.5-turbo`,则表示仅启用 `gpt-3.5-turbo`
用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,用英文逗号隔开。
用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。

## 开发

Expand All @@ -138,7 +139,7 @@ Azure Api 版本,你可以在这里找到:[Azure 文档](https://learn.micro
OPENAI_API_KEY=<your api key here>
# 中国大陆用户,可以使用本项目自带的代理进行开发,你也可以自由选择其他代理地址
BASE_URL=https://nb.nextweb.fun/api/proxy
BASE_URL=https://a.nextweb.fun/api/proxy
```

### 本地开发
Expand Down
7 changes: 7 additions & 0 deletions app/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ export function auth(req: NextRequest) {
};
}

if (serverConfig.hideUserApiKey && !!apiKey) {
return {
error: true,
msg: "you are not allowed to access openai with your own api key",
};
}

// if user does not provide an api key, inject system api key
if (!apiKey) {
const serverApiKey = serverConfig.isAzure
Expand Down
4 changes: 2 additions & 2 deletions app/api/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export async function requestOpenai(req: NextRequest) {
);

let baseUrl =
serverConfig.azureUrl ?? serverConfig.baseUrl ?? OPENAI_BASE_URL;
serverConfig.azureUrl || serverConfig.baseUrl || OPENAI_BASE_URL;

if (!baseUrl.startsWith("http")) {
baseUrl = `https://${baseUrl}`;
Expand Down Expand Up @@ -81,7 +81,7 @@ export async function requestOpenai(req: NextRequest) {
const jsonBody = JSON.parse(clonedBody) as { model?: string };

// not undefined and is false
if (modelTable[jsonBody?.model ?? ""] === false) {
if (modelTable[jsonBody?.model ?? ""].available === false) {
return NextResponse.json(
{
error: true,
Expand Down
1 change: 1 addition & 0 deletions app/api/openai/[...path]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,4 @@ export const GET = handle;
export const POST = handle;

export const runtime = "edge";
export const preferredRegion = ['arn1', 'bom1', 'cdg1', 'cle1', 'cpt1', 'dub1', 'fra1', 'gru1', 'hnd1', 'iad1', 'icn1', 'kix1', 'lhr1', 'pdx1', 'sfo1', 'sin1', 'syd1'];
28 changes: 25 additions & 3 deletions app/client/platforms/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,35 @@ export class ChatGPTApi implements LLMApi {

if (shouldStream) {
let responseText = "";
let remainText = "";
let finished = false;

// animate response to make it looks smooth
function animateResponseText() {
if (finished || controller.signal.aborted) {
responseText += remainText;
console.log("[Response Animation] finished");
return;
}

if (remainText.length > 0) {
const fetchCount = Math.max(1, Math.round(remainText.length / 60));
const fetchText = remainText.slice(0, fetchCount);
responseText += fetchText;
remainText = remainText.slice(fetchCount);
options.onUpdate?.(responseText, fetchText);
}

requestAnimationFrame(animateResponseText);
}

// start animaion
animateResponseText();

const finish = () => {
if (!finished) {
options.onFinish(responseText);
finished = true;
options.onFinish(responseText + remainText);
}
};

Expand Down Expand Up @@ -183,8 +206,7 @@ export class ChatGPTApi implements LLMApi {
};
const delta = json.choices[0]?.delta?.content;
if (delta) {
responseText += delta;
options.onUpdate?.(responseText, delta);
remainText += delta;
}
} catch (e) {
console.error("[Request] parse error", text);
Expand Down
5 changes: 4 additions & 1 deletion app/components/chat-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ export function ChatItem(props: {
{props.narrow ? (
<div className={styles["chat-item-narrow"]}>
<div className={styles["chat-item-avatar"] + " no-dark"}>
<MaskAvatar mask={props.mask} />
<MaskAvatar
avatar={props.mask.avatar}
model={props.mask.modelConfig.model}
/>
</div>
<div className={styles["chat-item-narrow-count"]}>
{props.count}
Expand Down
33 changes: 27 additions & 6 deletions app/components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -431,11 +431,27 @@ export function ChatActions(props: {

// switch model
const currentModel = chatStore.currentSession().mask.modelConfig.model;
const models = useAllModels()
.filter((m) => m.available)
.map((m) => m.name);
const allModels = useAllModels();
const models = useMemo(
() => allModels.filter((m) => m.available),
[allModels],
);
const [showModelSelector, setShowModelSelector] = useState(false);

useEffect(() => {
// if current model is not available
// switch to first available model
const isUnavaliableModel = !models.some((m) => m.name === currentModel);
if (isUnavaliableModel && models.length > 0) {
const nextModel = models[0].name as ModelType;
chatStore.updateCurrentSession(
(session) => (session.mask.modelConfig.model = nextModel),
);
showToast(nextModel);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentModel, models]);

return (
<div className={styles["chat-input-actions"]}>
{couldStop && (
Expand Down Expand Up @@ -515,8 +531,8 @@ export function ChatActions(props: {
<Selector
defaultSelectedValue={currentModel}
items={models.map((m) => ({
title: m,
value: m,
title: m.displayName,
value: m.name,
}))}
onClose={() => setShowModelSelector(false)}
onSelection={(s) => {
Expand Down Expand Up @@ -1160,7 +1176,12 @@ function _Chat() {
{["system"].includes(message.role) ? (
<Avatar avatar="2699-fe0f" />
) : (
<MaskAvatar mask={session.mask} />
<MaskAvatar
avatar={session.mask.avatar}
model={
message.model || session.mask.modelConfig.model
}
/>
)}
</>
)}
Expand Down
3 changes: 2 additions & 1 deletion app/components/exporter.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@
box-shadow: var(--card-shadow);
border: var(--border-in-light);

*:not(li) {
code,
pre {
overflow: hidden;
}
}
Expand Down
36 changes: 25 additions & 11 deletions app/components/exporter.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @next/next/no-img-element */
import { ChatMessage, useAppConfig, useChatStore } from "../store";
import { ChatMessage, ModelType, useAppConfig, useChatStore } from "../store";
import Locale from "../locales";
import styles from "./exporter.module.scss";
import {
Expand Down Expand Up @@ -27,7 +27,7 @@ import { Avatar } from "./emoji";
import dynamic from "next/dynamic";
import NextImage from "next/image";

import { toBlob, toJpeg, toPng } from "html-to-image";
import { toBlob, toPng } from "html-to-image";
import { DEFAULT_MASK_AVATAR } from "../store/mask";
import { api } from "../client/api";
import { prettyObject } from "../utils/format";
Expand All @@ -41,7 +41,22 @@ const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
export function ExportMessageModal(props: { onClose: () => void }) {
return (
<div className="modal-mask">
<Modal title={Locale.Export.Title} onClose={props.onClose}>
<Modal
title={Locale.Export.Title}
onClose={props.onClose}
footer={
<div
style={{
width: "100%",
textAlign: "center",
fontSize: 14,
opacity: 0.5,
}}
>
{Locale.Exporter.Description.Title}
</div>
}
>
<div style={{ minHeight: "40vh" }}>
<MessageExporter />
</div>
Expand Down Expand Up @@ -149,7 +164,7 @@ export function MessageExporter() {
if (exportConfig.includeContext) {
ret.push(...session.mask.context);
}
ret.push(...session.messages.filter((m, i) => selection.has(m.id)));
ret.push(...session.messages.filter((m) => selection.has(m.id)));
return ret;
}, [
exportConfig.includeContext,
Expand Down Expand Up @@ -260,7 +275,8 @@ export function RenderExport(props: {
});

props.onRender(renderMsgs);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
<div ref={domRef}>
Expand Down Expand Up @@ -437,13 +453,13 @@ export function ImagePreviewer(props: {
showToast(Locale.Export.Image.Toast);
const dom = previewRef.current;
if (!dom) return;

const isApp = getClientConfig()?.isApp;

try {
const blob = await toPng(dom);
if (!blob) return;

if (isMobile || (isApp && window.__TAURI__)) {
if (isApp && window.__TAURI__) {
const result = await window.__TAURI__.dialog.save({
Expand All @@ -459,7 +475,7 @@ export function ImagePreviewer(props: {
},
],
});

if (result !== null) {
const response = await fetch(blob);
const buffer = await response.arrayBuffer();
Expand Down Expand Up @@ -604,8 +620,6 @@ export function MarkdownPreviewer(props: {
);
}

// modified by BackTrackZ now it's looks better

export function JsonPreviewer(props: {
messages: ChatMessage[];
topic: string;
Expand Down
28 changes: 25 additions & 3 deletions app/components/markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import RemarkBreaks from "remark-breaks";
import RehypeKatex from "rehype-katex";
import RemarkGfm from "remark-gfm";
import RehypeHighlight from "rehype-highlight";
import { useRef, useState, RefObject, useEffect } from "react";
import { useRef, useState, RefObject, useEffect, useMemo } from "react";
import { copyToClipboard } from "../utils";
import mermaid from "mermaid";

import LoadingIcon from "../icons/three-dots.svg";
import React from "react";
import { useDebouncedCallback, useThrottledCallback } from "use-debounce";
import { useDebouncedCallback } from "use-debounce";
import { showImageModal } from "./ui-lib";

export function Mermaid(props: { code: string }) {
Expand Down Expand Up @@ -99,7 +99,29 @@ export function PreCode(props: { children: any }) {
);
}

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 _MarkDownContent(props: { content: string }) {
const escapedContent = useMemo(
() => escapeDollarNumber(props.content),
[props.content],
);

return (
<ReactMarkdown
remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
Expand All @@ -124,7 +146,7 @@ function _MarkDownContent(props: { content: string }) {
},
}}
>
{props.content}
{escapedContent}
</ReactMarkdown>
);
}
Expand Down
Loading

0 comments on commit 5654600

Please sign in to comment.