diff --git a/CHANGELOG.md b/CHANGELOG.md index 0007b550060..cbfa71bcf7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,74 @@ # Changelog +### [Version 1.141.1](https://github.com/lobehub/lobe-chat/compare/v1.141.0...v1.141.1) + +Released on **2025-10-21** + +#### ♻ Code Refactoring + +- **misc**: Refactor context engine. + +
+ +
+Improvements and Fixes + +#### Code refactoring + +- **misc**: Refactor context engine, closes [#9821](https://github.com/lobehub/lobe-chat/issues/9821) ([e99f12f](https://github.com/lobehub/lobe-chat/commit/e99f12f)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ +## [Version 1.141.0](https://github.com/lobehub/lobe-chat/compare/v1.140.0...v1.141.0) + +Released on **2025-10-21** + +#### ✨ Features + +- **misc**: Add PDF export functionality to share modal. + +#### 🐛 Bug Fixes + +- **misc**: Ignore abort signal errors in TRPC client, slove when pwa user info have code cannot be viewed in full. + +#### 💄 Styles + +- **misc**: Add knowledge base mansory layout \[LOB-496], improve rich text link display. + +
+ +
+Improvements and Fixes + +#### What's improved + +- **misc**: Add PDF export functionality to share modal, closes [#9300](https://github.com/lobehub/lobe-chat/issues/9300) [#9299](https://github.com/lobehub/lobe-chat/issues/9299) ([2b7761c](https://github.com/lobehub/lobe-chat/commit/2b7761c)) + +#### What's fixed + +- **misc**: Ignore abort signal errors in TRPC client, closes [#9809](https://github.com/lobehub/lobe-chat/issues/9809) [#9401](https://github.com/lobehub/lobe-chat/issues/9401) ([7f7dcfb](https://github.com/lobehub/lobe-chat/commit/7f7dcfb)) +- **misc**: Slove when pwa user info have code cannot be viewed in full, closes [#9817](https://github.com/lobehub/lobe-chat/issues/9817) ([6734a47](https://github.com/lobehub/lobe-chat/commit/6734a47)) + +#### Styles + +- **misc**: Add knowledge base mansory layout \[LOB-496], closes [#9722](https://github.com/lobehub/lobe-chat/issues/9722) ([69f21da](https://github.com/lobehub/lobe-chat/commit/69f21da)) +- **misc**: Improve rich text link display, closes [#9816](https://github.com/lobehub/lobe-chat/issues/9816) ([af33543](https://github.com/lobehub/lobe-chat/commit/af33543)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ ## [Version 1.140.0](https://github.com/lobehub/lobe-chat/compare/v1.139.5...v1.140.0) Released on **2025-10-21** diff --git a/changelog/v1.json b/changelog/v1.json index 9f3e43ea1a0..d7ee3aa1913 100644 --- a/changelog/v1.json +++ b/changelog/v1.json @@ -1,4 +1,24 @@ [ + { + "children": { + "improvements": ["Refactor context engine."] + }, + "date": "2025-10-21", + "version": "1.141.1" + }, + { + "children": { + "features": ["Add PDF export functionality to share modal."], + "fixes": [ + "Ignore abort signal errors in TRPC client, slove when pwa user info have code cannot be viewed in full." + ], + "improvements": [ + "Add knowledge base mansory layout [LOB-496], improve rich text link display." + ] + }, + "date": "2025-10-21", + "version": "1.141.0" + }, { "children": { "features": ["Add ComfyUI integration Phase1(RFC-128)."] diff --git a/package.json b/package.json index 2a1b4e0f82f..ab41c4ce5dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lobehub/chat", - "version": "1.140.0", + "version": "1.141.1", "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", @@ -168,13 +168,13 @@ "@lobehub/icons": "^2.42.0", "@lobehub/market-sdk": "^0.22.7", "@lobehub/tts": "^2.0.1", - "@react-pdf/renderer": "^4.3.0", "@lobehub/ui": "^2.13.2", "@modelcontextprotocol/sdk": "^1.20.0", "@neondatabase/serverless": "^1.0.2", "@next/third-parties": "^15.5.4", "@opentelemetry/exporter-jaeger": "^2.1.0", "@opentelemetry/winston-transport": "^0.17.0", + "@react-pdf/renderer": "^4.3.0", "@react-spring/web": "^9.7.5", "@saintno/comfyui-sdk": "^0.2.48", "@serwist/next": "^9.2.1", @@ -399,4 +399,4 @@ "mdast-util-gfm-autolink-literal": "2.0.0" } } -} \ No newline at end of file +} diff --git a/packages/context-engine/src/__tests__/pipeline.test.ts b/packages/context-engine/src/__tests__/pipeline.test.ts index ab2330d1d1d..71eac67f3da 100644 --- a/packages/context-engine/src/__tests__/pipeline.test.ts +++ b/packages/context-engine/src/__tests__/pipeline.test.ts @@ -36,19 +36,9 @@ describe('ContextEngine', () => { }); const createInitialContext = (): { - initialState: any; - maxTokens: number; messages: any[]; - model: string; } => ({ - initialState: { - messages: [], - model: 'test-model', - provider: 'test-provider', - }, - maxTokens: 4000, messages: [{ content: 'test', role: 'user' }], - model: 'test-model', }); describe('constructor', () => { @@ -206,8 +196,7 @@ describe('ContextEngine', () => { const processor = createMockProcessor('p1'); const engine = new ContextEngine({ pipeline: [processor] }); - const input = { ...createInitialContext(), messages: undefined }; - const result = await engine.process(input); + const result = await engine.process({ messages: [] }); expect(result.messages).toEqual([]); }); @@ -216,19 +205,14 @@ describe('ContextEngine', () => { const processor: ContextProcessor = { name: 'test', process: vi.fn(async (context) => { - expect(context.metadata.maxTokens).toBe(4000); - expect(context.metadata.model).toBe('test-model'); - expect(context.metadata.customKey).toBe('customValue'); + expect(context.metadata).toBeDefined(); return context; }), }; const engine = new ContextEngine({ pipeline: [processor] }); - await engine.process({ - ...createInitialContext(), - metadata: { customKey: 'customValue' }, - }); + await engine.process(createInitialContext()); }); it('should track execution stats', async () => { @@ -278,12 +262,7 @@ describe('ContextEngine', () => { pipeline: [processor1, processor2], }); - const input = { - ...createInitialContext(), - }; - input.initialState = { ...input.initialState, messages: [] }; - - const result = await engine.process(input); + const result = await engine.process(createInitialContext()); expect(result.isAborted).toBe(true); expect(result.stats.processedCount).toBe(1); @@ -333,16 +312,17 @@ describe('ContextEngine', () => { }); it('should preserve initial state', async () => { + const testContext = createInitialContext(); const processor: ContextProcessor = { name: 'test', process: vi.fn(async (context) => { - expect(context.initialState).toEqual(createInitialContext().initialState); + expect(context.initialState.messages).toEqual(testContext.messages); return context; }), }; const engine = new ContextEngine({ pipeline: [processor] }); - await engine.process(createInitialContext()); + await engine.process(testContext); }); }); diff --git a/packages/context-engine/src/pipeline.ts b/packages/context-engine/src/pipeline.ts index d629f124923..a020866840c 100644 --- a/packages/context-engine/src/pipeline.ts +++ b/packages/context-engine/src/pipeline.ts @@ -1,12 +1,6 @@ import debug from 'debug'; -import type { - AgentState, - ContextProcessor, - PipelineContext, - PipelineResult, - ProcessorOptions, -} from './types'; +import type { ContextProcessor, PipelineContext, PipelineResult, ProcessorOptions } from './types'; import { PipelineError } from './types'; const log = debug('context-engine:ContextEngine'); @@ -70,26 +64,16 @@ export class ContextEngine { /** * Execute pipeline processing */ - async process(input: { - initialState: AgentState; - maxTokens: number; - messages?: Array; - metadata?: Record; - model: string; - }): Promise { + async process(input: { messages: Array }): Promise { const startTime = Date.now(); const processorDurations: Record = {}; // Create initial pipeline context let context: PipelineContext = { - initialState: input.initialState, + initialState: { messages: input.messages }, isAborted: false, - messages: Array.isArray(input.messages) ? [...input.messages] : [], - metadata: { - maxTokens: input.maxTokens, - model: input.model, - ...input.metadata, - }, + messages: [...input.messages], + metadata: {}, }; log('Starting pipeline processing'); diff --git a/packages/context-engine/src/types.ts b/packages/context-engine/src/types.ts index 95e757e3ef4..373e8590cee 100644 --- a/packages/context-engine/src/types.ts +++ b/packages/context-engine/src/types.ts @@ -60,9 +60,9 @@ export interface PipelineContext { /** 当前 token 估算值 */ currentTokenCount?: number; /** 最大 token 限制 */ - maxTokens: number; + maxTokens?: number; /** 模型标识 */ - model: string; + model?: string; }; } diff --git a/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Tags/index.tsx b/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Tags/index.tsx index c2d8f535d2b..72c98c85212 100644 --- a/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Tags/index.tsx +++ b/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Tags/index.tsx @@ -28,7 +28,7 @@ const TitleTags = memo(() => { agentSelectors.isAgentConfigLoading(s), ]); - const plugins = useAgentStore(agentSelectors.currentAgentPlugins, isEqual); + const plugins = useAgentStore(agentSelectors.displayableAgentPlugins, isEqual); const enabledKnowledge = useAgentStore(agentSelectors.currentEnabledKnowledge, isEqual); const enableHistoryCount = useAgentStore(agentChatConfigSelectors.enableHistoryCount); diff --git a/src/features/Conversation/components/ShareMessageModal/ShareImage/Preview.tsx b/src/features/Conversation/components/ShareMessageModal/ShareImage/Preview.tsx index 238413d3ab1..2d98f02f4ba 100644 --- a/src/features/Conversation/components/ShareMessageModal/ShareImage/Preview.tsx +++ b/src/features/Conversation/components/ShareMessageModal/ShareImage/Preview.tsx @@ -29,7 +29,7 @@ const Preview = memo( ({ title, withBackground, withFooter, message, previewId = 'preview' }) => { const [model, plugins] = useAgentStore((s) => [ agentSelectors.currentAgentModel(s), - agentSelectors.currentAgentPlugins(s), + agentSelectors.displayableAgentPlugins(s), ]); const [isInbox, description, avatar, backgroundColor] = useSessionStore((s) => [ diff --git a/src/features/ShareModal/ShareImage/Preview.tsx b/src/features/ShareModal/ShareImage/Preview.tsx index 9a9f6b7317b..977325c01e2 100644 --- a/src/features/ShareModal/ShareImage/Preview.tsx +++ b/src/features/ShareModal/ShareImage/Preview.tsx @@ -22,7 +22,7 @@ const Preview = memo( ({ title, withSystemRole, withBackground, withFooter }) => { const [model, plugins, systemRole] = useAgentStore((s) => [ agentSelectors.currentAgentModel(s), - agentSelectors.currentAgentPlugins(s), + agentSelectors.displayableAgentPlugins(s), agentSelectors.currentAgentSystemRole(s), ]); const [isInbox, description, avatar, backgroundColor] = useSessionStore((s) => [ diff --git a/src/helpers/toolEngineering/index.ts b/src/helpers/toolEngineering/index.ts index abfb349f4fa..339936679ad 100644 --- a/src/helpers/toolEngineering/index.ts +++ b/src/helpers/toolEngineering/index.ts @@ -6,12 +6,13 @@ import type { PluginEnableChecker } from '@lobechat/context-engine'; import { ChatCompletionTool, WorkingModel } from '@lobechat/types'; import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk'; -import { getSearchConfig } from '@/helpers/getSearchConfig'; import { getToolStoreState } from '@/store/tool'; import { pluginSelectors } from '@/store/tool/selectors'; import { WebBrowsingManifest } from '@/tools/web-browsing'; +import { getSearchConfig } from '../getSearchConfig'; import { isCanUseFC } from '../isCanUseFC'; +import { shouldEnableTool } from '../toolFilters'; /** * Tools engine configuration options @@ -58,6 +59,11 @@ export const createChatToolsEngine = (workingModel: WorkingModel) => defaultToolIds: [WebBrowsingManifest.identifier], // Create search-aware enableChecker for this request enableChecker: ({ pluginId }) => { + // Check platform-specific constraints (e.g., LocalSystem desktop-only) + if (!shouldEnableTool(pluginId)) { + return false; + } + // For WebBrowsingManifest, apply search logic if (pluginId === WebBrowsingManifest.identifier) { const searchConfig = getSearchConfig(workingModel.model, workingModel.provider); diff --git a/src/helpers/toolFilters.ts b/src/helpers/toolFilters.ts new file mode 100644 index 00000000000..9d4a531a898 --- /dev/null +++ b/src/helpers/toolFilters.ts @@ -0,0 +1,35 @@ +/** + * Shared tool filtering logic used across both runtime (ToolsEngine) + * and display layer (selectors) + */ +import { isDesktop } from '@lobechat/const'; + +import { LocalSystemManifest } from '@/tools/local-system'; + +/** + * Check if a tool should be enabled based on platform-specific constraints + * @param toolId - The tool identifier to check + * @returns true if the tool should be enabled, false otherwise + */ +export const shouldEnableTool = (toolId: string): boolean => { + // Filter LocalSystem tool in non-desktop environment + if (toolId === LocalSystemManifest.identifier) { + return isDesktop; + } + + // Add more platform-specific filters here as needed + // if (toolId === SomeOtherPlatformSpecificTool.identifier) { + // return someCondition; + // } + + return true; +}; + +/** + * Filter tool IDs based on platform constraints + * @param toolIds - Array of tool identifiers to filter + * @returns Filtered array of tool identifiers + */ +export const filterToolIds = (toolIds: string[]): string[] => { + return toolIds.filter(shouldEnableTool); +}; diff --git a/src/locales/default/chat.ts b/src/locales/default/chat.ts index 2376a959356..7df75ec5486 100644 --- a/src/locales/default/chat.ts +++ b/src/locales/default/chat.ts @@ -334,10 +334,10 @@ export default { shareModal: { copy: '复制', download: '下载截图', + downloadError: '下载失败', downloadFile: '下载文件', downloadPdf: '下载 PDF', downloadSuccess: '下载成功', - downloadError: '下载失败', exportPdf: '导出为 PDF', exportTitle: '默认标题', generatePdf: '生成 PDF', diff --git a/src/locales/default/components.ts b/src/locales/default/components.ts index 37fa74b5e36..77a4d441a14 100644 --- a/src/locales/default/components.ts +++ b/src/locales/default/components.ts @@ -35,10 +35,6 @@ export default { config: { showFilesInKnowledgeBase: '显示知识库中内容', }, - view: { - list: '列表视图', - masonry: '网格视图', - }, emptyStatus: { actions: { file: '上传文件', @@ -57,6 +53,10 @@ export default { fileCount: '共 {{count}} 项', selectedCount: '已选 {{count}} 项', }, + view: { + list: '列表视图', + masonry: '网格视图', + }, }, FileParsingStatus: { chunks: { diff --git a/src/services/chat/contextEngineering.ts b/src/services/chat/contextEngineering.ts index c2f34c3313d..fab4459cb2b 100644 --- a/src/services/chat/contextEngineering.ts +++ b/src/services/chat/contextEngineering.ts @@ -1,6 +1,5 @@ import { INBOX_GUIDE_SYSTEMROLE, INBOX_SESSION_ID, isDesktop, isServerMode } from '@lobechat/const'; import { - type AgentState, ContextEngine, HistorySummaryProvider, HistoryTruncateProcessor, @@ -122,14 +121,7 @@ export const contextEngineering = async ({ ], }); - const initialState: AgentState = { messages, model, provider, systemRole, tools }; - - const result = await pipeline.process({ - initialState, - maxTokens: 10_000_000, - messages, - model, - }); + const result = await pipeline.process({ messages }); return result.messages; }; diff --git a/src/store/agent/slices/chat/selectors/agent.ts b/src/store/agent/slices/chat/selectors/agent.ts index 968e3bfe217..892354c7d6c 100644 --- a/src/store/agent/slices/chat/selectors/agent.ts +++ b/src/store/agent/slices/chat/selectors/agent.ts @@ -1,16 +1,16 @@ -import { VoiceList } from '@lobehub/tts'; - -import { INBOX_SESSION_ID } from '@/const/session'; import { DEFAULT_AGENT_CONFIG, DEFAULT_MODEL, DEFAULT_PROVIDER, DEFAUTT_AGENT_TTS_CONFIG, -} from '@/const/settings'; + INBOX_SESSION_ID, +} from '@lobechat/const'; +import { KnowledgeItem, KnowledgeType, LobeAgentConfig, LobeAgentTTSConfig } from '@lobechat/types'; +import { VoiceList } from '@lobehub/tts'; + import { DEFAULT_OPENING_QUESTIONS } from '@/features/AgentSetting/store/selectors'; +import { filterToolIds } from '@/helpers/toolFilters'; import { AgentStoreState } from '@/store/agent/initialState'; -import { LobeAgentConfig, LobeAgentTTSConfig } from '@/types/agent'; -import { KnowledgeItem, KnowledgeType } from '@/types/knowledgeBase'; import { merge } from '@/utils/merge'; const isInboxSession = (s: AgentStoreState) => s.activeId === INBOX_SESSION_ID; @@ -68,6 +68,15 @@ const currentAgentPlugins = (s: AgentStoreState) => { return config?.plugins || []; }; +/** + * Get displayable agent plugins by filtering out platform-specific tools + * that shouldn't be shown in the current environment + */ +const displayableAgentPlugins = (s: AgentStoreState) => { + const plugins = currentAgentPlugins(s); + return filterToolIds(plugins); +}; + const currentAgentKnowledgeBases = (s: AgentStoreState) => { const config = currentAgentConfig(s); @@ -172,6 +181,7 @@ export const agentSelectors = { currentAgentTTSVoice, currentEnabledKnowledge, currentKnowledgeIds, + displayableAgentPlugins, getAgentConfigByAgentId, getAgentConfigById, hasEnabledKnowledge, diff --git a/src/store/tool/slices/builtin/selectors.ts b/src/store/tool/slices/builtin/selectors.ts index 0194417e519..6aaffcc472b 100644 --- a/src/store/tool/slices/builtin/selectors.ts +++ b/src/store/tool/slices/builtin/selectors.ts @@ -1,5 +1,7 @@ +import { LobeToolMeta } from '@lobechat/types'; + +import { shouldEnableTool } from '@/helpers/toolFilters'; import { DalleManifest } from '@/tools/dalle'; -import { LobeToolMeta } from '@/types/tool/tool'; import type { ToolStoreState } from '../../initialState'; @@ -7,10 +9,18 @@ const metaList = (showDalle?: boolean) => (s: ToolStoreState): LobeToolMeta[] => s.builtinTools - .filter( - (item) => - !item.hidden && (!showDalle ? item.identifier !== DalleManifest.identifier : true), - ) + .filter((item) => { + // Filter hidden tools + if (item.hidden) return false; + + // Filter Dalle if not enabled + if (!showDalle && item.identifier === DalleManifest.identifier) return false; + + // Filter platform-specific tools (e.g., LocalSystem desktop-only) + if (!shouldEnableTool(item.identifier)) return false; + + return true; + }) .map((t) => ({ author: 'LobeHub', identifier: t.identifier,