Merge remote-tracking branch 'upstream/main'
This commit is contained in:
parent
8fbcc653a0
commit
f1c9c96082
12
README.md
12
README.md
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
[![Web][Web-image]][web-url]
|
[![Web][Web-image]][web-url]
|
||||||
|
|
||||||
[网页版](https://n3xt.chat) / [反馈](https://github.com/Hk-Gosuto/ChatGPT-Next-Web-LangChain/issues)
|
[网页版](https://n3xt.chat) / [反馈](https://github.com/Hk-Gosuto/ChatGPT-Next-Web-LangChain/issues) / [Discord](https://discord.gg/zTwDFtSC)
|
||||||
|
|
||||||
[web-url]: https://n3xt.chat/
|
[web-url]: https://n3xt.chat/
|
||||||
[download-url]: https://github.com/Hk-Gosuto/ChatGPT-Next-Web-LangChain/releases
|
[download-url]: https://github.com/Hk-Gosuto/ChatGPT-Next-Web-LangChain/releases
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
|
|
||||||
- 支持 GPT-4V(视觉) 模型
|
- 支持 GPT-4V(视觉) 模型
|
||||||
- ~~需要配置对象存储服务,请参考 [对象存储服务配置指南](./docs/s3-oss.md) 配置~~
|
- ~~需要配置对象存储服务,请参考 [对象存储服务配置指南](./docs/s3-oss.md) 配置~~
|
||||||
- 已同步上游仓库视觉模型调用方式(压缩图片),不过这里还是会有撑爆 localstorage 的风险(https://github.com/Hk-Gosuto/ChatGPT-Next-Web-LangChain/issues/77#issuecomment-1846410078),后续会兼容两种形式的图片本地存储,如果配置了对象存储会优先使用对象存储。
|
- 已同步上游仓库视觉模型调用方式(压缩图片),这里还是会有撑爆 LocalStorage 的风险(https://github.com/Hk-Gosuto/ChatGPT-Next-Web-LangChain/issues/77#issuecomment-1846410078),后面如果出现类似问题会再适配对象存储来存储图像。
|
||||||
|
|
||||||
- 基于 [LangChain](https://github.com/hwchase17/langchainjs) 实现的插件功能,目前支持以下插件,未来会添加更多
|
- 基于 [LangChain](https://github.com/hwchase17/langchainjs) 实现的插件功能,目前支持以下插件,未来会添加更多
|
||||||
- 搜索(优先级:`GoogleCustomSearch > SerpAPI > BingSerpAPI > ChooseSearchEngine > DuckDuckGo`)
|
- 搜索(优先级:`GoogleCustomSearch > SerpAPI > BingSerpAPI > ChooseSearchEngine > DuckDuckGo`)
|
||||||
|
@ -138,8 +138,6 @@
|
||||||
|
|
||||||
最新版本中已经移除上面两个模型。
|
最新版本中已经移除上面两个模型。
|
||||||
|
|
||||||
- [ ] ~~支持添加自定义插件~~
|
|
||||||
|
|
||||||
- [ ] 支持其他类型文件上传 https://github.com/Hk-Gosuto/ChatGPT-Next-Web-LangChain/issues/77
|
- [ ] 支持其他类型文件上传 https://github.com/Hk-Gosuto/ChatGPT-Next-Web-LangChain/issues/77
|
||||||
|
|
||||||
- [ ] 支持 Azure Storage https://github.com/Hk-Gosuto/ChatGPT-Next-Web-LangChain/issues/217
|
- [ ] 支持 Azure Storage https://github.com/Hk-Gosuto/ChatGPT-Next-Web-LangChain/issues/217
|
||||||
|
@ -301,12 +299,6 @@ docker run -d -p 3000:3000 \
|
||||||
<img src="https://contrib.rocks/image?repo=Hk-Gosuto/ChatGPT-Next-Web-LangChain" />
|
<img src="https://contrib.rocks/image?repo=Hk-Gosuto/ChatGPT-Next-Web-LangChain" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
## 截图
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## 捐赠
|
## 捐赠
|
||||||
|
|
||||||
[请项目原作者喝杯咖啡](https://www.buymeacoffee.com/yidadaa)
|
[请项目原作者喝杯咖啡](https://www.buymeacoffee.com/yidadaa)
|
||||||
|
|
|
@ -308,7 +308,7 @@ export class ChatGPTApi implements LLMApi {
|
||||||
async toolAgentChat(options: AgentChatOptions) {
|
async toolAgentChat(options: AgentChatOptions) {
|
||||||
const messages = options.messages.map((v) => ({
|
const messages = options.messages.map((v) => ({
|
||||||
role: v.role,
|
role: v.role,
|
||||||
content: v.content,
|
content: getMessageTextContent(v),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const modelConfig = {
|
const modelConfig = {
|
||||||
|
|
|
@ -648,39 +648,6 @@
|
||||||
bottom: 32px;
|
bottom: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-input-image {
|
|
||||||
background-color: var(--primary);
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
right: 28px;
|
|
||||||
bottom: 78px;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
|
|
||||||
border-radius: 4px;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-input-image-close {
|
|
||||||
background-color: white;
|
|
||||||
fill: black;
|
|
||||||
border: none;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
display: flex;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
width: 22px;
|
|
||||||
height: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-input-image-close:hover {
|
|
||||||
background-color: #f3f3f3;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 600px) {
|
@media only screen and (max-width: 600px) {
|
||||||
.chat-input {
|
.chat-input {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
@ -689,4 +656,4 @@
|
||||||
.chat-input-send {
|
.chat-input-send {
|
||||||
bottom: 30px;
|
bottom: 30px;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -90,7 +90,6 @@ import {
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
CHAT_PAGE_SIZE,
|
CHAT_PAGE_SIZE,
|
||||||
LAST_INPUT_IMAGE_KEY,
|
|
||||||
LAST_INPUT_KEY,
|
LAST_INPUT_KEY,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
Path,
|
Path,
|
||||||
|
@ -105,7 +104,6 @@ import { prettyObject } from "../utils/format";
|
||||||
import { ExportMessageModal } from "./exporter";
|
import { ExportMessageModal } from "./exporter";
|
||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
import { useAllModels } from "../utils/hooks";
|
import { useAllModels } from "../utils/hooks";
|
||||||
import Image from "next/image";
|
|
||||||
import { ClientApi } from "../client/api";
|
import { ClientApi } from "../client/api";
|
||||||
import { createTTSPlayer } from "../utils/audio";
|
import { createTTSPlayer } from "../utils/audio";
|
||||||
import { MultimodalContent } from "../client/api";
|
import { MultimodalContent } from "../client/api";
|
||||||
|
@ -455,7 +453,6 @@ export function ChatActions(props: {
|
||||||
showPromptModal: () => void;
|
showPromptModal: () => void;
|
||||||
scrollToBottom: () => void;
|
scrollToBottom: () => void;
|
||||||
showPromptHints: () => void;
|
showPromptHints: () => void;
|
||||||
imageSelected: (img: any) => void;
|
|
||||||
hitBottom: boolean;
|
hitBottom: boolean;
|
||||||
uploading: boolean;
|
uploading: boolean;
|
||||||
}) {
|
}) {
|
||||||
|
@ -717,7 +714,6 @@ function _Chat() {
|
||||||
|
|
||||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const [userInput, setUserInput] = useState("");
|
const [userInput, setUserInput] = useState("");
|
||||||
const [userImage, setUserImage] = useState<any>();
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const { submitKey, shouldSubmit } = useSubmitHandler();
|
const { submitKey, shouldSubmit } = useSubmitHandler();
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
|
@ -803,7 +799,7 @@ function _Chat() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const doSubmit = (userInput: string, userImage?: any) => {
|
const doSubmit = (userInput: string) => {
|
||||||
if (userInput.trim() === "") return;
|
if (userInput.trim() === "") return;
|
||||||
const matchCommand = chatCommands.match(userInput);
|
const matchCommand = chatCommands.match(userInput);
|
||||||
if (matchCommand.matched) {
|
if (matchCommand.matched) {
|
||||||
|
@ -818,10 +814,8 @@ function _Chat() {
|
||||||
.then(() => setIsLoading(false));
|
.then(() => setIsLoading(false));
|
||||||
setAttachImages([]);
|
setAttachImages([]);
|
||||||
localStorage.setItem(LAST_INPUT_KEY, userInput);
|
localStorage.setItem(LAST_INPUT_KEY, userInput);
|
||||||
localStorage.setItem(LAST_INPUT_IMAGE_KEY, userImage);
|
|
||||||
setUserInput("");
|
setUserInput("");
|
||||||
setPromptHints([]);
|
setPromptHints([]);
|
||||||
setUserImage(null);
|
|
||||||
if (!isMobileScreen) inputRef.current?.focus();
|
if (!isMobileScreen) inputRef.current?.focus();
|
||||||
setAutoScroll(true);
|
setAutoScroll(true);
|
||||||
};
|
};
|
||||||
|
@ -886,12 +880,11 @@ function _Chat() {
|
||||||
!(e.metaKey || e.altKey || e.ctrlKey)
|
!(e.metaKey || e.altKey || e.ctrlKey)
|
||||||
) {
|
) {
|
||||||
setUserInput(localStorage.getItem(LAST_INPUT_KEY) ?? "");
|
setUserInput(localStorage.getItem(LAST_INPUT_KEY) ?? "");
|
||||||
setUserImage(localStorage.getItem(LAST_INPUT_IMAGE_KEY));
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (shouldSubmit(e) && promptHints.length === 0) {
|
if (shouldSubmit(e) && promptHints.length === 0) {
|
||||||
doSubmit(userInput, userImage);
|
doSubmit(userInput);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1069,7 +1062,6 @@ function _Chat() {
|
||||||
isLoading,
|
isLoading,
|
||||||
session.messages,
|
session.messages,
|
||||||
userInput,
|
userInput,
|
||||||
userImage?.fileUrl,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const [msgRenderIndex, _setMsgRenderIndex] = useState(
|
const [msgRenderIndex, _setMsgRenderIndex] = useState(
|
||||||
|
@ -1283,8 +1275,6 @@ function _Chat() {
|
||||||
setAttachImages(images);
|
setAttachImages(images);
|
||||||
}
|
}
|
||||||
|
|
||||||
const textareaMinHeight = userImage ? 121 : 68;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.chat} key={session.id}>
|
<div className={styles.chat} key={session.id}>
|
||||||
<div className="window-header" data-tauri-drag-region>
|
<div className="window-header" data-tauri-drag-region>
|
||||||
|
@ -1574,19 +1564,6 @@ function _Chat() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!isUser && message.model?.includes("vision") && (
|
|
||||||
<div
|
|
||||||
className={[
|
|
||||||
styles["chat-message-actions"],
|
|
||||||
styles["column-flex"],
|
|
||||||
].join(" ")}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{ marginTop: "6px" }}
|
|
||||||
className={styles["chat-input-actions"]}
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className={styles["chat-message-action-date"]}>
|
<div className={styles["chat-message-action-date"]}>
|
||||||
{isContext
|
{isContext
|
||||||
? Locale.Chat.IsContext
|
? Locale.Chat.IsContext
|
||||||
|
@ -1622,9 +1599,6 @@ function _Chat() {
|
||||||
setUserInput("/");
|
setUserInput("/");
|
||||||
onSearch("");
|
onSearch("");
|
||||||
}}
|
}}
|
||||||
imageSelected={(img: any) => {
|
|
||||||
setUserImage(img);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
className={`${styles["chat-input-panel-inner"]} ${
|
className={`${styles["chat-input-panel-inner"]} ${
|
||||||
|
@ -1649,7 +1623,6 @@ function _Chat() {
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
style={{
|
style={{
|
||||||
fontSize: config.fontSize,
|
fontSize: config.fontSize,
|
||||||
minHeight: textareaMinHeight,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{attachImages.length != 0 && (
|
{attachImages.length != 0 && (
|
||||||
|
@ -1680,7 +1653,7 @@ function _Chat() {
|
||||||
text={Locale.Chat.Send}
|
text={Locale.Chat.Send}
|
||||||
className={styles["chat-input-send"]}
|
className={styles["chat-input-send"]}
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => doSubmit(userInput, userImage)}
|
onClick={() => doSubmit(userInput)}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -116,41 +116,38 @@ function escapeDollarNumber(text: string) {
|
||||||
return escapedText;
|
return escapedText;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _MarkDownContent(props: { content: string; imageBase64?: string }) {
|
function _MarkDownContent(props: { content: string }) {
|
||||||
const escapedContent = useMemo(
|
const escapedContent = useMemo(
|
||||||
() => escapeDollarNumber(props.content),
|
() => escapeDollarNumber(props.content),
|
||||||
[props.content],
|
[props.content],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ fontSize: "inherit" }}>
|
<ReactMarkdown
|
||||||
{props.imageBase64 && <img src={props.imageBase64} alt="" />}
|
remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
|
||||||
<ReactMarkdown
|
rehypePlugins={[
|
||||||
remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
|
RehypeKatex,
|
||||||
rehypePlugins={[
|
[
|
||||||
RehypeKatex,
|
RehypeHighlight,
|
||||||
[
|
{
|
||||||
RehypeHighlight,
|
detect: false,
|
||||||
{
|
ignoreMissing: true,
|
||||||
detect: false,
|
|
||||||
ignoreMissing: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]}
|
|
||||||
components={{
|
|
||||||
pre: PreCode,
|
|
||||||
p: (pProps) => <p {...pProps} dir="auto" />,
|
|
||||||
a: (aProps) => {
|
|
||||||
const href = aProps.href || "";
|
|
||||||
const isInternal = /^\/#/i.test(href);
|
|
||||||
const target = isInternal ? "_self" : aProps.target ?? "_blank";
|
|
||||||
return <a {...aProps} target={target} />;
|
|
||||||
},
|
},
|
||||||
}}
|
],
|
||||||
>
|
]}
|
||||||
{escapedContent}
|
components={{
|
||||||
</ReactMarkdown>
|
pre: PreCode,
|
||||||
</div>
|
p: (pProps) => <p {...pProps} dir="auto" />,
|
||||||
|
a: (aProps) => {
|
||||||
|
const href = aProps.href || "";
|
||||||
|
const isInternal = /^\/#/i.test(href);
|
||||||
|
const target = isInternal ? "_self" : aProps.target ?? "_blank";
|
||||||
|
return <a {...aProps} target={target} />;
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{escapedContent}
|
||||||
|
</ReactMarkdown>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,10 +160,10 @@ export function Markdown(
|
||||||
fontSize?: number;
|
fontSize?: number;
|
||||||
parentRef?: RefObject<HTMLDivElement>;
|
parentRef?: RefObject<HTMLDivElement>;
|
||||||
defaultShow?: boolean;
|
defaultShow?: boolean;
|
||||||
imageBase64?: string;
|
|
||||||
} & React.DOMAttributes<HTMLDivElement>,
|
} & React.DOMAttributes<HTMLDivElement>,
|
||||||
) {
|
) {
|
||||||
const mdRef = useRef<HTMLDivElement>(null);
|
const mdRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="markdown-body"
|
className="markdown-body"
|
||||||
|
@ -181,10 +178,7 @@ export function Markdown(
|
||||||
{props.loading ? (
|
{props.loading ? (
|
||||||
<LoadingIcon />
|
<LoadingIcon />
|
||||||
) : (
|
) : (
|
||||||
<MarkdownContent
|
<MarkdownContent content={props.content} />
|
||||||
content={props.content}
|
|
||||||
imageBase64={props.imageBase64}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -58,7 +58,6 @@ export const NARROW_SIDEBAR_WIDTH = 100;
|
||||||
export const ACCESS_CODE_PREFIX = "nk-";
|
export const ACCESS_CODE_PREFIX = "nk-";
|
||||||
|
|
||||||
export const LAST_INPUT_KEY = "last-input";
|
export const LAST_INPUT_KEY = "last-input";
|
||||||
export const LAST_INPUT_IMAGE_KEY = "last-input-image";
|
|
||||||
export const UNFINISHED_INPUT = (id: string) => "unfinished-input-" + id;
|
export const UNFINISHED_INPUT = (id: string) => "unfinished-input-" + id;
|
||||||
|
|
||||||
export const STORAGE_KEY = "chatgpt-next-web";
|
export const STORAGE_KEY = "chatgpt-next-web";
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue