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 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 ./

View File

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

View File

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

View File

@@ -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,
};

View File

@@ -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>
);
}

View File

@@ -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>
</>
);
}

View File

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

View File

@@ -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",

View File

@@ -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;
},
},

View File

@@ -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: