mirror of
https://github.com/Yanyutin753/ChatGPT-Next-Web-LangChain-Gpt-4-All.git
synced 2025-10-13 14:35:00 +00:00
Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cad42d6b1b | ||
![]() |
8676585a11 | ||
![]() |
2da12d397e | ||
![]() |
24c8ca610c | ||
![]() |
e7cc2a8c55 | ||
![]() |
344c532ca9 | ||
![]() |
765e2eed6f | ||
![]() |
d7690d843a | ||
![]() |
80fcf29353 | ||
![]() |
5306ac1b14 | ||
![]() |
fbfc70e2bf | ||
![]() |
a25a8ed812 | ||
![]() |
16bf0e5366 | ||
![]() |
0f9372717e | ||
![]() |
e52eb5580b | ||
![]() |
3dddad317e | ||
![]() |
da333f8f20 | ||
![]() |
58d94818b4 | ||
![]() |
7fd9653e7c | ||
![]() |
79a863c636 |
@@ -19,7 +19,7 @@ ENV OPENAI_API_KEY=""
|
||||
ENV GOOGLE_API_KEY=""
|
||||
ENV CODE=""
|
||||
ENV NEXT_PUBLIC_ENABLE_NODEJS_PLUGIN=1
|
||||
ENV NEXT_PUBLIC_ENABLE_BASE64=0
|
||||
ENV NEXT_PUBLIC_ENABLE_BASE64=1
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
@@ -36,7 +36,7 @@ ENV PROXY_URL=""
|
||||
ENV OPENAI_API_KEY=""
|
||||
ENV GOOGLE_API_KEY=""
|
||||
ENV CODE=""
|
||||
ENV NEXT_PUBLIC_ENABLE_BASE64=0
|
||||
ENV NEXT_PUBLIC_ENABLE_BASE64=1
|
||||
|
||||
COPY --from=builder /app/public ./public
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
|
@@ -57,6 +57,7 @@ export interface RequestBody {
|
||||
apiKey?: string;
|
||||
maxIterations: number;
|
||||
returnIntermediateSteps: boolean;
|
||||
updateTypes: boolean;
|
||||
useTools: (undefined | string)[];
|
||||
}
|
||||
|
||||
|
@@ -28,6 +28,7 @@ export interface LLMConfig {
|
||||
stream?: boolean;
|
||||
presence_penalty?: number;
|
||||
frequency_penalty?: number;
|
||||
updateTypes?: boolean;
|
||||
}
|
||||
|
||||
export interface LLMAgentConfig {
|
||||
|
@@ -79,6 +79,7 @@ export class ChatGPTApi implements LLMApi {
|
||||
|
||||
async chat(options: ChatOptions) {
|
||||
const messages: any[] = [];
|
||||
const accessStore = useAccessStore.getState();
|
||||
|
||||
const getImageBase64Data = async (url: string) => {
|
||||
const response = await axios.get(url, { responseType: "arraybuffer" });
|
||||
@@ -104,7 +105,7 @@ export class ChatGPTApi implements LLMApi {
|
||||
});
|
||||
if (v.image_url) {
|
||||
let image_url_data = "";
|
||||
if (process.env.NEXT_PUBLIC_ENABLE_BASE64) {
|
||||
if (options.config.updateTypes && !options.config.model.includes("coze")) {
|
||||
var base64Data = await getImageBase64Data(v.image_url);
|
||||
let mimeType: string | null;
|
||||
try {
|
||||
@@ -128,8 +129,8 @@ export class ChatGPTApi implements LLMApi {
|
||||
else {
|
||||
const match = v.image_url.match(/\.(\w+)$/);
|
||||
if (match && match[1]) {
|
||||
const fileExtension = match[1].toLowerCase();
|
||||
v.image_url = v.image_url.replace(/\.\w+$/, '.' + fileExtension);
|
||||
const fileExtension = match[1].toLowerCase();
|
||||
v.image_url = v.image_url.replace(/\.\w+$/, '.' + fileExtension);
|
||||
}
|
||||
var port = window.location.port ? ':' + window.location.port : '';
|
||||
var url = window.location.protocol + "//" + window.location.hostname + port;
|
||||
@@ -175,8 +176,16 @@ export class ChatGPTApi implements LLMApi {
|
||||
// max_tokens: Math.max(modelConfig.max_tokens, 1024),
|
||||
// Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore.
|
||||
};
|
||||
|
||||
console.log("[Request] openai payload: ", requestPayload);
|
||||
// 用于隐藏传参变量
|
||||
const moonshotPayload = {
|
||||
messages,
|
||||
stream: options.config.stream,
|
||||
model: modelConfig.model,
|
||||
use_search:
|
||||
modelConfig.model.includes("vision")
|
||||
? false
|
||||
: true,
|
||||
}
|
||||
|
||||
const shouldStream = !!options.config.stream;
|
||||
const controller = new AbortController();
|
||||
@@ -190,6 +199,13 @@ export class ChatGPTApi implements LLMApi {
|
||||
signal: controller.signal,
|
||||
headers: getHeaders(),
|
||||
};
|
||||
if (modelConfig.model.includes("moonshot")) {
|
||||
console.log("[Request] moonshot payload: ", moonshotPayload);
|
||||
chatPayload.body = JSON.stringify(moonshotPayload)
|
||||
}
|
||||
else {
|
||||
console.log("[Request] openai payload: ", requestPayload);
|
||||
}
|
||||
|
||||
// make a fetch request
|
||||
const requestTimeoutId = setTimeout(
|
||||
@@ -347,6 +363,7 @@ export class ChatGPTApi implements LLMApi {
|
||||
baseUrl: baseUrl,
|
||||
maxIterations: options.agentConfig.maxIterations,
|
||||
returnIntermediateSteps: options.agentConfig.returnIntermediateSteps,
|
||||
updateTypes: modelConfig.updateTypes,
|
||||
useTools: options.agentConfig.useTools,
|
||||
};
|
||||
|
||||
|
@@ -373,17 +373,17 @@ function ChatAction(props: {
|
||||
style={
|
||||
props.icon && !props.loding
|
||||
? ({
|
||||
"--icon-width": `${width.icon}px`,
|
||||
"--full-width": `${width.full}px`,
|
||||
...props.style,
|
||||
} as React.CSSProperties)
|
||||
"--icon-width": `${width.icon}px`,
|
||||
"--full-width": `${width.full}px`,
|
||||
...props.style,
|
||||
} as React.CSSProperties)
|
||||
: props.loding
|
||||
? ({
|
||||
? ({
|
||||
"--icon-width": `30px`,
|
||||
"--full-width": `30px`,
|
||||
...props.style,
|
||||
} as React.CSSProperties)
|
||||
: props.style
|
||||
: props.style
|
||||
}
|
||||
>
|
||||
{props.icon ? (
|
||||
@@ -476,22 +476,38 @@ export function ChatActions(props: {
|
||||
}
|
||||
|
||||
const onImageSelected = async (e: any) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
const files = e.target.files;
|
||||
if (!files.length) return;
|
||||
|
||||
const api = new ClientApi();
|
||||
setUploadLoading(true);
|
||||
const uploadFile = await api.file
|
||||
.upload(file)
|
||||
.catch((e) => {
|
||||
console.error("[Upload]", e);
|
||||
showToast(prettyObject(e));
|
||||
})
|
||||
.finally(() => setUploadLoading(false));
|
||||
props.imageSelected({
|
||||
fileName: uploadFile.fileName,
|
||||
fileUrl: uploadFile.filePath,
|
||||
|
||||
// Here we create a Promise for each file upload,
|
||||
// then use Promise.all to wait for all of them to complete.
|
||||
const uploadPromises = Array.from(files).map(async (file) => {
|
||||
setUploadLoading(true);
|
||||
const uploadFile = await api.file
|
||||
.upload(file)
|
||||
.catch((e) => {
|
||||
console.error("[Upload]", e);
|
||||
showToast(prettyObject(e));
|
||||
// return null if upload fails
|
||||
return null;
|
||||
})
|
||||
.finally(() => setUploadLoading(false));
|
||||
|
||||
if (uploadFile) {
|
||||
// use a callback or event to inform about the new file
|
||||
props.imageSelected({
|
||||
fileName: uploadFile.fileName,
|
||||
fileUrl: uploadFile.filePath,
|
||||
});
|
||||
}
|
||||
// Clear file input on each iteration.
|
||||
e.target.value = null;
|
||||
});
|
||||
e.target.value = null;
|
||||
|
||||
// Wait for all uploads to finish.
|
||||
await Promise.all(uploadPromises);
|
||||
};
|
||||
|
||||
// switch model
|
||||
@@ -518,7 +534,7 @@ export function ChatActions(props: {
|
||||
const items = event.clipboardData?.items || [];
|
||||
const api = new ClientApi();
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i].type.indexOf("image") === -1) continue;
|
||||
// if (items[i].type.indexOf("image") === -1) continue;
|
||||
const file = items[i].getAsFile();
|
||||
if (file !== null) {
|
||||
setUploadLoading(true);
|
||||
@@ -623,12 +639,13 @@ export function ChatActions(props: {
|
||||
{(currentModel.includes("vision") || currentModel.includes("gizmo")) && (
|
||||
<ChatAction
|
||||
onClick={selectImage}
|
||||
text="选择图片"
|
||||
text="选择文件"
|
||||
loding={uploadLoading}
|
||||
icon={<UploadIcon />}
|
||||
innerNode={
|
||||
<input
|
||||
type="file"
|
||||
multiple
|
||||
accept="*/*"
|
||||
id="chat-image-file-select-upload"
|
||||
style={{ display: "none" }}
|
||||
@@ -826,7 +843,7 @@ function _Chat() {
|
||||
}
|
||||
};
|
||||
|
||||
const doSubmit = (userInput: string, userImage?: any) => {
|
||||
const doSubmit = (userInput: string, userImages?: any[]) => {
|
||||
if (userInput.trim() === "") return;
|
||||
const matchCommand = chatCommands.match(userInput);
|
||||
if (matchCommand.matched) {
|
||||
@@ -836,11 +853,16 @@ function _Chat() {
|
||||
return;
|
||||
}
|
||||
setIsLoading(true);
|
||||
|
||||
const userImageUrls = userImages?.map(image => image.fileUrl) || [];
|
||||
|
||||
chatStore
|
||||
.onUserInput(userInput, userImage?.fileUrl)
|
||||
.onUserInput(userInput, ...userImageUrls)
|
||||
.then(() => setIsLoading(false));
|
||||
|
||||
localStorage.setItem(LAST_INPUT_KEY, userInput);
|
||||
localStorage.setItem(LAST_INPUT_IMAGE_KEY, userImage);
|
||||
localStorage.setItem(LAST_INPUT_IMAGE_KEY, JSON.stringify(userImages));
|
||||
|
||||
setUserInput("");
|
||||
setPromptHints([]);
|
||||
setUserImage(null);
|
||||
@@ -908,7 +930,13 @@ function _Chat() {
|
||||
!(e.metaKey || e.altKey || e.ctrlKey)
|
||||
) {
|
||||
setUserInput(localStorage.getItem(LAST_INPUT_KEY) ?? "");
|
||||
setUserImage(localStorage.getItem(LAST_INPUT_IMAGE_KEY));
|
||||
if(localStorage.getItem(LAST_INPUT_IMAGE_KEY) !== null){
|
||||
let retrievedUserImages = JSON.parse(localStorage.getItem(LAST_INPUT_IMAGE_KEY)!);
|
||||
setUserImage(retrievedUserImages);
|
||||
}
|
||||
else{
|
||||
setUserImage(null);
|
||||
}
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
@@ -1031,28 +1059,28 @@ function _Chat() {
|
||||
.concat(
|
||||
isLoading
|
||||
? [
|
||||
{
|
||||
...createMessage({
|
||||
role: "assistant",
|
||||
content: "……",
|
||||
}),
|
||||
preview: true,
|
||||
},
|
||||
]
|
||||
{
|
||||
...createMessage({
|
||||
role: "assistant",
|
||||
content: "……",
|
||||
}),
|
||||
preview: true,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
)
|
||||
.concat(
|
||||
userInput.length > 0 && config.sendPreviewBubble
|
||||
? [
|
||||
{
|
||||
...createMessage({
|
||||
role: "user",
|
||||
content: userInput,
|
||||
image_url: userImage?.fileUrl,
|
||||
}),
|
||||
preview: true,
|
||||
},
|
||||
]
|
||||
{
|
||||
...createMessage({
|
||||
role: "user",
|
||||
content: userInput,
|
||||
image_url: userImage?.fileUrl,
|
||||
}),
|
||||
preview: true,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
);
|
||||
}, [
|
||||
@@ -1149,7 +1177,7 @@ function _Chat() {
|
||||
if (payload.key || payload.url) {
|
||||
showConfirm(
|
||||
Locale.URLCommand.Settings +
|
||||
`\n${JSON.stringify(payload, null, 4)}`,
|
||||
`\n${JSON.stringify(payload, null, 4)}`,
|
||||
).then((res) => {
|
||||
if (!res) return;
|
||||
if (payload.key) {
|
||||
@@ -1457,7 +1485,7 @@ function _Chat() {
|
||||
onSearch("");
|
||||
}}
|
||||
imageSelected={(img: any) => {
|
||||
setUserImage(img);
|
||||
setUserImage([...userImage, img]); // Add new image to the array
|
||||
}}
|
||||
/>
|
||||
<div className={styles["chat-input-panel-inner"]}>
|
||||
@@ -1477,8 +1505,8 @@ function _Chat() {
|
||||
minHeight: textareaMinHeight,
|
||||
}}
|
||||
/>
|
||||
{userImage && (
|
||||
<div className={styles["chat-input-image"]}>
|
||||
{userImages.map((userImage, index) => (
|
||||
<div key={index} className={styles["chat-input-image"]}>
|
||||
<div
|
||||
style={{ position: "relative", width: "48px", height: "48px" }}
|
||||
>
|
||||
@@ -1494,21 +1522,21 @@ function _Chat() {
|
||||
</div>
|
||||
<button
|
||||
className={styles["chat-input-image-close"]}
|
||||
id="chat-input-image-close"
|
||||
id={`chat-input-image-close-${index}`}
|
||||
onClick={() => {
|
||||
setUserImage(null);
|
||||
setUserImage(userImage.filter((_: any, i: any) => i !== index)); // Remove image
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
))}
|
||||
<IconButton
|
||||
icon={<SendWhiteIcon />}
|
||||
text={Locale.Chat.Send}
|
||||
className={styles["chat-input-send"]}
|
||||
type="primary"
|
||||
onClick={() => doSubmit(userInput, userImage)}
|
||||
onClick={() => doSubmit(userInput, userImages)} // Pass the new array
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1525,6 +1553,7 @@ function _Chat() {
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -19,9 +19,9 @@ export function ModelConfigList(props: {
|
||||
onChange={(e) => {
|
||||
props.updateConfig(
|
||||
(config) =>
|
||||
(config.model = ModalConfigValidator.model(
|
||||
e.currentTarget.value,
|
||||
)),
|
||||
(config.model = ModalConfigValidator.model(
|
||||
e.currentTarget.value,
|
||||
)),
|
||||
);
|
||||
}}
|
||||
>
|
||||
@@ -46,9 +46,9 @@ export function ModelConfigList(props: {
|
||||
onChange={(e) => {
|
||||
props.updateConfig(
|
||||
(config) =>
|
||||
(config.temperature = ModalConfigValidator.temperature(
|
||||
e.currentTarget.valueAsNumber,
|
||||
)),
|
||||
(config.temperature = ModalConfigValidator.temperature(
|
||||
e.currentTarget.valueAsNumber,
|
||||
)),
|
||||
);
|
||||
}}
|
||||
></InputRange>
|
||||
@@ -65,9 +65,9 @@ export function ModelConfigList(props: {
|
||||
onChange={(e) => {
|
||||
props.updateConfig(
|
||||
(config) =>
|
||||
(config.top_p = ModalConfigValidator.top_p(
|
||||
e.currentTarget.valueAsNumber,
|
||||
)),
|
||||
(config.top_p = ModalConfigValidator.top_p(
|
||||
e.currentTarget.valueAsNumber,
|
||||
)),
|
||||
);
|
||||
}}
|
||||
></InputRange>
|
||||
@@ -84,9 +84,9 @@ export function ModelConfigList(props: {
|
||||
onChange={(e) =>
|
||||
props.updateConfig(
|
||||
(config) =>
|
||||
(config.max_tokens = ModalConfigValidator.max_tokens(
|
||||
e.currentTarget.valueAsNumber,
|
||||
)),
|
||||
(config.max_tokens = ModalConfigValidator.max_tokens(
|
||||
e.currentTarget.valueAsNumber,
|
||||
)),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
@@ -106,10 +106,10 @@ export function ModelConfigList(props: {
|
||||
onChange={(e) => {
|
||||
props.updateConfig(
|
||||
(config) =>
|
||||
(config.presence_penalty =
|
||||
ModalConfigValidator.presence_penalty(
|
||||
e.currentTarget.valueAsNumber,
|
||||
)),
|
||||
(config.presence_penalty =
|
||||
ModalConfigValidator.presence_penalty(
|
||||
e.currentTarget.valueAsNumber,
|
||||
)),
|
||||
);
|
||||
}}
|
||||
></InputRange>
|
||||
@@ -127,10 +127,10 @@ export function ModelConfigList(props: {
|
||||
onChange={(e) => {
|
||||
props.updateConfig(
|
||||
(config) =>
|
||||
(config.frequency_penalty =
|
||||
ModalConfigValidator.frequency_penalty(
|
||||
e.currentTarget.valueAsNumber,
|
||||
)),
|
||||
(config.frequency_penalty =
|
||||
ModalConfigValidator.frequency_penalty(
|
||||
e.currentTarget.valueAsNumber,
|
||||
)),
|
||||
);
|
||||
}}
|
||||
></InputRange>
|
||||
@@ -146,8 +146,8 @@ export function ModelConfigList(props: {
|
||||
onChange={(e) =>
|
||||
props.updateConfig(
|
||||
(config) =>
|
||||
(config.enableInjectSystemPrompts =
|
||||
e.currentTarget.checked),
|
||||
(config.enableInjectSystemPrompts =
|
||||
e.currentTarget.checked),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
@@ -199,8 +199,8 @@ export function ModelConfigList(props: {
|
||||
onChange={(e) =>
|
||||
props.updateConfig(
|
||||
(config) =>
|
||||
(config.compressMessageLengthThreshold =
|
||||
e.currentTarget.valueAsNumber),
|
||||
(config.compressMessageLengthThreshold =
|
||||
e.currentTarget.valueAsNumber),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
@@ -216,6 +216,25 @@ export function ModelConfigList(props: {
|
||||
}
|
||||
></input>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
title={Locale.Settings.UpdateType.Title}
|
||||
subTitle={Locale.Settings.UpdateType.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={
|
||||
props.modelConfig.updateTypes
|
||||
}
|
||||
onChange={(e) =>
|
||||
props.updateConfig(
|
||||
(config) =>
|
||||
(config.updateTypes =
|
||||
e.currentTarget.checked),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</ListItem>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@@ -359,6 +359,10 @@ const cn = {
|
||||
Title: "频率惩罚度 (frequency_penalty)",
|
||||
SubTitle: "值越大,越有可能降低重复字词",
|
||||
},
|
||||
UpdateType: {
|
||||
Title: "上传类型",
|
||||
SubTitle: "是否上传Base64格式消息",
|
||||
},
|
||||
Plugin: {
|
||||
Enable: {
|
||||
Title: "启用插件",
|
||||
|
@@ -365,6 +365,10 @@ const en: LocaleType = {
|
||||
SubTitle:
|
||||
"A larger value decreasing the likelihood to repeat the same line",
|
||||
},
|
||||
UpdateType: {
|
||||
Title: "Upload type",
|
||||
SubTitle: "Upload Base64 format message",
|
||||
},
|
||||
Plugin: {
|
||||
Enable: {
|
||||
Title: "Enable Plugin",
|
||||
|
@@ -56,6 +56,7 @@ export const DEFAULT_CONFIG = {
|
||||
historyMessageCount: 4,
|
||||
compressMessageLengthThreshold: 1000,
|
||||
enableInjectSystemPrompts: true,
|
||||
updateTypes: true,
|
||||
template: DEFAULT_INPUT_TEMPLATE,
|
||||
},
|
||||
|
||||
@@ -169,7 +170,6 @@ export const useAppConfig = createPersistStore(
|
||||
if (version < 3.8) {
|
||||
state.lastUpdate = Date.now();
|
||||
}
|
||||
|
||||
return state as any;
|
||||
},
|
||||
},
|
||||
|
@@ -3,7 +3,7 @@ services:
|
||||
chatgpt-next-web:
|
||||
profiles: [ "no-proxy" ]
|
||||
container_name: chatgpt-next-web
|
||||
image: gosuto/chatgpt-next-web-langchain
|
||||
image: yangclivia/chatgpt-next-web-langchain
|
||||
volumes:
|
||||
- next_chat_upload:/app/uploads
|
||||
ports:
|
||||
@@ -23,7 +23,7 @@ services:
|
||||
chatgpt-next-web-proxy:
|
||||
profiles: [ "proxy" ]
|
||||
container_name: chatgpt-next-web-proxy
|
||||
image: gosuto/chatgpt-next-web-langchain
|
||||
image: yangclivia/chatgpt-next-web-langchain
|
||||
volumes:
|
||||
- next_chat_upload:/app/uploads
|
||||
ports:
|
||||
@@ -42,4 +42,4 @@ services:
|
||||
- OPENAI_SB=$OPENAI_SB
|
||||
|
||||
volumes:
|
||||
next_chat_upload:
|
||||
next_chat_upload:
|
||||
|
Reference in New Issue
Block a user