This commit is contained in:
GH Action - Upstream Sync 2024-05-17 00:27:17 +00:00
commit b9f8df1a29
12 changed files with 110 additions and 67 deletions

View File

@ -245,13 +245,17 @@ To control custom models, use `+` to add a custom model, use `-` to hide a model
User `-all` to disable all default models, `+all` to enable all default models. User `-all` to disable all default models, `+all` to enable all default models.
### `WHITE_WEBDEV_ENDPOINTS` (可选) ### `WHITE_WEBDEV_ENDPOINTS` (optional)
You can use this option if you want to increase the number of webdav service addresses you are allowed to access, as required by the format You can use this option if you want to increase the number of webdav service addresses you are allowed to access, as required by the format
- Each address must be a complete endpoint - Each address must be a complete endpoint
> `https://xxxx/yyy` > `https://xxxx/yyy`
- Multiple addresses are connected by ', ' - Multiple addresses are connected by ', '
### `DEFAULT_INPUT_TEMPLATE` (optional)
Customize the default template used to initialize the User Input Preprocessing configuration item in Settings.
## Requirements ## Requirements
NodeJS >= 18, Docker >= 20 NodeJS >= 18, Docker >= 20

View File

@ -156,6 +156,9 @@ anthropic claude Api Url.
用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。 用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。
### `DEFAULT_INPUT_TEMPLATE` (可选)
自定义默认的 template用于初始化『设置』中的『用户输入预处理』配置项
## 开发 ## 开发
点击下方按钮,开始二次开发: 点击下方按钮,开始二次开发:

View File

@ -59,9 +59,10 @@ import {
getMessageTextContent, getMessageTextContent,
getMessageImages, getMessageImages,
isVisionModel, isVisionModel,
compressImage,
} from "../utils"; } from "../utils";
import { compressImage } from "@/app/utils/chat";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { ChatControllerPool } from "../client/controller"; import { ChatControllerPool } from "../client/controller";

View File

@ -1,4 +1,5 @@
import tauriConfig from "../../src-tauri/tauri.conf.json"; import tauriConfig from "../../src-tauri/tauri.conf.json";
import { DEFAULT_INPUT_TEMPLATE } from "../constant";
export const getBuildConfig = () => { export const getBuildConfig = () => {
if (typeof process === "undefined") { if (typeof process === "undefined") {
@ -38,6 +39,7 @@ export const getBuildConfig = () => {
...commitInfo, ...commitInfo,
buildMode, buildMode,
isApp, isApp,
template: process.env.DEFAULT_INPUT_TEMPLATE ?? DEFAULT_INPUT_TEMPLATE,
}; };
}; };

View File

@ -34,6 +34,9 @@ declare global {
// google tag manager // google tag manager
GTM_ID?: string; GTM_ID?: string;
// custom template for preprocessing user input
DEFAULT_INPUT_TEMPLATE?: string;
} }
} }
} }

View File

@ -432,14 +432,13 @@ export const useChatStore = createPersistStore(
getMemoryPrompt() { getMemoryPrompt() {
const session = get().currentSession(); const session = get().currentSession();
if (session.memoryPrompt.length) {
return { return {
role: "system", role: "system",
content: content: Locale.Store.Prompt.History(session.memoryPrompt),
session.memoryPrompt.length > 0
? Locale.Store.Prompt.History(session.memoryPrompt)
: "",
date: "", date: "",
} as ChatMessage; } as ChatMessage;
}
}, },
getMessagesWithMemory() { getMessagesWithMemory() {
@ -475,16 +474,15 @@ export const useChatStore = createPersistStore(
systemPrompts.at(0)?.content ?? "empty", systemPrompts.at(0)?.content ?? "empty",
); );
} }
const memoryPrompt = get().getMemoryPrompt();
// long term memory // long term memory
const shouldSendLongTermMemory = const shouldSendLongTermMemory =
modelConfig.sendMemory && modelConfig.sendMemory &&
session.memoryPrompt && session.memoryPrompt &&
session.memoryPrompt.length > 0 && session.memoryPrompt.length > 0 &&
session.lastSummarizeIndex > clearContextIndex; session.lastSummarizeIndex > clearContextIndex;
const longTermMemoryPrompts = shouldSendLongTermMemory const longTermMemoryPrompts =
? [get().getMemoryPrompt()] shouldSendLongTermMemory && memoryPrompt ? [memoryPrompt] : [];
: [];
const longTermMemoryStartIndex = session.lastSummarizeIndex; const longTermMemoryStartIndex = session.lastSummarizeIndex;
// short term memory // short term memory
@ -609,9 +607,11 @@ export const useChatStore = createPersistStore(
Math.max(0, n - modelConfig.historyMessageCount), Math.max(0, n - modelConfig.historyMessageCount),
); );
} }
const memoryPrompt = get().getMemoryPrompt();
if (memoryPrompt) {
// add memory prompt // add memory prompt
toBeSummarizedMsgs.unshift(get().getMemoryPrompt()); toBeSummarizedMsgs.unshift(memoryPrompt);
}
const lastSummarizeIndex = session.messages.length; const lastSummarizeIndex = session.messages.length;

View File

@ -1,5 +1,4 @@
import { LLMModel } from "../client/api"; import { LLMModel } from "../client/api";
import { isMacOS } from "../utils";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { import {
DEFAULT_INPUT_TEMPLATE, DEFAULT_INPUT_TEMPLATE,
@ -25,6 +24,8 @@ export enum Theme {
Light = "light", Light = "light",
} }
const config = getClientConfig();
export const DEFAULT_CONFIG = { export const DEFAULT_CONFIG = {
lastUpdate: Date.now(), // timestamp, to merge state lastUpdate: Date.now(), // timestamp, to merge state
@ -32,7 +33,7 @@ export const DEFAULT_CONFIG = {
avatar: "1f603", avatar: "1f603",
fontSize: 14, fontSize: 14,
theme: Theme.Auto as Theme, theme: Theme.Auto as Theme,
tightBorder: !!getClientConfig()?.isApp, tightBorder: !!config?.isApp,
sendPreviewBubble: true, sendPreviewBubble: true,
enableAutoGenerateTitle: true, enableAutoGenerateTitle: true,
sidebarWidth: DEFAULT_SIDEBAR_WIDTH, sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
@ -56,7 +57,7 @@ export const DEFAULT_CONFIG = {
historyMessageCount: 64, historyMessageCount: 64,
compressMessageLengthThreshold: 10000, compressMessageLengthThreshold: 10000,
enableInjectSystemPrompts: true, enableInjectSystemPrompts: true,
template: DEFAULT_INPUT_TEMPLATE, template: config?.template ?? DEFAULT_INPUT_TEMPLATE,
}, },
}; };
@ -132,7 +133,7 @@ export const useAppConfig = createPersistStore(
}), }),
{ {
name: StoreKey.Config, name: StoreKey.Config,
version: 3.8, version: 3.9,
migrate(persistedState, version) { migrate(persistedState, version) {
const state = persistedState as ChatConfig; const state = persistedState as ChatConfig;
@ -163,6 +164,13 @@ export const useAppConfig = createPersistStore(
state.lastUpdate = Date.now(); state.lastUpdate = Date.now();
} }
if (version < 3.9) {
state.modelConfig.template =
state.modelConfig.template !== DEFAULT_INPUT_TEMPLATE
? state.modelConfig.template
: config?.template ?? DEFAULT_INPUT_TEMPLATE;
}
return state as any; return state as any;
}, },
}, },

View File

@ -83,48 +83,6 @@ export async function downloadAs(text: string, filename: string) {
} }
} }
export function compressImage(file: File, maxSize: number): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (readerEvent: any) => {
const image = new Image();
image.onload = () => {
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
let width = image.width;
let height = image.height;
let quality = 0.9;
let dataUrl;
do {
canvas.width = width;
canvas.height = height;
ctx?.clearRect(0, 0, canvas.width, canvas.height);
ctx?.drawImage(image, 0, 0, width, height);
dataUrl = canvas.toDataURL("image/jpeg", quality);
if (dataUrl.length < maxSize) break;
if (quality > 0.5) {
// Prioritize quality reduction
quality -= 0.1;
} else {
// Then reduce the size
width *= 0.9;
height *= 0.9;
}
} while (dataUrl.length > maxSize);
resolve(dataUrl);
};
image.onerror = reject;
image.src = readerEvent.target.result;
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
export function readFromFile() { export function readFromFile() {
return new Promise<string>((res, rej) => { return new Promise<string>((res, rej) => {
const fileInput = document.createElement("input"); const fileInput = document.createElement("input");
@ -297,6 +255,7 @@ export function isVisionModel(model: string) {
"claude-3", "claude-3",
"gemini-1.5-pro", "gemini-1.5-pro",
"gemini-1.5-flash", "gemini-1.5-flash",
"gpt-4o",
]; ];
const isGpt4Turbo = const isGpt4Turbo =
model.includes("gpt-4-turbo") && !model.includes("preview"); model.includes("gpt-4-turbo") && !model.includes("preview");

54
app/utils/chat.ts Normal file
View File

@ -0,0 +1,54 @@
import heic2any from "heic2any";
export function compressImage(file: File, maxSize: number): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (readerEvent: any) => {
const image = new Image();
image.onload = () => {
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
let width = image.width;
let height = image.height;
let quality = 0.9;
let dataUrl;
do {
canvas.width = width;
canvas.height = height;
ctx?.clearRect(0, 0, canvas.width, canvas.height);
ctx?.drawImage(image, 0, 0, width, height);
dataUrl = canvas.toDataURL("image/jpeg", quality);
if (dataUrl.length < maxSize) break;
if (quality > 0.5) {
// Prioritize quality reduction
quality -= 0.1;
} else {
// Then reduce the size
width *= 0.9;
height *= 0.9;
}
} while (dataUrl.length > maxSize);
resolve(dataUrl);
};
image.onerror = reject;
image.src = readerEvent.target.result;
};
reader.onerror = reject;
if (file.type.includes("heic")) {
heic2any({ blob: file, toType: "image/jpeg" })
.then((blob) => {
reader.readAsDataURL(blob as Blob);
})
.catch((e) => {
reject(e);
});
}
reader.readAsDataURL(file);
});
}

View File

@ -93,14 +93,17 @@ export function createUpstashClient(store: SyncStore) {
} }
let url; let url;
if (proxyUrl.length > 0 || proxyUrl === "/") { const pathPrefix = "/api/upstash/";
let u = new URL(proxyUrl + "/api/upstash/" + path);
try {
let u = new URL(proxyUrl + pathPrefix + path);
// add query params // add query params
u.searchParams.append("endpoint", config.endpoint); u.searchParams.append("endpoint", config.endpoint);
url = u.toString(); url = u.toString();
} else { } catch (e) {
url = "/api/upstash/" + path + "?endpoint=" + config.endpoint; url = pathPrefix + path + "?endpoint=" + config.endpoint;
} }
return url; return url;
}, },
}; };

View File

@ -24,6 +24,7 @@
"@vercel/speed-insights": "^1.0.2", "@vercel/speed-insights": "^1.0.2",
"emoji-picker-react": "^4.9.2", "emoji-picker-react": "^4.9.2",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"heic2any": "^0.0.4",
"html-to-image": "^1.11.11", "html-to-image": "^1.11.11",
"mermaid": "^10.6.1", "mermaid": "^10.6.1",
"nanoid": "^5.0.3", "nanoid": "^5.0.3",

View File

@ -3669,6 +3669,11 @@ heap@^0.2.6:
resolved "https://registry.npmmirror.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" resolved "https://registry.npmmirror.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc"
integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==
heic2any@^0.0.4:
version "0.0.4"
resolved "https://registry.npmmirror.com/heic2any/-/heic2any-0.0.4.tgz#eddb8e6fec53c8583a6e18b65069bb5e8d19028a"
integrity sha512-3lLnZiDELfabVH87htnRolZ2iehX9zwpRyGNz22GKXIu0fznlblf0/ftppXKNqS26dqFSeqfIBhAmAj/uSp0cA==
highlight.js@~11.7.0: highlight.js@~11.7.0:
version "11.7.0" version "11.7.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.7.0.tgz#3ff0165bc843f8c9bce1fd89e2fda9143d24b11e" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.7.0.tgz#3ff0165bc843f8c9bce1fd89e2fda9143d24b11e"