mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
♻️ refactor: refactor context engine (#9821)
* refactor context engine * fix LocalSystem issue * refactor mcp in local
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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<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');
|
||||
|
||||
@@ -60,9 +60,9 @@ export interface PipelineContext {
|
||||
/** 当前 token 估算值 */
|
||||
currentTokenCount?: number;
|
||||
/** 最大 token 限制 */
|
||||
maxTokens: number;
|
||||
maxTokens?: number;
|
||||
/** 模型标识 */
|
||||
model: string;
|
||||
model?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) => [
|
||||
|
||||
@@ -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) => [
|
||||
|
||||
@@ -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);
|
||||
|
||||
35
src/helpers/toolFilters.ts
Normal file
35
src/helpers/toolFilters.ts
Normal file
@@ -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);
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user