fix: errors

This commit is contained in:
lin onetwo 2025-07-19 16:08:43 +08:00
parent 5f4a236927
commit b74553c914
20 changed files with 255 additions and 290 deletions

View file

@ -192,7 +192,6 @@
"ExitFullScreen": "Exit Fullscreen",
"Flat": "Flat View",
"FormEditor": "Form Editor",
"GeneratingPreview": "Generating preview...",
"LastUpdated": "Last updated",
"Loading": "Loading preview...",
"NoConfigFound": "No configuration found",

View file

@ -192,7 +192,6 @@
"ExitFullScreen": "退出全屏",
"Flat": "平铺视图",
"FormEditor": "表单编辑器",
"GeneratingPreview": "正在生成预览...",
"LastUpdated": "上次更新时间",
"Loading": "加载预览中...",
"NoConfigFound": "未找到配置",

View file

@ -46,7 +46,6 @@ Object.defineProperty(window, 'observables', {
},
workspace: {
workspaces$: new BehaviorSubject([]).asObservable(),
get$: vi.fn().mockReturnValue(new BehaviorSubject(null).asObservable()),
},
updater: {
updaterMetaData$: new BehaviorSubject(undefined).asObservable(),
@ -54,21 +53,6 @@ Object.defineProperty(window, 'observables', {
auth: {
userInfo$: new BehaviorSubject(undefined).asObservable(),
},
agentInstance: {
subscribeToAgentUpdates: vi.fn().mockReturnValue(new BehaviorSubject(null).asObservable()),
},
agentBrowser: {
tabs$: new BehaviorSubject([]).asObservable(),
},
notification: {
pauseNotificationsInfo$: new BehaviorSubject(null).asObservable(),
},
systemPreference: {
systemPreference$: new BehaviorSubject(null).asObservable(),
},
theme: {
theme$: new BehaviorSubject(null).asObservable(),
},
},
});
@ -101,8 +85,6 @@ const mockServiceInstances = {
workspace: {
countWorkspaces: vi.fn().mockResolvedValue(5),
openWorkspaceTiddler: vi.fn().mockResolvedValue(undefined),
},
agentInstance: {
concatPrompt: vi.fn().mockReturnValue(
new BehaviorSubject({
processedPrompts: [],
@ -115,7 +97,6 @@ const mockServiceInstances = {
isComplete: true,
}),
),
subscribeToAgentUpdates: vi.fn().mockReturnValue(new BehaviorSubject(null)),
},
workspaceView: {
setActiveWorkspaceView: vi.fn().mockResolvedValue(undefined),
@ -173,7 +154,7 @@ vi.mock('@services/container', () => {
'Symbol(Context)': 'context',
'Symbol(Preference)': 'preference',
'Symbol(ExternalAPI)': 'externalAPI',
'Symbol(AgentInstance)': 'agentInstance',
'Symbol(AgentInstance)': 'workspace',
'Symbol(AgentDefinition)': 'agentDefinition',
};
const serviceKey = identifierMap[identifier.toString()];

View file

@ -0,0 +1,76 @@
import React from 'react';
import { LinearProgress, Box, Typography, Chip } from '@mui/material';
import { useShallow } from 'zustand/react/shallow';
import { useAgentChatStore } from '../../store/agentChatStore';
interface PreviewProgressBarProps {
/**
* Whether to show the progress bar
*/
show: boolean;
}
/**
* Progress bar component for preview generation
* Shows real-time progress and current processing step
*/
export const PreviewProgressBar: React.FC<PreviewProgressBarProps> = ({ show }) => {
const {
previewProgress,
previewCurrentStep,
previewCurrentPlugin,
previewLoading,
} = useAgentChatStore(
useShallow((state) => ({
previewProgress: state.previewProgress,
previewCurrentStep: state.previewCurrentStep,
previewCurrentPlugin: state.previewCurrentPlugin,
previewLoading: state.previewLoading,
})),
);
if (!show || !previewLoading) {
return null;
}
const progressPercentage = Math.round(previewProgress * 100);
return (
<Box sx={{ width: '100%', mb: 2, p: 2, bgcolor: 'background.paper', borderRadius: 1 }}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1 }}>
<Typography variant="body2" color="text.secondary">
{previewCurrentStep}
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{previewCurrentPlugin && (
<Chip
label={previewCurrentPlugin}
size="small"
variant="outlined"
color="primary"
/>
)}
<Typography variant="body2" color="text.secondary">
{progressPercentage}%
</Typography>
</Box>
</Box>
<LinearProgress
variant="determinate"
value={progressPercentage}
sx={{
height: 6,
borderRadius: 3,
'& .MuiLinearProgress-bar': {
borderRadius: 3,
},
}}
/>
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: 'block' }}>
Live preview - this is not the final version and is still loading
</Typography>
</Box>
);
};

View file

@ -188,7 +188,7 @@ export const agentActions = (
// Subscribe to overall agent updates (primarily for new messages)
const agentSubscription = window.observables.agentInstance.subscribeToAgentUpdates(agentId).subscribe({
next: async (fullAgent: AgentInstance) => {
next: async (fullAgent) => {
// Ensure fullAgent exists before processing
if (!fullAgent) return;
@ -197,7 +197,7 @@ export const agentActions = (
const newMessageIds: string[] = [];
// Process new messages - backend already sorts messages by modified time
fullAgent.messages.forEach((message: AgentInstanceMessage) => {
fullAgent.messages.forEach(message => {
const existingMessage = currentMessages.get(message.id);
// If this is a new message
@ -214,7 +214,7 @@ export const agentActions = (
messageSubscriptions.set(
message.id,
window.observables.agentInstance.subscribeToAgentUpdates(agentId, message.id).subscribe({
next: (status: any) => {
next: (status) => {
if (status?.message) {
// Update the message in our map
get().messages.set(status.message.id, status.message);
@ -224,7 +224,7 @@ export const agentActions = (
}
}
},
error: (error: any) => {
error: (error) => {
console.error(`Error in message subscription for ${message.id}:`, error);
},
complete: () => {
@ -252,7 +252,7 @@ export const agentActions = (
set({ agent: agentWithoutMessages });
}
},
error: (error: any) => {
error: (error) => {
console.error('Error in agent subscription:', error);
set({ error: error instanceof Error ? error : new Error(String(error)) });
},

View file

@ -1,12 +1,12 @@
import type { AgentPromptDescription } from '@services/agentInstance/promptConcat/promptConcatSchema';
import { timeout } from 'rxjs/operators';
import { nanoid } from 'nanoid';
import { timeout, tap } from 'rxjs/operators';
import { lastValueFrom } from 'rxjs';
import { StateCreator } from 'zustand';
import { AgentChatStoreType, PreviewActions } from '../types';
/**
* Preview dialog related actions
* Handles dialog state and preview generation with real-time updates
* Handles dialog state and preview generation
*/
export const previewActionsMiddleware: StateCreator<AgentChatStoreType, [], [], PreviewActions> = (
set,
@ -22,8 +22,6 @@ export const previewActionsMiddleware: StateCreator<AgentChatStoreType, [], [],
lastUpdated: null,
expandedArrayItems: new Map(),
formFieldsToScrollTo: [],
previewProgress: undefined,
previewStep: undefined,
});
},
@ -31,14 +29,12 @@ export const previewActionsMiddleware: StateCreator<AgentChatStoreType, [], [],
set({ previewDialogTab: tab });
},
setFormFieldsToScrollTo: (fieldIds: string[]) => {
set({ formFieldsToScrollTo: fieldIds });
setFormFieldsToScrollTo: (fieldPaths: string[]) => {
set({ formFieldsToScrollTo: fieldPaths });
},
setArrayItemExpanded: (itemId: string, expanded: boolean) => {
const { expandedArrayItems } = get();
const newMap = new Map(expandedArrayItems);
if (expanded) {
newMap.set(itemId, true);
} else {
@ -46,33 +42,10 @@ export const previewActionsMiddleware: StateCreator<AgentChatStoreType, [], [],
}
set({ expandedArrayItems: newMap });
},
toggleArrayItemExpansion: (itemId: string) => {
const { expandedArrayItems } = get();
const newMap = new Map(expandedArrayItems);
if (newMap.has(itemId)) {
newMap.delete(itemId);
} else {
newMap.set(itemId, true);
}
set({ expandedArrayItems: newMap });
},
collapseArrayItem: (itemId: string) => {
const { expandedArrayItems } = get();
const newMap = new Map(expandedArrayItems);
if (newMap.has(itemId)) {
newMap.delete(itemId);
}
set({ expandedArrayItems: newMap });
},
isArrayItemExpanded: (itemId: string) => {
const { expandedArrayItems } = get();
return expandedArrayItems.get(itemId) ?? false;
},
expandPathToTarget: (targetPath: string[]) => {
const { expandedArrayItems } = get();
const newMap = new Map(expandedArrayItems);
@ -88,37 +61,28 @@ export const previewActionsMiddleware: StateCreator<AgentChatStoreType, [], [],
set({ expandedArrayItems: newMap });
},
/**
* Generate preview result with real-time progress updates
* Shows processing status and intermediate results to the user
*/
updatePreviewProgress: (progress: number, step: string, currentPlugin?: string) => {
set({
previewProgress: progress,
previewCurrentStep: step,
previewCurrentPlugin: currentPlugin || null,
});
},
getPreviewPromptResult: async (
inputText: string,
promptConfig: AgentPromptDescription['promptConfig'],
) => {
try {
// Initialize loading state
set({
previewLoading: true,
previewProgress: 0,
previewStep: 'Starting...',
previewResult: null
});
set({ previewLoading: true });
const messages = Array.from(get().messages.values());
// Safety check - if promptConfig is empty, fail early
if (Object.keys(promptConfig).length === 0) {
set({
previewLoading: false,
previewResult: null,
previewProgress: undefined,
previewStep: undefined,
});
set({ previewLoading: false, previewResult: null });
return null;
}
// Add input text as a preview message
if (inputText.trim()) {
messages.push({
id: 'preview-input',
@ -129,72 +93,99 @@ export const previewActionsMiddleware: StateCreator<AgentChatStoreType, [], [],
});
}
// Use the streaming API for real-time progress
const concatStream = window.service.agentInstance.concatPrompt({ promptConfig }, messages);
// Use the streaming API with progress updates
const concatStream = window.observables.agentInstance.concatPrompt({ promptConfig }, messages);
// Create promise to track completion and provide real-time updates
return new Promise((resolve, reject) => {
let finalResult: any = null;
// Initialize progress
set({
previewProgress: 0,
previewCurrentStep: 'Starting...',
previewCurrentPlugin: null,
});
const subscription = concatStream.pipe(
timeout(15000) // 15 second timeout
).subscribe({
let finalResult: any = null;
let completed = false;
// Create a promise that resolves when the stream completes
const streamPromise = new Promise<any>((resolve, reject) => {
// Subscribe to the stream and update progress in real-time
const subscription = concatStream.subscribe({
next: (state) => {
// Update UI with real-time progress
// Update progress and current step
const stepDescription = state.step === 'plugin'
? `Processing plugin: ${state.currentPlugin?.pluginId || 'unknown'}`
: state.step === 'finalize'
? 'Finalizing prompts...'
: state.step === 'flatten'
? 'Flattening prompt tree...'
: 'Completing...';
set({
previewProgress: state.progress,
previewStep: state.step,
previewCurrentStep: stepDescription,
previewCurrentPlugin: state.currentPlugin?.pluginId || null,
// Update intermediate results
previewResult: {
flatPrompts: state.flatPrompts,
processedPrompts: state.processedPrompts,
},
});
// Store final result when processing is complete
// Store final result
if (state.isComplete) {
finalResult = {
flatPrompts: state.flatPrompts,
processedPrompts: state.processedPrompts,
};
set({
previewResult: finalResult,
previewLoading: false,
lastUpdated: new Date(),
});
}
// Log progress for development debugging
console.log(`🔄 Preview progress: ${Math.round(state.progress * 100)}% - ${state.step}`, {
currentPlugin: state.currentPlugin?.pluginId,
flatPromptsCount: state.flatPrompts.length,
processedPromptsCount: state.processedPrompts.length,
});
},
error: (error: any) => {
console.error('Error generating preview prompt result:', error);
error: (error) => {
console.error('Error generating preview prompt result:', error);
set({
previewResult: null,
previewLoading: false,
previewProgress: undefined,
previewStep: undefined,
previewProgress: 0,
previewCurrentStep: 'Error occurred',
previewCurrentPlugin: null,
});
reject(error);
},
complete: () => {
console.log('✅ Preview prompt generation completed');
completed = true;
set({
previewResult: finalResult,
previewLoading: false,
previewProgress: undefined,
previewStep: undefined,
previewProgress: 1,
previewCurrentStep: 'Complete',
previewCurrentPlugin: null,
lastUpdated: new Date(),
});
resolve(finalResult);
},
}
});
// Set up timeout
setTimeout(() => {
if (!completed) {
subscription.unsubscribe();
set({
previewResult: null,
previewLoading: false,
previewProgress: 0,
previewCurrentStep: 'Timeout',
previewCurrentPlugin: null,
});
reject(new Error('Preview generation timed out'));
}
}, 15000);
});
} catch (error: any) {
console.error('❌ Error in preview generation:', error);
return streamPromise;
} catch (error) {
console.error('Error generating preview prompt result:', error);
set({
previewResult: null,
previewLoading: false,
previewProgress: undefined,
previewStep: undefined,
});
return null;
}

View file

@ -21,6 +21,9 @@ export const useAgentChatStore = create<AgentChatStoreType>()((set, get, api) =>
previewDialogOpen: false,
previewDialogTab: 'tree',
previewLoading: false,
previewProgress: 0,
previewCurrentStep: '',
previewCurrentPlugin: null,
previewResult: null,
lastUpdated: null,
formFieldsToScrollTo: [],

View file

@ -1,6 +1,7 @@
import { AgentDefinition } from '@services/agentDefinition/interface';
import type { AgentInstance, AgentInstanceMessage } from '@services/agentInstance/interface';
import type { AgentPromptDescription, IPrompt } from '@services/agentInstance/promptConcat/promptConcatSchema';
import { PromptConcatStreamState } from '@services/agentInstance/promptConcat/promptConcat';
import { CoreMessage } from 'ai';
// Type for agent data without messages - exported for use in other components
@ -29,8 +30,9 @@ export interface PreviewDialogState {
previewDialogOpen: boolean;
previewDialogTab: 'flat' | 'tree';
previewLoading: boolean;
previewProgress?: number; // 0-1 progress value
previewStep?: string; // Current processing step
previewProgress: number; // 0-1, processing progress
previewCurrentStep: string; // current processing step description
previewCurrentPlugin: string | null; // current plugin being processed
previewResult: {
flatPrompts: CoreMessage[];
processedPrompts: IPrompt[];
@ -176,17 +178,29 @@ export interface PreviewActions {
*/
expandPathToTarget: (targetPath: string[]) => void;
/**
* Updates preview progress state
* @param progress Progress value from 0 to 1
* @param step Current processing step description
* @param currentPlugin Current plugin being processed
*/
updatePreviewProgress: (progress: number, step: string, currentPlugin?: string) => void;
/**
* Generates a preview of prompts for the current agent state
* Now uses streaming API for real-time progress updates
* @param inputText Input text to include in the preview
* @param promptConfig Prompt configuration to use for preview
* @returns Promise that resolves to a cleanup function to cancel the subscription
* @returns Promise that resolves when preview is generated and state is updated
*/
getPreviewPromptResult: (
inputText: string,
promptConfig: AgentPromptDescription['promptConfig'],
) => Promise<(() => void) | null>;
) => Promise<
{
flatPrompts: CoreMessage[];
processedPrompts: IPrompt[];
} | null
>;
/**
* Resets the lastUpdated timestamp, typically called when dialog is closed

View file

@ -1,22 +1,17 @@
import { Box, CircularProgress, LinearProgress, Typography } from '@mui/material';
import { Box, CircularProgress, Typography } from '@mui/material';
import React from 'react';
import { useTranslation } from 'react-i18next';
interface LoadingViewProps {
message?: string;
progress?: number; // 0-1 progress value
step?: string; // Current processing step
}
/**
* Enhanced loading state component with progress indicator and step information
* Loading state component with spinner and messages
*/
export const LoadingView: React.FC<LoadingViewProps> = ({ message, progress, step }) => {
export const LoadingView: React.FC<LoadingViewProps> = ({ message }) => {
const { t } = useTranslation('agent');
const showProgress = typeof progress === 'number';
const progressPercentage = showProgress ? Math.round(progress * 100) : 0;
return (
<Box
display='flex'
@ -25,52 +20,11 @@ export const LoadingView: React.FC<LoadingViewProps> = ({ message, progress, ste
alignItems='center'
minHeight={300}
gap={2}
sx={{ px: 3 }}
>
{/* Main spinner */}
<CircularProgress
variant={showProgress ? 'determinate' : 'indeterminate'}
value={progressPercentage}
size={60}
/>
{/* Progress percentage */}
{showProgress && (
<Typography variant='h6' color='primary' fontWeight='bold'>
{progressPercentage}%
</Typography>
)}
{/* Current step information */}
{step && (
<Typography variant='body1' color='text.primary' textAlign='center'>
{step}
</Typography>
)}
{/* Main loading message */}
<Typography variant='body2' color='text.secondary' textAlign='center'>
<CircularProgress />
<Typography variant='body2' color='text.secondary'>
{message || t('Prompt.Loading')}
</Typography>
{/* Linear progress bar for better visual feedback */}
{showProgress && (
<Box sx={{ width: '100%', maxWidth: 400, mt: 2 }}>
<LinearProgress
variant='determinate'
value={progressPercentage}
sx={{
height: 8,
borderRadius: 5,
'& .MuiLinearProgress-bar': {
borderRadius: 5,
}
}}
/>
</Box>
)}
{/* Auto-refresh hint */}
<Typography variant='caption' color='text.secondary' sx={{ mt: 1, maxWidth: '80%', textAlign: 'center' }}>
{t('Prompt.AutoRefresh')}
</Typography>

View file

@ -51,8 +51,6 @@ export const PreviewTabsView: React.FC<PreviewTabsViewProps> = ({
previewDialogTab: tab,
previewLoading,
previewResult,
previewProgress,
previewStep,
lastUpdated,
setPreviewDialogTab,
} = useAgentChatStore(
@ -60,8 +58,6 @@ export const PreviewTabsView: React.FC<PreviewTabsViewProps> = ({
previewDialogTab: state.previewDialogTab,
previewLoading: state.previewLoading,
previewResult: state.previewResult,
previewProgress: state.previewProgress,
previewStep: state.previewStep,
lastUpdated: state.lastUpdated,
setPreviewDialogTab: state.setPreviewDialogTab,
})),
@ -89,13 +85,7 @@ export const PreviewTabsView: React.FC<PreviewTabsViewProps> = ({
}, [setPreviewDialogTab]);
if (previewLoading) {
return (
<LoadingView
progress={previewProgress}
step={previewStep}
message={t('Prompt.GeneratingPreview')}
/>
);
return <LoadingView />;
}
return (

View file

@ -14,6 +14,7 @@ import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useShallow } from 'zustand/react/shallow';
import { useAgentChatStore } from '../../../Agent/store/agentChatStore/index';
import { PreviewProgressBar } from '../../../Agent/components/PreviewDialog/PreviewProgressBar';
import { EditView } from './EditView';
import { PreviewTabsView } from './PreviewTabsView';
@ -42,9 +43,13 @@ export const PromptPreviewDialog: React.FC<PromptPreviewDialogProps> = ({
agentId: agent?.id,
});
const { getPreviewPromptResult } = useAgentChatStore(
const {
getPreviewPromptResult,
previewLoading,
} = useAgentChatStore(
useShallow((state) => ({
getPreviewPromptResult: state.getPreviewPromptResult,
previewLoading: state.previewLoading,
})),
);
useEffect(() => {
@ -59,7 +64,7 @@ export const PromptPreviewDialog: React.FC<PromptPreviewDialogProps> = ({
}
};
void fetchInitialPreview();
}, [agent?.agentDefId, handlerConfig, handlerConfigLoading, getPreviewPromptResult, inputText, open]);
}, [agent?.agentDefId, handlerConfig, handlerConfigLoading, inputText, open]); // 移除 getPreviewPromptResult
const handleToggleFullScreen = useCallback((): void => {
setIsFullScreen(previous => !previous);
@ -143,6 +148,7 @@ export const PromptPreviewDialog: React.FC<PromptPreviewDialogProps> = ({
}),
}}
>
<PreviewProgressBar show={previewLoading} />
{isEditMode
? (
<Box sx={{ display: 'flex', gap: 2, height: isFullScreen ? '100%' : '70vh' }}>

View file

@ -31,7 +31,7 @@ import { ExternalAPIServiceIPCDescriptor, IExternalAPIService } from '../../serv
export const agentBrowser = createProxy<AsyncifyProxy<IAgentBrowserService>>(AgentBrowserServiceIPCDescriptor);
export const agentDefinition = createProxy<AsyncifyProxy<IAgentDefinitionService>>(AgentDefinitionServiceIPCDescriptor);
export const agentInstance = createProxy<IAgentInstanceService>(AgentInstanceServiceIPCDescriptor);
export const agentInstance = createProxy<AsyncifyProxy<IAgentInstanceService>>(AgentInstanceServiceIPCDescriptor);
export const auth = createProxy<IAuthenticationService>(AuthenticationServiceIPCDescriptor);
export const context = createProxy<IContextService>(ContextServiceIPCDescriptor);
export const deepLink = createProxy<IDeepLinkService>(DeepLinkServiceIPCDescriptor);

View file

@ -340,7 +340,7 @@ describe('basicPromptConcatHandler', () => {
describe('error handling', () => {
it('should handle unexpected errors', async () => {
// Mock service to return error observable
mockServiceInstances.agentInstance.concatPrompt = vi.fn().mockReturnValue(
mockServiceInstances.workspace.concatPrompt = vi.fn().mockReturnValue(
new BehaviorSubject({
processedPrompts: [],
flatPrompts: [],

View file

@ -1,51 +1,9 @@
import { AsyncSeriesWaterfallHook } from 'tapable';
import { logger } from '@services/libs/log';
import { IPrompt } from '../promptConcatSchema';
import { Plugin } from '../promptConcatSchema/plugin';
import { fullReplacementPlugin, dynamicPositionPlugin, modelContextProtocolPlugin, retrievalAugmentedGenerationPlugin } from './promptPlugins';
import { toolCallingPlugin, autoReplyPlugin } from './responsePlugins';
import { AgentInstanceMessage } from '@services/agentInstance/interface';
import { PromptConcatHooks, PromptConcatHookContext, PromptConcatPlugin, AgentResponse, ResponseHookContext } from './types';
/**
* Context passed to plugin hooks
*/
export interface PromptConcatHookContext {
/** Array of agent instance messages for context */
messages: AgentInstanceMessage[];
/** Current prompt tree */
prompts: IPrompt[];
/** Plugin configuration */
plugin: Plugin;
/** Additional context data */
metadata?: Record<string, any>;
}
/**
* Plugin function type - receives hooks object and registers handlers
*/
export type PromptConcatPlugin = (hooks: PromptConcatHooks) => void;
/**
* Hooks system for prompt concatenation
*/
export class PromptConcatHooks {
/** Hook for processing prompt modifications */
public readonly processPrompts = new AsyncSeriesWaterfallHook<[PromptConcatHookContext]>(['context']);
/** Hook for finalizing prompts before LLM call */
public readonly finalizePrompts = new AsyncSeriesWaterfallHook<[PromptConcatHookContext]>(['context']);
/** Hook for post-processing after LLM response */
public readonly postProcess = new AsyncSeriesWaterfallHook<[PromptConcatHookContext & { llmResponse: string }]>(['context']);
/**
* Register a plugin
*/
public registerPlugin(plugin: PromptConcatPlugin): void {
logger.debug('Registering prompt concat plugin');
plugin(this);
}
}
// Re-export types for convenience
export type { PromptConcatHookContext, PromptConcatPlugin, AgentResponse, ResponseHookContext };
export { PromptConcatHooks };
/**
* Registry for built-in plugins
@ -60,23 +18,29 @@ export function registerBuiltInPlugin(pluginId: string, plugin: PromptConcatPlug
logger.debug(`Registered built-in plugin: ${pluginId}`);
}
/**
* Register all built-in plugins
*/
export function registerAllBuiltInPlugins(): void {
// Prompt processing plugins
registerBuiltInPlugin('fullReplacement', fullReplacementPlugin);
registerBuiltInPlugin('dynamicPosition', dynamicPositionPlugin);
registerBuiltInPlugin('modelContextProtocol', modelContextProtocolPlugin);
registerBuiltInPlugin('retrievalAugmentedGeneration', retrievalAugmentedGenerationPlugin);
// Use dynamic imports to avoid circular dependency issues
Promise.all([
import('./promptPlugins'),
import('./responsePlugins')
]).then(([promptPluginsModule, responsePluginsModule]) => {
// Prompt processing plugins
registerBuiltInPlugin('fullReplacement', promptPluginsModule.fullReplacementPlugin);
registerBuiltInPlugin('dynamicPosition', promptPluginsModule.dynamicPositionPlugin);
registerBuiltInPlugin('modelContextProtocol', promptPluginsModule.modelContextProtocolPlugin);
registerBuiltInPlugin('retrievalAugmentedGeneration', promptPluginsModule.retrievalAugmentedGenerationPlugin);
// Response processing plugins
registerBuiltInPlugin('toolCalling', toolCallingPlugin);
registerBuiltInPlugin('autoReply', autoReplyPlugin);
// Response processing plugins
registerBuiltInPlugin('toolCalling', responsePluginsModule.toolCallingPlugin);
registerBuiltInPlugin('autoReply', responsePluginsModule.autoReplyPlugin);
// Note: fullReplacementResponsePlugin is separate from fullReplacementPlugin
// They handle different phases of processing
logger.debug('All built-in plugins registered successfully');
}).catch(error => {
logger.error('Failed to register built-in plugins:', error);
});
}
/**

View file

@ -3,10 +3,9 @@
*/
import { logger } from '@services/libs/log';
import { cloneDeep } from 'lodash';
import { PromptConcatPlugin, PromptConcatHookContext } from './index';
import { PromptConcatPlugin, PromptConcatHookContext, AgentResponse, ResponseHookContext } from './types';
import { findPromptById } from '../promptConcat';
import { IPrompt } from '../promptConcatSchema';
import { AgentResponse, ResponseHookContext } from './responsePlugins';
/**
* Full replacement plugin

View file

@ -2,18 +2,10 @@
* Response processing plugins
*/
import { logger } from '@services/libs/log';
import { PromptConcatPlugin, PromptConcatHookContext } from './index';
/**
* Agent response interface
* Represents a structured response from an agent
*/
export interface AgentResponse {
id: string;
text?: string;
enabled?: boolean;
children?: AgentResponse[];
}
import { AgentInstanceMessage } from '../../interface';
import { IPrompt } from '../promptConcatSchema';
import { Plugin } from '../promptConcatSchema/plugin';
import { PromptConcatPlugin, PromptConcatHookContext, AgentResponse, ResponseHookContext } from './types';
/**
* Find response by ID in response array
@ -52,14 +44,6 @@ function parseRegexString(regexString: string): RegExp | null {
}
}
/**
* Context for response processing hooks
*/
export interface ResponseHookContext extends PromptConcatHookContext {
llmResponse: string;
responses: AgentResponse[];
}
/**
* Tool calling plugin
* Processes function calls in AI responses

View file

@ -1,8 +1,8 @@
import { AsyncSeriesWaterfallHook } from 'tapable';
import { logger } from '@services/libs/log';
import { AgentInstanceMessage } from '../interface';
import { IPrompt } from './promptConcatSchema';
import { Plugin } from './promptConcatSchema/plugin';
import { IPrompt } from '../promptConcatSchema';
import { Plugin } from '../promptConcatSchema/plugin';
import { AgentInstanceMessage } from '@services/agentInstance/interface';
/**
* Context passed to plugin hooks
@ -18,6 +18,25 @@ export interface PromptConcatHookContext {
metadata?: Record<string, any>;
}
/**
* Agent response interface
* Represents a structured response from an agent
*/
export interface AgentResponse {
id: string;
text?: string;
enabled?: boolean;
children?: AgentResponse[];
}
/**
* Context for response processing hooks
*/
export interface ResponseHookContext extends PromptConcatHookContext {
llmResponse: string;
responses: AgentResponse[];
}
/**
* Plugin function type - receives hooks object and registers handlers
*/
@ -44,16 +63,3 @@ export class PromptConcatHooks {
plugin(this);
}
}
/**
* Registry for built-in plugins
*/
export const builtInPlugins = new Map<string, PromptConcatPlugin>();
/**
* Register a built-in plugin
*/
export function registerBuiltInPlugin(pluginId: string, plugin: PromptConcatPlugin): void {
builtInPlugins.set(pluginId, plugin);
logger.debug(`Registered built-in plugin: ${pluginId}`);
}

View file

@ -19,8 +19,7 @@ import { cloneDeep } from 'lodash';
import { AgentInstanceMessage } from '../interface';
import { AgentPromptDescription, IPrompt } from './promptConcatSchema';
import { Plugin } from './promptConcatSchema/plugin';
import { PromptConcatHooks, PromptConcatHookContext, builtInPlugins } from './hooks';
import { initializePluginSystem } from './plugins';
import { PromptConcatHooks, PromptConcatHookContext, builtInPlugins, initializePluginSystem } from './plugins';
// Initialize plugin system on module load
initializePluginSystem();

View file

@ -10,7 +10,7 @@ import { AgentInstanceMessage } from '../interface';
import { AgentPromptDescription } from './promptConcatSchema';
import { Plugin } from './promptConcatSchema/plugin';
import { PromptConcatHooks, PromptConcatHookContext, builtInPlugins } from './plugins';
import { ResponseHookContext, AgentResponse } from './plugins/responsePlugins';
import { ResponseHookContext, AgentResponse } from './plugins/types';
/**
* Process response configuration, apply plugins, and return final response

View file

@ -2,7 +2,7 @@
* Utility functions for prompt concatenation
*/
import { Observable } from 'rxjs';
import { Observable, lastValueFrom } from 'rxjs';
import { last } from 'rxjs/operators';
import { PromptConcatStreamState } from './promptConcat';
@ -19,7 +19,7 @@ export async function getFinalPromptResult(
flatPrompts: PromptConcatStreamState['flatPrompts'];
processedPrompts: PromptConcatStreamState['processedPrompts'];
}> {
const finalState = await last<PromptConcatStreamState>()(stream).toPromise();
const finalState = await lastValueFrom(stream.pipe(last()));
if (!finalState) {
throw new Error('Prompt concatenation stream ended without final result');