20 Commits

Author SHA1 Message Date
Yanyutin753
cad42d6b1b Update chat.tsx 2024-04-13 11:59:10 +08:00
Yanyutin753
8676585a11 Update chat.tsx 2024-04-13 11:25:53 +08:00
Yanyutin753
2da12d397e Update chat.tsx 2024-04-13 11:22:05 +08:00
Yanyutin753
24c8ca610c Update chat.tsx 2024-04-13 11:14:19 +08:00
Clivia
e7cc2a8c55 Update docker-compose.yml 2024-03-13 15:23:03 +08:00
Yanyutin753
344c532ca9 feat updateTypes 2024-03-11 22:56:06 +08:00
Yanyutin753
765e2eed6f Update model-config.tsx 2024-03-11 16:19:47 +08:00
Yanyutin753
d7690d843a feat support 2024-03-11 15:37:42 +08:00
Clivia
80fcf29353 Update Dockerfile 2024-03-11 14:52:19 +08:00
Clivia
5306ac1b14 修改coze开头的模型,使用url传image_url 2024-03-11 14:51:45 +08:00
Clivia
fbfc70e2bf feat 传url直接上传速度更快 2024-03-11 14:09:18 +08:00
Yanyutin753
a25a8ed812 feat moonshot in nextweb 2024-03-11 12:13:42 +08:00
Clivia
16bf0e5366 Update openai.ts 2024-03-11 00:28:02 +08:00
Clivia
0f9372717e feat update url 2024-03-11 00:18:24 +08:00
Clivia
e52eb5580b 优化上传链接 2024-03-10 23:47:21 +08:00
Clivia
3dddad317e feat moonshot-v1-vision 2024-03-10 23:39:35 +08:00
Clivia
da333f8f20 feat moonshot-vision 2024-03-10 23:10:22 +08:00
Yanyutin753
58d94818b4 feat base64 or url 2024-03-08 00:16:28 +08:00
Yanyutin753
7fd9653e7c feat send base64 or url 2024-03-07 23:33:32 +08:00
Clivia
79a863c636 feat "选择图片"改为"选择文件" 2024-03-03 15:09:02 +00:00
10 changed files with 161 additions and 86 deletions

View File

@@ -19,7 +19,7 @@ ENV OPENAI_API_KEY=""
ENV GOOGLE_API_KEY="" ENV GOOGLE_API_KEY=""
ENV CODE="" ENV CODE=""
ENV NEXT_PUBLIC_ENABLE_NODEJS_PLUGIN=1 ENV NEXT_PUBLIC_ENABLE_NODEJS_PLUGIN=1
ENV NEXT_PUBLIC_ENABLE_BASE64=0 ENV NEXT_PUBLIC_ENABLE_BASE64=1
WORKDIR /app WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/node_modules ./node_modules
@@ -36,7 +36,7 @@ ENV PROXY_URL=""
ENV OPENAI_API_KEY="" ENV OPENAI_API_KEY=""
ENV GOOGLE_API_KEY="" ENV GOOGLE_API_KEY=""
ENV CODE="" ENV CODE=""
ENV NEXT_PUBLIC_ENABLE_BASE64=0 ENV NEXT_PUBLIC_ENABLE_BASE64=1
COPY --from=builder /app/public ./public COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/standalone ./

View File

@@ -57,6 +57,7 @@ export interface RequestBody {
apiKey?: string; apiKey?: string;
maxIterations: number; maxIterations: number;
returnIntermediateSteps: boolean; returnIntermediateSteps: boolean;
updateTypes: boolean;
useTools: (undefined | string)[]; useTools: (undefined | string)[];
} }

View File

@@ -28,6 +28,7 @@ export interface LLMConfig {
stream?: boolean; stream?: boolean;
presence_penalty?: number; presence_penalty?: number;
frequency_penalty?: number; frequency_penalty?: number;
updateTypes?: boolean;
} }
export interface LLMAgentConfig { export interface LLMAgentConfig {

View File

@@ -79,6 +79,7 @@ export class ChatGPTApi implements LLMApi {
async chat(options: ChatOptions) { async chat(options: ChatOptions) {
const messages: any[] = []; const messages: any[] = [];
const accessStore = useAccessStore.getState();
const getImageBase64Data = async (url: string) => { const getImageBase64Data = async (url: string) => {
const response = await axios.get(url, { responseType: "arraybuffer" }); const response = await axios.get(url, { responseType: "arraybuffer" });
@@ -104,7 +105,7 @@ export class ChatGPTApi implements LLMApi {
}); });
if (v.image_url) { if (v.image_url) {
let image_url_data = ""; 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); var base64Data = await getImageBase64Data(v.image_url);
let mimeType: string | null; let mimeType: string | null;
try { try {
@@ -175,8 +176,16 @@ export class ChatGPTApi implements LLMApi {
// max_tokens: Math.max(modelConfig.max_tokens, 1024), // 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. // 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 shouldStream = !!options.config.stream;
const controller = new AbortController(); const controller = new AbortController();
@@ -190,6 +199,13 @@ export class ChatGPTApi implements LLMApi {
signal: controller.signal, signal: controller.signal,
headers: getHeaders(), 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 // make a fetch request
const requestTimeoutId = setTimeout( const requestTimeoutId = setTimeout(
@@ -347,6 +363,7 @@ export class ChatGPTApi implements LLMApi {
baseUrl: baseUrl, baseUrl: baseUrl,
maxIterations: options.agentConfig.maxIterations, maxIterations: options.agentConfig.maxIterations,
returnIntermediateSteps: options.agentConfig.returnIntermediateSteps, returnIntermediateSteps: options.agentConfig.returnIntermediateSteps,
updateTypes: modelConfig.updateTypes,
useTools: options.agentConfig.useTools, useTools: options.agentConfig.useTools,
}; };

View File

@@ -476,22 +476,38 @@ export function ChatActions(props: {
} }
const onImageSelected = async (e: any) => { const onImageSelected = async (e: any) => {
const file = e.target.files[0]; const files = e.target.files;
if (!file) return; if (!files.length) return;
const api = new ClientApi(); const api = new ClientApi();
// 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); setUploadLoading(true);
const uploadFile = await api.file const uploadFile = await api.file
.upload(file) .upload(file)
.catch((e) => { .catch((e) => {
console.error("[Upload]", e); console.error("[Upload]", e);
showToast(prettyObject(e)); showToast(prettyObject(e));
// return null if upload fails
return null;
}) })
.finally(() => setUploadLoading(false)); .finally(() => setUploadLoading(false));
if (uploadFile) {
// use a callback or event to inform about the new file
props.imageSelected({ props.imageSelected({
fileName: uploadFile.fileName, fileName: uploadFile.fileName,
fileUrl: uploadFile.filePath, 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 // switch model
@@ -518,7 +534,7 @@ export function ChatActions(props: {
const items = event.clipboardData?.items || []; const items = event.clipboardData?.items || [];
const api = new ClientApi(); const api = new ClientApi();
for (let i = 0; i < items.length; i++) { 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(); const file = items[i].getAsFile();
if (file !== null) { if (file !== null) {
setUploadLoading(true); setUploadLoading(true);
@@ -623,12 +639,13 @@ export function ChatActions(props: {
{(currentModel.includes("vision") || currentModel.includes("gizmo")) && ( {(currentModel.includes("vision") || currentModel.includes("gizmo")) && (
<ChatAction <ChatAction
onClick={selectImage} onClick={selectImage}
text="选择图片" text="选择文件"
loding={uploadLoading} loding={uploadLoading}
icon={<UploadIcon />} icon={<UploadIcon />}
innerNode={ innerNode={
<input <input
type="file" type="file"
multiple
accept="*/*" accept="*/*"
id="chat-image-file-select-upload" id="chat-image-file-select-upload"
style={{ display: "none" }} 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; if (userInput.trim() === "") return;
const matchCommand = chatCommands.match(userInput); const matchCommand = chatCommands.match(userInput);
if (matchCommand.matched) { if (matchCommand.matched) {
@@ -836,11 +853,16 @@ function _Chat() {
return; return;
} }
setIsLoading(true); setIsLoading(true);
const userImageUrls = userImages?.map(image => image.fileUrl) || [];
chatStore chatStore
.onUserInput(userInput, userImage?.fileUrl) .onUserInput(userInput, ...userImageUrls)
.then(() => setIsLoading(false)); .then(() => setIsLoading(false));
localStorage.setItem(LAST_INPUT_KEY, userInput); localStorage.setItem(LAST_INPUT_KEY, userInput);
localStorage.setItem(LAST_INPUT_IMAGE_KEY, userImage); localStorage.setItem(LAST_INPUT_IMAGE_KEY, JSON.stringify(userImages));
setUserInput(""); setUserInput("");
setPromptHints([]); setPromptHints([]);
setUserImage(null); setUserImage(null);
@@ -908,7 +930,13 @@ 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)); 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(); e.preventDefault();
return; return;
} }
@@ -1457,7 +1485,7 @@ function _Chat() {
onSearch(""); onSearch("");
}} }}
imageSelected={(img: any) => { imageSelected={(img: any) => {
setUserImage(img); setUserImage([...userImage, img]); // Add new image to the array
}} }}
/> />
<div className={styles["chat-input-panel-inner"]}> <div className={styles["chat-input-panel-inner"]}>
@@ -1477,8 +1505,8 @@ function _Chat() {
minHeight: textareaMinHeight, minHeight: textareaMinHeight,
}} }}
/> />
{userImage && ( {userImages.map((userImage, index) => (
<div className={styles["chat-input-image"]}> <div key={index} className={styles["chat-input-image"]}>
<div <div
style={{ position: "relative", width: "48px", height: "48px" }} style={{ position: "relative", width: "48px", height: "48px" }}
> >
@@ -1494,21 +1522,21 @@ function _Chat() {
</div> </div>
<button <button
className={styles["chat-input-image-close"]} className={styles["chat-input-image-close"]}
id="chat-input-image-close" id={`chat-input-image-close-${index}`}
onClick={() => { onClick={() => {
setUserImage(null); setUserImage(userImage.filter((_: any, i: any) => i !== index)); // Remove image
}} }}
> >
<CloseIcon /> <CloseIcon />
</button> </button>
</div> </div>
)} ))}
<IconButton <IconButton
icon={<SendWhiteIcon />} icon={<SendWhiteIcon />}
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, userImages)} // Pass the new array
/> />
</div> </div>
</div> </div>
@@ -1525,6 +1553,7 @@ function _Chat() {
/> />
)} )}
</div> </div>
); );
} }

View File

@@ -216,6 +216,25 @@ export function ModelConfigList(props: {
} }
></input> ></input>
</ListItem> </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>
</> </>
); );
} }

View File

@@ -359,6 +359,10 @@ const cn = {
Title: "频率惩罚度 (frequency_penalty)", Title: "频率惩罚度 (frequency_penalty)",
SubTitle: "值越大,越有可能降低重复字词", SubTitle: "值越大,越有可能降低重复字词",
}, },
UpdateType: {
Title: "上传类型",
SubTitle: "是否上传Base64格式消息",
},
Plugin: { Plugin: {
Enable: { Enable: {
Title: "启用插件", Title: "启用插件",

View File

@@ -365,6 +365,10 @@ const en: LocaleType = {
SubTitle: SubTitle:
"A larger value decreasing the likelihood to repeat the same line", "A larger value decreasing the likelihood to repeat the same line",
}, },
UpdateType: {
Title: "Upload type",
SubTitle: "Upload Base64 format message",
},
Plugin: { Plugin: {
Enable: { Enable: {
Title: "Enable Plugin", Title: "Enable Plugin",

View File

@@ -56,6 +56,7 @@ export const DEFAULT_CONFIG = {
historyMessageCount: 4, historyMessageCount: 4,
compressMessageLengthThreshold: 1000, compressMessageLengthThreshold: 1000,
enableInjectSystemPrompts: true, enableInjectSystemPrompts: true,
updateTypes: true,
template: DEFAULT_INPUT_TEMPLATE, template: DEFAULT_INPUT_TEMPLATE,
}, },
@@ -169,7 +170,6 @@ export const useAppConfig = createPersistStore(
if (version < 3.8) { if (version < 3.8) {
state.lastUpdate = Date.now(); state.lastUpdate = Date.now();
} }
return state as any; return state as any;
}, },
}, },

View File

@@ -3,7 +3,7 @@ services:
chatgpt-next-web: chatgpt-next-web:
profiles: [ "no-proxy" ] profiles: [ "no-proxy" ]
container_name: chatgpt-next-web container_name: chatgpt-next-web
image: gosuto/chatgpt-next-web-langchain image: yangclivia/chatgpt-next-web-langchain
volumes: volumes:
- next_chat_upload:/app/uploads - next_chat_upload:/app/uploads
ports: ports:
@@ -23,7 +23,7 @@ services:
chatgpt-next-web-proxy: chatgpt-next-web-proxy:
profiles: [ "proxy" ] profiles: [ "proxy" ]
container_name: chatgpt-next-web-proxy container_name: chatgpt-next-web-proxy
image: gosuto/chatgpt-next-web-langchain image: yangclivia/chatgpt-next-web-langchain
volumes: volumes:
- next_chat_upload:/app/uploads - next_chat_upload:/app/uploads
ports: ports: