Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
c5c3fc1cc2
|
@ -281,13 +281,16 @@ anthropic claude Api Url.
|
||||||
|
|
||||||
如果你想禁用从链接解析预制设置,将此环境变量设置为 1 即可。
|
如果你想禁用从链接解析预制设置,将此环境变量设置为 1 即可。
|
||||||
|
|
||||||
### `WHITE_WEBDEV_ENDPOINTS` (可选)
|
### `WHITE_WEBDEV_ENDPOINTS` (optional)
|
||||||
|
|
||||||
如果你想增加允许访问的webdav服务地址,可以使用该选项,格式要求:
|
如果你想增加允许访问的webdav服务地址,可以使用该选项,格式要求:
|
||||||
- 每一个地址必须是一个完整的 endpoint
|
- 每一个地址必须是一个完整的 endpoint
|
||||||
> `https://xxxx/xxx`
|
> `https://xxxx/xxx`
|
||||||
- 多个地址以`,`相连
|
- 多个地址以`,`相连
|
||||||
|
|
||||||
|
### `DEFAULT_INPUT_TEMPLATE` (可选)
|
||||||
|
自定义默认的 template,用于初始化『设置』中的『用户输入预处理』配置项
|
||||||
|
|
||||||
## 部署
|
## 部署
|
||||||
|
|
||||||
### 容器部署 (推荐)
|
### 容器部署 (推荐)
|
||||||
|
|
|
@ -156,6 +156,9 @@ anthropic claude Api Url.
|
||||||
|
|
||||||
用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。
|
用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。
|
||||||
|
|
||||||
|
### `DEFAULT_INPUT_TEMPLATE` (可选)
|
||||||
|
自定义默认的 template,用于初始化『设置』中的『用户输入预处理』配置项
|
||||||
|
|
||||||
## 开发
|
## 开发
|
||||||
|
|
||||||
点击下方按钮,开始二次开发:
|
点击下方按钮,开始二次开发:
|
||||||
|
|
|
@ -67,11 +67,12 @@ import {
|
||||||
getMessageTextContent,
|
getMessageTextContent,
|
||||||
getMessageImages,
|
getMessageImages,
|
||||||
isVisionModel,
|
isVisionModel,
|
||||||
compressImage,
|
|
||||||
isFirefox,
|
isFirefox,
|
||||||
isSupportRAGModel,
|
isSupportRAGModel,
|
||||||
} 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";
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -548,14 +548,13 @@ export const useChatStore = createPersistStore(
|
||||||
getMemoryPrompt() {
|
getMemoryPrompt() {
|
||||||
const session = get().currentSession();
|
const session = get().currentSession();
|
||||||
|
|
||||||
return {
|
if (session.memoryPrompt.length) {
|
||||||
role: "system",
|
return {
|
||||||
content:
|
role: "system",
|
||||||
session.memoryPrompt.length > 0
|
content: Locale.Store.Prompt.History(session.memoryPrompt),
|
||||||
? Locale.Store.Prompt.History(session.memoryPrompt)
|
date: "",
|
||||||
: "",
|
} as ChatMessage;
|
||||||
date: "",
|
}
|
||||||
} as ChatMessage;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getMessagesWithMemory() {
|
getMessagesWithMemory() {
|
||||||
|
@ -591,16 +590,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
|
||||||
|
@ -726,9 +724,11 @@ export const useChatStore = createPersistStore(
|
||||||
Math.max(0, n - modelConfig.historyMessageCount),
|
Math.max(0, n - modelConfig.historyMessageCount),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
const memoryPrompt = get().getMemoryPrompt();
|
||||||
// add memory prompt
|
if (memoryPrompt) {
|
||||||
toBeSummarizedMsgs.unshift(get().getMemoryPrompt());
|
// add memory prompt
|
||||||
|
toBeSummarizedMsgs.unshift(memoryPrompt);
|
||||||
|
}
|
||||||
|
|
||||||
const lastSummarizeIndex = session.messages.length;
|
const lastSummarizeIndex = session.messages.length;
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -35,6 +34,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
|
||||||
|
|
||||||
|
@ -42,7 +43,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,
|
||||||
|
@ -66,7 +67,7 @@ export const DEFAULT_CONFIG = {
|
||||||
historyMessageCount: 4,
|
historyMessageCount: 4,
|
||||||
compressMessageLengthThreshold: 1000,
|
compressMessageLengthThreshold: 1000,
|
||||||
enableInjectSystemPrompts: true,
|
enableInjectSystemPrompts: true,
|
||||||
template: DEFAULT_INPUT_TEMPLATE,
|
template: config?.template ?? DEFAULT_INPUT_TEMPLATE,
|
||||||
},
|
},
|
||||||
|
|
||||||
pluginConfig: {
|
pluginConfig: {
|
||||||
|
@ -182,7 +183,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;
|
||||||
|
|
||||||
|
@ -213,6 +214,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;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
44
app/utils.ts
44
app/utils.ts
|
@ -84,48 +84,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");
|
||||||
|
@ -298,7 +256,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"
|
"gpt-4o",
|
||||||
];
|
];
|
||||||
const isGpt4Turbo =
|
const isGpt4Turbo =
|
||||||
model.includes("gpt-4-turbo") && !model.includes("preview");
|
model.includes("gpt-4-turbo") && !model.includes("preview");
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
|
@ -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;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
"epub2": "^3.0.2",
|
"epub2": "^3.0.2",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"html-entities": "^2.4.0",
|
"html-entities": "^2.4.0",
|
||||||
|
"heic2any": "^0.0.4",
|
||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.11",
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
"https-proxy-agent": "^7.0.2",
|
"https-proxy-agent": "^7.0.2",
|
||||||
|
|
|
@ -5548,9 +5548,14 @@ heap@^0.2.6:
|
||||||
resolved "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz"
|
resolved "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz"
|
||||||
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.8.0:
|
highlight.js@~11.8.0:
|
||||||
version "11.8.0"
|
version "11.8.0"
|
||||||
resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-11.8.0.tgz"
|
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.8.0.tgz#966518ea83257bae2e7c9a48596231856555bb65"
|
||||||
integrity sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==
|
integrity sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==
|
||||||
|
|
||||||
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
|
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
|
||||||
|
|
Loading…
Reference in New Issue