diff --git a/CHANGELOG.md b/CHANGELOG.md index 97aa264e31e28..75014afba8ea8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,74 @@ # Changelog +### [Version 1.46.7](https://github.com/lobehub/lobe-chat/compare/v1.46.6...v1.46.7) + +Released on **2025-01-17** + +#### 🐛 Bug Fixes + +- **misc**: Improve validation for provider and model in parseFilesConfig, temporarily disable S3 client integrity check for Cloudflare R2. + +
+ +
+Improvements and Fixes + +#### What's fixed + +- **misc**: Improve validation for provider and model in parseFilesConfig, closes [#5454](https://github.com/lobehub/lobe-chat/issues/5454) ([b4808f8](https://github.com/lobehub/lobe-chat/commit/b4808f8)) +- **misc**: Temporarily disable S3 client integrity check for Cloudflare R2, closes [#5479](https://github.com/lobehub/lobe-chat/issues/5479) ([a638238](https://github.com/lobehub/lobe-chat/commit/a638238)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ +### [Version 1.46.6](https://github.com/lobehub/lobe-chat/compare/v1.46.5...v1.46.6) + +Released on **2025-01-16** + +#### 🐛 Bug Fixes + +- **misc**: Gemini models HarmBlockThreshold. + +
+ +
+Improvements and Fixes + +#### What's fixed + +- **misc**: Gemini models HarmBlockThreshold, closes [#5477](https://github.com/lobehub/lobe-chat/issues/5477) ([f98375c](https://github.com/lobehub/lobe-chat/commit/f98375c)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ +### [Version 1.46.5](https://github.com/lobehub/lobe-chat/compare/v1.46.4...v1.46.5) + +Released on **2025-01-16** + +
+ +
+Improvements and Fixes + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ ### [Version 1.46.4](https://github.com/lobehub/lobe-chat/compare/v1.46.3...v1.46.4) Released on **2025-01-16** diff --git a/changelog/v1.json b/changelog/v1.json index 4cf588ab80406..f80d570b54d2f 100644 --- a/changelog/v1.json +++ b/changelog/v1.json @@ -1,4 +1,25 @@ [ + { + "children": { + "fixes": [ + "Improve validation for provider and model in parseFilesConfig, temporarily disable S3 client integrity check for Cloudflare R2." + ] + }, + "date": "2025-01-17", + "version": "1.46.7" + }, + { + "children": { + "fixes": ["Gemini models HarmBlockThreshold."] + }, + "date": "2025-01-16", + "version": "1.46.6" + }, + { + "children": {}, + "date": "2025-01-16", + "version": "1.46.5" + }, { "children": { "improvements": ["Refactor some implement for the next performance improvement."] diff --git a/netlify.toml b/netlify.toml index 411a59cac5441..0546e7e2c2ee5 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,5 +1,5 @@ [build] -command = "pnpm run build" +command = "rm -rf .next node_modules/.cache && pnpm run build" publish = ".next" [build.environment] diff --git a/package.json b/package.json index 57cb45d2b9262..c5148745550a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lobehub/chat", - "version": "1.46.4", + "version": "1.46.7", "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.", "keywords": [ "framework", diff --git a/src/const/settings/knowledge.ts b/src/const/settings/knowledge.ts index 7593358525ced..de74f4929b707 100644 --- a/src/const/settings/knowledge.ts +++ b/src/const/settings/knowledge.ts @@ -20,6 +20,6 @@ export const DEFAULT_FILE_RERANK_MODEL_ITEM: FilesConfigItem = { export const DEFAULT_FILES_CONFIG: FilesConfig = { embeddingModel: DEFAULT_FILE_EMBEDDING_MODEL_ITEM, - queryModel: DEFAULT_RERANK_QUERY_MODE, + queryMode: DEFAULT_RERANK_QUERY_MODE, rerankerModel: DEFAULT_FILE_RERANK_MODEL_ITEM, }; diff --git a/src/libs/agent-runtime/google/index.ts b/src/libs/agent-runtime/google/index.ts index 55a8f99474df2..3354a3a8bf35d 100644 --- a/src/libs/agent-runtime/google/index.ts +++ b/src/libs/agent-runtime/google/index.ts @@ -36,7 +36,14 @@ enum HarmCategory { enum HarmBlockThreshold { BLOCK_NONE = 'BLOCK_NONE', - OFF = 'OFF', // https://discuss.ai.google.dev/t/59352 +} + +function getThreshold(model: string): HarmBlockThreshold { + const useOFF = ['gemini-2.0-flash-exp']; + if (useOFF.includes(model)) { + return 'OFF' as HarmBlockThreshold; // https://discuss.ai.google.dev/t/59352 + } + return HarmBlockThreshold.BLOCK_NONE; } export class LobeGoogleAI implements LobeRuntimeAI { @@ -71,19 +78,19 @@ export class LobeGoogleAI implements LobeRuntimeAI { safetySettings: [ { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, - threshold: model.includes('2.0') ? (HarmBlockThreshold.OFF as any) : HarmBlockThreshold.BLOCK_NONE, + threshold: getThreshold(model), }, { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, - threshold: model.includes('2.0') ? (HarmBlockThreshold.OFF as any) : HarmBlockThreshold.BLOCK_NONE, + threshold: getThreshold(model), }, { category: HarmCategory.HARM_CATEGORY_HARASSMENT, - threshold: model.includes('2.0') ? (HarmBlockThreshold.OFF as any) : HarmBlockThreshold.BLOCK_NONE, + threshold: getThreshold(model), }, { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, - threshold: model.includes('2.0') ? (HarmBlockThreshold.OFF as any) : HarmBlockThreshold.BLOCK_NONE, + threshold: getThreshold(model), }, ], }, diff --git a/src/server/globalConfig/parseFilesConfig.test.ts b/src/server/globalConfig/parseFilesConfig.test.ts index c001ead1748ca..798d70163d1b0 100644 --- a/src/server/globalConfig/parseFilesConfig.test.ts +++ b/src/server/globalConfig/parseFilesConfig.test.ts @@ -3,15 +3,134 @@ import { describe, expect, it } from 'vitest'; import { parseFilesConfig } from './parseFilesConfig'; describe('parseFilesConfig', () => { + it('parses full configuration correctly', () => { + const envStr = + 'embedding_model=openai/embedding-text-3-small,reranker_model=cohere/rerank-english-v3.0,query_mode=full_text'; + const expected = { + embeddingModel: { provider: 'openai', model: 'embedding-text-3-small' }, + rerankerModel: { provider: 'cohere', model: 'rerank-english-v3.0' }, + queryMode: 'full_text', + }; + expect(parseFilesConfig(envStr)).toEqual(expected); + }); + // 测试embeddings配置是否被正确解析 it('parses embeddings configuration correctly', () => { - const envStr = - 'embedding_model=openai/embedding-text-3-large,reranker_model=cohere/rerank-english-v3.0,query_model=full_text'; + const envStr = 'embedding_model=openai/embedding-text-3-large'; const expected = { embeddingModel: { provider: 'openai', model: 'embedding-text-3-large' }, + }; + expect(parseFilesConfig(envStr)).toEqual(expected); + }); + + it('parses rerank configuration correctly', () => { + const envStr = 'reranker_model=cohere/rerank-english-v3.0'; + const expected = { + rerankerModel: { provider: 'cohere', model: 'rerank-english-v3.0' }, + }; + expect(parseFilesConfig(envStr)).toEqual(expected); + }); + + it('parses queryMode configuration correctly', () => { + const envStr = 'query_mode=full_text'; + const expected = { + queryMode: 'full_text', + }; + expect(parseFilesConfig(envStr)).toEqual(expected); + }); + + it('parses queryMode rerank configuration correctly', () => { + const envStr = 'reranker_model=cohere/rerank-english-v3.0,query_mode=full_text'; + const expected = { + queryMode: 'full_text', + rerankerModel: { provider: 'cohere', model: 'rerank-english-v3.0' }, + }; + expect(parseFilesConfig(envStr)).toEqual(expected); + }); + + it('parses queryMode embeddings configuration correctly', () => { + const envStr = 'embedding_model=openai/embedding-text-3-small,query_mode=full_text'; + const expected = { + queryMode: 'full_text', + embeddingModel: { provider: 'openai', model: 'embedding-text-3-small' }, + }; + expect(parseFilesConfig(envStr)).toEqual(expected); + }); + + it('parses rerank embeddings configuration correctly', () => { + const envStr = + 'reranker_model=cohere/rerank-english-v3.0,embedding_model=openai/embedding-text-3-small'; + const expected = { + embeddingModel: { provider: 'openai', model: 'embedding-text-3-small' }, rerankerModel: { provider: 'cohere', model: 'rerank-english-v3.0' }, - queryModel: 'full_text', }; expect(parseFilesConfig(envStr)).toEqual(expected); }); + + it('should throw an error for invalid embedding_model format', () => { + const envStr = + 'reranker_model=cohere/rerank-english-v3.0,embedding_model=/embedding-text-3-small'; + expect(() => { + parseFilesConfig(envStr); + }).toThrow( + new Error( + 'Invalid environment variable format. expected of the form embedding_model=provider/model', + ), + ); + }); + + it('should throw an error for invalid embedding_model format', () => { + const envStr = 'reranker_model=cohere/rerank-english-v3.0,embedding_model=openai'; + expect(() => { + parseFilesConfig(envStr); + }).toThrow( + new Error( + 'Invalid environment variable format. expected of the form embedding_model=provider/model', + ), + ); + }); + + it('should throw an error for invalid embedding_model format', () => { + const envStr = 'reranker_model=cohere/rerank-english-v3.0,embedding_model='; + expect(() => { + parseFilesConfig(envStr); + }).toThrowError(new Error('Invalid environment variable format.')); + }); + + it('should throw an error for invalid reranker_model format', () => { + const envStr = + 'reranker_model=/rerank-english-v3.0,embedding_model=openai/embedding-text-3-small'; + expect(() => { + parseFilesConfig(envStr); + }).toThrow( + new Error( + 'Invalid environment variable format. expected of the form reranker_model=provider/model', + ), + ); + }); + + it('should throw an error for invalid reranker_model format', () => { + const envStr = 'reranker_model=cohere/,embedding_model=openai/embedding-text-3-small'; + expect(() => { + parseFilesConfig(envStr); + }).toThrow( + new Error( + 'Invalid environment variable format. expected of the form reranker_model=provider/model', + ), + ); + }); + + it('should throw an error for invalid reranker_model format', () => { + const envStr = 'reranker_model=,embedding_model=openai/embedding-text-3-small'; + expect(() => { + parseFilesConfig(envStr); + }).toThrow(new Error('Invalid environment variable format.')); + }); + + it('should throw an error for invalid query_mode format', () => { + const envStr = 'query_mode='; + expect(() => { + parseFilesConfig(envStr); + }).toThrow(new Error('Invalid environment variable format.')); + }); }); diff --git a/src/server/globalConfig/parseFilesConfig.ts b/src/server/globalConfig/parseFilesConfig.ts index 7175b3206448f..bea497b772bb9 100644 --- a/src/server/globalConfig/parseFilesConfig.ts +++ b/src/server/globalConfig/parseFilesConfig.ts @@ -4,7 +4,7 @@ import { FilesConfig } from '@/types/user/settings/filesConfig'; const protectedKeys = Object.keys({ embedding_model: null, - query_model: null, + query_mode: null, reranker_model: null, }); @@ -24,34 +24,40 @@ export const parseFilesConfig = (envString: string = ''): SystemEmbeddingConfig const [provider, ...modelParts] = value.split('/'); const model = modelParts.join('/'); - if ((!provider || !model) && key !== 'query_model') { - throw new Error('Missing model or provider value'); - } - - if (key === 'query_model' && value === '') { - throw new Error('Missing query mode value'); - } - if (protectedKeys.includes(key)) { switch (key) { case 'embedding_model': { + if (!provider || !model) { + throw new Error( + 'Invalid environment variable format. expected of the form embedding_model=provider/model', + ); + } config.embeddingModel = { model: model.trim(), provider: provider.trim() }; break; } case 'reranker_model': { + if (!provider || !model) { + throw new Error( + 'Invalid environment variable format. expected of the form reranker_model=provider/model', + ); + } config.rerankerModel = { model: model.trim(), provider: provider.trim() }; break; } - case 'query_model': { - config.queryModel = value; + case 'query_mode': { + config.queryMode = value; break; } + default: { + throw new Error( + 'Invalid environment variable format. expected one of embedding_model, reranker_model, query_mode', + ); + } } } } else { - throw new Error('Invalid environment variable format'); + throw new Error('Invalid environment variable format.'); } } - return config; }; diff --git a/src/server/modules/S3/index.ts b/src/server/modules/S3/index.ts index 10bc512472ca8..07f09cd95a512 100644 --- a/src/server/modules/S3/index.ts +++ b/src/server/modules/S3/index.ts @@ -44,6 +44,9 @@ export class S3 { endpoint: fileEnv.S3_ENDPOINT, forcePathStyle: fileEnv.S3_ENABLE_PATH_STYLE, region: fileEnv.S3_REGION || DEFAULT_S3_REGION, + // refs: https://github.com/lobehub/lobe-chat/pull/5479 + requestChecksumCalculation: 'WHEN_REQUIRED', + responseChecksumValidation: 'WHEN_REQUIRED', }); } diff --git a/src/types/knowledgeBase/index.ts b/src/types/knowledgeBase/index.ts index 20355071c9ff2..d87319f1222e5 100644 --- a/src/types/knowledgeBase/index.ts +++ b/src/types/knowledgeBase/index.ts @@ -48,6 +48,6 @@ export interface KnowledgeItem { export interface SystemEmbeddingConfig { embeddingModel: FilesConfigItem; - queryModel: string; + queryMode: string; rerankerModel: FilesConfigItem; } diff --git a/src/types/user/settings/filesConfig.ts b/src/types/user/settings/filesConfig.ts index ecfb3b6e3290e..77c35c8dbd578 100644 --- a/src/types/user/settings/filesConfig.ts +++ b/src/types/user/settings/filesConfig.ts @@ -4,6 +4,6 @@ export interface FilesConfigItem { } export interface FilesConfig { embeddingModel: FilesConfigItem; - queryModel: string; + queryMode: string; rerankerModel: FilesConfigItem; } diff --git a/vercel.json b/vercel.json index 78c7b37970236..3c364a1878bb4 100644 --- a/vercel.json +++ b/vercel.json @@ -1,3 +1,4 @@ { + "buildCommand": "NODE_OPTIONS=--max-old-space-size=6144 next build", "installCommand": "bun install" }