Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,74 @@

# Changelog

### [Version 1.141.1](https://github.com/lobehub/lobe-chat/compare/v1.141.0...v1.141.1)

<sup>Released on **2025-10-21**</sup>

#### ♻ Code Refactoring

- **misc**: Refactor context engine.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### 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))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>

## [Version 1.141.0](https://github.com/lobehub/lobe-chat/compare/v1.140.0...v1.141.0)

<sup>Released on **2025-10-21**</sup>

#### ✨ 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.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### 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))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>

## [Version 1.140.0](https://github.com/lobehub/lobe-chat/compare/v1.139.5...v1.140.0)

<sup>Released on **2025-10-21**</sup>
Expand Down
20 changes: 20 additions & 0 deletions changelog/v1.json
Original file line number Diff line number Diff line change
@@ -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)."]
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -399,4 +399,4 @@
"mdast-util-gfm-autolink-literal": "2.0.0"
}
}
}
}
34 changes: 7 additions & 27 deletions packages/context-engine/src/__tests__/pipeline.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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([]);
});
Expand All @@ -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 () => {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
});
});

Expand Down
26 changes: 5 additions & 21 deletions packages/context-engine/src/pipeline.ts
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -70,26 +64,16 @@ export class ContextEngine {
/**
* Execute pipeline processing
*/
async process(input: {
initialState: AgentState;
maxTokens: number;
messages?: Array<any>;
metadata?: Record<string, any>;
model: string;
}): Promise<PipelineResult> {
async process(input: { messages: Array<any> }): Promise<PipelineResult> {
const startTime = Date.now();
const processorDurations: Record<string, number> = {};

// 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');
Expand Down
4 changes: 2 additions & 2 deletions packages/context-engine/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ export interface PipelineContext {
/** 当前 token 估算值 */
currentTokenCount?: number;
/** 最大 token 限制 */
maxTokens: number;
maxTokens?: number;
/** 模型标识 */
model: string;
model?: string;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const Preview = memo<PreviewProps>(
({ 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) => [
Expand Down
2 changes: 1 addition & 1 deletion src/features/ShareModal/ShareImage/Preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const Preview = memo<FieldType & { title?: string }>(
({ 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) => [
Expand Down
8 changes: 7 additions & 1 deletion src/helpers/toolEngineering/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
35 changes: 35 additions & 0 deletions src/helpers/toolFilters.ts
Original file line number Diff line number Diff line change
@@ -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);
};
2 changes: 1 addition & 1 deletion src/locales/default/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,10 @@ export default {
shareModal: {
copy: '复制',
download: '下载截图',
downloadError: '下载失败',
downloadFile: '下载文件',
downloadPdf: '下载 PDF',
downloadSuccess: '下载成功',
downloadError: '下载失败',
exportPdf: '导出为 PDF',
exportTitle: '默认标题',
generatePdf: '生成 PDF',
Expand Down
8 changes: 4 additions & 4 deletions src/locales/default/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ export default {
config: {
showFilesInKnowledgeBase: '显示知识库中内容',
},
view: {
list: '列表视图',
masonry: '网格视图',
},
emptyStatus: {
actions: {
file: '上传文件',
Expand All @@ -57,6 +53,10 @@ export default {
fileCount: '共 {{count}} 项',
selectedCount: '已选 {{count}} 项',
},
view: {
list: '列表视图',
masonry: '网格视图',
},
},
FileParsingStatus: {
chunks: {
Expand Down
Loading
Loading