mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-05 18:20:39 -08:00
rename: handler -> agent framework; plugin -> tool
rename: handler -> agent framework; plugin -> tool lint refactor: more rename further rename
This commit is contained in:
parent
8963527b41
commit
8a84d9b468
60 changed files with 1382 additions and 1368 deletions
|
|
@ -46,7 +46,7 @@ Object.defineProperty(window, 'observables', {
|
||||||
userInfo$: new BehaviorSubject(undefined).asObservable(),
|
userInfo$: new BehaviorSubject(undefined).asObservable(),
|
||||||
},
|
},
|
||||||
agentInstance: {
|
agentInstance: {
|
||||||
concatPrompt: vi.fn((promptDescription: Pick<AgentPromptDescription, 'handlerConfig'>, messages: AgentInstanceMessage[]) => {
|
concatPrompt: vi.fn((promptDescription: Pick<AgentPromptDescription, 'agentFrameworkConfig'>, messages: AgentInstanceMessage[]) => {
|
||||||
const agentInstanceService = container.get<AgentInstanceService>(serviceIdentifier.AgentInstance);
|
const agentInstanceService = container.get<AgentInstanceService>(serviceIdentifier.AgentInstance);
|
||||||
// Initialize handlers (plugins and built-in handlers) before calling concatPrompt
|
// Initialize handlers (plugins and built-in handlers) before calling concatPrompt
|
||||||
// We need to wrap this in an Observable since concatPrompt returns an Observable
|
// We need to wrap this in an Observable since concatPrompt returns an Observable
|
||||||
|
|
@ -55,7 +55,7 @@ Object.defineProperty(window, 'observables', {
|
||||||
try {
|
try {
|
||||||
// Need to register plugins first. In test environment, this needs to be called manually. While in real
|
// Need to register plugins first. In test environment, this needs to be called manually. While in real
|
||||||
// environment, this is handled in `main.ts` when app start.
|
// environment, this is handled in `main.ts` when app start.
|
||||||
await agentInstanceService.initializeHandlers();
|
await agentInstanceService.initializeFrameworks();
|
||||||
const resultObservable = agentInstanceService.concatPrompt(promptDescription, messages);
|
const resultObservable = agentInstanceService.concatPrompt(promptDescription, messages);
|
||||||
// Subscribe to the result and forward to our observer
|
// Subscribe to the result and forward to our observer
|
||||||
resultObservable.subscribe(observer);
|
resultObservable.subscribe(observer);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { Box, Button, Container, Step, StepLabel, Stepper, TextField, Typography
|
||||||
import { styled } from '@mui/material/styles';
|
import { styled } from '@mui/material/styles';
|
||||||
import type { RJSFSchema } from '@rjsf/utils';
|
import type { RJSFSchema } from '@rjsf/utils';
|
||||||
import type { AgentDefinition } from '@services/agentDefinition/interface';
|
import type { AgentDefinition } from '@services/agentDefinition/interface';
|
||||||
import { HandlerConfig } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
import { AgentFrameworkConfig } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
import useDebouncedCallback from 'beautiful-react-hooks/useDebouncedCallback';
|
import useDebouncedCallback from 'beautiful-react-hooks/useDebouncedCallback';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
@ -99,19 +99,19 @@ export const CreateNewAgentContent: React.FC<CreateNewAgentContentProps> = ({ ta
|
||||||
// Load schema when temporaryAgentDefinition is available
|
// Load schema when temporaryAgentDefinition is available
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadSchema = async () => {
|
const loadSchema = async () => {
|
||||||
if (temporaryAgentDefinition?.handlerID) {
|
if (temporaryAgentDefinition?.agentFrameworkID) {
|
||||||
try {
|
try {
|
||||||
const schema = await window.service.agentInstance.getHandlerConfigSchema(temporaryAgentDefinition.handlerID);
|
const schema = await window.service.agentInstance.getFrameworkConfigSchema(temporaryAgentDefinition.agentFrameworkID);
|
||||||
setPromptSchema(schema as RJSFSchema);
|
setPromptSchema(schema as RJSFSchema);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load handler config schema:', error);
|
console.error('Failed to load framework config schema:', error);
|
||||||
setPromptSchema(null);
|
setPromptSchema(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void loadSchema();
|
void loadSchema();
|
||||||
}, [temporaryAgentDefinition?.handlerID]);
|
}, [temporaryAgentDefinition?.agentFrameworkID]);
|
||||||
|
|
||||||
// Create preview agent when entering step 3
|
// Create preview agent when entering step 3
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -374,11 +374,11 @@ export const CreateNewAgentContent: React.FC<CreateNewAgentContentProps> = ({ ta
|
||||||
<Box sx={{ mt: 2, height: 400, overflow: 'auto' }}>
|
<Box sx={{ mt: 2, height: 400, overflow: 'auto' }}>
|
||||||
<PromptConfigForm
|
<PromptConfigForm
|
||||||
schema={promptSchema}
|
schema={promptSchema}
|
||||||
formData={(temporaryAgentDefinition.handlerConfig || {}) as HandlerConfig}
|
formData={(temporaryAgentDefinition.agentFrameworkConfig || {}) as AgentFrameworkConfig}
|
||||||
onChange={(updatedConfig) => {
|
onChange={(updatedConfig) => {
|
||||||
void handleAgentDefinitionChange({
|
void handleAgentDefinitionChange({
|
||||||
...temporaryAgentDefinition,
|
...temporaryAgentDefinition,
|
||||||
handlerConfig: updatedConfig as Record<string, unknown>,
|
agentFrameworkConfig: updatedConfig as Record<string, unknown>,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
loading={false}
|
loading={false}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { Box, Button, CircularProgress, Container, Divider, TextField, Typograph
|
||||||
import { styled } from '@mui/material/styles';
|
import { styled } from '@mui/material/styles';
|
||||||
import type { RJSFSchema } from '@rjsf/utils';
|
import type { RJSFSchema } from '@rjsf/utils';
|
||||||
import type { AgentDefinition } from '@services/agentDefinition/interface';
|
import type { AgentDefinition } from '@services/agentDefinition/interface';
|
||||||
import { HandlerConfig } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
import { AgentFrameworkConfig } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
import useDebouncedCallback from 'beautiful-react-hooks/useDebouncedCallback';
|
import useDebouncedCallback from 'beautiful-react-hooks/useDebouncedCallback';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
@ -94,30 +94,30 @@ export const EditAgentDefinitionContent: React.FC<EditAgentDefinitionContentProp
|
||||||
void loadAgentDefinition();
|
void loadAgentDefinition();
|
||||||
}, [tab.agentDefId]);
|
}, [tab.agentDefId]);
|
||||||
|
|
||||||
// Load handler config schema
|
// Load framework config schema
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadSchema = async () => {
|
const loadSchema = async () => {
|
||||||
if (!agentDefinition?.handlerID) {
|
if (!agentDefinition?.agentFrameworkID) {
|
||||||
// No handlerID found
|
// No agentFrameworkID found
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Loading handler config schema
|
// Loading framework config schema
|
||||||
const schema = await window.service.agentInstance.getHandlerConfigSchema(agentDefinition.handlerID);
|
const schema = await window.service.agentInstance.getFrameworkConfigSchema(agentDefinition.agentFrameworkID);
|
||||||
// Schema loaded successfully
|
// Schema loaded successfully
|
||||||
setPromptSchema(schema);
|
setPromptSchema(schema);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
void window.service.native.log('error', 'EditAgentDefinitionContent: Failed to load handler config schema', {
|
void window.service.native.log('error', 'EditAgentDefinitionContent: Failed to load framework config schema', {
|
||||||
error,
|
error,
|
||||||
handlerID: agentDefinition.handlerID,
|
agentFrameworkID: agentDefinition.agentFrameworkID,
|
||||||
});
|
});
|
||||||
console.error('Failed to load handler config schema:', error);
|
console.error('Failed to load framework config schema:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void loadSchema();
|
void loadSchema();
|
||||||
}, [agentDefinition?.handlerID]);
|
}, [agentDefinition?.agentFrameworkID]);
|
||||||
|
|
||||||
// Auto-save to backend whenever agentDefinition changes (debounced)
|
// Auto-save to backend whenever agentDefinition changes (debounced)
|
||||||
const saveToBackendDebounced = useDebouncedCallback(
|
const saveToBackendDebounced = useDebouncedCallback(
|
||||||
|
|
@ -235,7 +235,7 @@ export const EditAgentDefinitionContent: React.FC<EditAgentDefinitionContentProp
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...previous,
|
...previous,
|
||||||
handlerConfig: formData as Record<string, unknown>,
|
agentFrameworkConfig: formData as Record<string, unknown>,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -356,7 +356,7 @@ export const EditAgentDefinitionContent: React.FC<EditAgentDefinitionContentProp
|
||||||
<Box sx={{ mt: 2 }} data-testid='edit-agent-prompt-form'>
|
<Box sx={{ mt: 2 }} data-testid='edit-agent-prompt-form'>
|
||||||
<PromptConfigForm
|
<PromptConfigForm
|
||||||
schema={promptSchema}
|
schema={promptSchema}
|
||||||
formData={agentDefinition.handlerConfig as HandlerConfig}
|
formData={agentDefinition.agentFrameworkConfig as AgentFrameworkConfig}
|
||||||
onChange={handlePromptConfigChange}
|
onChange={handlePromptConfigChange}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ export const NewTabContent: React.FC<NewTabContentProps> = ({ tab: _tab }) => {
|
||||||
|
|
||||||
const createAgentChatTab = async (agentDefinitionId?: string) => {
|
const createAgentChatTab = async (agentDefinitionId?: string) => {
|
||||||
try {
|
try {
|
||||||
const agentDefinitionIdToUse = agentDefinitionId || 'example-agent';
|
const agentDefinitionIdToUse = agentDefinitionId || 'task-agent';
|
||||||
|
|
||||||
// Handle current active tab - close temp tabs or NEW_TAB type tabs
|
// Handle current active tab - close temp tabs or NEW_TAB type tabs
|
||||||
if (activeTabId) {
|
if (activeTabId) {
|
||||||
|
|
@ -162,7 +162,7 @@ export const NewTabContent: React.FC<NewTabContentProps> = ({ tab: _tab }) => {
|
||||||
|
|
||||||
const handleEditDefinition = useCallback(() => {
|
const handleEditDefinition = useCallback(() => {
|
||||||
// Use the example agent ID for now - in the future this could be configurable
|
// Use the example agent ID for now - in the future this could be configurable
|
||||||
void editAgentDefinitionTab('example-agent');
|
void editAgentDefinitionTab('task-agent');
|
||||||
handleCloseContextMenu();
|
handleCloseContextMenu();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ const mockGetAgentDefs = vi.fn();
|
||||||
const mockUpdateTab = vi.fn();
|
const mockUpdateTab = vi.fn();
|
||||||
const mockGetAllTabs = vi.fn();
|
const mockGetAllTabs = vi.fn();
|
||||||
const mockGetActiveTabId = vi.fn();
|
const mockGetActiveTabId = vi.fn();
|
||||||
const mockGetHandlerConfigSchema = vi.fn();
|
const mockGetFrameworkConfigSchema = vi.fn();
|
||||||
|
|
||||||
Object.defineProperty(window, 'service', {
|
Object.defineProperty(window, 'service', {
|
||||||
writable: true,
|
writable: true,
|
||||||
|
|
@ -30,7 +30,7 @@ Object.defineProperty(window, 'service', {
|
||||||
getAgentDefs: mockGetAgentDefs,
|
getAgentDefs: mockGetAgentDefs,
|
||||||
},
|
},
|
||||||
agentInstance: {
|
agentInstance: {
|
||||||
getHandlerConfigSchema: mockGetHandlerConfigSchema,
|
getFrameworkConfigSchema: mockGetFrameworkConfigSchema,
|
||||||
},
|
},
|
||||||
agentBrowser: {
|
agentBrowser: {
|
||||||
updateTab: mockUpdateTab,
|
updateTab: mockUpdateTab,
|
||||||
|
|
@ -117,7 +117,7 @@ describe('CreateNewAgentContent', () => {
|
||||||
mockUpdateTab.mockResolvedValue(undefined);
|
mockUpdateTab.mockResolvedValue(undefined);
|
||||||
mockGetAllTabs.mockResolvedValue([]);
|
mockGetAllTabs.mockResolvedValue([]);
|
||||||
mockGetActiveTabId.mockResolvedValue('test-tab-123');
|
mockGetActiveTabId.mockResolvedValue('test-tab-123');
|
||||||
mockGetHandlerConfigSchema.mockResolvedValue({
|
mockGetFrameworkConfigSchema.mockResolvedValue({
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
prompts: {
|
prompts: {
|
||||||
|
|
@ -157,7 +157,7 @@ describe('CreateNewAgentContent', () => {
|
||||||
id: 'template-1',
|
id: 'template-1',
|
||||||
name: 'Test Template',
|
name: 'Test Template',
|
||||||
description: 'Test Description',
|
description: 'Test Description',
|
||||||
handlerConfig: { systemPrompt: 'Test prompt' },
|
agentFrameworkConfig: { systemPrompt: 'Test prompt' },
|
||||||
};
|
};
|
||||||
|
|
||||||
mockCreateAgentDef.mockResolvedValue({
|
mockCreateAgentDef.mockResolvedValue({
|
||||||
|
|
@ -258,8 +258,8 @@ describe('CreateNewAgentContent', () => {
|
||||||
const mockAgentDefinition = {
|
const mockAgentDefinition = {
|
||||||
id: 'temp-123',
|
id: 'temp-123',
|
||||||
name: 'Test Agent',
|
name: 'Test Agent',
|
||||||
handlerID: 'test-handler',
|
agentFrameworkID: 'test-handler',
|
||||||
handlerConfig: { prompts: [{ text: 'Original prompt', role: 'system' }] },
|
agentFrameworkConfig: { prompts: [{ text: 'Original prompt', role: 'system' }] },
|
||||||
};
|
};
|
||||||
|
|
||||||
mockGetAgentDef.mockResolvedValue(mockAgentDefinition);
|
mockGetAgentDef.mockResolvedValue(mockAgentDefinition);
|
||||||
|
|
@ -285,13 +285,13 @@ describe('CreateNewAgentContent', () => {
|
||||||
expect(mockUpdateAgentDef).not.toHaveBeenCalled();
|
expect(mockUpdateAgentDef).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should trigger schema loading when temporaryAgentDefinition has handlerID', async () => {
|
it('should trigger schema loading when temporaryAgentDefinition has agentFrameworkID', async () => {
|
||||||
// Mock agent definition with handlerID that will be restored
|
// Mock agent definition with agentFrameworkID that will be restored
|
||||||
const mockAgentDefinition = {
|
const mockAgentDefinition = {
|
||||||
id: 'temp-123',
|
id: 'temp-123',
|
||||||
name: 'Test Agent',
|
name: 'Test Agent',
|
||||||
handlerID: 'test-handler',
|
agentFrameworkID: 'test-handler',
|
||||||
handlerConfig: { prompts: [{ text: 'Test prompt', role: 'system' }] },
|
agentFrameworkConfig: { prompts: [{ text: 'Test prompt', role: 'system' }] },
|
||||||
};
|
};
|
||||||
|
|
||||||
mockGetAgentDef.mockResolvedValue(mockAgentDefinition);
|
mockGetAgentDef.mockResolvedValue(mockAgentDefinition);
|
||||||
|
|
@ -313,9 +313,9 @@ describe('CreateNewAgentContent', () => {
|
||||||
expect(mockGetAgentDef).toHaveBeenCalledWith('temp-123');
|
expect(mockGetAgentDef).toHaveBeenCalledWith('temp-123');
|
||||||
}, { timeout: 1000 });
|
}, { timeout: 1000 });
|
||||||
|
|
||||||
// After restoration, the component should have the handlerID and trigger schema loading
|
// After restoration, the component should have the agentFrameworkID and trigger schema loading
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(mockGetHandlerConfigSchema).toHaveBeenCalledWith('test-handler');
|
expect(mockGetFrameworkConfigSchema).toHaveBeenCalledWith('test-handler');
|
||||||
}, { timeout: 2000 });
|
}, { timeout: 2000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -341,8 +341,8 @@ describe('CreateNewAgentContent', () => {
|
||||||
const mockTemplate = {
|
const mockTemplate = {
|
||||||
id: 'template-1',
|
id: 'template-1',
|
||||||
name: 'Test Template',
|
name: 'Test Template',
|
||||||
handlerID: 'test-handler',
|
agentFrameworkID: 'test-handler',
|
||||||
handlerConfig: { prompts: [{ text: 'Test prompt', role: 'system' }] },
|
agentFrameworkConfig: { prompts: [{ text: 'Test prompt', role: 'system' }] },
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockCreatedDefinition = {
|
const mockCreatedDefinition = {
|
||||||
|
|
@ -378,8 +378,8 @@ describe('CreateNewAgentContent', () => {
|
||||||
const mockTemplate = {
|
const mockTemplate = {
|
||||||
id: 'template-1',
|
id: 'template-1',
|
||||||
name: 'Test Template',
|
name: 'Test Template',
|
||||||
handlerID: 'test-handler',
|
agentFrameworkID: 'test-handler',
|
||||||
handlerConfig: { prompts: [{ text: 'Original prompt' }] },
|
agentFrameworkConfig: { prompts: [{ text: 'Original prompt' }] },
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockCreatedDefinition = {
|
const mockCreatedDefinition = {
|
||||||
|
|
@ -443,7 +443,7 @@ describe('CreateNewAgentContent', () => {
|
||||||
expect(mockCreateAgentDef).toHaveBeenCalledWith(
|
expect(mockCreateAgentDef).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
name: 'My Agent',
|
name: 'My Agent',
|
||||||
handlerID: 'test-handler',
|
agentFrameworkID: 'test-handler',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -459,19 +459,19 @@ describe('CreateNewAgentContent', () => {
|
||||||
expect(mockUpdateAgentDef).toHaveBeenCalledWith(
|
expect(mockUpdateAgentDef).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.stringContaining('temp-'),
|
id: expect.stringContaining('temp-'),
|
||||||
handlerID: 'test-handler',
|
agentFrameworkID: 'test-handler',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}, { timeout: 500 });
|
}, { timeout: 500 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle nested prompt structure like defaultAgents.json', async () => {
|
it('should handle nested prompt structure like taskAgents.json', async () => {
|
||||||
// This is the actual structure from defaultAgents.json
|
// This is the actual structure from taskAgents.json
|
||||||
const mockTemplate = {
|
const mockTemplate = {
|
||||||
id: 'example-agent',
|
id: 'task-agent',
|
||||||
name: 'Example Agent',
|
name: 'Example Agent',
|
||||||
handlerID: 'basicPromptConcatHandler',
|
agentFrameworkID: 'basicPromptConcatHandler',
|
||||||
handlerConfig: {
|
agentFrameworkConfig: {
|
||||||
prompts: [
|
prompts: [
|
||||||
{
|
{
|
||||||
id: 'system',
|
id: 'system',
|
||||||
|
|
@ -503,7 +503,7 @@ describe('CreateNewAgentContent', () => {
|
||||||
// Step 1: Create agent definition (simulates template selection)
|
// Step 1: Create agent definition (simulates template selection)
|
||||||
const createdDef = await window.service.agentDefinition.createAgentDef(mockCreatedDefinition);
|
const createdDef = await window.service.agentDefinition.createAgentDef(mockCreatedDefinition);
|
||||||
expect(createdDef).toBeDefined();
|
expect(createdDef).toBeDefined();
|
||||||
const prompts = (createdDef.handlerConfig).prompts as Array<{
|
const prompts = (createdDef.agentFrameworkConfig).prompts as Array<{
|
||||||
children?: Array<{ text?: string }>;
|
children?: Array<{ text?: string }>;
|
||||||
}>;
|
}>;
|
||||||
expect((prompts as Array<{ children?: Array<{ text?: string }> }>)[0]?.children?.[0]?.text).toBe('You are a helpful assistant for Tiddlywiki user.');
|
expect((prompts as Array<{ children?: Array<{ text?: string }> }>)[0]?.children?.[0]?.text).toBe('You are a helpful assistant for Tiddlywiki user.');
|
||||||
|
|
@ -511,14 +511,14 @@ describe('CreateNewAgentContent', () => {
|
||||||
// Step 2: Update system prompt in nested structure
|
// Step 2: Update system prompt in nested structure
|
||||||
const updatedDefinition = {
|
const updatedDefinition = {
|
||||||
...mockCreatedDefinition,
|
...mockCreatedDefinition,
|
||||||
handlerConfig: {
|
agentFrameworkConfig: {
|
||||||
...mockCreatedDefinition.handlerConfig,
|
...mockCreatedDefinition.agentFrameworkConfig,
|
||||||
prompts: [
|
prompts: [
|
||||||
{
|
{
|
||||||
...mockCreatedDefinition.handlerConfig.prompts[0],
|
...mockCreatedDefinition.agentFrameworkConfig.prompts[0],
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
...mockCreatedDefinition.handlerConfig.prompts[0].children[0],
|
...mockCreatedDefinition.agentFrameworkConfig.prompts[0].children[0],
|
||||||
text: '你是一个专业的代码助手,请用中文回答编程问题。',
|
text: '你是一个专业的代码助手,请用中文回答编程问题。',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -532,7 +532,7 @@ describe('CreateNewAgentContent', () => {
|
||||||
// Verify the correct nested structure is updated
|
// Verify the correct nested structure is updated
|
||||||
expect(mockUpdateAgentDef).toHaveBeenCalledWith(
|
expect(mockUpdateAgentDef).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
handlerConfig: expect.objectContaining({
|
agentFrameworkConfig: expect.objectContaining({
|
||||||
prompts: expect.arrayContaining([
|
prompts: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
role: 'system',
|
role: 'system',
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ const mockCreateAgent = vi.fn();
|
||||||
const mockDeleteAgent = vi.fn();
|
const mockDeleteAgent = vi.fn();
|
||||||
const mockGetAgentDef = vi.fn();
|
const mockGetAgentDef = vi.fn();
|
||||||
const mockUpdateAgentDef = vi.fn();
|
const mockUpdateAgentDef = vi.fn();
|
||||||
const mockGetHandlerConfigSchema = vi.fn();
|
const mockGetFrameworkConfigSchema = vi.fn();
|
||||||
const mockLog = vi.fn();
|
const mockLog = vi.fn();
|
||||||
|
|
||||||
Object.defineProperty(window, 'service', {
|
Object.defineProperty(window, 'service', {
|
||||||
|
|
@ -33,7 +33,7 @@ Object.defineProperty(window, 'service', {
|
||||||
agentInstance: {
|
agentInstance: {
|
||||||
createAgent: mockCreateAgent,
|
createAgent: mockCreateAgent,
|
||||||
deleteAgent: mockDeleteAgent,
|
deleteAgent: mockDeleteAgent,
|
||||||
getHandlerConfigSchema: mockGetHandlerConfigSchema,
|
getFrameworkConfigSchema: mockGetFrameworkConfigSchema,
|
||||||
},
|
},
|
||||||
agentDefinition: {
|
agentDefinition: {
|
||||||
getAgentDef: mockGetAgentDef,
|
getAgentDef: mockGetAgentDef,
|
||||||
|
|
@ -49,7 +49,7 @@ const mockAgentDefinition = {
|
||||||
id: 'test-agent-def-id',
|
id: 'test-agent-def-id',
|
||||||
name: 'Test Agent',
|
name: 'Test Agent',
|
||||||
description: 'A test agent for editing',
|
description: 'A test agent for editing',
|
||||||
handlerID: 'testHandler',
|
agentFrameworkID: 'testHandler',
|
||||||
config: {},
|
config: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -69,7 +69,7 @@ describe('EditAgentDefinitionContent', () => {
|
||||||
mockGetAllTabs.mockResolvedValue([]);
|
mockGetAllTabs.mockResolvedValue([]);
|
||||||
mockGetActiveTabId.mockResolvedValue(null);
|
mockGetActiveTabId.mockResolvedValue(null);
|
||||||
mockGetAgentDef.mockResolvedValue(mockAgentDefinition);
|
mockGetAgentDef.mockResolvedValue(mockAgentDefinition);
|
||||||
mockGetHandlerConfigSchema.mockResolvedValue(mockSchema);
|
mockGetFrameworkConfigSchema.mockResolvedValue(mockSchema);
|
||||||
mockCreateAgent.mockResolvedValue({
|
mockCreateAgent.mockResolvedValue({
|
||||||
id: 'test-agent-id',
|
id: 'test-agent-id',
|
||||||
name: 'Test Agent',
|
name: 'Test Agent',
|
||||||
|
|
@ -234,7 +234,7 @@ describe('EditAgentDefinitionContent', () => {
|
||||||
await renderComponent();
|
await renderComponent();
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(mockGetHandlerConfigSchema).toHaveBeenCalledWith('testHandler');
|
expect(mockGetFrameworkConfigSchema).toHaveBeenCalledWith('testHandler');
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
|
|
@ -277,7 +277,7 @@ describe('EditAgentDefinitionContent', () => {
|
||||||
await renderComponent();
|
await renderComponent();
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(mockGetHandlerConfigSchema).toHaveBeenCalledWith('testHandler');
|
expect(mockGetFrameworkConfigSchema).toHaveBeenCalledWith('testHandler');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ describe('NewTabContent', () => {
|
||||||
mockCreateAgent.mockResolvedValue({
|
mockCreateAgent.mockResolvedValue({
|
||||||
id: 'test-agent-id',
|
id: 'test-agent-id',
|
||||||
name: 'Test Agent',
|
name: 'Test Agent',
|
||||||
agentDefId: 'example-agent',
|
agentDefId: 'task-agent',
|
||||||
});
|
});
|
||||||
mockAddTab.mockResolvedValue({
|
mockAddTab.mockResolvedValue({
|
||||||
id: 'test-tab-id',
|
id: 'test-tab-id',
|
||||||
|
|
|
||||||
|
|
@ -302,35 +302,35 @@ export const agentActions = (
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getHandlerId: async () => {
|
getAgentFrameworkId: async () => {
|
||||||
try {
|
try {
|
||||||
const { agent, agentDef } = get();
|
const { agent, agentDef } = get();
|
||||||
if (agentDef?.handlerID) {
|
if (agentDef?.agentFrameworkID) {
|
||||||
return agentDef.handlerID;
|
return agentDef.agentFrameworkID;
|
||||||
}
|
}
|
||||||
if (agent?.agentDefId) {
|
if (agent?.agentDefId) {
|
||||||
const fetchedAgentDefinition = await window.service.agentDefinition.getAgentDef(agent.agentDefId);
|
const fetchedAgentDefinition = await window.service.agentDefinition.getAgentDef(agent.agentDefId);
|
||||||
if (fetchedAgentDefinition?.handlerID) {
|
if (fetchedAgentDefinition?.agentFrameworkID) {
|
||||||
return fetchedAgentDefinition.handlerID;
|
return fetchedAgentDefinition.agentFrameworkID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('No active agent in store or handler ID not found');
|
throw new Error('No active agent in store or agent framework ID not found');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
const finalError = new Error(`Failed to get handler ID: ${errorMessage}`);
|
const finalError = new Error(`Failed to get agent framework ID: ${errorMessage}`);
|
||||||
set({ error: finalError });
|
set({ error: finalError });
|
||||||
throw finalError;
|
throw finalError;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get handler configuration schema for current handler
|
* Get framework configuration schema for current framework
|
||||||
*/
|
*/
|
||||||
getHandlerConfigSchema: async () => {
|
getFrameworkConfigSchema: async () => {
|
||||||
try {
|
try {
|
||||||
const handlerId = await get().getHandlerId();
|
const agentFrameworkId = await get().getAgentFrameworkId();
|
||||||
return await window.service.agentInstance.getHandlerConfigSchema(handlerId);
|
return await window.service.agentInstance.getFrameworkConfigSchema(agentFrameworkId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
const finalError = new Error(`Failed to get handler schema: ${errorMessage}`);
|
const finalError = new Error(`Failed to get handler schema: ${errorMessage}`);
|
||||||
|
|
|
||||||
|
|
@ -70,14 +70,14 @@ export const previewActionsMiddleware: StateCreator<AgentChatStoreType, [], [],
|
||||||
|
|
||||||
getPreviewPromptResult: async (
|
getPreviewPromptResult: async (
|
||||||
inputText: string,
|
inputText: string,
|
||||||
handlerConfig: AgentPromptDescription['handlerConfig'],
|
agentFrameworkConfig: AgentPromptDescription['agentFrameworkConfig'],
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
set({ previewLoading: true });
|
set({ previewLoading: true });
|
||||||
const messages = Array.from(get().messages.values());
|
const messages = Array.from(get().messages.values());
|
||||||
|
|
||||||
// Safety check - if handlerConfig is empty, fail early
|
// Safety check - if agentFrameworkConfig is empty, fail early
|
||||||
if (Object.keys(handlerConfig).length === 0) {
|
if (!agentFrameworkConfig || Object.keys(agentFrameworkConfig).length === 0) {
|
||||||
set({ previewLoading: false, previewResult: null });
|
set({ previewLoading: false, previewResult: null });
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +93,7 @@ export const previewActionsMiddleware: StateCreator<AgentChatStoreType, [], [],
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the streaming API with progress updates
|
// Use the streaming API with progress updates
|
||||||
const concatStream = window.observables.agentInstance.concatPrompt({ handlerConfig }, messages);
|
const concatStream = window.observables.agentInstance.concatPrompt({ agentFrameworkConfig }, messages);
|
||||||
|
|
||||||
// Initialize progress
|
// Initialize progress
|
||||||
set({
|
set({
|
||||||
|
|
@ -113,7 +113,7 @@ export const previewActionsMiddleware: StateCreator<AgentChatStoreType, [], [],
|
||||||
next: (state) => {
|
next: (state) => {
|
||||||
// Update progress and current step
|
// Update progress and current step
|
||||||
const stepDescription = state.step === 'plugin'
|
const stepDescription = state.step === 'plugin'
|
||||||
? `Processing plugin: ${state.currentPlugin?.pluginId || 'unknown'}`
|
? `Processing tool: ${state.currentPlugin?.toolId || 'unknown'}`
|
||||||
: state.step === 'finalize'
|
: state.step === 'finalize'
|
||||||
? 'Finalizing prompts...'
|
? 'Finalizing prompts...'
|
||||||
: state.step === 'flatten'
|
: state.step === 'flatten'
|
||||||
|
|
@ -123,7 +123,7 @@ export const previewActionsMiddleware: StateCreator<AgentChatStoreType, [], [],
|
||||||
set({
|
set({
|
||||||
previewProgress: state.progress,
|
previewProgress: state.progress,
|
||||||
previewCurrentStep: stepDescription,
|
previewCurrentStep: stepDescription,
|
||||||
previewCurrentPlugin: state.currentPlugin?.pluginId || null,
|
previewCurrentPlugin: state.currentPlugin?.toolId || null,
|
||||||
// Update intermediate results
|
// Update intermediate results
|
||||||
previewResult: {
|
previewResult: {
|
||||||
flatPrompts: state.flatPrompts,
|
flatPrompts: state.flatPrompts,
|
||||||
|
|
|
||||||
|
|
@ -90,10 +90,10 @@ export interface BasicActions {
|
||||||
cancelAgent: () => Promise<void>;
|
cancelAgent: () => Promise<void>;
|
||||||
|
|
||||||
/** Get the handler ID for the current agent */
|
/** Get the handler ID for the current agent */
|
||||||
getHandlerId: () => Promise<string>;
|
getAgentFrameworkId: () => Promise<string>;
|
||||||
|
|
||||||
/** Get the configuration schema for the current handler */
|
/** Get the configuration schema for the current handler */
|
||||||
getHandlerConfigSchema: () => Promise<Record<string, unknown>>;
|
getFrameworkConfigSchema: () => Promise<Record<string, unknown>>;
|
||||||
|
|
||||||
/** Process raw agent data into store format */
|
/** Process raw agent data into store format */
|
||||||
processAgentData: (
|
processAgentData: (
|
||||||
|
|
@ -188,12 +188,12 @@ export interface PreviewActions {
|
||||||
/**
|
/**
|
||||||
* Generates a preview of prompts for the current agent state
|
* Generates a preview of prompts for the current agent state
|
||||||
* @param inputText Input text to include in the preview
|
* @param inputText Input text to include in the preview
|
||||||
* @param handlerConfig Prompt configuration to use for preview
|
* @param agentFrameworkConfig Framework configuration to use for preview
|
||||||
* @returns Promise that resolves when preview is generated and state is updated
|
* @returns Promise that resolves when preview is generated and state is updated
|
||||||
*/
|
*/
|
||||||
getPreviewPromptResult: (
|
getPreviewPromptResult: (
|
||||||
inputText: string,
|
inputText: string,
|
||||||
handlerConfig: AgentPromptDescription['handlerConfig'],
|
agentFrameworkConfig: AgentPromptDescription['agentFrameworkConfig'],
|
||||||
) => Promise<
|
) => Promise<
|
||||||
{
|
{
|
||||||
flatPrompts: ModelMessage[];
|
flatPrompts: ModelMessage[];
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useHandlerConfigManagement } from '@/windows/Preferences/sections/ExternalAPI/useHandlerConfigManagement';
|
import { useAgentFrameworkConfigManagement } from '@/windows/Preferences/sections/ExternalAPI/useAgentFrameworkConfigManagement';
|
||||||
import MonacoEditor from '@monaco-editor/react';
|
import MonacoEditor from '@monaco-editor/react';
|
||||||
import { Box, styled } from '@mui/material';
|
import { Box, styled } from '@mui/material';
|
||||||
import Tab from '@mui/material/Tab';
|
import Tab from '@mui/material/Tab';
|
||||||
|
|
@ -9,7 +9,7 @@ import React, { FC, SyntheticEvent, useCallback, useEffect, useState } from 'rea
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
|
|
||||||
import { HandlerConfig } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
import { AgentFrameworkConfig } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
import { useAgentChatStore } from '../../../Agent/store/agentChatStore/index';
|
import { useAgentChatStore } from '../../../Agent/store/agentChatStore/index';
|
||||||
import { PromptConfigForm } from './PromptConfigForm';
|
import { PromptConfigForm } from './PromptConfigForm';
|
||||||
|
|
||||||
|
|
@ -40,11 +40,11 @@ export const EditView: FC<EditViewProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
loading: handlerConfigLoading,
|
loading: agentFrameworkConfigLoading,
|
||||||
config: handlerConfig,
|
config: agentFrameworkConfig,
|
||||||
schema: handlerSchema,
|
schema: handlerSchema,
|
||||||
handleConfigChange,
|
handleConfigChange,
|
||||||
} = useHandlerConfigManagement({
|
} = useAgentFrameworkConfigManagement({
|
||||||
agentDefId: agent?.agentDefId,
|
agentDefId: agent?.agentDefId,
|
||||||
agentId: agent?.id,
|
agentId: agent?.id,
|
||||||
});
|
});
|
||||||
|
|
@ -98,7 +98,7 @@ export const EditView: FC<EditViewProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFormChange = useDebouncedCallback(
|
const handleFormChange = useDebouncedCallback(
|
||||||
async (updatedConfig: HandlerConfig) => {
|
async (updatedConfig: AgentFrameworkConfig) => {
|
||||||
try {
|
try {
|
||||||
// Ensure the config change is fully persisted before proceeding
|
// Ensure the config change is fully persisted before proceeding
|
||||||
await handleConfigChange(updatedConfig);
|
await handleConfigChange(updatedConfig);
|
||||||
|
|
@ -121,7 +121,7 @@ export const EditView: FC<EditViewProps> = ({
|
||||||
const handleEditorChange = useCallback((value: string | undefined) => {
|
const handleEditorChange = useCallback((value: string | undefined) => {
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
try {
|
try {
|
||||||
const parsedConfig = JSON.parse(value) as HandlerConfig;
|
const parsedConfig = JSON.parse(value) as AgentFrameworkConfig;
|
||||||
void handleFormChange(parsedConfig);
|
void handleFormChange(parsedConfig);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
void window.service.native.log('error', 'EditView: Invalid JSON in code editor:', { error });
|
void window.service.native.log('error', 'EditView: Invalid JSON in code editor:', { error });
|
||||||
|
|
@ -163,16 +163,16 @@ export const EditView: FC<EditViewProps> = ({
|
||||||
{editorMode === 'form' && (
|
{editorMode === 'form' && (
|
||||||
<PromptConfigForm
|
<PromptConfigForm
|
||||||
schema={handlerSchema ?? {}}
|
schema={handlerSchema ?? {}}
|
||||||
formData={handlerConfig}
|
formData={agentFrameworkConfig}
|
||||||
onChange={handleFormChange}
|
onChange={handleFormChange}
|
||||||
loading={handlerConfigLoading}
|
loading={agentFrameworkConfigLoading}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{editorMode === 'code' && (
|
{editorMode === 'code' && (
|
||||||
<MonacoEditor
|
<MonacoEditor
|
||||||
height='100%'
|
height='100%'
|
||||||
defaultLanguage='json'
|
defaultLanguage='json'
|
||||||
value={handlerConfig ? JSON.stringify(handlerConfig, null, 2) : '{}'}
|
value={agentFrameworkConfig ? JSON.stringify(agentFrameworkConfig, null, 2) : '{}'}
|
||||||
onChange={handleEditorChange}
|
onChange={handleEditorChange}
|
||||||
options={{
|
options={{
|
||||||
minimap: { enabled: true },
|
minimap: { enabled: true },
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { IChangeEvent } from '@rjsf/core';
|
||||||
import Form from '@rjsf/mui';
|
import Form from '@rjsf/mui';
|
||||||
import { ObjectFieldTemplateProps, RJSFSchema, RJSFValidationError } from '@rjsf/utils';
|
import { ObjectFieldTemplateProps, RJSFSchema, RJSFValidationError } from '@rjsf/utils';
|
||||||
import validator from '@rjsf/validator-ajv8';
|
import validator from '@rjsf/validator-ajv8';
|
||||||
import { HandlerConfig } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
import { AgentFrameworkConfig } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ErrorDisplay } from './components/ErrorDisplay';
|
import { ErrorDisplay } from './components/ErrorDisplay';
|
||||||
|
|
@ -37,9 +37,9 @@ interface PromptConfigFormProps {
|
||||||
/** UI schema for layout customization */
|
/** UI schema for layout customization */
|
||||||
uiSchema?: Record<string, unknown>;
|
uiSchema?: Record<string, unknown>;
|
||||||
/** Initial form data */
|
/** Initial form data */
|
||||||
formData?: HandlerConfig;
|
formData?: AgentFrameworkConfig;
|
||||||
/** Change handler for form data */
|
/** Change handler for form data */
|
||||||
onChange?: (formData: HandlerConfig) => void;
|
onChange?: (formData: AgentFrameworkConfig) => void;
|
||||||
/** Error handler for form validation errors */
|
/** Error handler for form validation errors */
|
||||||
onError?: (errors: RJSFValidationError[]) => void;
|
onError?: (errors: RJSFValidationError[]) => void;
|
||||||
/** Whether the form is disabled */
|
/** Whether the form is disabled */
|
||||||
|
|
@ -87,7 +87,7 @@ export const PromptConfigForm: React.FC<PromptConfigFormProps> = ({
|
||||||
onError?.(errors);
|
onError?.(errors);
|
||||||
}, [onError]);
|
}, [onError]);
|
||||||
|
|
||||||
const handleChange = useCallback((changeEvent: IChangeEvent<HandlerConfig>) => {
|
const handleChange = useCallback((changeEvent: IChangeEvent<AgentFrameworkConfig>) => {
|
||||||
const formData = changeEvent.formData;
|
const formData = changeEvent.formData;
|
||||||
if (formData) {
|
if (formData) {
|
||||||
onChange?.(formData);
|
onChange?.(formData);
|
||||||
|
|
|
||||||
|
|
@ -9,16 +9,16 @@ import { ThemeProvider } from '@mui/material/styles';
|
||||||
import { lightTheme } from '@services/theme/defaultTheme';
|
import { lightTheme } from '@services/theme/defaultTheme';
|
||||||
|
|
||||||
import { useAgentChatStore } from '@/pages/Agent/store/agentChatStore/index';
|
import { useAgentChatStore } from '@/pages/Agent/store/agentChatStore/index';
|
||||||
import defaultAgents from '@services/agentInstance/buildInAgentHandlers/defaultAgents.json';
|
import defaultAgents from '@services/agentInstance/agentFrameworks/taskAgents.json';
|
||||||
import { IPrompt } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
import { IPrompt } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
import { ModelMessage } from 'ai';
|
import { ModelMessage } from 'ai';
|
||||||
import { PromptPreviewDialog } from '../index';
|
import { PromptPreviewDialog } from '../index';
|
||||||
|
|
||||||
// Mock handler config management hook
|
// Mock handler config management hook
|
||||||
vi.mock('@/windows/Preferences/sections/ExternalAPI/useHandlerConfigManagement', () => ({
|
vi.mock('@/windows/Preferences/sections/ExternalAPI/useAgentFrameworkConfigManagement', () => ({
|
||||||
useHandlerConfigManagement: vi.fn(() => ({
|
useAgentFrameworkConfigManagement: vi.fn(() => ({
|
||||||
loading: false,
|
loading: false,
|
||||||
config: defaultAgents[0].handlerConfig,
|
config: defaultAgents[0].agentFrameworkConfig,
|
||||||
handleConfigChange: vi.fn(),
|
handleConfigChange: vi.fn(),
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
@ -36,7 +36,7 @@ describe('PromptPreviewDialog - Tool Information Rendering', () => {
|
||||||
useAgentChatStore.setState({
|
useAgentChatStore.setState({
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'example-agent',
|
agentDefId: 'task-agent',
|
||||||
status: { state: 'working', modified: new Date() },
|
status: { state: 'working', modified: new Date() },
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
},
|
},
|
||||||
|
|
@ -63,8 +63,8 @@ describe('PromptPreviewDialog - Tool Information Rendering', () => {
|
||||||
// Test if the real concatPrompt is working
|
// Test if the real concatPrompt is working
|
||||||
expect(globalThis.window?.observables?.agentInstance?.concatPrompt).toBeDefined();
|
expect(globalThis.window?.observables?.agentInstance?.concatPrompt).toBeDefined();
|
||||||
|
|
||||||
// Create test data matching defaultAgents.json - cast to avoid type issues in test
|
// Create test data matching taskAgents.json - cast to avoid type issues in test
|
||||||
const handlerConfig = defaultAgents[0].handlerConfig as never;
|
const agentFrameworkConfig = defaultAgents[0].agentFrameworkConfig as never;
|
||||||
const messages = [{
|
const messages = [{
|
||||||
id: 'test-message-1',
|
id: 'test-message-1',
|
||||||
agentId: 'test-agent',
|
agentId: 'test-agent',
|
||||||
|
|
@ -74,7 +74,7 @@ describe('PromptPreviewDialog - Tool Information Rendering', () => {
|
||||||
|
|
||||||
// Call the real concatPrompt implementation
|
// Call the real concatPrompt implementation
|
||||||
const observable = globalThis.window.observables.agentInstance.concatPrompt(
|
const observable = globalThis.window.observables.agentInstance.concatPrompt(
|
||||||
{ handlerConfig },
|
{ agentFrameworkConfig },
|
||||||
messages,
|
messages,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -118,11 +118,11 @@ describe('PromptPreviewDialog - Tool Information Rendering', () => {
|
||||||
|
|
||||||
it('should render workspaces and tools info from real concatPrompt execution', async () => {
|
it('should render workspaces and tools info from real concatPrompt execution', async () => {
|
||||||
// First execute real concatPrompt to get the structured data
|
// First execute real concatPrompt to get the structured data
|
||||||
const handlerConfig = defaultAgents[0].handlerConfig;
|
const agentFrameworkConfig = defaultAgents[0].agentFrameworkConfig;
|
||||||
const messages = [{ id: 'test', role: 'user' as const, content: 'Hello world', created: new Date(), modified: new Date(), agentId: 'test' }];
|
const messages = [{ id: 'test', role: 'user' as const, content: 'Hello world', created: new Date(), modified: new Date(), agentId: 'test' }];
|
||||||
|
|
||||||
// Pass handlerConfig wrapped (same shape used elsewhere)
|
// Pass agentFrameworkConfig wrapped (same shape used elsewhere)
|
||||||
const observable = window.observables.agentInstance.concatPrompt({ handlerConfig } as never, messages);
|
const observable = window.observables.agentInstance.concatPrompt({ agentFrameworkConfig } as never, messages);
|
||||||
|
|
||||||
const results: unknown[] = [];
|
const results: unknown[] = [];
|
||||||
let finalResult: { flatPrompts: ModelMessage[]; processedPrompts: IPrompt[] } | undefined;
|
let finalResult: { flatPrompts: ModelMessage[]; processedPrompts: IPrompt[] } | undefined;
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,14 @@ import { ThemeProvider } from '@mui/material/styles';
|
||||||
import { lightTheme } from '@services/theme/defaultTheme';
|
import { lightTheme } from '@services/theme/defaultTheme';
|
||||||
|
|
||||||
import { useAgentChatStore } from '@/pages/Agent/store/agentChatStore/index';
|
import { useAgentChatStore } from '@/pages/Agent/store/agentChatStore/index';
|
||||||
import defaultAgents from '@services/agentInstance/buildInAgentHandlers/defaultAgents.json';
|
import defaultAgents from '@services/agentInstance/agentFrameworks/taskAgents.json';
|
||||||
import { PromptPreviewDialog } from '../index';
|
import { PromptPreviewDialog } from '../index';
|
||||||
|
|
||||||
// Mock handler config management hook
|
// Mock handler config management hook
|
||||||
vi.mock('@/windows/Preferences/sections/ExternalAPI/useHandlerConfigManagement', () => ({
|
vi.mock('@/windows/Preferences/sections/ExternalAPI/useAgentFrameworkConfigManagement', () => ({
|
||||||
useHandlerConfigManagement: vi.fn(() => ({
|
useAgentFrameworkConfigManagement: vi.fn(() => ({
|
||||||
loading: false,
|
loading: false,
|
||||||
config: defaultAgents[0].handlerConfig,
|
config: defaultAgents[0].agentFrameworkConfig,
|
||||||
handleConfigChange: vi.fn(),
|
handleConfigChange: vi.fn(),
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useHandlerConfigManagement } from '@/windows/Preferences/sections/ExternalAPI/useHandlerConfigManagement';
|
import { useAgentFrameworkConfigManagement } from '@/windows/Preferences/sections/ExternalAPI/useAgentFrameworkConfigManagement';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import EditIcon from '@mui/icons-material/Edit';
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
import FullscreenIcon from '@mui/icons-material/Fullscreen';
|
import FullscreenIcon from '@mui/icons-material/Fullscreen';
|
||||||
|
|
@ -36,9 +36,9 @@ export const PromptPreviewDialog: React.FC<PromptPreviewDialogProps> = ({
|
||||||
const [isEditMode, setIsEditMode] = useState(false);
|
const [isEditMode, setIsEditMode] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
loading: handlerConfigLoading,
|
loading: agentFrameworkConfigLoading,
|
||||||
config: handlerConfig,
|
config: agentFrameworkConfig,
|
||||||
} = useHandlerConfigManagement({
|
} = useAgentFrameworkConfigManagement({
|
||||||
agentDefId: agent?.agentDefId,
|
agentDefId: agent?.agentDefId,
|
||||||
agentId: agent?.id,
|
agentId: agent?.id,
|
||||||
});
|
});
|
||||||
|
|
@ -54,17 +54,17 @@ export const PromptPreviewDialog: React.FC<PromptPreviewDialogProps> = ({
|
||||||
);
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchInitialPreview = async () => {
|
const fetchInitialPreview = async () => {
|
||||||
if (!agent?.agentDefId || handlerConfigLoading || !handlerConfig || !open) {
|
if (!agent?.agentDefId || agentFrameworkConfigLoading || !agentFrameworkConfig || !open) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await getPreviewPromptResult(inputText, handlerConfig);
|
await getPreviewPromptResult(inputText, agentFrameworkConfig);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('PromptPreviewDialog: Error fetching initial preview:', error);
|
console.error('PromptPreviewDialog: Error fetching initial preview:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
void fetchInitialPreview();
|
void fetchInitialPreview();
|
||||||
}, [agent?.agentDefId, handlerConfig, handlerConfigLoading, inputText, open]); // 移除 getPreviewPromptResult
|
}, [agent?.agentDefId, agentFrameworkConfig, agentFrameworkConfigLoading, inputText, open]); // 移除 getPreviewPromptResult
|
||||||
|
|
||||||
const handleToggleFullScreen = useCallback((): void => {
|
const handleToggleFullScreen = useCallback((): void => {
|
||||||
setIsFullScreen(previous => !previous);
|
setIsFullScreen(previous => !previous);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { AgentDefinitionService } from '@services/agentDefinition';
|
import { AgentDefinitionService } from '@services/agentDefinition';
|
||||||
import { AgentDefinition } from '@services/agentDefinition/interface';
|
import { AgentDefinition } from '@services/agentDefinition/interface';
|
||||||
import defaultAgents from '@services/agentInstance/buildInAgentHandlers/defaultAgents.json';
|
import defaultAgents from '@services/agentInstance/agentFrameworks/taskAgents.json';
|
||||||
import type { IAgentInstanceService } from '@services/agentInstance/interface';
|
import type { IAgentInstanceService } from '@services/agentInstance/interface';
|
||||||
import { container } from '@services/container';
|
import { container } from '@services/container';
|
||||||
import type { IDatabaseService } from '@services/database/interface';
|
import type { IDatabaseService } from '@services/database/interface';
|
||||||
|
|
@ -90,12 +90,13 @@ describe('AgentDefinitionService getAgentDefs integration', () => {
|
||||||
const defs = await freshService.getAgentDefs();
|
const defs = await freshService.getAgentDefs();
|
||||||
expect(defs.length).toBeGreaterThan(0);
|
expect(defs.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
const exampleAgent = defs.find(d => d.id === (defaultAgents as AgentDefinition[])[0].id);
|
// Fixed
|
||||||
|
const exampleAgent = defs.find(d => d.id === (defaultAgents as unknown as AgentDefinition[])[0].id);
|
||||||
expect(exampleAgent).toBeDefined();
|
expect(exampleAgent).toBeDefined();
|
||||||
expect(exampleAgent!.name).toBeDefined();
|
expect(exampleAgent!.name).toBeDefined();
|
||||||
expect(exampleAgent!.handlerID).toBeDefined();
|
expect(exampleAgent!.agentFrameworkID).toBeDefined();
|
||||||
expect(exampleAgent!.handlerConfig).toBeDefined();
|
expect(exampleAgent!.agentFrameworkConfig).toBeDefined();
|
||||||
expect(typeof exampleAgent!.handlerConfig).toBe('object');
|
expect(typeof exampleAgent!.agentFrameworkConfig).toBe('object');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return only database data without fallback to defaultAgents', async () => {
|
it('should return only database data without fallback to defaultAgents', async () => {
|
||||||
|
|
@ -105,7 +106,7 @@ describe('AgentDefinitionService getAgentDefs integration', () => {
|
||||||
const agentDefRepo = realDataSource.getRepository(AgentDefinitionEntity);
|
const agentDefRepo = realDataSource.getRepository(AgentDefinitionEntity);
|
||||||
|
|
||||||
// Save only minimal record (id only) to test new behavior
|
// Save only minimal record (id only) to test new behavior
|
||||||
const example = (defaultAgents as AgentDefinition[])[0];
|
const example = (defaultAgents as unknown as AgentDefinition[])[0];
|
||||||
await agentDefRepo.save({
|
await agentDefRepo.save({
|
||||||
id: example.id,
|
id: example.id,
|
||||||
});
|
});
|
||||||
|
|
@ -116,11 +117,11 @@ describe('AgentDefinitionService getAgentDefs integration', () => {
|
||||||
expect(found).toBeDefined();
|
expect(found).toBeDefined();
|
||||||
// With new behavior, only id should be present, other fields should be undefined or empty
|
// With new behavior, only id should be present, other fields should be undefined or empty
|
||||||
expect(found!.id).toBe(example.id);
|
expect(found!.id).toBe(example.id);
|
||||||
expect(found!.handlerID).toBeUndefined();
|
expect(found!.agentFrameworkID).toBeUndefined();
|
||||||
expect(found!.name).toBeUndefined();
|
expect(found!.name).toBeUndefined();
|
||||||
expect(found!.description).toBeUndefined();
|
expect(found!.description).toBeUndefined();
|
||||||
expect(found!.avatarUrl).toBeUndefined();
|
expect(found!.avatarUrl).toBeUndefined();
|
||||||
expect(found!.handlerConfig).toEqual({});
|
expect(found!.agentFrameworkConfig).toEqual({});
|
||||||
expect(found!.aiApiConfig).toBeUndefined();
|
expect(found!.aiApiConfig).toBeUndefined();
|
||||||
expect(found!.agentTools).toBeUndefined();
|
expect(found!.agentTools).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
@ -132,7 +133,7 @@ describe('AgentDefinitionService getAgentDefs integration', () => {
|
||||||
const agentDefRepo = realDataSource.getRepository(AgentDefinitionEntity);
|
const agentDefRepo = realDataSource.getRepository(AgentDefinitionEntity);
|
||||||
|
|
||||||
// Save only minimal record (id only) as per new behavior
|
// Save only minimal record (id only) as per new behavior
|
||||||
const example = (defaultAgents as AgentDefinition[])[0];
|
const example = (defaultAgents as unknown as AgentDefinition[])[0];
|
||||||
await agentDefRepo.save({
|
await agentDefRepo.save({
|
||||||
id: example.id,
|
id: example.id,
|
||||||
});
|
});
|
||||||
|
|
@ -148,8 +149,8 @@ describe('AgentDefinitionService getAgentDefs integration', () => {
|
||||||
expect(entity!.name).toBeNull();
|
expect(entity!.name).toBeNull();
|
||||||
expect(entity!.description).toBeNull();
|
expect(entity!.description).toBeNull();
|
||||||
expect(entity!.avatarUrl).toBeNull();
|
expect(entity!.avatarUrl).toBeNull();
|
||||||
expect(entity!.handlerID).toBeNull();
|
expect(entity!.agentFrameworkID).toBeNull();
|
||||||
expect(entity!.handlerConfig).toBeNull();
|
expect(entity!.agentFrameworkConfig).toBeNull();
|
||||||
expect(entity!.aiApiConfig).toBeNull();
|
expect(entity!.aiApiConfig).toBeNull();
|
||||||
expect(entity!.agentTools).toBeNull();
|
expect(entity!.agentTools).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
@ -158,15 +159,15 @@ describe('AgentDefinitionService getAgentDefs integration', () => {
|
||||||
const templates = await agentDefinitionService.getAgentTemplates();
|
const templates = await agentDefinitionService.getAgentTemplates();
|
||||||
|
|
||||||
// Should include all default agents
|
// Should include all default agents
|
||||||
expect(templates.length).toBe((defaultAgents as AgentDefinition[]).length);
|
expect(templates.length).toBe((defaultAgents as unknown as AgentDefinition[]).length);
|
||||||
|
|
||||||
// Check that template has complete data from defaultAgents.json
|
// Check that template has complete data from taskAgents.json
|
||||||
const exampleTemplate = templates.find(t => t.id === (defaultAgents as AgentDefinition[])[0].id);
|
const exampleTemplate = templates.find(t => t.id === (defaultAgents as unknown as AgentDefinition[])[0].id);
|
||||||
expect(exampleTemplate).toBeDefined();
|
expect(exampleTemplate).toBeDefined();
|
||||||
expect(exampleTemplate!.name).toBeDefined();
|
expect(exampleTemplate!.name).toBeDefined();
|
||||||
expect(exampleTemplate!.handlerID).toBeDefined();
|
expect(exampleTemplate!.agentFrameworkID).toBeDefined();
|
||||||
expect(exampleTemplate!.handlerConfig).toBeDefined();
|
expect(exampleTemplate!.agentFrameworkConfig).toBeDefined();
|
||||||
expect(typeof exampleTemplate!.handlerConfig).toBe('object');
|
expect(typeof exampleTemplate!.agentFrameworkConfig).toBe('object');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not throw when searchName filtering is requested (client-side filtering expected)', async () => {
|
it('should not throw when searchName filtering is requested (client-side filtering expected)', async () => {
|
||||||
|
|
@ -185,6 +186,6 @@ describe('AgentDefinitionService getAgentDefs integration', () => {
|
||||||
|
|
||||||
// Should still return default agents and not throw
|
// Should still return default agents and not throw
|
||||||
const templates = await agentDefinitionService.getAgentTemplates();
|
const templates = await agentDefinitionService.getAgentTemplates();
|
||||||
expect(templates.length).toBe((defaultAgents as AgentDefinition[]).length);
|
expect(templates.length).toBe((defaultAgents as unknown as AgentDefinition[]).length);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -116,21 +116,21 @@ export function validateAndConvertWikiTiddlerToAgentTemplate(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Try to parse the tiddler text as JSON for agent configuration
|
// Try to parse the tiddler text as JSON for agent configuration
|
||||||
let handlerConfig: Record<string, unknown>;
|
let agentFrameworkConfig: Record<string, unknown>;
|
||||||
try {
|
try {
|
||||||
const textContent = typeof tiddler.text === 'string' ? tiddler.text : JSON.stringify(tiddler.text || '{}');
|
const textContent = typeof tiddler.text === 'string' ? tiddler.text : JSON.stringify(tiddler.text || '{}');
|
||||||
const parsed = JSON.parse(textContent) as unknown;
|
const parsed = JSON.parse(textContent) as unknown;
|
||||||
|
|
||||||
// Ensure handlerConfig is a valid object
|
// Ensure agentFrameworkConfig is a valid object
|
||||||
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
||||||
logger.warn('Invalid handlerConfig in tiddler', {
|
logger.warn('Invalid agentFrameworkConfig in tiddler', {
|
||||||
function: 'validateAndConvertWikiTiddlerToAgentTemplate',
|
function: 'validateAndConvertWikiTiddlerToAgentTemplate',
|
||||||
title: getStringField(tiddler.title),
|
title: getStringField(tiddler.title),
|
||||||
reason: 'not an object',
|
reason: 'not an object',
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
handlerConfig = parsed as Record<string, unknown>;
|
agentFrameworkConfig = parsed as Record<string, unknown>;
|
||||||
} catch (parseError) {
|
} catch (parseError) {
|
||||||
logger.warn('Failed to parse agent template from tiddler', {
|
logger.warn('Failed to parse agent template from tiddler', {
|
||||||
function: 'validateAndConvertWikiTiddlerToAgentTemplate',
|
function: 'validateAndConvertWikiTiddlerToAgentTemplate',
|
||||||
|
|
@ -146,8 +146,8 @@ export function validateAndConvertWikiTiddlerToAgentTemplate(
|
||||||
name: getStringField(tiddler.caption) || getStringField(tiddler.title),
|
name: getStringField(tiddler.caption) || getStringField(tiddler.title),
|
||||||
description: getStringField(tiddler.description) || `Agent template from ${workspaceName || 'wiki'}`,
|
description: getStringField(tiddler.description) || `Agent template from ${workspaceName || 'wiki'}`,
|
||||||
avatarUrl: getStringField(tiddler.avatar_url) || undefined,
|
avatarUrl: getStringField(tiddler.avatar_url) || undefined,
|
||||||
handlerID: getStringField(tiddler.handler_id) || 'basicPromptConcatHandler',
|
agentFrameworkID: getStringField(tiddler.agentFrameworkID) || 'basicPromptConcatHandler',
|
||||||
handlerConfig,
|
agentFrameworkConfig,
|
||||||
aiApiConfig: parseAiApiConfig(tiddler.ai_api_config),
|
aiApiConfig: parseAiApiConfig(tiddler.ai_api_config),
|
||||||
agentTools: parseAgentTools(tiddler.agent_tools),
|
agentTools: parseAgentTools(tiddler.agent_tools),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { nanoid } from 'nanoid';
|
||||||
import { DataSource, Repository } from 'typeorm';
|
import { DataSource, Repository } from 'typeorm';
|
||||||
|
|
||||||
import type { IAgentBrowserService } from '@services/agentBrowser/interface';
|
import type { IAgentBrowserService } from '@services/agentBrowser/interface';
|
||||||
import defaultAgents from '@services/agentInstance/buildInAgentHandlers/defaultAgents.json';
|
import defaultAgents from '@services/agentInstance/agentFrameworks/taskAgents.json';
|
||||||
import type { IAgentInstanceService } from '@services/agentInstance/interface';
|
import type { IAgentInstanceService } from '@services/agentInstance/interface';
|
||||||
import { container } from '@services/container';
|
import { container } from '@services/container';
|
||||||
import type { IDatabaseService } from '@services/database/interface';
|
import type { IDatabaseService } from '@services/database/interface';
|
||||||
|
|
@ -71,15 +71,15 @@ export class AgentDefinitionService implements IAgentDefinitionService {
|
||||||
if (existingCount === 0) {
|
if (existingCount === 0) {
|
||||||
logger.info('Agent database is empty, initializing with default agents');
|
logger.info('Agent database is empty, initializing with default agents');
|
||||||
const defaultAgentsList = defaultAgents as AgentDefinition[];
|
const defaultAgentsList = defaultAgents as AgentDefinition[];
|
||||||
// Create agent definition entities with complete data from defaultAgents.json
|
// Create agent definition entities with complete data from taskAgents.json
|
||||||
const agentDefinitionEntities = defaultAgentsList.map(defaultAgent =>
|
const agentDefinitionEntities = defaultAgentsList.map(defaultAgent =>
|
||||||
this.agentDefRepository!.create({
|
this.agentDefRepository!.create({
|
||||||
id: defaultAgent.id,
|
id: defaultAgent.id,
|
||||||
name: defaultAgent.name,
|
name: defaultAgent.name,
|
||||||
description: defaultAgent.description,
|
description: defaultAgent.description,
|
||||||
avatarUrl: defaultAgent.avatarUrl,
|
avatarUrl: defaultAgent.avatarUrl,
|
||||||
handlerID: defaultAgent.handlerID,
|
agentFrameworkID: defaultAgent.agentFrameworkID,
|
||||||
handlerConfig: defaultAgent.handlerConfig,
|
agentFrameworkConfig: defaultAgent.agentFrameworkConfig,
|
||||||
aiApiConfig: defaultAgent.aiApiConfig,
|
aiApiConfig: defaultAgent.aiApiConfig,
|
||||||
agentTools: defaultAgent.agentTools,
|
agentTools: defaultAgent.agentTools,
|
||||||
})
|
})
|
||||||
|
|
@ -143,7 +143,7 @@ export class AgentDefinitionService implements IAgentDefinitionService {
|
||||||
throw new Error(`Agent definition not found: ${agent.id}`);
|
throw new Error(`Agent definition not found: ${agent.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pickedProperties = pick(agent, ['name', 'description', 'avatarUrl', 'handlerID', 'handlerConfig', 'aiApiConfig']);
|
const pickedProperties = pick(agent, ['name', 'description', 'avatarUrl', 'agentFrameworkID', 'agentFrameworkConfig', 'aiApiConfig']);
|
||||||
Object.assign(existingAgent, pickedProperties);
|
Object.assign(existingAgent, pickedProperties);
|
||||||
|
|
||||||
await this.agentDefRepository!.save(existingAgent);
|
await this.agentDefRepository!.save(existingAgent);
|
||||||
|
|
@ -171,8 +171,8 @@ export class AgentDefinitionService implements IAgentDefinitionService {
|
||||||
name: entity.name || undefined,
|
name: entity.name || undefined,
|
||||||
description: entity.description || undefined,
|
description: entity.description || undefined,
|
||||||
avatarUrl: entity.avatarUrl || undefined,
|
avatarUrl: entity.avatarUrl || undefined,
|
||||||
handlerID: entity.handlerID || undefined,
|
agentFrameworkID: entity.agentFrameworkID || undefined,
|
||||||
handlerConfig: entity.handlerConfig || {},
|
agentFrameworkConfig: entity.agentFrameworkConfig || {},
|
||||||
aiApiConfig: entity.aiApiConfig || undefined,
|
aiApiConfig: entity.aiApiConfig || undefined,
|
||||||
agentTools: entity.agentTools || undefined,
|
agentTools: entity.agentTools || undefined,
|
||||||
}));
|
}));
|
||||||
|
|
@ -212,8 +212,8 @@ export class AgentDefinitionService implements IAgentDefinitionService {
|
||||||
name: entity.name || undefined,
|
name: entity.name || undefined,
|
||||||
description: entity.description || undefined,
|
description: entity.description || undefined,
|
||||||
avatarUrl: entity.avatarUrl || undefined,
|
avatarUrl: entity.avatarUrl || undefined,
|
||||||
handlerID: entity.handlerID || undefined,
|
agentFrameworkID: entity.agentFrameworkID || undefined,
|
||||||
handlerConfig: entity.handlerConfig || {},
|
agentFrameworkConfig: entity.agentFrameworkConfig || {},
|
||||||
aiApiConfig: entity.aiApiConfig || undefined,
|
aiApiConfig: entity.aiApiConfig || undefined,
|
||||||
agentTools: entity.agentTools || undefined,
|
agentTools: entity.agentTools || undefined,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -42,10 +42,10 @@ export interface AgentDefinition {
|
||||||
description?: string;
|
description?: string;
|
||||||
/** Agent icon or avatar URL */
|
/** Agent icon or avatar URL */
|
||||||
avatarUrl?: string;
|
avatarUrl?: string;
|
||||||
/** Agent handler function's id, we will find function by this id */
|
/** Agent framework function's id, we will find function by this id */
|
||||||
handlerID?: string;
|
agentFrameworkID?: string;
|
||||||
/** Agent handler's config, specific to the handler. This is required to ensure agent has valid configuration. */
|
/** Agent framework's config, specific to the framework. This is required to ensure agent has valid configuration. */
|
||||||
handlerConfig: Record<string, unknown>;
|
agentFrameworkConfig: Record<string, unknown>;
|
||||||
/**
|
/**
|
||||||
* Overwrite the default AI configuration for this agent.
|
* Overwrite the default AI configuration for this agent.
|
||||||
* Priority is higher than the global default agent config.
|
* Priority is higher than the global default agent config.
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import type { IExternalAPIService } from '@services/externalAPI/interface';
|
||||||
import serviceIdentifier from '@services/serviceIdentifier';
|
import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { AgentInstanceService } from '..';
|
import { AgentInstanceService } from '..';
|
||||||
import { basicPromptConcatHandler } from '../buildInAgentHandlers/basicPromptConcatHandler';
|
import { basicPromptConcatHandler } from '../agentFrameworks/taskAgent';
|
||||||
import type { AgentInstanceMessage, IAgentInstanceService } from '../interface';
|
import type { AgentInstanceMessage, IAgentInstanceService } from '../interface';
|
||||||
import type { AiAPIConfig } from '../promptConcat/promptConcatSchema';
|
import type { AiAPIConfig } from '../promptConcat/promptConcatSchema';
|
||||||
|
|
||||||
|
|
@ -78,7 +78,7 @@ describe('AgentInstance failure path - external API logs on error', () => {
|
||||||
agentDef: {
|
agentDef: {
|
||||||
id: 'def-1',
|
id: 'def-1',
|
||||||
name: 'Def 1',
|
name: 'Def 1',
|
||||||
handlerConfig: {},
|
agentFrameworkConfig: {},
|
||||||
aiApiConfig: { api: { provider: 'test-provider', model: 'test-model' }, modelParameters: { temperature: 0.7, systemPrompt: '', topP: 0.95 } },
|
aiApiConfig: { api: { provider: 'test-provider', model: 'test-model' }, modelParameters: { temperature: 0.7, systemPrompt: '', topP: 0.95 } },
|
||||||
},
|
},
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import { container } from '@services/container';
|
||||||
import type { IDatabaseService } from '@services/database/interface';
|
import type { IDatabaseService } from '@services/database/interface';
|
||||||
import type { IExternalAPIService } from '@services/externalAPI/interface';
|
import type { IExternalAPIService } from '@services/externalAPI/interface';
|
||||||
import serviceIdentifier from '@services/serviceIdentifier';
|
import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
import defaultAgents from '../buildInAgentHandlers/defaultAgents.json';
|
import defaultAgents from '../agentFrameworks/taskAgents.json';
|
||||||
|
|
||||||
describe('AgentInstanceService Streaming Behavior', () => {
|
describe('AgentInstanceService Streaming Behavior', () => {
|
||||||
let agentInstanceService: IAgentInstanceService;
|
let agentInstanceService: IAgentInstanceService;
|
||||||
|
|
@ -55,7 +55,7 @@ describe('AgentInstanceService Streaming Behavior', () => {
|
||||||
agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
||||||
|
|
||||||
await agentInstanceService.initialize();
|
await agentInstanceService.initialize();
|
||||||
// Setup test agent instance using data from defaultAgents.json
|
// Setup test agent instance using data from taskAgents.json
|
||||||
const exampleAgent = defaultAgents[0];
|
const exampleAgent = defaultAgents[0];
|
||||||
testAgentInstance = {
|
testAgentInstance = {
|
||||||
id: nanoid(),
|
id: nanoid(),
|
||||||
|
|
@ -73,7 +73,7 @@ describe('AgentInstanceService Streaming Behavior', () => {
|
||||||
// Mock agent definition service to return our test agent definition
|
// Mock agent definition service to return our test agent definition
|
||||||
mockAgentDefinitionService.getAgentDef = vi.fn().mockResolvedValue({
|
mockAgentDefinitionService.getAgentDef = vi.fn().mockResolvedValue({
|
||||||
...exampleAgent,
|
...exampleAgent,
|
||||||
handlerID: 'basicPromptConcatHandler',
|
agentFrameworkID: 'basicPromptConcatHandler',
|
||||||
});
|
});
|
||||||
// Mock the getAgent method to return our test instance
|
// Mock the getAgent method to return our test instance
|
||||||
vi.spyOn(agentInstanceService, 'getAgent').mockResolvedValue(testAgentInstance);
|
vi.spyOn(agentInstanceService, 'getAgent').mockResolvedValue(testAgentInstance);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
import type { IWikiService } from '@services/wiki/interface';
|
import type { IWikiService } from '@services/wiki/interface';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import defaultAgents from '../buildInAgentHandlers/defaultAgents.json';
|
import defaultAgents from '../agentFrameworks/taskAgents.json';
|
||||||
|
|
||||||
// Follow structure of index.streaming.test.ts
|
// Follow structure of index.streaming.test.ts
|
||||||
describe('AgentInstanceService Wiki Operation', () => {
|
describe('AgentInstanceService Wiki Operation', () => {
|
||||||
|
|
@ -26,9 +26,9 @@ describe('AgentInstanceService Wiki Operation', () => {
|
||||||
agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
||||||
|
|
||||||
// Initialize both database repositories and handlers
|
// Initialize both database repositories and handlers
|
||||||
await agentInstanceService.initializeHandlers();
|
await agentInstanceService.initializeFrameworks();
|
||||||
|
|
||||||
// Setup test agent instance using data from defaultAgents.json
|
// Setup test agent instance using data from taskAgents.json
|
||||||
const exampleAgent = defaultAgents[0];
|
const exampleAgent = defaultAgents[0];
|
||||||
testAgentInstance = {
|
testAgentInstance = {
|
||||||
id: nanoid(),
|
id: nanoid(),
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ import { describe, expect, it } from 'vitest';
|
||||||
import { createAgentInstanceData } from '../utilities';
|
import { createAgentInstanceData } from '../utilities';
|
||||||
|
|
||||||
describe('createAgentInstanceData', () => {
|
describe('createAgentInstanceData', () => {
|
||||||
it('should create agent instance with undefined handlerConfig (fallback to definition)', () => {
|
it('should create agent instance with undefined agentFrameworkConfig (fallback to definition)', () => {
|
||||||
const agentDefinition = {
|
const agentDefinition = {
|
||||||
id: 'test-agent-def',
|
id: 'test-agent-def',
|
||||||
name: 'Test Agent',
|
name: 'Test Agent',
|
||||||
handlerConfig: {
|
agentFrameworkConfig: {
|
||||||
prompts: [
|
prompts: [
|
||||||
{
|
{
|
||||||
text: 'You are a helpful assistant.',
|
text: 'You are a helpful assistant.',
|
||||||
|
|
@ -14,29 +14,29 @@ describe('createAgentInstanceData', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
handlerID: 'basicPromptConcatHandler',
|
agentFrameworkID: 'basicPromptConcatHandler',
|
||||||
};
|
};
|
||||||
|
|
||||||
const { instanceData } = createAgentInstanceData(agentDefinition);
|
const { instanceData } = createAgentInstanceData(agentDefinition);
|
||||||
|
|
||||||
expect(instanceData.handlerConfig).toBeUndefined();
|
expect(instanceData.agentFrameworkConfig).toBeUndefined();
|
||||||
expect(instanceData.agentDefId).toBe('test-agent-def');
|
expect(instanceData.agentDefId).toBe('test-agent-def');
|
||||||
expect(instanceData.handlerID).toBe('basicPromptConcatHandler');
|
expect(instanceData.agentFrameworkID).toBe('basicPromptConcatHandler');
|
||||||
expect(instanceData.name).toContain('Test Agent');
|
expect(instanceData.name).toContain('Test Agent');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create agent instance with undefined handlerConfig even when definition has required handlerConfig', () => {
|
it('should create agent instance with undefined agentFrameworkConfig even when definition has required agentFrameworkConfig', () => {
|
||||||
const agentDefinition = {
|
const agentDefinition = {
|
||||||
id: 'test-agent-def-no-config',
|
id: 'test-agent-def-no-config',
|
||||||
name: 'Test Agent No Config',
|
name: 'Test Agent No Config',
|
||||||
handlerID: 'basicPromptConcatHandler',
|
agentFrameworkID: 'basicPromptConcatHandler',
|
||||||
handlerConfig: {}, // Required by AgentDefinition interface
|
agentFrameworkConfig: {}, // Required by AgentDefinition interface
|
||||||
};
|
};
|
||||||
|
|
||||||
const { instanceData } = createAgentInstanceData(agentDefinition);
|
const { instanceData } = createAgentInstanceData(agentDefinition);
|
||||||
|
|
||||||
expect(instanceData.handlerConfig).toBeUndefined();
|
expect(instanceData.agentFrameworkConfig).toBeUndefined();
|
||||||
expect(instanceData.agentDefId).toBe('test-agent-def-no-config');
|
expect(instanceData.agentDefId).toBe('test-agent-def-no-config');
|
||||||
expect(instanceData.handlerID).toBe('basicPromptConcatHandler');
|
expect(instanceData.agentFrameworkID).toBe('basicPromptConcatHandler');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import type { AgentInstanceMessage, IAgentInstanceService } from '../../interface';
|
import type { AgentInstanceMessage, IAgentInstanceService } from '../../interface';
|
||||||
import type { AiAPIConfig } from '../../promptConcat/promptConcatSchema';
|
import type { AiAPIConfig } from '../../promptConcat/promptConcatSchema';
|
||||||
import { basicPromptConcatHandler } from '../basicPromptConcatHandler';
|
import { basicPromptConcatHandler } from '../taskAgent';
|
||||||
import type { AgentHandlerContext } from '../type';
|
import type { AgentFrameworkContext } from '../utilities/type';
|
||||||
|
|
||||||
// Use real normalizeRole implementation — do not mock plugins or persistence in these integration tests
|
// Use real normalizeRole implementation — do not mock plugins or persistence in these integration tests
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ const mockErrorDetail = {
|
||||||
message: 'Invalid prompt: message must be a ModelMessage or a UI message',
|
message: 'Invalid prompt: message must be a ModelMessage or a UI message',
|
||||||
};
|
};
|
||||||
|
|
||||||
function makeContext(agentId: string, agentDefId: string, messages: AgentInstanceMessage[]): AgentHandlerContext {
|
function makeContext(agentId: string, agentDefId: string, messages: AgentInstanceMessage[]): AgentFrameworkContext {
|
||||||
return {
|
return {
|
||||||
agent: {
|
agent: {
|
||||||
id: agentId,
|
id: agentId,
|
||||||
|
|
@ -33,11 +33,11 @@ function makeContext(agentId: string, agentDefId: string, messages: AgentInstanc
|
||||||
agentDef: {
|
agentDef: {
|
||||||
id: agentDefId,
|
id: agentDefId,
|
||||||
name: 'Test Agent',
|
name: 'Test Agent',
|
||||||
handlerConfig: {},
|
agentFrameworkConfig: {},
|
||||||
aiApiConfig: { api: { provider: 'test-provider', model: 'test-model' }, modelParameters: { temperature: 0.7, systemPrompt: '', topP: 0.95 } } as AiAPIConfig,
|
aiApiConfig: { api: { provider: 'test-provider', model: 'test-model' }, modelParameters: { temperature: 0.7, systemPrompt: '', topP: 0.95 } } as AiAPIConfig,
|
||||||
},
|
},
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
} as unknown as AgentHandlerContext;
|
} as unknown as AgentFrameworkContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('basicPromptConcatHandler - failure path persists error message and logs', () => {
|
describe('basicPromptConcatHandler - failure path persists error message and logs', () => {
|
||||||
|
|
@ -94,10 +94,10 @@ describe('basicPromptConcatHandler - failure path persists error message and log
|
||||||
vi.spyOn(agentDefSvc, 'getAgentDef').mockResolvedValue({
|
vi.spyOn(agentDefSvc, 'getAgentDef').mockResolvedValue({
|
||||||
id: 'def-1',
|
id: 'def-1',
|
||||||
name: 'Def 1',
|
name: 'Def 1',
|
||||||
handlerID: 'basicPromptConcatHandler',
|
agentFrameworkID: 'basicPromptConcatHandler',
|
||||||
handlerConfig: {
|
agentFrameworkConfig: {
|
||||||
plugins: [
|
plugins: [
|
||||||
{ pluginId: 'wikiOperation', wikiOperationParam: {} },
|
{ toolId: 'wikiOperation', wikiOperationParam: {} },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -18,7 +18,7 @@ import { WikiChannel } from '@/constants/channels';
|
||||||
// types are provided by shared mock; no local type assertions needed
|
// types are provided by shared mock; no local type assertions needed
|
||||||
|
|
||||||
// Import defaultAgents configuration
|
// Import defaultAgents configuration
|
||||||
import defaultAgents from '../defaultAgents.json';
|
import defaultAgents from '../taskAgents.json';
|
||||||
|
|
||||||
// Configurable test hooks for mocks
|
// Configurable test hooks for mocks
|
||||||
let testWikiImplementation: ((channel: WikiChannel, workspaceId?: string, args?: string[]) => Promise<unknown>) | undefined;
|
let testWikiImplementation: ((channel: WikiChannel, workspaceId?: string, args?: string[]) => Promise<unknown>) | undefined;
|
||||||
|
|
@ -27,12 +27,12 @@ let testStreamResponses: Array<{ status: string; content: string; requestId: str
|
||||||
// Use real AgentInstanceService in tests; do not mock
|
// Use real AgentInstanceService in tests; do not mock
|
||||||
|
|
||||||
// Import plugin components for direct testing
|
// Import plugin components for direct testing
|
||||||
import type { IPromptConcatPlugin } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
import type { IPromptConcatTool } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
import type { IDatabaseService } from '@services/database/interface';
|
import type { IDatabaseService } from '@services/database/interface';
|
||||||
import { createHandlerHooks, createHooksWithPlugins, initializePluginSystem, PromptConcatHookContext } from '../../plugins/index';
|
import { createAgentFrameworkHooks, createHooksWithTools, initializeToolSystem, PromptConcatHookContext } from '../../tools/index';
|
||||||
import { wikiSearchPlugin } from '../../plugins/wikiSearchPlugin';
|
import { wikiSearchTool } from '../../tools/wikiSearch';
|
||||||
import { basicPromptConcatHandler } from '../basicPromptConcatHandler';
|
import { basicPromptConcatHandler } from '../taskAgent';
|
||||||
import type { AgentHandlerContext } from '../type';
|
import type { AgentFrameworkContext } from '../utilities/type';
|
||||||
|
|
||||||
describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|
@ -41,8 +41,8 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
testStreamResponses = [];
|
testStreamResponses = [];
|
||||||
const { container } = await import('@services/container');
|
const { container } = await import('@services/container');
|
||||||
|
|
||||||
// Ensure built-in plugin registry includes all built-in plugins
|
// Ensure built-in tool registry includes all built-in tools
|
||||||
await initializePluginSystem();
|
await initializeToolSystem();
|
||||||
|
|
||||||
// Prepare a mock DataSource/repository so AgentInstanceService.initialize() can run
|
// Prepare a mock DataSource/repository so AgentInstanceService.initialize() can run
|
||||||
const mockRepo = {
|
const mockRepo = {
|
||||||
|
|
@ -88,32 +88,32 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
|
|
||||||
describe('Complete Workflow Integration', () => {
|
describe('Complete Workflow Integration', () => {
|
||||||
it('should complete full wiki search workflow: tool list -> tool execution -> response', async () => {
|
it('should complete full wiki search workflow: tool list -> tool execution -> response', async () => {
|
||||||
// Use real agent config from defaultAgents.json
|
// Use real agent config from taskAgents.json
|
||||||
const exampleAgent = defaultAgents[0];
|
const exampleAgent = defaultAgents[0];
|
||||||
const handlerConfig = exampleAgent.handlerConfig;
|
const agentFrameworkConfig = exampleAgent.agentFrameworkConfig;
|
||||||
|
|
||||||
// Get the wiki search plugin configuration
|
// Get the wiki search tool configuration
|
||||||
const wikiPlugin = handlerConfig.plugins.find(p => p.pluginId === 'wikiSearch');
|
const wikiPlugin = agentFrameworkConfig.plugins.find(p => p.toolId === 'wikiSearch');
|
||||||
expect(wikiPlugin).toBeDefined();
|
expect(wikiPlugin).toBeDefined();
|
||||||
if (!wikiPlugin) throw new Error('wikiPlugin not found');
|
if (!wikiPlugin) throw new Error('wikiPlugin not found');
|
||||||
|
|
||||||
const prompts = JSON.parse(JSON.stringify(handlerConfig.prompts));
|
const prompts = JSON.parse(JSON.stringify(agentFrameworkConfig.prompts));
|
||||||
|
|
||||||
// Phase 1: Tool List Injection
|
// Phase 1: Tool List Injection
|
||||||
const promptConcatHookContext: PromptConcatHookContext = {
|
const promptConcatHookContext: PromptConcatHookContext = {
|
||||||
handlerContext: {
|
agentFrameworkContext: {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
messages: [],
|
messages: [],
|
||||||
agentDefId: exampleAgent.id,
|
agentDefId: exampleAgent.id,
|
||||||
status: { state: 'working' as const, modified: new Date() },
|
status: { state: 'working' as const, modified: new Date() },
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
handlerConfig: {},
|
agentFrameworkConfig: {},
|
||||||
},
|
},
|
||||||
agentDef: { id: exampleAgent.id, name: exampleAgent.name, handlerConfig: exampleAgent.handlerConfig },
|
agentDef: { id: exampleAgent.id, name: exampleAgent.name, agentFrameworkConfig: exampleAgent.agentFrameworkConfig },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
},
|
},
|
||||||
pluginConfig: wikiPlugin as IPromptConcatPlugin,
|
toolConfig: wikiPlugin as IPromptConcatTool,
|
||||||
prompts,
|
prompts,
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
|
|
@ -127,12 +127,12 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create hooks and register plugins as defined in handlerConfig
|
// Create hooks and register tools as defined in agentFrameworkConfig
|
||||||
const { hooks: promptHooks } = await createHooksWithPlugins(handlerConfig);
|
const { hooks: promptHooks } = await createHooksWithTools(agentFrameworkConfig);
|
||||||
// First run workspacesList plugin to inject available workspaces (if present)
|
// First run workspacesList tool to inject available workspaces (if present)
|
||||||
const workspacesPlugin = handlerConfig.plugins?.find(p => p.pluginId === 'workspacesList');
|
const workspacesPlugin = agentFrameworkConfig.plugins?.find(p => p.toolId === 'workspacesList');
|
||||||
if (workspacesPlugin) {
|
if (workspacesPlugin) {
|
||||||
const workspacesContext = { ...promptConcatHookContext, pluginConfig: workspacesPlugin } as unknown as PromptConcatHookContext;
|
const workspacesContext = { ...promptConcatHookContext, toolConfig: workspacesPlugin } as unknown as PromptConcatHookContext;
|
||||||
await promptHooks.processPrompts.promise(workspacesContext);
|
await promptHooks.processPrompts.promise(workspacesContext);
|
||||||
}
|
}
|
||||||
// Then run wikiSearch plugin to inject the tool list
|
// Then run wikiSearch plugin to inject the tool list
|
||||||
|
|
@ -169,7 +169,7 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const responseContext = {
|
const responseContext = {
|
||||||
handlerContext: {
|
agentFrameworkContext: {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -179,9 +179,9 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
},
|
},
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
messages: [],
|
messages: [],
|
||||||
handlerConfig: {},
|
agentFrameworkConfig: {},
|
||||||
},
|
},
|
||||||
agentDef: { id: 'test-agent-def', name: 'test-agent-def', handlerConfig: {} } as AgentDefinition,
|
agentDef: { id: 'test-agent-def', name: 'test-agent-def', agentFrameworkConfig: {} } as unknown as AgentDefinition,
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
},
|
},
|
||||||
response: {
|
response: {
|
||||||
|
|
@ -191,7 +191,7 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
},
|
},
|
||||||
requestId: 'test-request',
|
requestId: 'test-request',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
pluginConfig: wikiPlugin as IPromptConcatPlugin,
|
toolConfig: wikiPlugin as IPromptConcatTool,
|
||||||
prompts: [],
|
prompts: [],
|
||||||
messages: [],
|
messages: [],
|
||||||
llmResponse: 'I will search for important content using wiki-search tool.',
|
llmResponse: 'I will search for important content using wiki-search tool.',
|
||||||
|
|
@ -199,8 +199,8 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
actions: {} as unknown as Record<string, unknown>,
|
actions: {} as unknown as Record<string, unknown>,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use hooks registered with all plugins from handlerConfig
|
// Use hooks registered with all plugins import { AgentFrameworkConfig }
|
||||||
const { hooks: responseHooks } = await createHooksWithPlugins(handlerConfig);
|
const { hooks: responseHooks } = await createHooksWithTools(agentFrameworkConfig);
|
||||||
// Execute the response complete hook
|
// Execute the response complete hook
|
||||||
await responseHooks.responseComplete.promise(responseContext);
|
await responseHooks.responseComplete.promise(responseContext);
|
||||||
// reuse containerForAssert from above assertions
|
// reuse containerForAssert from above assertions
|
||||||
|
|
@ -213,8 +213,8 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
expect(responseContext.actions.yieldNextRoundTo).toBe('self');
|
expect(responseContext.actions.yieldNextRoundTo).toBe('self');
|
||||||
|
|
||||||
// Verify tool result message was added to agent history
|
// Verify tool result message was added to agent history
|
||||||
expect(responseContext.handlerContext.agent.messages.length).toBeGreaterThan(0);
|
expect(responseContext.agentFrameworkContext.agent.messages.length).toBeGreaterThan(0);
|
||||||
const toolResultMessage = responseContext.handlerContext.agent.messages[responseContext.handlerContext.agent.messages.length - 1] as AgentInstanceMessage;
|
const toolResultMessage = responseContext.agentFrameworkContext.agent.messages[responseContext.agentFrameworkContext.agent.messages.length - 1] as AgentInstanceMessage;
|
||||||
expect(toolResultMessage.role).toBe('tool'); // Tool result message
|
expect(toolResultMessage.role).toBe('tool'); // Tool result message
|
||||||
expect(toolResultMessage.content).toContain('<functions_result>');
|
expect(toolResultMessage.content).toContain('<functions_result>');
|
||||||
expect(toolResultMessage.content).toContain('Tool: wiki-search');
|
expect(toolResultMessage.content).toContain('Tool: wiki-search');
|
||||||
|
|
@ -222,18 +222,18 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle errors in wiki search gracefully', async () => {
|
it('should handle errors in wiki search gracefully', async () => {
|
||||||
// Use real agent config from defaultAgents.json
|
// Use real agent config from taskAgents.json
|
||||||
const exampleAgent = defaultAgents[0];
|
const exampleAgent = defaultAgents[0];
|
||||||
const handlerConfig = exampleAgent.handlerConfig;
|
const agentFrameworkConfig = exampleAgent.agentFrameworkConfig;
|
||||||
|
|
||||||
// Get the wiki search plugin configuration
|
// Get the wiki search tool configuration
|
||||||
const wikiPlugin = handlerConfig.plugins.find(p => p.pluginId === 'wikiSearch');
|
const wikiPlugin = agentFrameworkConfig.plugins.find(p => p.toolId === 'wikiSearch');
|
||||||
expect(wikiPlugin).toBeDefined();
|
expect(wikiPlugin).toBeDefined();
|
||||||
|
|
||||||
// Mock tool calling with invalid workspace
|
// Mock tool calling with invalid workspace
|
||||||
|
|
||||||
const responseContext = {
|
const responseContext = {
|
||||||
handlerContext: {
|
agentFrameworkContext: {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -243,9 +243,9 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
},
|
},
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
messages: [],
|
messages: [],
|
||||||
handlerConfig: {},
|
agentFrameworkConfig: {},
|
||||||
},
|
},
|
||||||
agentDef: { id: 'test-agent-def', name: 'test-agent-def', handlerConfig: {} } as AgentDefinition,
|
agentDef: { id: 'test-agent-def', name: 'test-agent-def', agentFrameworkConfig: {} } as unknown as AgentDefinition,
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
},
|
},
|
||||||
response: {
|
response: {
|
||||||
|
|
@ -255,7 +255,7 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
},
|
},
|
||||||
requestId: 'test-request',
|
requestId: 'test-request',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
pluginConfig: wikiPlugin as IPromptConcatPlugin,
|
toolConfig: wikiPlugin as IPromptConcatTool,
|
||||||
prompts: [],
|
prompts: [],
|
||||||
messages: [],
|
messages: [],
|
||||||
llmResponse: 'Search in nonexistent wiki',
|
llmResponse: 'Search in nonexistent wiki',
|
||||||
|
|
@ -264,10 +264,10 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use real handler hooks
|
// Use real handler hooks
|
||||||
const responseHooks = createHandlerHooks();
|
const responseHooks = createAgentFrameworkHooks();
|
||||||
|
|
||||||
// Register the plugin
|
// Register the plugin
|
||||||
wikiSearchPlugin(responseHooks);
|
wikiSearchTool(responseHooks);
|
||||||
|
|
||||||
// Execute the response complete hook
|
// Execute the response complete hook
|
||||||
await responseHooks.responseComplete.promise(responseContext);
|
await responseHooks.responseComplete.promise(responseContext);
|
||||||
|
|
@ -276,8 +276,8 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
expect(responseContext.actions.yieldNextRoundTo).toBe('self');
|
expect(responseContext.actions.yieldNextRoundTo).toBe('self');
|
||||||
|
|
||||||
// Verify error message was added to agent history
|
// Verify error message was added to agent history
|
||||||
expect(responseContext.handlerContext.agent.messages.length).toBeGreaterThan(0);
|
expect(responseContext.agentFrameworkContext.agent.messages.length).toBeGreaterThan(0);
|
||||||
const errorResultMessage = responseContext.handlerContext.agent.messages[responseContext.handlerContext.agent.messages.length - 1] as AgentInstanceMessage;
|
const errorResultMessage = responseContext.agentFrameworkContext.agent.messages[responseContext.agentFrameworkContext.agent.messages.length - 1] as AgentInstanceMessage;
|
||||||
expect(errorResultMessage.role).toBe('tool'); // Tool error message
|
expect(errorResultMessage.role).toBe('tool'); // Tool error message
|
||||||
|
|
||||||
// The error should be indicated in the message content
|
// The error should be indicated in the message content
|
||||||
|
|
@ -295,7 +295,7 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
const exampleAgent = defaultAgents[0];
|
const exampleAgent = defaultAgents[0];
|
||||||
const testAgentId = `test-agent-${Date.now()}`;
|
const testAgentId = `test-agent-${Date.now()}`;
|
||||||
|
|
||||||
const context: AgentHandlerContext = {
|
const context: AgentFrameworkContext = {
|
||||||
agent: {
|
agent: {
|
||||||
id: testAgentId,
|
id: testAgentId,
|
||||||
agentDefId: exampleAgent.id,
|
agentDefId: exampleAgent.id,
|
||||||
|
|
@ -315,7 +315,7 @@ describe('WikiSearch Plugin Integration & YieldNextRound Mechanism', () => {
|
||||||
agentDef: {
|
agentDef: {
|
||||||
id: exampleAgent.id,
|
id: exampleAgent.id,
|
||||||
name: exampleAgent.name,
|
name: exampleAgent.name,
|
||||||
handlerConfig: exampleAgent.handlerConfig,
|
agentFrameworkConfig: exampleAgent.agentFrameworkConfig,
|
||||||
},
|
},
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
};
|
};
|
||||||
|
|
@ -4,14 +4,14 @@ import { logger } from '@services/libs/log';
|
||||||
import serviceIdentifier from '@services/serviceIdentifier';
|
import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
import { merge } from 'lodash';
|
import { merge } from 'lodash';
|
||||||
import type { AgentInstanceLatestStatus, AgentInstanceMessage, IAgentInstanceService } from '../interface';
|
import type { AgentInstanceLatestStatus, AgentInstanceMessage, IAgentInstanceService } from '../interface';
|
||||||
import { createHooksWithPlugins } from '../plugins';
|
import { AgentFrameworkConfig, AgentPromptDescription, AiAPIConfig } from '../promptConcat/promptConcatSchema';
|
||||||
import { YieldNextRoundTarget } from '../plugins/types';
|
import type { IPromptConcatTool } from '../promptConcat/promptConcatSchema/plugin';
|
||||||
import { AgentPromptDescription, AiAPIConfig, HandlerConfig } from '../promptConcat/promptConcatSchema';
|
|
||||||
import type { IPromptConcatPlugin } from '../promptConcat/promptConcatSchema/plugin';
|
|
||||||
import { responseConcat } from '../promptConcat/responseConcat';
|
import { responseConcat } from '../promptConcat/responseConcat';
|
||||||
import { getFinalPromptResult } from '../promptConcat/utilities';
|
import { getFinalPromptResult } from '../promptConcat/utilities';
|
||||||
import { canceled, completed, error, working } from './statusUtilities';
|
import { createHooksWithTools } from '../tools';
|
||||||
import { AgentHandlerContext } from './type';
|
import { YieldNextRoundTarget } from '../tools/types';
|
||||||
|
import { canceled, completed, error, working } from './utilities/statusUtilities';
|
||||||
|
import { AgentFrameworkContext } from './utilities/type';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main conversation orchestrator for AI agents
|
* Main conversation orchestrator for AI agents
|
||||||
|
|
@ -27,19 +27,19 @@ import { AgentHandlerContext } from './type';
|
||||||
*
|
*
|
||||||
* @param context - Agent handling context containing configuration and message history
|
* @param context - Agent handling context containing configuration and message history
|
||||||
*/
|
*/
|
||||||
export async function* basicPromptConcatHandler(context: AgentHandlerContext) {
|
export async function* basicPromptConcatHandler(context: AgentFrameworkContext) {
|
||||||
// Initialize variables for request tracking
|
// Initialize variables for request tracking
|
||||||
let currentRequestId: string | undefined;
|
let currentRequestId: string | undefined;
|
||||||
const lastUserMessage: AgentInstanceMessage | undefined = context.agent.messages[context.agent.messages.length - 1];
|
const lastUserMessage: AgentInstanceMessage | undefined = context.agent.messages[context.agent.messages.length - 1];
|
||||||
// Create and register handler hooks based on handler config
|
// Create and register handler hooks based on framework config
|
||||||
const { hooks: handlerHooks, pluginConfigs } = await createHooksWithPlugins(context.agentDef.handlerConfig || {});
|
const { hooks: agentFrameworkHooks, toolConfigs } = await createHooksWithTools(context.agentDef.agentFrameworkConfig || {});
|
||||||
|
|
||||||
// Log the start of handler execution with context information
|
// Log the start of handler execution with context information
|
||||||
logger.debug('Starting prompt handler execution', {
|
logger.debug('Starting prompt handler execution', {
|
||||||
method: 'basicPromptConcatHandler',
|
method: 'basicPromptConcatHandler',
|
||||||
agentId: context.agent.id,
|
agentId: context.agent.id,
|
||||||
defId: context.agentDef.id,
|
defId: context.agentDef.id,
|
||||||
handlerId: context.agentDef.handlerID,
|
agentFrameworkId: context.agentDef.agentFrameworkID,
|
||||||
messageCount: context.agent.messages.length,
|
messageCount: context.agent.messages.length,
|
||||||
});
|
});
|
||||||
// Check if there's a new user message to process - trigger user message received hook
|
// Check if there's a new user message to process - trigger user message received hook
|
||||||
|
|
@ -48,8 +48,8 @@ export async function* basicPromptConcatHandler(context: AgentHandlerContext) {
|
||||||
|
|
||||||
if (isNewUserMessage) {
|
if (isNewUserMessage) {
|
||||||
// Trigger user message received hook
|
// Trigger user message received hook
|
||||||
await handlerHooks.userMessageReceived.promise({
|
await agentFrameworkHooks.userMessageReceived.promise({
|
||||||
handlerContext: context,
|
agentFrameworkContext: context,
|
||||||
content: {
|
content: {
|
||||||
text: lastUserMessage.content,
|
text: lastUserMessage.content,
|
||||||
file: lastUserMessage.metadata?.file as File | undefined,
|
file: lastUserMessage.metadata?.file as File | undefined,
|
||||||
|
|
@ -62,8 +62,8 @@ export async function* basicPromptConcatHandler(context: AgentHandlerContext) {
|
||||||
lastUserMessage.metadata = { ...lastUserMessage.metadata, processed: true };
|
lastUserMessage.metadata = { ...lastUserMessage.metadata, processed: true };
|
||||||
|
|
||||||
// Trigger agent status change to working
|
// Trigger agent status change to working
|
||||||
await handlerHooks.agentStatusChanged.promise({
|
await agentFrameworkHooks.agentStatusChanged.promise({
|
||||||
handlerContext: context,
|
agentFrameworkContext: context,
|
||||||
status: {
|
status: {
|
||||||
state: 'working',
|
state: 'working',
|
||||||
modified: new Date(),
|
modified: new Date(),
|
||||||
|
|
@ -94,12 +94,12 @@ export async function* basicPromptConcatHandler(context: AgentHandlerContext) {
|
||||||
|
|
||||||
// Process prompts using common handler function
|
// Process prompts using common handler function
|
||||||
try {
|
try {
|
||||||
const handlerConfig: HandlerConfig = context.agentDef.handlerConfig as HandlerConfig;
|
const agentFrameworkConfig = context.agentDef.agentFrameworkConfig as AgentFrameworkConfig;
|
||||||
const agentPromptDescription: AgentPromptDescription = {
|
const agentPromptDescription: AgentPromptDescription = {
|
||||||
id: context.agentDef.id,
|
id: context.agentDef.id,
|
||||||
api: aiApiConfig.api,
|
api: aiApiConfig.api,
|
||||||
modelParameters: aiApiConfig.modelParameters,
|
modelParameters: aiApiConfig.modelParameters,
|
||||||
handlerConfig,
|
agentFrameworkConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
const agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
const agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
||||||
|
|
@ -146,12 +146,12 @@ export async function* basicPromptConcatHandler(context: AgentHandlerContext) {
|
||||||
if (response.status === 'update') {
|
if (response.status === 'update') {
|
||||||
// For responseUpdate, we'll skip plugin-specific config for now
|
// For responseUpdate, we'll skip plugin-specific config for now
|
||||||
// since it's called frequently during streaming
|
// since it's called frequently during streaming
|
||||||
await handlerHooks.responseUpdate.promise({
|
await agentFrameworkHooks.responseUpdate.promise({
|
||||||
handlerContext: context,
|
agentFrameworkContext: context,
|
||||||
response,
|
response,
|
||||||
requestId: currentRequestId,
|
requestId: currentRequestId,
|
||||||
isFinal: false,
|
isFinal: false,
|
||||||
pluginConfig: {} as IPromptConcatPlugin, // Empty config for streaming updates
|
toolConfig: {} as IPromptConcatTool, // Empty config for streaming updates
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,16 +164,16 @@ export async function* basicPromptConcatHandler(context: AgentHandlerContext) {
|
||||||
|
|
||||||
// Delegate final response processing to handler hooks
|
// Delegate final response processing to handler hooks
|
||||||
const responseCompleteContext = {
|
const responseCompleteContext = {
|
||||||
handlerContext: context,
|
agentFrameworkContext: context,
|
||||||
response,
|
response,
|
||||||
requestId: currentRequestId,
|
requestId: currentRequestId,
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
pluginConfig: (pluginConfigs.length > 0 ? pluginConfigs[0] : {}) as IPromptConcatPlugin, // First config for compatibility
|
toolConfig: (toolConfigs.length > 0 ? toolConfigs[0] : {}) as IPromptConcatTool, // First config for compatibility
|
||||||
handlerConfig: context.agentDef.handlerConfig, // Pass complete config for plugin access
|
agentFrameworkConfig: context.agentDef.agentFrameworkConfig, // Pass complete config for tool access
|
||||||
actions: undefined as { yieldNextRoundTo?: 'self' | 'human'; newUserMessage?: string } | undefined,
|
actions: undefined as { yieldNextRoundTo?: 'self' | 'human'; newUserMessage?: string } | undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
await handlerHooks.responseComplete.promise(responseCompleteContext);
|
await agentFrameworkHooks.responseComplete.promise(responseCompleteContext);
|
||||||
|
|
||||||
// Check if responseComplete hooks set yieldNextRoundTo
|
// Check if responseComplete hooks set yieldNextRoundTo
|
||||||
let yieldNextRoundFromHooks: YieldNextRoundTarget | undefined;
|
let yieldNextRoundFromHooks: YieldNextRoundTarget | undefined;
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": "example-agent",
|
"id": "task-agent",
|
||||||
"name": "Example Agent",
|
"name": "Task Agent",
|
||||||
"description": "Example agent with prompt processing",
|
"description": "Example agent with prompt processing",
|
||||||
"avatarUrl": "https://example.com/example-agent.png",
|
"avatarUrl": "https://example.com/task-agent.png",
|
||||||
"handlerID": "basicPromptConcatHandler",
|
"agentFrameworkID": "basicPromptConcatHandler",
|
||||||
"handlerConfig": {
|
"agentFrameworkConfig": {
|
||||||
"prompts": [
|
"prompts": [
|
||||||
{
|
{
|
||||||
"id": "system",
|
"id": "system",
|
||||||
|
|
@ -72,7 +72,7 @@
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"id": "efe5be74-540d-487d-8a05-7377e486953d",
|
"id": "efe5be74-540d-487d-8a05-7377e486953d",
|
||||||
"pluginId": "fullReplacement",
|
"toolId": "fullReplacement",
|
||||||
"fullReplacementParam": {
|
"fullReplacementParam": {
|
||||||
"targetId": "default-history",
|
"targetId": "default-history",
|
||||||
"sourceType": "historyOfSession"
|
"sourceType": "historyOfSession"
|
||||||
|
|
@ -84,7 +84,7 @@
|
||||||
"id": "f0e1b2c3-4d5e-6f7g-8h9i-j0k1l2m3n4o5",
|
"id": "f0e1b2c3-4d5e-6f7g-8h9i-j0k1l2m3n4o5",
|
||||||
"caption": "Wiki工作空间列表",
|
"caption": "Wiki工作空间列表",
|
||||||
"description": "自动在提示词中注入可用的Wiki工作空间列表",
|
"description": "自动在提示词中注入可用的Wiki工作空间列表",
|
||||||
"pluginId": "workspacesList",
|
"toolId": "workspacesList",
|
||||||
"workspacesListParam": {
|
"workspacesListParam": {
|
||||||
"targetId": "default-before-tool",
|
"targetId": "default-before-tool",
|
||||||
"position": "after"
|
"position": "after"
|
||||||
|
|
@ -94,7 +94,7 @@
|
||||||
"id": "d0f1b2c3-4d5e-6f7g-8h9i-j0k1l2m3n4o5",
|
"id": "d0f1b2c3-4d5e-6f7g-8h9i-j0k1l2m3n4o5",
|
||||||
"caption": "Wiki搜索和向量索引工具",
|
"caption": "Wiki搜索和向量索引工具",
|
||||||
"description": "提供Wiki搜索(filter和vector)以及向量嵌入索引管理功能",
|
"description": "提供Wiki搜索(filter和vector)以及向量嵌入索引管理功能",
|
||||||
"pluginId": "wikiSearch",
|
"toolId": "wikiSearch",
|
||||||
"wikiSearchParam": {
|
"wikiSearchParam": {
|
||||||
"sourceType": "wiki",
|
"sourceType": "wiki",
|
||||||
"toolListPosition": {
|
"toolListPosition": {
|
||||||
|
|
@ -107,7 +107,7 @@
|
||||||
"id": "e1f2b3c4-5d6e-7f8g-9h0i-k1l2m3n4o5p6",
|
"id": "e1f2b3c4-5d6e-7f8g-9h0i-k1l2m3n4o5p6",
|
||||||
"caption": "Wiki操作工具",
|
"caption": "Wiki操作工具",
|
||||||
"description": "允许AI在Wiki工作空间中创建、更新和删除笔记",
|
"description": "允许AI在Wiki工作空间中创建、更新和删除笔记",
|
||||||
"pluginId": "wikiOperation",
|
"toolId": "wikiOperation",
|
||||||
"wikiOperationParam": {
|
"wikiOperationParam": {
|
||||||
"toolListPosition": {
|
"toolListPosition": {
|
||||||
"position": "after",
|
"position": "after",
|
||||||
|
|
@ -117,7 +117,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "a0f1b2c3-4d5e-6f7g-8h9i-j0k1l2m3n4o5",
|
"id": "a0f1b2c3-4d5e-6f7g-8h9i-j0k1l2m3n4o5",
|
||||||
"pluginId": "fullReplacement",
|
"toolId": "fullReplacement",
|
||||||
"fullReplacementParam": {
|
"fullReplacementParam": {
|
||||||
"targetId": "default-response",
|
"targetId": "default-response",
|
||||||
"sourceType": "llmResponse"
|
"sourceType": "llmResponse"
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { AgentInstanceLatestStatus } from '../interface';
|
import { AgentInstanceLatestStatus } from '../../interface';
|
||||||
import { AgentHandlerContext } from './type';
|
import { AgentFrameworkContext } from './type';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a completed status with error information in message metadata
|
* Creates a completed status with error information in message metadata
|
||||||
|
|
@ -22,7 +22,7 @@ export function completedWithError(
|
||||||
provider: string;
|
provider: string;
|
||||||
message?: string;
|
message?: string;
|
||||||
} | undefined,
|
} | undefined,
|
||||||
context: AgentHandlerContext,
|
context: AgentFrameworkContext,
|
||||||
messageId?: string,
|
messageId?: string,
|
||||||
): AgentInstanceLatestStatus {
|
): AgentInstanceLatestStatus {
|
||||||
return {
|
return {
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { AgentInstanceLatestStatus } from '../interface';
|
import { AgentInstanceLatestStatus } from '../../interface';
|
||||||
import { AgentHandlerContext } from './type';
|
import { AgentFrameworkContext } from './type';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a working status with a message
|
* Creates a working status with a message
|
||||||
|
|
@ -11,7 +11,7 @@ import { AgentHandlerContext } from './type';
|
||||||
*/
|
*/
|
||||||
export function working(
|
export function working(
|
||||||
content: string,
|
content: string,
|
||||||
context: AgentHandlerContext,
|
context: AgentFrameworkContext,
|
||||||
messageId?: string,
|
messageId?: string,
|
||||||
): AgentInstanceLatestStatus {
|
): AgentInstanceLatestStatus {
|
||||||
return {
|
return {
|
||||||
|
|
@ -34,7 +34,7 @@ export function working(
|
||||||
*/
|
*/
|
||||||
export function completed(
|
export function completed(
|
||||||
content: string,
|
content: string,
|
||||||
context: AgentHandlerContext,
|
context: AgentFrameworkContext,
|
||||||
messageId?: string,
|
messageId?: string,
|
||||||
): AgentInstanceLatestStatus {
|
): AgentInstanceLatestStatus {
|
||||||
return {
|
return {
|
||||||
|
|
@ -72,7 +72,7 @@ export function error(
|
||||||
provider: string;
|
provider: string;
|
||||||
message?: string;
|
message?: string;
|
||||||
} | undefined,
|
} | undefined,
|
||||||
context: AgentHandlerContext,
|
context: AgentFrameworkContext,
|
||||||
messageId?: string,
|
messageId?: string,
|
||||||
): AgentInstanceLatestStatus {
|
): AgentInstanceLatestStatus {
|
||||||
return {
|
return {
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
/* eslint-disable @typescript-eslint/no-invalid-void-type */
|
/* eslint-disable @typescript-eslint/no-invalid-void-type */
|
||||||
import { AgentDefinition } from '../../agentDefinition/interface';
|
import { AgentDefinition } from '../../../agentDefinition/interface';
|
||||||
import { AgentInstance, AgentInstanceLatestStatus } from '../interface';
|
import { AgentInstance, AgentInstanceLatestStatus } from '../../interface';
|
||||||
|
|
||||||
export interface AgentHandlerContext {
|
export interface AgentFrameworkContext {
|
||||||
agent: AgentInstance;
|
agent: AgentInstance;
|
||||||
agentDef: AgentDefinition;
|
agentDef: AgentDefinition;
|
||||||
|
|
||||||
|
|
@ -28,6 +28,6 @@ export interface AgentHandlerContext {
|
||||||
* (needed for non-streaming 'tasks/send'). If void is returned, the server uses the
|
* (needed for non-streaming 'tasks/send'). If void is returned, the server uses the
|
||||||
* last known state from the store after processing all yields.
|
* last known state from the store after processing all yields.
|
||||||
*/
|
*/
|
||||||
export type AgentHandler = (
|
export type AgentFramework = (
|
||||||
context: AgentHandlerContext,
|
context: AgentFrameworkContext,
|
||||||
) => AsyncGenerator<AgentInstanceLatestStatus, AgentInstance | undefined | void, unknown>;
|
) => AsyncGenerator<AgentInstanceLatestStatus, AgentInstance | undefined | void, unknown>;
|
||||||
|
|
@ -5,12 +5,12 @@ import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { DataSource, Repository } from 'typeorm';
|
import { DataSource, Repository } from 'typeorm';
|
||||||
|
|
||||||
import type { IAgentDefinitionService } from '@services/agentDefinition/interface';
|
import type { IAgentDefinitionService } from '@services/agentDefinition/interface';
|
||||||
import { basicPromptConcatHandler } from '@services/agentInstance/buildInAgentHandlers/basicPromptConcatHandler';
|
import { basicPromptConcatHandler } from '@services/agentInstance/agentFrameworks/taskAgent';
|
||||||
import type { AgentHandler, AgentHandlerContext } from '@services/agentInstance/buildInAgentHandlers/type';
|
import type { AgentFramework, AgentFrameworkContext } from '@services/agentInstance/agentFrameworks/utilities/type';
|
||||||
import { createHooksWithPlugins, initializePluginSystem } from '@services/agentInstance/plugins';
|
|
||||||
import { promptConcatStream, PromptConcatStreamState } from '@services/agentInstance/promptConcat/promptConcat';
|
import { promptConcatStream, PromptConcatStreamState } from '@services/agentInstance/promptConcat/promptConcat';
|
||||||
import type { AgentPromptDescription } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
import type { AgentPromptDescription } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
import { getPromptConcatHandlerConfigJsonSchema } from '@services/agentInstance/promptConcat/promptConcatSchema/jsonSchema';
|
import { getPromptConcatAgentFrameworkConfigJsonSchema } from '@services/agentInstance/promptConcat/promptConcatSchema/jsonSchema';
|
||||||
|
import { createHooksWithTools, initializeToolSystem } from '@services/agentInstance/tools';
|
||||||
import type { IDatabaseService } from '@services/database/interface';
|
import type { IDatabaseService } from '@services/database/interface';
|
||||||
import { AgentInstanceEntity, AgentInstanceMessageEntity } from '@services/database/schema/agent';
|
import { AgentInstanceEntity, AgentInstanceMessageEntity } from '@services/database/schema/agent';
|
||||||
import { logger } from '@services/libs/log';
|
import { logger } from '@services/libs/log';
|
||||||
|
|
@ -34,15 +34,15 @@ export class AgentInstanceService implements IAgentInstanceService {
|
||||||
private agentInstanceSubjects: Map<string, BehaviorSubject<AgentInstance | undefined>> = new Map();
|
private agentInstanceSubjects: Map<string, BehaviorSubject<AgentInstance | undefined>> = new Map();
|
||||||
private statusSubjects: Map<string, BehaviorSubject<AgentInstanceLatestStatus | undefined>> = new Map();
|
private statusSubjects: Map<string, BehaviorSubject<AgentInstanceLatestStatus | undefined>> = new Map();
|
||||||
|
|
||||||
private agentHandlers: Map<string, AgentHandler> = new Map();
|
private agentFrameworks: Map<string, AgentFramework> = new Map();
|
||||||
private handlerSchemas: Map<string, Record<string, unknown>> = new Map();
|
private frameworkSchemas: Map<string, Record<string, unknown>> = new Map();
|
||||||
private cancelTokenMap: Map<string, { value: boolean }> = new Map();
|
private cancelTokenMap: Map<string, { value: boolean }> = new Map();
|
||||||
private debouncedUpdateFunctions: Map<string, (message: AgentInstanceLatestStatus['message'] & { id: string }, agentId?: string) => void> = new Map();
|
private debouncedUpdateFunctions: Map<string, (message: AgentInstanceLatestStatus['message'] & { id: string }, agentId?: string) => void> = new Map();
|
||||||
|
|
||||||
public async initialize(): Promise<void> {
|
public async initialize(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await this.initializeDatabase();
|
await this.initializeDatabase();
|
||||||
await this.initializeHandlers();
|
await this.initializeFrameworks();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to initialize agent instance service', { error });
|
logger.error('Failed to initialize agent instance service', { error });
|
||||||
throw error;
|
throw error;
|
||||||
|
|
@ -62,37 +62,37 @@ export class AgentInstanceService implements IAgentInstanceService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async initializeHandlers(): Promise<void> {
|
public async initializeFrameworks(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// Register plugins to global registry once during initialization
|
// Register tools to global registry once during initialization
|
||||||
await initializePluginSystem();
|
await initializeToolSystem();
|
||||||
logger.debug('AgentInstance Plugin system initialized and plugins registered to global registry');
|
logger.debug('AgentInstance Tool system initialized and tools registered to global registry');
|
||||||
|
|
||||||
// Register built-in handlers
|
// Register built-in frameworks
|
||||||
this.registerBuiltinHandlers();
|
this.registerBuiltinFrameworks();
|
||||||
logger.debug('AgentInstance handlers registered');
|
logger.debug('AgentInstance frameworks registered');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to initialize agent instance handlers', { error });
|
logger.error('Failed to initialize agent instance frameworks', { error });
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public registerBuiltinHandlers(): void {
|
public registerBuiltinFrameworks(): void {
|
||||||
// Plugins are already registered in initialize(), so we only register handlers here
|
// Tools are already registered in initialize(), so we only register frameworks here
|
||||||
// Register basic prompt concatenation handler with its schema
|
// Register basic prompt concatenation framework with its schema
|
||||||
this.registerHandler('basicPromptConcatHandler', basicPromptConcatHandler, getPromptConcatHandlerConfigJsonSchema());
|
this.registerFramework('basicPromptConcatHandler', basicPromptConcatHandler, getPromptConcatAgentFrameworkConfigJsonSchema());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a handler with an optional schema
|
* Register a framework with an optional schema
|
||||||
* @param handlerId ID for the handler
|
* @param frameworkId ID for the framework
|
||||||
* @param handler The handler function
|
* @param framework The framework function
|
||||||
* @param schema Optional JSON schema for the handler configuration
|
* @param schema Optional JSON schema for the framework configuration
|
||||||
*/
|
*/
|
||||||
private registerHandler(handlerId: string, handler: AgentHandler, schema?: Record<string, unknown>): void {
|
private registerFramework(frameworkId: string, framework: AgentFramework, schema?: Record<string, unknown>): void {
|
||||||
this.agentHandlers.set(handlerId, handler);
|
this.agentFrameworks.set(frameworkId, framework);
|
||||||
if (schema) {
|
if (schema) {
|
||||||
this.handlerSchemas.set(handlerId, schema);
|
this.frameworkSchemas.set(frameworkId, schema);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,7 +216,7 @@ export class AgentInstanceService implements IAgentInstanceService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update fields using pick + Object.assign for consistency with updateAgentDef
|
// Update fields using pick + Object.assign for consistency with updateAgentDef
|
||||||
const pickedProperties = pick(data, ['name', 'status', 'avatarUrl', 'aiApiConfig', 'closed', 'handlerConfig']);
|
const pickedProperties = pick(data, ['name', 'status', 'avatarUrl', 'aiApiConfig', 'closed', 'agentFrameworkConfig']);
|
||||||
Object.assign(instanceEntity, pickedProperties);
|
Object.assign(instanceEntity, pickedProperties);
|
||||||
|
|
||||||
// Save instance updates
|
// Save instance updates
|
||||||
|
|
@ -353,20 +353,20 @@ export class AgentInstanceService implements IAgentInstanceService {
|
||||||
throw new Error(`Agent definition not found: ${agentInstance.agentDefId}`);
|
throw new Error(`Agent definition not found: ${agentInstance.agentDefId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get appropriate handler
|
// Get appropriate framework
|
||||||
const handlerId = agentDefinition.handlerID;
|
const agentFrameworkId = agentDefinition.agentFrameworkID;
|
||||||
if (!handlerId) {
|
if (!agentFrameworkId) {
|
||||||
throw new Error(`Handler ID not found in agent definition: ${agentDefinition.id}`);
|
throw new Error(`Agent framework ID not found in agent definition: ${agentDefinition.id}`);
|
||||||
}
|
}
|
||||||
const handler = this.agentHandlers.get(handlerId);
|
const framework = this.agentFrameworks.get(agentFrameworkId);
|
||||||
if (!handler) {
|
if (!framework) {
|
||||||
throw new Error(`Handler not found: ${handlerId}`);
|
throw new Error(`Framework not found: ${agentFrameworkId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create handler context with temporary message added for processing
|
// Create framework context with temporary message added for processing
|
||||||
const cancelToken = { value: false };
|
const cancelToken = { value: false };
|
||||||
this.cancelTokenMap.set(agentId, cancelToken);
|
this.cancelTokenMap.set(agentId, cancelToken);
|
||||||
const handlerContext: AgentHandlerContext = {
|
const frameworkContext: AgentFrameworkContext = {
|
||||||
agent: {
|
agent: {
|
||||||
...agentInstance,
|
...agentInstance,
|
||||||
messages: [...agentInstance.messages],
|
messages: [...agentInstance.messages],
|
||||||
|
|
@ -379,23 +379,23 @@ export class AgentInstanceService implements IAgentInstanceService {
|
||||||
isCancelled: () => cancelToken.value,
|
isCancelled: () => cancelToken.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create fresh hooks for this handler execution and register plugins based on handlerConfig
|
// Create fresh hooks for this framework execution and register tools based on frameworkConfig
|
||||||
const { hooks: handlerHooks } = await createHooksWithPlugins(agentDefinition.handlerConfig || {});
|
const { hooks: frameworkHooks } = await createHooksWithTools(agentDefinition.agentFrameworkConfig || {});
|
||||||
|
|
||||||
// Trigger userMessageReceived hook with the configured plugins
|
// Trigger userMessageReceived hook with the configured tools
|
||||||
await handlerHooks.userMessageReceived.promise({
|
await frameworkHooks.userMessageReceived.promise({
|
||||||
handlerContext,
|
agentFrameworkContext: frameworkContext,
|
||||||
content,
|
content,
|
||||||
messageId,
|
messageId,
|
||||||
timestamp: now,
|
timestamp: now,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Notify agent update after user message is added
|
// Notify agent update after user message is added
|
||||||
this.notifyAgentUpdate(agentId, handlerContext.agent);
|
this.notifyAgentUpdate(agentId, frameworkContext.agent);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create async generator
|
// Create async generator
|
||||||
const generator = handler(handlerContext);
|
const generator = framework(frameworkContext);
|
||||||
|
|
||||||
// Track the last message for completion handling
|
// Track the last message for completion handling
|
||||||
let lastResult: AgentInstanceLatestStatus | undefined;
|
let lastResult: AgentInstanceLatestStatus | undefined;
|
||||||
|
|
@ -415,7 +415,7 @@ export class AgentInstanceService implements IAgentInstanceService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify agent update with latest messages for real-time UI updates
|
// Notify agent update with latest messages for real-time UI updates
|
||||||
this.notifyAgentUpdate(agentId, handlerContext.agent);
|
this.notifyAgentUpdate(agentId, frameworkContext.agent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the last result for completion handling
|
// Store the last result for completion handling
|
||||||
|
|
@ -442,8 +442,8 @@ export class AgentInstanceService implements IAgentInstanceService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger agentStatusChanged hook for completion
|
// Trigger agentStatusChanged hook for completion
|
||||||
await handlerHooks.agentStatusChanged.promise({
|
await frameworkHooks.agentStatusChanged.promise({
|
||||||
handlerContext,
|
agentFrameworkContext: frameworkContext,
|
||||||
status: {
|
status: {
|
||||||
state: 'completed',
|
state: 'completed',
|
||||||
modified: new Date(),
|
modified: new Date(),
|
||||||
|
|
@ -458,8 +458,8 @@ export class AgentInstanceService implements IAgentInstanceService {
|
||||||
logger.error(`Agent handler execution failed: ${errorMessage}`);
|
logger.error(`Agent handler execution failed: ${errorMessage}`);
|
||||||
|
|
||||||
// Trigger agentStatusChanged hook for failure
|
// Trigger agentStatusChanged hook for failure
|
||||||
await handlerHooks.agentStatusChanged.promise({
|
await frameworkHooks.agentStatusChanged.promise({
|
||||||
handlerContext,
|
agentFrameworkContext: frameworkContext,
|
||||||
status: {
|
status: {
|
||||||
state: 'failed',
|
state: 'failed',
|
||||||
modified: new Date(),
|
modified: new Date(),
|
||||||
|
|
@ -848,31 +848,31 @@ export class AgentInstanceService implements IAgentInstanceService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public concatPrompt(promptDescription: Pick<AgentPromptDescription, 'handlerConfig'>, messages: AgentInstanceMessage[]): Observable<PromptConcatStreamState> {
|
public concatPrompt(promptDescription: Pick<AgentPromptDescription, 'agentFrameworkConfig'>, messages: AgentInstanceMessage[]): Observable<PromptConcatStreamState> {
|
||||||
logger.debug('AgentInstanceService.concatPrompt called', {
|
logger.debug('AgentInstanceService.concatPrompt called', {
|
||||||
hasPromptConfig: !!promptDescription.handlerConfig,
|
hasPromptConfig: !!promptDescription.agentFrameworkConfig,
|
||||||
promptConfigKeys: Object.keys(promptDescription.handlerConfig),
|
promptConfigKeys: Object.keys(promptDescription.agentFrameworkConfig || {}),
|
||||||
messagesCount: messages.length,
|
messagesCount: messages.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Observable<PromptConcatStreamState>((observer) => {
|
return new Observable<PromptConcatStreamState>((observer) => {
|
||||||
const processStream = async () => {
|
const processStream = async () => {
|
||||||
try {
|
try {
|
||||||
// Create a minimal handler context for prompt concatenation
|
// Create a minimal framework context for prompt concatenation
|
||||||
const handlerContext = {
|
const frameworkContext = {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'temp',
|
id: 'temp',
|
||||||
messages,
|
messages,
|
||||||
agentDefId: 'temp',
|
agentDefId: 'temp',
|
||||||
status: { state: 'working' as const, modified: new Date() },
|
status: { state: 'working' as const, modified: new Date() },
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
handlerConfig: {},
|
agentFrameworkConfig: {},
|
||||||
},
|
},
|
||||||
agentDef: { id: 'temp', name: 'temp', handlerConfig: promptDescription.handlerConfig },
|
agentDef: { id: 'temp', name: 'temp', agentFrameworkConfig: promptDescription.agentFrameworkConfig || {} },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const streamGenerator = promptConcatStream(promptDescription as AgentPromptDescription, messages, handlerContext);
|
const streamGenerator = promptConcatStream(promptDescription as AgentPromptDescription, messages, frameworkContext);
|
||||||
for await (const state of streamGenerator) {
|
for await (const state of streamGenerator) {
|
||||||
observer.next(state);
|
observer.next(state);
|
||||||
if (state.isComplete) {
|
if (state.isComplete) {
|
||||||
|
|
@ -893,21 +893,21 @@ export class AgentInstanceService implements IAgentInstanceService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getHandlerConfigSchema(handlerId: string): Record<string, unknown> {
|
public getFrameworkConfigSchema(frameworkId: string): Record<string, unknown> {
|
||||||
try {
|
try {
|
||||||
logger.debug('AgentInstanceService.getHandlerConfigSchema called', { handlerId });
|
logger.debug('AgentInstanceService.getFrameworkConfigSchema called', { frameworkId });
|
||||||
// Check if we have a schema for this handler
|
// Check if we have a schema for this framework
|
||||||
const schema = this.handlerSchemas.get(handlerId);
|
const schema = this.frameworkSchemas.get(frameworkId);
|
||||||
if (schema) {
|
if (schema) {
|
||||||
return schema;
|
return schema;
|
||||||
}
|
}
|
||||||
// If no schema found, return an empty schema
|
// If no schema found, return an empty schema
|
||||||
logger.warn(`No schema found for handler: ${handlerId}`);
|
logger.warn(`No schema found for framework: ${frameworkId}`);
|
||||||
return { type: 'object', properties: {} };
|
return { type: 'object', properties: {} };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error in AgentInstanceService.getHandlerConfigSchema', {
|
logger.error('Error in AgentInstanceService.getFrameworkConfigSchema', {
|
||||||
error,
|
error,
|
||||||
handlerId,
|
frameworkId,
|
||||||
});
|
});
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,16 @@ import { AgentPromptDescription } from '@services/agentInstance/promptConcat/pro
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content of a session instance that user chat with an agent.
|
* Content of a session instance that user chat with an agent.
|
||||||
* Inherits from AgentDefinition but makes handlerConfig optional to allow fallback.
|
* Inherits import { AgentFrameworkConfig } optional to allow fallback.
|
||||||
* The instance can override the definition's configuration, or fall back to using it.
|
* The instance can override the definition's configuration, or fall back to using it.
|
||||||
*/
|
*/
|
||||||
export interface AgentInstance extends Omit<AgentDefinition, 'name' | 'handlerConfig'> {
|
export interface AgentInstance extends Omit<AgentDefinition, 'name' | 'agentFrameworkConfig'> {
|
||||||
/** Agent description ID that generates this instance */
|
/** Agent description ID that generates this instance */
|
||||||
agentDefId: string;
|
agentDefId: string;
|
||||||
/** Session name, optional in instance unlike definition */
|
/** Session name, optional in instance unlike definition */
|
||||||
name?: string;
|
name?: string;
|
||||||
/** Agent handler's config - optional, falls back to AgentDefinition.handlerConfig if not set */
|
/** Agent framework's config - optional, falls back to AgentDefinition.agentFrameworkConfig if not set */
|
||||||
handlerConfig?: Record<string, unknown>;
|
agentFrameworkConfig?: Record<string, unknown>;
|
||||||
/**
|
/**
|
||||||
* Message history.
|
* Message history.
|
||||||
* latest on top, so it's easy to get first one as user's latest input, and rest as history.
|
* latest on top, so it's easy to get first one as user's latest input, and rest as history.
|
||||||
|
|
@ -119,7 +119,7 @@ export interface IAgentInstanceService {
|
||||||
/**
|
/**
|
||||||
* For testing purposes, only initialize the built-in handlers without database
|
* For testing purposes, only initialize the built-in handlers without database
|
||||||
*/
|
*/
|
||||||
initializeHandlers(): Promise<void>;
|
initializeFrameworks(): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new agent instance from a definition
|
* Create a new agent instance from a definition
|
||||||
|
|
@ -196,15 +196,15 @@ export interface IAgentInstanceService {
|
||||||
* @param messages Messages to be included in prompt generation
|
* @param messages Messages to be included in prompt generation
|
||||||
* @returns Observable stream of processing states, with final state containing complete results
|
* @returns Observable stream of processing states, with final state containing complete results
|
||||||
*/
|
*/
|
||||||
concatPrompt(promptDescription: Pick<AgentPromptDescription, 'handlerConfig'>, messages: AgentInstanceMessage[]): Observable<PromptConcatStreamState>;
|
concatPrompt(promptDescription: Pick<AgentPromptDescription, 'agentFrameworkConfig'>, messages: AgentInstanceMessage[]): Observable<PromptConcatStreamState>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get JSON Schema for handler configuration
|
* Get JSON Schema for handler configuration
|
||||||
* This allows frontend to generate a form based on the schema for a specific handler
|
* This allows frontend to generate a form based on the schema for a specific handler
|
||||||
* @param handlerId Handler ID to get schema for
|
* @param agentFrameworkID Handler ID to get schema for
|
||||||
* @returns JSON Schema for handler configuration
|
* @returns JSON Schema for handler configuration
|
||||||
*/
|
*/
|
||||||
getHandlerConfigSchema(handlerId: string): Record<string, unknown>;
|
getFrameworkConfigSchema(frameworkId: string): Record<string, unknown>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save user message to database
|
* Save user message to database
|
||||||
|
|
@ -233,7 +233,7 @@ export const AgentInstanceServiceIPCDescriptor = {
|
||||||
deleteAgent: ProxyPropertyType.Function,
|
deleteAgent: ProxyPropertyType.Function,
|
||||||
getAgent: ProxyPropertyType.Function,
|
getAgent: ProxyPropertyType.Function,
|
||||||
getAgents: ProxyPropertyType.Function,
|
getAgents: ProxyPropertyType.Function,
|
||||||
getHandlerConfigSchema: ProxyPropertyType.Function,
|
getFrameworkConfigSchema: ProxyPropertyType.Function,
|
||||||
saveUserMessage: ProxyPropertyType.Function,
|
saveUserMessage: ProxyPropertyType.Function,
|
||||||
sendMsgToAgent: ProxyPropertyType.Function,
|
sendMsgToAgent: ProxyPropertyType.Function,
|
||||||
subscribeToAgentUpdates: ProxyPropertyType.Function$,
|
subscribeToAgentUpdates: ProxyPropertyType.Function$,
|
||||||
|
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
import { logger } from '@services/libs/log';
|
|
||||||
import { AsyncSeriesHook, AsyncSeriesWaterfallHook } from 'tapable';
|
|
||||||
import { registerPluginParameterSchema } from './schemaRegistry';
|
|
||||||
import { AgentResponse, PromptConcatHookContext, PromptConcatHooks, PromptConcatPlugin, ResponseHookContext } from './types';
|
|
||||||
|
|
||||||
// Re-export types for convenience
|
|
||||||
export type { AgentResponse, PromptConcatHookContext, PromptConcatHooks, PromptConcatPlugin, ResponseHookContext };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registry for built-in plugins
|
|
||||||
*/
|
|
||||||
export const builtInPlugins = new Map<string, PromptConcatPlugin>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create unified hooks instance for the complete plugin system
|
|
||||||
*/
|
|
||||||
export function createHandlerHooks(): PromptConcatHooks {
|
|
||||||
return {
|
|
||||||
// Prompt processing hooks
|
|
||||||
processPrompts: new AsyncSeriesWaterfallHook(['context']),
|
|
||||||
finalizePrompts: new AsyncSeriesWaterfallHook(['context']),
|
|
||||||
postProcess: new AsyncSeriesWaterfallHook(['context']),
|
|
||||||
// Agent lifecycle hooks
|
|
||||||
userMessageReceived: new AsyncSeriesHook(['context']),
|
|
||||||
agentStatusChanged: new AsyncSeriesHook(['context']),
|
|
||||||
toolExecuted: new AsyncSeriesHook(['context']),
|
|
||||||
responseUpdate: new AsyncSeriesHook(['context']),
|
|
||||||
responseComplete: new AsyncSeriesHook(['context']),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all available plugins
|
|
||||||
*/
|
|
||||||
async function getAllPlugins() {
|
|
||||||
const [
|
|
||||||
promptPluginsModule,
|
|
||||||
wikiSearchModule,
|
|
||||||
wikiOperationModule,
|
|
||||||
workspacesListModule,
|
|
||||||
messageManagementModule,
|
|
||||||
] = await Promise.all([
|
|
||||||
import('./promptPlugins'),
|
|
||||||
import('./wikiSearchPlugin'),
|
|
||||||
import('./wikiOperationPlugin'),
|
|
||||||
import('./workspacesListPlugin'),
|
|
||||||
import('./messageManagementPlugin'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
messageManagementPlugin: messageManagementModule.messageManagementPlugin,
|
|
||||||
fullReplacementPlugin: promptPluginsModule.fullReplacementPlugin,
|
|
||||||
wikiSearchPlugin: wikiSearchModule.wikiSearchPlugin,
|
|
||||||
wikiOperationPlugin: wikiOperationModule.wikiOperationPlugin,
|
|
||||||
workspacesListPlugin: workspacesListModule.workspacesListPlugin,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register plugins to hooks based on handler configuration
|
|
||||||
* @param hooks - The hooks instance to register plugins to
|
|
||||||
* @param handlerConfig - The handler configuration containing plugin settings
|
|
||||||
*/
|
|
||||||
export async function registerPluginsToHooksFromConfig(
|
|
||||||
hooks: PromptConcatHooks,
|
|
||||||
handlerConfig: { plugins?: Array<{ pluginId: string; [key: string]: unknown }> },
|
|
||||||
): Promise<void> {
|
|
||||||
// Always register core plugins that are needed for basic functionality
|
|
||||||
const messageManagementModule = await import('./messageManagementPlugin');
|
|
||||||
messageManagementModule.messageManagementPlugin(hooks);
|
|
||||||
logger.debug('Registered messageManagementPlugin to hooks');
|
|
||||||
|
|
||||||
// Register plugins based on handler configuration
|
|
||||||
if (handlerConfig.plugins) {
|
|
||||||
for (const pluginConfig of handlerConfig.plugins) {
|
|
||||||
const { pluginId } = pluginConfig;
|
|
||||||
|
|
||||||
// Get plugin from global registry (supports both built-in and dynamic plugins)
|
|
||||||
const plugin = builtInPlugins.get(pluginId);
|
|
||||||
if (plugin) {
|
|
||||||
plugin(hooks);
|
|
||||||
logger.debug(`Registered plugin ${pluginId} to hooks`);
|
|
||||||
} else {
|
|
||||||
logger.warn(`Plugin not found in registry: ${pluginId}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize plugin system - register all built-in plugins to global registry
|
|
||||||
* This should be called once during service initialization
|
|
||||||
*/
|
|
||||||
export async function initializePluginSystem(): Promise<void> {
|
|
||||||
// Import plugin schemas and register them
|
|
||||||
const [
|
|
||||||
promptPluginsModule,
|
|
||||||
wikiSearchModule,
|
|
||||||
wikiOperationModule,
|
|
||||||
workspacesListModule,
|
|
||||||
modelContextProtocolModule,
|
|
||||||
] = await Promise.all([
|
|
||||||
import('./promptPlugins'),
|
|
||||||
import('./wikiSearchPlugin'),
|
|
||||||
import('./wikiOperationPlugin'),
|
|
||||||
import('./workspacesListPlugin'),
|
|
||||||
import('./modelContextProtocolPlugin'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Register plugin parameter schemas
|
|
||||||
registerPluginParameterSchema(
|
|
||||||
'fullReplacement',
|
|
||||||
promptPluginsModule.getFullReplacementParameterSchema(),
|
|
||||||
{
|
|
||||||
displayName: 'Full Replacement',
|
|
||||||
description: 'Replace target content with content from specified source',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
registerPluginParameterSchema(
|
|
||||||
'dynamicPosition',
|
|
||||||
promptPluginsModule.getDynamicPositionParameterSchema(),
|
|
||||||
{
|
|
||||||
displayName: 'Dynamic Position',
|
|
||||||
description: 'Insert content at a specific position relative to a target element',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
registerPluginParameterSchema(
|
|
||||||
'wikiSearch',
|
|
||||||
wikiSearchModule.getWikiSearchParameterSchema(),
|
|
||||||
{
|
|
||||||
displayName: 'Wiki Search',
|
|
||||||
description: 'Search content in wiki workspaces and manage vector embeddings',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
registerPluginParameterSchema(
|
|
||||||
'wikiOperation',
|
|
||||||
wikiOperationModule.getWikiOperationParameterSchema(),
|
|
||||||
{
|
|
||||||
displayName: 'Wiki Operation',
|
|
||||||
description: 'Perform operations on wiki workspaces (create, update, delete tiddlers)',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
registerPluginParameterSchema(
|
|
||||||
'workspacesList',
|
|
||||||
workspacesListModule.getWorkspacesListParameterSchema(),
|
|
||||||
{
|
|
||||||
displayName: 'Workspaces List',
|
|
||||||
description: 'Inject available wiki workspaces list into prompts',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
registerPluginParameterSchema(
|
|
||||||
'modelContextProtocol',
|
|
||||||
modelContextProtocolModule.getModelContextProtocolParameterSchema(),
|
|
||||||
{
|
|
||||||
displayName: 'Model Context Protocol',
|
|
||||||
description: 'MCP (Model Context Protocol) integration',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const plugins = await getAllPlugins();
|
|
||||||
// Register all built-in plugins to global registry for discovery
|
|
||||||
builtInPlugins.set('messageManagement', plugins.messageManagementPlugin);
|
|
||||||
builtInPlugins.set('fullReplacement', plugins.fullReplacementPlugin);
|
|
||||||
builtInPlugins.set('wikiSearch', plugins.wikiSearchPlugin);
|
|
||||||
builtInPlugins.set('wikiOperation', plugins.wikiOperationPlugin);
|
|
||||||
builtInPlugins.set('workspacesList', plugins.workspacesListPlugin);
|
|
||||||
logger.debug('All built-in plugins and schemas registered successfully');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create hooks and register plugins based on handler configuration
|
|
||||||
* This creates a new hooks instance and registers plugins for that specific context
|
|
||||||
*/
|
|
||||||
export async function createHooksWithPlugins(
|
|
||||||
handlerConfig: { plugins?: Array<{ pluginId: string; [key: string]: unknown }> },
|
|
||||||
): Promise<{ hooks: PromptConcatHooks; pluginConfigs: Array<{ pluginId: string; [key: string]: unknown }> }> {
|
|
||||||
const hooks = createHandlerHooks();
|
|
||||||
await registerPluginsToHooksFromConfig(hooks, handlerConfig);
|
|
||||||
return {
|
|
||||||
hooks,
|
|
||||||
pluginConfigs: handlerConfig.plugins || [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,165 +0,0 @@
|
||||||
/**
|
|
||||||
* Plugin Schema Registry
|
|
||||||
*
|
|
||||||
* This system allows plugins to register their parameter schemas dynamically,
|
|
||||||
* enabling dynamic plugin loading while maintaining type safety and validation.
|
|
||||||
*/
|
|
||||||
import { identity } from 'lodash';
|
|
||||||
import { z } from 'zod/v4';
|
|
||||||
|
|
||||||
const t = identity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registry for plugin parameter schemas
|
|
||||||
*/
|
|
||||||
const pluginSchemas = new Map<string, z.ZodType>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registry for plugin metadata
|
|
||||||
*/
|
|
||||||
const pluginMetadata = new Map<string, {
|
|
||||||
displayName: string;
|
|
||||||
description: string;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a plugin parameter schema
|
|
||||||
* @param pluginId The plugin ID (should match pluginId enum values)
|
|
||||||
* @param schema The Zod schema for this plugin's parameters
|
|
||||||
* @param metadata Optional metadata for display purposes
|
|
||||||
*/
|
|
||||||
export function registerPluginParameterSchema(
|
|
||||||
pluginId: string,
|
|
||||||
schema: z.ZodType,
|
|
||||||
metadata?: {
|
|
||||||
displayName: string;
|
|
||||||
description: string;
|
|
||||||
},
|
|
||||||
): void {
|
|
||||||
pluginSchemas.set(pluginId, schema);
|
|
||||||
if (metadata) {
|
|
||||||
pluginMetadata.set(pluginId, metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a plugin parameter schema by ID
|
|
||||||
* @param pluginId The plugin ID
|
|
||||||
* @returns The schema or undefined if not found
|
|
||||||
*/
|
|
||||||
export function getPluginParameterSchema(pluginId: string): z.ZodType | undefined {
|
|
||||||
return pluginSchemas.get(pluginId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all registered plugin IDs
|
|
||||||
* @returns Array of all registered plugin IDs
|
|
||||||
*/
|
|
||||||
export function getAllRegisteredPluginIds(): string[] {
|
|
||||||
return Array.from(pluginSchemas.keys());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get plugin metadata
|
|
||||||
* @param pluginId The plugin ID
|
|
||||||
* @returns Plugin metadata or undefined if not found
|
|
||||||
*/
|
|
||||||
export function getPluginMetadata(pluginId: string): { displayName: string; description: string } | undefined {
|
|
||||||
return pluginMetadata.get(pluginId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dynamically create the PromptConcatPluginSchema based on registered plugins
|
|
||||||
* This is called whenever the schema is needed, ensuring it includes all registered plugins
|
|
||||||
*/
|
|
||||||
export function createDynamicPromptConcatPluginSchema(): z.ZodType {
|
|
||||||
// Base plugin configuration without parameter-specific fields
|
|
||||||
const basePluginSchema = z.object({
|
|
||||||
id: z.string().meta({
|
|
||||||
title: t('Schema.Plugin.IdTitle'),
|
|
||||||
description: t('Schema.Plugin.Id'),
|
|
||||||
}),
|
|
||||||
caption: z.string().optional().meta({
|
|
||||||
title: t('Schema.Plugin.CaptionTitle'),
|
|
||||||
description: t('Schema.Plugin.Caption'),
|
|
||||||
}),
|
|
||||||
content: z.string().optional().meta({
|
|
||||||
title: t('Schema.Plugin.ContentTitle'),
|
|
||||||
description: t('Schema.Plugin.Content'),
|
|
||||||
}),
|
|
||||||
forbidOverrides: z.boolean().optional().default(false).meta({
|
|
||||||
title: t('Schema.Plugin.ForbidOverridesTitle'),
|
|
||||||
description: t('Schema.Plugin.ForbidOverrides'),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get all registered plugin IDs
|
|
||||||
const registeredPluginIds = getAllRegisteredPluginIds();
|
|
||||||
|
|
||||||
if (registeredPluginIds.length === 0) {
|
|
||||||
// Fallback to a basic schema if no plugins are registered yet
|
|
||||||
return basePluginSchema.extend({
|
|
||||||
pluginId: z.string().meta({
|
|
||||||
title: t('Schema.Plugin.PluginIdTitle'),
|
|
||||||
description: t('Schema.Plugin.PluginId'),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create enum from registered plugin IDs
|
|
||||||
const pluginIdEnum = z.enum(registeredPluginIds as [string, ...string[]]).meta({
|
|
||||||
title: t('Schema.Plugin.PluginIdTitle'),
|
|
||||||
description: t('Schema.Plugin.PluginId'),
|
|
||||||
enumOptions: registeredPluginIds.map(pluginId => {
|
|
||||||
const metadata = getPluginMetadata(pluginId);
|
|
||||||
return {
|
|
||||||
value: pluginId,
|
|
||||||
label: metadata?.displayName || pluginId,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create parameter schema object with all registered plugins
|
|
||||||
const parameterSchema: Record<string, z.ZodType> = {};
|
|
||||||
|
|
||||||
for (const pluginId of registeredPluginIds) {
|
|
||||||
const schema = getPluginParameterSchema(pluginId);
|
|
||||||
if (schema) {
|
|
||||||
const metadata = getPluginMetadata(pluginId);
|
|
||||||
parameterSchema[`${pluginId}Param`] = schema.optional().meta({
|
|
||||||
title: metadata?.displayName || pluginId,
|
|
||||||
description: metadata?.description || `Parameters for ${pluginId} plugin`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combine base schema with plugin ID and parameters
|
|
||||||
return basePluginSchema.extend({
|
|
||||||
pluginId: pluginIdEnum,
|
|
||||||
...parameterSchema,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the type of a plugin's parameters
|
|
||||||
* @param pluginId The plugin ID
|
|
||||||
* @returns The inferred TypeScript type of the plugin's parameters
|
|
||||||
*/
|
|
||||||
export type PluginParameterType<T extends string> = T extends keyof ReturnType<typeof createPluginParameterTypes> ? ReturnType<typeof createPluginParameterTypes>[T] : never;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create type definitions for all registered plugin parameters
|
|
||||||
* This is used internally for type inference
|
|
||||||
*/
|
|
||||||
export function createPluginParameterTypes() {
|
|
||||||
const types: Record<string, unknown> = {};
|
|
||||||
|
|
||||||
for (const pluginId of getAllRegisteredPluginIds()) {
|
|
||||||
const schema = getPluginParameterSchema(pluginId);
|
|
||||||
if (schema) {
|
|
||||||
types[pluginId] = schema;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return types as Record<string, z.ZodType>;
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +1,55 @@
|
||||||
# Prompt Concat Tools
|
# Prompt Concat Tools
|
||||||
|
|
||||||
Prompt engineering and message processing with a plugin-based architecture.
|
Prompt engineering and message processing with a tool-based architecture.
|
||||||
|
|
||||||
If final prompt is a food, then `handlerConfig.prompts` is the recipe. Chat history and user input are raw materials.
|
If final prompt is a food, then `agentFrameworkConfig.prompts` is the recipe. Chat history and user input are raw materials.
|
||||||
|
|
||||||
## Implementation
|
## Implementation
|
||||||
|
|
||||||
The `promptConcat` function uses a tapable hooks-based plugin system. Built-in plugins are registered by `pluginId` and loaded based on configuration in `defaultAgents.json`.
|
The `promptConcat` function uses a tapable hooks-based tool system. Built-in tools are registered by `toolId` and loaded based on configuration in `taskAgents.json`.
|
||||||
|
|
||||||
### Plugin System Architecture
|
### Tool System Architecture
|
||||||
|
|
||||||
1. **Hooks**: Uses tapable `AsyncSeriesWaterfallHook` for plugin execution
|
1. **Hooks**: Uses tapable `AsyncSeriesWaterfallHook` for tool execution
|
||||||
- `processPrompts`: Modifies prompt tree during processing
|
- `processPrompts`: Modifies prompt tree during processing
|
||||||
- `finalizePrompts`: Final processing before LLM call
|
- `finalizePrompts`: Final processing before LLM call
|
||||||
- `postProcess`: Handles response processing
|
- `postProcess`: Handles response processing
|
||||||
|
|
||||||
2. **Built-in Plugins**:
|
2. **Built-in Tools**:
|
||||||
- `fullReplacement`: Replaces content from various sources
|
- `fullReplacement`: Replaces content from various sources
|
||||||
- `dynamicPosition`: Inserts content at specific positions
|
- `dynamicPosition`: Inserts content at specific positions
|
||||||
- `retrievalAugmentedGeneration`: Retrieves content from wiki/external sources
|
- `retrievalAugmentedGeneration`: Retrieves content from wiki/external sources
|
||||||
- `modelContextProtocol`: Integrates with external MCP servers
|
- `modelContextProtocol`: Integrates with external MCP servers
|
||||||
- `toolCalling`: Processes function calls in responses
|
- `toolCalling`: Processes function calls in responses
|
||||||
|
|
||||||
3. **Plugin Registration**:
|
3. **Tool Registration**:
|
||||||
- Plugins are registered by `pluginId` field in the `plugins` array
|
- Tools are registered by `toolId` field in the `plugins` array
|
||||||
- Each plugin instance has its own configuration parameters
|
- Each tool instance has its own configuration parameters
|
||||||
- Built-in plugins are auto-registered on system initialization
|
- Built-in tools are auto-registered on system initialization
|
||||||
|
|
||||||
### Plugin Lifecycle
|
### Tool Lifecycle
|
||||||
|
|
||||||
2. **Configuration**: Plugins are loaded based on `handlerConfig.plugins` array
|
1. **Registration**: Tools are registered during initialization
|
||||||
3. **Execution**: Hooks execute plugins in registration order
|
2. **Configuration**: Tools are loaded based on `agentFrameworkConfig.plugins` array
|
||||||
4. **Error Handling**: Individual plugin failures don't stop the pipeline
|
3. **Execution**: Hooks execute tools in registration order
|
||||||
|
4. **Error Handling**: Individual tool failures don't stop the pipeline
|
||||||
|
|
||||||
### Adding New Plugins
|
### Adding New Tools
|
||||||
|
|
||||||
1. Create plugin function in `plugins/` directory
|
1. Create tool function in `tools/` directory
|
||||||
2. Register in `plugins/index.ts`
|
2. Register in `tools/index.ts`
|
||||||
3. Add `pluginId` to schema enum
|
3. Add `toolId` to schema enum
|
||||||
4. Add parameter schema if needed
|
4. Add parameter schema if needed
|
||||||
|
|
||||||
Each plugin receives a hooks object and registers handlers for specific hook points. Plugins can modify prompt trees, inject content, process responses, and trigger additional LLM calls.
|
Each tool receives a hooks object and registers handlers for specific hook points. Tools can modify prompt trees, inject content, process responses, and trigger additional LLM calls.
|
||||||
|
|
||||||
### Example Plugin Structure
|
### Example Tool Structure
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
export const myPlugin: PromptConcatPlugin = (hooks) => {
|
export const myTool: PromptConcatTool = (hooks) => {
|
||||||
hooks.processPrompts.tapAsync('myPlugin', async (context, callback) => {
|
hooks.processPrompts.tapAsync('myTool', async (context, callback) => {
|
||||||
const { plugin, prompts, messages } = context;
|
const { tool, prompts, messages } = context;
|
||||||
// Plugin logic here
|
// Tool logic here
|
||||||
callback(null, context);
|
callback(null, context);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,17 +10,17 @@
|
||||||
* Main Concepts:
|
* Main Concepts:
|
||||||
* - Prompts are tree-structured, can have roles (system/user/assistant) and children.
|
* - Prompts are tree-structured, can have roles (system/user/assistant) and children.
|
||||||
* - Plugins use hooks to modify the prompt tree at runtime.
|
* - Plugins use hooks to modify the prompt tree at runtime.
|
||||||
* - Built-in plugins are registered by pluginId and executed when matching plugins are found.
|
* - Built-in tools are registered by toolId and executed when matching tools are found.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { logger } from '@services/libs/log';
|
import { logger } from '@services/libs/log';
|
||||||
import { ModelMessage } from 'ai';
|
import { ModelMessage } from 'ai';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { AgentHandlerContext } from '../buildInAgentHandlers/type';
|
import { AgentFrameworkContext } from '../agentFrameworks/utilities/type';
|
||||||
import { AgentInstanceMessage } from '../interface';
|
import { AgentInstanceMessage } from '../interface';
|
||||||
import { builtInPlugins, createHandlerHooks, PromptConcatHookContext } from '../plugins';
|
import { builtInTools, createAgentFrameworkHooks, PromptConcatHookContext } from '../tools';
|
||||||
import type { AgentPromptDescription, IPrompt } from './promptConcatSchema';
|
import type { AgentPromptDescription, IPrompt } from './promptConcatSchema';
|
||||||
import type { IPromptConcatPlugin } from './promptConcatSchema/plugin';
|
import type { IPromptConcatTool } from './promptConcatSchema/plugin';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context type specific for prompt concatenation operations
|
* Context type specific for prompt concatenation operations
|
||||||
|
|
@ -37,7 +37,7 @@ export interface PromptConcatContext {
|
||||||
* Generate ID-based path mapping for prompts to enable source tracking
|
* Generate ID-based path mapping for prompts to enable source tracking
|
||||||
* Uses actual node IDs instead of indices to avoid path conflicts with dynamic content
|
* Uses actual node IDs instead of indices to avoid path conflicts with dynamic content
|
||||||
*/
|
*/
|
||||||
function generateSourcePaths(prompts: IPrompt[], plugins: IPromptConcatPlugin[] = []): Map<string, string[]> {
|
function generateSourcePaths(prompts: IPrompt[], plugins: IPromptConcatTool[] = []): Map<string, string[]> {
|
||||||
const pathMap = new Map<string, string[]>();
|
const pathMap = new Map<string, string[]>();
|
||||||
function traversePrompts(items: IPrompt[], currentPath: string[]): void {
|
function traversePrompts(items: IPrompt[], currentPath: string[]): void {
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
|
|
@ -48,7 +48,7 @@ function generateSourcePaths(prompts: IPrompt[], plugins: IPromptConcatPlugin[]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function traversePlugins(items: IPromptConcatPlugin[], currentPath: string[]): void {
|
function traversePlugins(items: IPromptConcatTool[], currentPath: string[]): void {
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
const itemPath = [...currentPath, item.id];
|
const itemPath = [...currentPath, item.id];
|
||||||
pathMap.set(item.id, itemPath);
|
pathMap.set(item.id, itemPath);
|
||||||
|
|
@ -191,7 +191,7 @@ export interface PromptConcatStreamState {
|
||||||
/** Current processing step */
|
/** Current processing step */
|
||||||
step: 'plugin' | 'finalize' | 'flatten' | 'complete';
|
step: 'plugin' | 'finalize' | 'flatten' | 'complete';
|
||||||
/** Current plugin being processed (if step is 'plugin') */
|
/** Current plugin being processed (if step is 'plugin') */
|
||||||
currentPlugin?: IPromptConcatPlugin;
|
currentPlugin?: IPromptConcatTool;
|
||||||
/** Processing progress (0-1) */
|
/** Processing progress (0-1) */
|
||||||
progress: number;
|
progress: number;
|
||||||
/** Whether processing is complete */
|
/** Whether processing is complete */
|
||||||
|
|
@ -203,40 +203,41 @@ export interface PromptConcatStreamState {
|
||||||
* Yields intermediate results for real-time UI updates
|
* Yields intermediate results for real-time UI updates
|
||||||
*/
|
*/
|
||||||
export async function* promptConcatStream(
|
export async function* promptConcatStream(
|
||||||
agentConfig: Pick<AgentPromptDescription, 'handlerConfig'>,
|
agentConfig: Pick<AgentPromptDescription, 'agentFrameworkConfig'>,
|
||||||
messages: AgentInstanceMessage[],
|
messages: AgentInstanceMessage[],
|
||||||
handlerContext: AgentHandlerContext,
|
agentFrameworkContext: AgentFrameworkContext,
|
||||||
): AsyncGenerator<PromptConcatStreamState, PromptConcatStreamState, unknown> {
|
): AsyncGenerator<PromptConcatStreamState, PromptConcatStreamState, unknown> {
|
||||||
const promptConfigs = Array.isArray(agentConfig.handlerConfig.prompts) ? agentConfig.handlerConfig.prompts : [];
|
const agentFrameworkConfig = agentConfig.agentFrameworkConfig;
|
||||||
const pluginConfigs = (Array.isArray(agentConfig.handlerConfig.plugins) ? agentConfig.handlerConfig.plugins : []) as IPromptConcatPlugin[];
|
const promptConfigs = Array.isArray(agentFrameworkConfig?.prompts) ? agentFrameworkConfig.prompts : [];
|
||||||
|
const toolConfigs = (Array.isArray(agentFrameworkConfig?.plugins) ? agentFrameworkConfig.plugins : []) as IPromptConcatTool[];
|
||||||
const promptsCopy = cloneDeep(promptConfigs);
|
const promptsCopy = cloneDeep(promptConfigs);
|
||||||
const sourcePaths = generateSourcePaths(promptsCopy, pluginConfigs);
|
const sourcePaths = generateSourcePaths(promptsCopy, toolConfigs);
|
||||||
|
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
// Register plugins that match the configuration
|
// Register tools that match the configuration
|
||||||
for (const plugin of pluginConfigs) {
|
for (const tool of toolConfigs) {
|
||||||
const builtInPlugin = builtInPlugins.get(plugin.pluginId);
|
const builtInTool = builtInTools.get(tool.toolId);
|
||||||
if (builtInPlugin) {
|
if (builtInTool) {
|
||||||
builtInPlugin(hooks);
|
builtInTool(hooks);
|
||||||
logger.debug('Registered plugin', {
|
logger.debug('Registered tool', {
|
||||||
pluginId: plugin.pluginId,
|
toolId: tool.toolId,
|
||||||
pluginInstanceId: plugin.id,
|
toolInstanceId: tool.id,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
logger.info(`No built-in plugin found for pluginId: ${plugin.pluginId}`);
|
logger.info(`No built-in tool found for toolId: ${tool.toolId}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process each plugin through hooks with streaming
|
// Process each plugin through hooks with streaming
|
||||||
let modifiedPrompts = promptsCopy;
|
let modifiedPrompts = promptsCopy;
|
||||||
const totalSteps = pluginConfigs.length + 2; // plugins + finalize + flatten
|
const totalSteps = toolConfigs.length + 2; // plugins + finalize + flatten
|
||||||
|
|
||||||
for (let index = 0; index < pluginConfigs.length; index++) {
|
for (let index = 0; index < toolConfigs.length; index++) {
|
||||||
const context: PromptConcatHookContext = {
|
const context: PromptConcatHookContext = {
|
||||||
handlerContext,
|
agentFrameworkContext: agentFrameworkContext,
|
||||||
messages,
|
messages,
|
||||||
prompts: modifiedPrompts,
|
prompts: modifiedPrompts,
|
||||||
pluginConfig: pluginConfigs[index],
|
toolConfig: toolConfigs[index],
|
||||||
metadata: { sourcePaths },
|
metadata: { sourcePaths },
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
|
|
@ -255,13 +256,13 @@ export async function* promptConcatStream(
|
||||||
processedPrompts: modifiedPrompts,
|
processedPrompts: modifiedPrompts,
|
||||||
flatPrompts: intermediateFlat,
|
flatPrompts: intermediateFlat,
|
||||||
step: 'plugin',
|
step: 'plugin',
|
||||||
currentPlugin: pluginConfigs[index],
|
currentPlugin: toolConfigs[index],
|
||||||
progress: (index + 1) / totalSteps,
|
progress: (index + 1) / totalSteps,
|
||||||
isComplete: false,
|
isComplete: false,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Plugin processing error', {
|
logger.error('Plugin processing error', {
|
||||||
pluginConfig: pluginConfigs[index],
|
toolConfig: toolConfigs[index],
|
||||||
error,
|
error,
|
||||||
});
|
});
|
||||||
// Continue processing other plugins even if one fails
|
// Continue processing other plugins even if one fails
|
||||||
|
|
@ -273,15 +274,15 @@ export async function* promptConcatStream(
|
||||||
processedPrompts: modifiedPrompts,
|
processedPrompts: modifiedPrompts,
|
||||||
flatPrompts: flattenPrompts(modifiedPrompts),
|
flatPrompts: flattenPrompts(modifiedPrompts),
|
||||||
step: 'finalize',
|
step: 'finalize',
|
||||||
progress: (pluginConfigs.length + 1) / totalSteps,
|
progress: (toolConfigs.length + 1) / totalSteps,
|
||||||
isComplete: false,
|
isComplete: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const finalContext: PromptConcatHookContext = {
|
const finalContext: PromptConcatHookContext = {
|
||||||
handlerContext,
|
agentFrameworkContext: agentFrameworkContext,
|
||||||
messages,
|
messages,
|
||||||
prompts: modifiedPrompts,
|
prompts: modifiedPrompts,
|
||||||
pluginConfig: {} as IPromptConcatPlugin, // Empty plugin for finalization
|
toolConfig: {} as IPromptConcatTool, // Empty tool for finalization
|
||||||
metadata: { sourcePaths },
|
metadata: { sourcePaths },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -297,7 +298,7 @@ export async function* promptConcatStream(
|
||||||
processedPrompts: modifiedPrompts,
|
processedPrompts: modifiedPrompts,
|
||||||
flatPrompts: flattenPrompts(modifiedPrompts),
|
flatPrompts: flattenPrompts(modifiedPrompts),
|
||||||
step: 'flatten',
|
step: 'flatten',
|
||||||
progress: (pluginConfigs.length + 2) / totalSteps,
|
progress: (toolConfigs.length + 2) / totalSteps,
|
||||||
isComplete: false,
|
isComplete: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -341,15 +342,15 @@ export async function* promptConcatStream(
|
||||||
* @returns Processed prompt array and original prompt tree
|
* @returns Processed prompt array and original prompt tree
|
||||||
*/
|
*/
|
||||||
export async function promptConcat(
|
export async function promptConcat(
|
||||||
agentConfig: Pick<AgentPromptDescription, 'handlerConfig'>,
|
agentConfig: Pick<AgentPromptDescription, 'agentFrameworkConfig'>,
|
||||||
messages: AgentInstanceMessage[],
|
messages: AgentInstanceMessage[],
|
||||||
handlerContext: AgentHandlerContext,
|
agentFrameworkContext: AgentFrameworkContext,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
flatPrompts: ModelMessage[];
|
flatPrompts: ModelMessage[];
|
||||||
processedPrompts: IPrompt[];
|
processedPrompts: IPrompt[];
|
||||||
}> {
|
}> {
|
||||||
// Use the streaming version and just return the final result
|
// Use the streaming version and just return the final result
|
||||||
const stream = promptConcatStream(agentConfig, messages, handlerContext);
|
const stream = promptConcatStream(agentConfig, messages, agentFrameworkContext);
|
||||||
let finalResult: PromptConcatStreamState;
|
let finalResult: PromptConcatStreamState;
|
||||||
|
|
||||||
// Consume all intermediate states to get the final result
|
// Consume all intermediate states to get the final result
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { createDynamicPromptConcatPluginSchema } from '@services/agentInstance/plugins/schemaRegistry';
|
import { createDynamicPromptConcatToolSchema } from '@services/agentInstance/tools/schemaRegistry';
|
||||||
import { t } from '@services/libs/i18n/placeholder';
|
import { t } from '@services/libs/i18n/placeholder';
|
||||||
import { z } from 'zod/v4';
|
import { z } from 'zod/v4';
|
||||||
import { ModelParametersSchema, ProviderModelSchema } from './modelParameters';
|
import { ModelParametersSchema, ProviderModelSchema } from './modelParameters';
|
||||||
|
|
@ -34,12 +34,12 @@ export const AIConfigSchema = BaseAPIConfigSchema
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler configuration schema
|
* Framework configuration schema
|
||||||
* Contains the handler-related configuration fields for prompts, responses, and plugins
|
* Contains the framework-related configuration fields for prompts, responses, and tools
|
||||||
* This is dynamically generated to include all registered plugins
|
* This is dynamically generated to include all registered tools
|
||||||
*/
|
*/
|
||||||
export function getHandlerConfigSchema() {
|
export function getFrameworkConfigSchema() {
|
||||||
const dynamicPluginSchema = createDynamicPromptConcatPluginSchema();
|
const dynamicToolSchema = createDynamicPromptConcatToolSchema();
|
||||||
|
|
||||||
return z.object({
|
return z.object({
|
||||||
prompts: z.array(PromptSchema).meta({
|
prompts: z.array(PromptSchema).meta({
|
||||||
|
|
@ -50,7 +50,7 @@ export function getHandlerConfigSchema() {
|
||||||
description: t('Schema.AgentConfig.PromptConfig.Response'),
|
description: t('Schema.AgentConfig.PromptConfig.Response'),
|
||||||
title: t('PromptConfig.Tabs.Response'),
|
title: t('PromptConfig.Tabs.Response'),
|
||||||
}),
|
}),
|
||||||
plugins: z.array(dynamicPluginSchema).meta({
|
plugins: z.array(dynamicToolSchema).meta({
|
||||||
description: t('Schema.AgentConfig.PromptConfig.Plugins'),
|
description: t('Schema.AgentConfig.PromptConfig.Plugins'),
|
||||||
title: t('PromptConfig.Tabs.Plugins'),
|
title: t('PromptConfig.Tabs.Plugins'),
|
||||||
}),
|
}),
|
||||||
|
|
@ -66,13 +66,13 @@ export function getHandlerConfigSchema() {
|
||||||
* @example
|
* @example
|
||||||
* ```json
|
* ```json
|
||||||
* {
|
* {
|
||||||
* "id": "example-agent",
|
* "id": "task-agent",
|
||||||
* "api": {
|
* "api": {
|
||||||
* "provider": "siliconflow",
|
* "provider": "siliconflow",
|
||||||
* "model": "Qwen/Qwen2.5-7B-Instruct"
|
* "model": "Qwen/Qwen2.5-7B-Instruct"
|
||||||
* },
|
* },
|
||||||
* "modelParameters": { ... },
|
* "modelParameters": { ... },
|
||||||
* "handlerConfig": {
|
* "agentFrameworkConfig": {
|
||||||
* "prompts": [ ... ],
|
* "prompts": [ ... ],
|
||||||
* "response": [ ... ],
|
* "response": [ ... ],
|
||||||
* "plugins": [ ... ],
|
* "plugins": [ ... ],
|
||||||
|
|
@ -81,14 +81,14 @@ export function getHandlerConfigSchema() {
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function getAgentConfigSchema() {
|
export function getAgentConfigSchema() {
|
||||||
const dynamicHandlerConfigSchema = getHandlerConfigSchema();
|
const dynamicFrameworkConfigSchema = getFrameworkConfigSchema();
|
||||||
|
|
||||||
return BaseAPIConfigSchema.extend({
|
return BaseAPIConfigSchema.extend({
|
||||||
id: z.string().meta({
|
id: z.string().meta({
|
||||||
title: t('Schema.AgentConfig.IdTitle'),
|
title: t('Schema.AgentConfig.IdTitle'),
|
||||||
description: t('Schema.AgentConfig.Id'),
|
description: t('Schema.AgentConfig.Id'),
|
||||||
}),
|
}),
|
||||||
handlerConfig: dynamicHandlerConfigSchema,
|
agentFrameworkConfig: dynamicFrameworkConfigSchema,
|
||||||
}).meta({
|
}).meta({
|
||||||
title: t('Schema.AgentConfig.Title'),
|
title: t('Schema.AgentConfig.Title'),
|
||||||
description: t('Schema.AgentConfig.Description'),
|
description: t('Schema.AgentConfig.Description'),
|
||||||
|
|
@ -110,10 +110,15 @@ export function getDefaultAgentsSchema() {
|
||||||
export type DefaultAgents = z.infer<ReturnType<typeof getDefaultAgentsSchema>>;
|
export type DefaultAgents = z.infer<ReturnType<typeof getDefaultAgentsSchema>>;
|
||||||
export type AgentPromptDescription = z.infer<ReturnType<typeof getAgentConfigSchema>>;
|
export type AgentPromptDescription = z.infer<ReturnType<typeof getAgentConfigSchema>>;
|
||||||
export type AiAPIConfig = z.infer<typeof AIConfigSchema>;
|
export type AiAPIConfig = z.infer<typeof AIConfigSchema>;
|
||||||
export type HandlerConfig = z.infer<ReturnType<typeof getHandlerConfigSchema>>;
|
export type AgentFrameworkConfig = z.infer<ReturnType<typeof getFrameworkConfigSchema>>;
|
||||||
|
// Backward compat aliases - deprecated, use AgentFrameworkConfig directly
|
||||||
|
export type HandlerConfig = AgentFrameworkConfig;
|
||||||
|
|
||||||
// Re-export all schemas and types
|
// Re-export all schemas and types
|
||||||
export * from './modelParameters';
|
export * from './modelParameters';
|
||||||
export * from './plugin';
|
export * from './plugin';
|
||||||
export * from './prompts';
|
export * from './prompts';
|
||||||
export * from './response';
|
export * from './response';
|
||||||
|
|
||||||
|
// Export IPromptConcatTool as IPromptConcatPlugin for backward compatibility
|
||||||
|
export type { IPromptConcatTool as IPromptConcatPlugin } from './plugin';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { z } from 'zod/v4';
|
import { z } from 'zod/v4';
|
||||||
import { getHandlerConfigSchema } from './index';
|
import { getFrameworkConfigSchema } from './index';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the dynamically generated JSON Schema for handler configuration
|
* Get the dynamically generated JSON Schema for handler configuration
|
||||||
|
|
@ -11,7 +11,7 @@ import { getHandlerConfigSchema } from './index';
|
||||||
*
|
*
|
||||||
* Description field is i18n key, use i18nAlly extension to see it on VSCode. And use react-i18next to translate it on frontend.
|
* Description field is i18n key, use i18nAlly extension to see it on VSCode. And use react-i18next to translate it on frontend.
|
||||||
*/
|
*/
|
||||||
export function getPromptConcatHandlerConfigJsonSchema() {
|
export function getPromptConcatAgentFrameworkConfigJsonSchema() {
|
||||||
const dynamicHandlerConfigSchema = getHandlerConfigSchema();
|
const dynamicFrameworkConfigSchema = getFrameworkConfigSchema();
|
||||||
return z.toJSONSchema(dynamicHandlerConfigSchema, { target: 'draft-7' });
|
return z.toJSONSchema(dynamicFrameworkConfigSchema, { target: 'draft-7' });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
// Import parameter types from plugin files
|
// Import parameter types from plugin files
|
||||||
import type { ModelContextProtocolParameter } from '@services/agentInstance/plugins/modelContextProtocolPlugin';
|
import type { ModelContextProtocolParameter } from '@services/agentInstance/tools/modelContextProtocol';
|
||||||
import type { DynamicPositionParameter, FullReplacementParameter } from '@services/agentInstance/plugins/promptPlugins';
|
import type { DynamicPositionParameter, FullReplacementParameter } from '@services/agentInstance/tools/prompt';
|
||||||
import type { WikiOperationParameter } from '@services/agentInstance/plugins/wikiOperationPlugin';
|
import type { WikiOperationParameter } from '@services/agentInstance/tools/wikiOperation';
|
||||||
import type { WikiSearchParameter } from '@services/agentInstance/plugins/wikiSearchPlugin';
|
import type { WikiSearchParameter } from '@services/agentInstance/tools/wikiSearch';
|
||||||
import type { WorkspacesListParameter } from '@services/agentInstance/plugins/workspacesListPlugin';
|
import type { WorkspacesListParameter } from '@services/agentInstance/tools/workspacesList';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type definition for prompt concat plugin
|
* Type definition for prompt concat tool
|
||||||
* This includes all possible parameter fields for type safety
|
* This includes all possible parameter fields for type safety
|
||||||
*/
|
*/
|
||||||
export type IPromptConcatPlugin = {
|
export type IPromptConcatTool = {
|
||||||
id: string;
|
id: string;
|
||||||
caption?: string;
|
caption?: string;
|
||||||
content?: string;
|
content?: string;
|
||||||
forbidOverrides?: boolean;
|
forbidOverrides?: boolean;
|
||||||
pluginId: string;
|
toolId: string;
|
||||||
|
|
||||||
// Plugin-specific parameters
|
// Tool-specific parameters
|
||||||
fullReplacementParam?: FullReplacementParameter;
|
fullReplacementParam?: FullReplacementParameter;
|
||||||
dynamicPositionParam?: DynamicPositionParameter;
|
dynamicPositionParam?: DynamicPositionParameter;
|
||||||
wikiOperationParam?: WikiOperationParameter;
|
wikiOperationParam?: WikiOperationParameter;
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
import { ToolCallingMatch } from '@services/agentDefinition/interface';
|
import { ToolCallingMatch } from '@services/agentDefinition/interface';
|
||||||
import { logger } from '@services/libs/log';
|
import { logger } from '@services/libs/log';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { AgentHandlerContext } from '../buildInAgentHandlers/type';
|
import { AgentFrameworkContext } from '../agentFrameworks/utilities/type';
|
||||||
import { AgentInstanceMessage } from '../interface';
|
import { AgentInstanceMessage } from '../interface';
|
||||||
import { builtInPlugins, createHandlerHooks } from '../plugins';
|
import { builtInTools, createAgentFrameworkHooks } from '../tools';
|
||||||
import { AgentResponse, PostProcessContext, YieldNextRoundTarget } from '../plugins/types';
|
import { AgentResponse, PostProcessContext, YieldNextRoundTarget } from '../tools/types';
|
||||||
import type { IPromptConcatPlugin } from './promptConcatSchema';
|
import type { IPromptConcatTool } from './promptConcatSchema';
|
||||||
import { AgentPromptDescription, HandlerConfig } from './promptConcatSchema';
|
import { AgentFrameworkConfig, AgentPromptDescription } from './promptConcatSchema';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process response configuration, apply plugins, and return final response
|
* Process response configuration, apply plugins, and return final response
|
||||||
|
|
@ -24,7 +24,7 @@ import { AgentPromptDescription, HandlerConfig } from './promptConcatSchema';
|
||||||
export async function responseConcat(
|
export async function responseConcat(
|
||||||
agentConfig: AgentPromptDescription,
|
agentConfig: AgentPromptDescription,
|
||||||
llmResponse: string,
|
llmResponse: string,
|
||||||
context: AgentHandlerContext,
|
context: AgentFrameworkContext,
|
||||||
messages: AgentInstanceMessage[] = [],
|
messages: AgentInstanceMessage[] = [],
|
||||||
): Promise<{
|
): Promise<{
|
||||||
processedResponse: string;
|
processedResponse: string;
|
||||||
|
|
@ -38,33 +38,33 @@ export async function responseConcat(
|
||||||
responseLength: llmResponse.length,
|
responseLength: llmResponse.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { handlerConfig } = agentConfig;
|
const { agentFrameworkConfig } = agentConfig;
|
||||||
const responses: HandlerConfig['response'] = Array.isArray(handlerConfig.response) ? handlerConfig.response : [];
|
const responses: AgentFrameworkConfig['response'] = Array.isArray(agentFrameworkConfig?.response) ? (agentFrameworkConfig?.response || []) : [];
|
||||||
const plugins = (Array.isArray(handlerConfig.plugins) ? handlerConfig.plugins : []) as IPromptConcatPlugin[];
|
const toolConfigs = (Array.isArray(agentFrameworkConfig.plugins) ? agentFrameworkConfig.plugins : []) as IPromptConcatTool[];
|
||||||
|
|
||||||
let modifiedResponses = cloneDeep(responses) as AgentResponse[];
|
let modifiedResponses = cloneDeep(responses) as AgentResponse[];
|
||||||
// Create hooks instance
|
// Create hooks instance
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
// Register all plugins from configuration
|
// Register all tools from configuration
|
||||||
for (const plugin of plugins) {
|
for (const tool of toolConfigs) {
|
||||||
const builtInPlugin = builtInPlugins.get(plugin.pluginId);
|
const builtInTool = builtInTools.get(tool.toolId);
|
||||||
if (builtInPlugin) {
|
if (builtInTool) {
|
||||||
builtInPlugin(hooks);
|
builtInTool(hooks);
|
||||||
} else {
|
} else {
|
||||||
logger.warn(`No built-in plugin found for response pluginId: ${plugin.pluginId}`);
|
logger.warn(`No built-in tool found for response toolId: ${tool.toolId}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process each plugin through hooks
|
// Process each tool through hooks
|
||||||
let yieldNextRoundTo: YieldNextRoundTarget | undefined;
|
let yieldNextRoundTo: YieldNextRoundTarget | undefined;
|
||||||
let toolCallInfo: ToolCallingMatch | undefined;
|
let toolCallInfo: ToolCallingMatch | undefined;
|
||||||
|
|
||||||
for (const plugin of plugins) {
|
for (const tool of toolConfigs) {
|
||||||
const responseContext: PostProcessContext = {
|
const responseContext: PostProcessContext = {
|
||||||
handlerContext: context,
|
agentFrameworkContext: context,
|
||||||
messages,
|
messages,
|
||||||
prompts: [], // Not used in response processing
|
prompts: [], // Not used in response processing
|
||||||
pluginConfig: plugin,
|
toolConfig: tool,
|
||||||
llmResponse,
|
llmResponse,
|
||||||
responses: modifiedResponses,
|
responses: modifiedResponses,
|
||||||
metadata: {},
|
metadata: {},
|
||||||
|
|
@ -78,31 +78,31 @@ export async function responseConcat(
|
||||||
modifiedResponses = result.responses;
|
modifiedResponses = result.responses;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if plugin indicated need for new LLM call via actions
|
// Check if tool indicated need for new LLM call via actions
|
||||||
if (result.actions?.yieldNextRoundTo) {
|
if (result.actions?.yieldNextRoundTo) {
|
||||||
yieldNextRoundTo = result.actions.yieldNextRoundTo;
|
yieldNextRoundTo = result.actions.yieldNextRoundTo;
|
||||||
if (result.actions.toolCalling) {
|
if (result.actions.toolCalling) {
|
||||||
toolCallInfo = result.actions.toolCalling;
|
toolCallInfo = result.actions.toolCalling;
|
||||||
}
|
}
|
||||||
logger.debug('Plugin requested yield next round', {
|
logger.debug('Tool requested yield next round', {
|
||||||
pluginId: plugin.pluginId,
|
toolId: tool.toolId,
|
||||||
pluginInstanceId: plugin.id,
|
toolInstanceId: tool.id,
|
||||||
yieldNextRoundTo,
|
yieldNextRoundTo,
|
||||||
hasToolCall: !!result.actions.toolCalling,
|
hasToolCall: !!result.actions.toolCalling,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('Response plugin processed successfully', {
|
logger.debug('Response tool processed successfully', {
|
||||||
pluginId: plugin.pluginId,
|
toolId: tool.toolId,
|
||||||
pluginInstanceId: plugin.id,
|
toolInstanceId: tool.id,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Response plugin processing error', {
|
logger.error('Response tool processing error', {
|
||||||
pluginId: plugin.pluginId,
|
toolId: tool.toolId,
|
||||||
pluginInstanceId: plugin.id,
|
toolInstanceId: tool.id,
|
||||||
error,
|
error,
|
||||||
});
|
});
|
||||||
// Continue processing other plugins even if one fails
|
// Continue processing other tools even if one fails
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
/**
|
/**
|
||||||
* Tests for Full Replacement plugin duration mechanism
|
* Tests for Full Replacement plugin duration mechanism
|
||||||
* Tests that expired messages (with duration) are filtered out from AI context
|
* Tests that expired messages (with duration) are filtered out from AI context
|
||||||
* Based on real configuration from defaultAgents.json
|
* Based on real configuration from taskAgents.json
|
||||||
*/
|
*/
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import type { AgentInstanceMessage } from '../../interface';
|
import type { AgentInstanceMessage } from '../../interface';
|
||||||
import type { IPromptConcatPlugin } from '../../promptConcat/promptConcatSchema';
|
import type { IPromptConcatTool } from '../../promptConcat/promptConcatSchema';
|
||||||
import type { IPrompt } from '../../promptConcat/promptConcatSchema/prompts';
|
import type { IPrompt } from '../../promptConcat/promptConcatSchema/prompts';
|
||||||
|
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import defaultAgents from '../../buildInAgentHandlers/defaultAgents.json';
|
import defaultAgents from '../../agentFrameworks/taskAgents.json';
|
||||||
import { createHandlerHooks, PromptConcatHookContext } from '../index';
|
import { createAgentFrameworkHooks, PromptConcatHookContext } from '../index';
|
||||||
import { fullReplacementPlugin } from '../promptPlugins';
|
import { fullReplacementTool } from '../prompt';
|
||||||
|
|
||||||
// Use the real agent config
|
// Use the real agent config
|
||||||
const exampleAgent = defaultAgents[0];
|
const exampleAgent = defaultAgents[0];
|
||||||
const realHandlerConfig = exampleAgent.handlerConfig;
|
const realAgentFrameworkConfig = exampleAgent.agentFrameworkConfig;
|
||||||
|
|
||||||
describe('Full Replacement Plugin - Duration Mechanism', () => {
|
describe('Full Replacement Plugin - Duration Mechanism', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
@ -24,15 +24,15 @@ describe('Full Replacement Plugin - Duration Mechanism', () => {
|
||||||
|
|
||||||
describe('History Source Type with Duration Filtering', () => {
|
describe('History Source Type with Duration Filtering', () => {
|
||||||
it('should filter out expired messages (duration=1) from historyOfSession', async () => {
|
it('should filter out expired messages (duration=1) from historyOfSession', async () => {
|
||||||
// Find the real fullReplacement plugin for history from defaultAgents.json
|
// Find the real fullReplacement plugin for history from taskAgents.json
|
||||||
const historyPlugin = realHandlerConfig.plugins.find(
|
const historyPlugin = realAgentFrameworkConfig.plugins.find(
|
||||||
p => p.pluginId === 'fullReplacement' && p.fullReplacementParam?.sourceType === 'historyOfSession',
|
p => p.toolId === 'fullReplacement' && p.fullReplacementParam?.sourceType === 'historyOfSession',
|
||||||
);
|
);
|
||||||
expect(historyPlugin).toBeDefined();
|
expect(historyPlugin).toBeDefined();
|
||||||
expect(historyPlugin!.fullReplacementParam!.targetId).toBe('default-history'); // Real target ID
|
expect(historyPlugin!.fullReplacementParam!.targetId).toBe('default-history'); // Real target ID
|
||||||
|
|
||||||
// Use real prompts structure from defaultAgents.json
|
// Use real prompts structure from taskAgents.json
|
||||||
const testPrompts = cloneDeep(realHandlerConfig.prompts) as IPrompt[];
|
const testPrompts = cloneDeep(realAgentFrameworkConfig.prompts) as IPrompt[];
|
||||||
|
|
||||||
const messages: AgentInstanceMessage[] = [
|
const messages: AgentInstanceMessage[] = [
|
||||||
// Message 0: User message, no duration - should be included
|
// Message 0: User message, no duration - should be included
|
||||||
|
|
@ -96,7 +96,7 @@ describe('Full Replacement Plugin - Duration Mechanism', () => {
|
||||||
];
|
];
|
||||||
|
|
||||||
const context: PromptConcatHookContext = {
|
const context: PromptConcatHookContext = {
|
||||||
handlerContext: {
|
agentFrameworkContext: {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
messages,
|
messages,
|
||||||
|
|
@ -104,16 +104,16 @@ describe('Full Replacement Plugin - Duration Mechanism', () => {
|
||||||
status: { state: 'working' as const, modified: new Date() },
|
status: { state: 'working' as const, modified: new Date() },
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
},
|
},
|
||||||
agentDef: { id: 'test-agent-def', name: 'test', handlerConfig: {} },
|
agentDef: { id: 'test-agent-def', name: 'test', agentFrameworkConfig: {} },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
},
|
},
|
||||||
pluginConfig: historyPlugin! as unknown as IPromptConcatPlugin, // Type cast due to JSON import limitations
|
toolConfig: historyPlugin! as unknown as IPromptConcatTool, // Type cast due to JSON import limitations
|
||||||
prompts: testPrompts,
|
prompts: testPrompts,
|
||||||
messages,
|
messages,
|
||||||
};
|
};
|
||||||
|
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
fullReplacementPlugin(hooks);
|
fullReplacementTool(hooks);
|
||||||
|
|
||||||
// Execute the processPrompts hook
|
// Execute the processPrompts hook
|
||||||
await hooks.processPrompts.promise(context);
|
await hooks.processPrompts.promise(context);
|
||||||
|
|
@ -126,8 +126,8 @@ describe('Full Replacement Plugin - Duration Mechanism', () => {
|
||||||
const targetPrompt = historyPrompt!.children?.find(child => child.id === targetId);
|
const targetPrompt = historyPrompt!.children?.find(child => child.id === targetId);
|
||||||
expect(targetPrompt).toBeDefined();
|
expect(targetPrompt).toBeDefined();
|
||||||
|
|
||||||
// The fullReplacementPlugin puts filtered messages in children array
|
// The fullReplacementTool puts filtered messages in children array
|
||||||
// Note: fullReplacementPlugin removes the last message (current user message)
|
// Note: fullReplacementTool removes the last message (current user message)
|
||||||
const children = (targetPrompt as unknown as { children?: IPrompt[] }).children || [];
|
const children = (targetPrompt as unknown as { children?: IPrompt[] }).children || [];
|
||||||
expect(children.length).toBe(2); // Only non-expired messages (user1, ai-response), excluding last user message
|
expect(children.length).toBe(2); // Only non-expired messages (user1, ai-response), excluding last user message
|
||||||
|
|
||||||
|
|
@ -147,8 +147,8 @@ describe('Full Replacement Plugin - Duration Mechanism', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include messages with duration=0 (visible in current round)', async () => {
|
it('should include messages with duration=0 (visible in current round)', async () => {
|
||||||
const historyPlugin = realHandlerConfig.plugins.find(
|
const historyPlugin = realAgentFrameworkConfig.plugins.find(
|
||||||
p => p.pluginId === 'fullReplacement' && p.fullReplacementParam?.sourceType === 'historyOfSession',
|
p => p.toolId === 'fullReplacement' && p.fullReplacementParam?.sourceType === 'historyOfSession',
|
||||||
);
|
);
|
||||||
|
|
||||||
const messages: AgentInstanceMessage[] = [
|
const messages: AgentInstanceMessage[] = [
|
||||||
|
|
@ -181,10 +181,10 @@ describe('Full Replacement Plugin - Duration Mechanism', () => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const testPrompts = cloneDeep(realHandlerConfig.prompts) as IPrompt[];
|
const testPrompts = cloneDeep(realAgentFrameworkConfig.prompts) as IPrompt[];
|
||||||
|
|
||||||
const context: PromptConcatHookContext = {
|
const context: PromptConcatHookContext = {
|
||||||
handlerContext: {
|
agentFrameworkContext: {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
messages,
|
messages,
|
||||||
|
|
@ -192,16 +192,16 @@ describe('Full Replacement Plugin - Duration Mechanism', () => {
|
||||||
status: { state: 'working' as const, modified: new Date() },
|
status: { state: 'working' as const, modified: new Date() },
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
},
|
},
|
||||||
agentDef: { id: 'test-agent-def', name: 'test', handlerConfig: {} },
|
agentDef: { id: 'test-agent-def', name: 'test', agentFrameworkConfig: {} },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
},
|
},
|
||||||
pluginConfig: historyPlugin! as unknown as IPromptConcatPlugin, // Type cast for JSON import
|
toolConfig: historyPlugin! as unknown as IPromptConcatTool, // Type cast for JSON import
|
||||||
prompts: testPrompts,
|
prompts: testPrompts,
|
||||||
messages,
|
messages,
|
||||||
};
|
};
|
||||||
|
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
fullReplacementPlugin(hooks);
|
fullReplacementTool(hooks);
|
||||||
|
|
||||||
await hooks.processPrompts.promise(context);
|
await hooks.processPrompts.promise(context);
|
||||||
|
|
||||||
|
|
@ -220,8 +220,8 @@ describe('Full Replacement Plugin - Duration Mechanism', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle mixed duration values correctly', async () => {
|
it('should handle mixed duration values correctly', async () => {
|
||||||
const historyPlugin = realHandlerConfig.plugins.find(
|
const historyPlugin = realAgentFrameworkConfig.plugins.find(
|
||||||
p => p.pluginId === 'fullReplacement' && p.fullReplacementParam?.sourceType === 'historyOfSession',
|
p => p.toolId === 'fullReplacement' && p.fullReplacementParam?.sourceType === 'historyOfSession',
|
||||||
);
|
);
|
||||||
|
|
||||||
const messages: AgentInstanceMessage[] = [
|
const messages: AgentInstanceMessage[] = [
|
||||||
|
|
@ -263,10 +263,10 @@ describe('Full Replacement Plugin - Duration Mechanism', () => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const testPrompts = cloneDeep(realHandlerConfig.prompts) as IPrompt[];
|
const testPrompts = cloneDeep(realAgentFrameworkConfig.prompts) as IPrompt[];
|
||||||
|
|
||||||
const context: PromptConcatHookContext = {
|
const context: PromptConcatHookContext = {
|
||||||
handlerContext: {
|
agentFrameworkContext: {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
messages,
|
messages,
|
||||||
|
|
@ -274,16 +274,16 @@ describe('Full Replacement Plugin - Duration Mechanism', () => {
|
||||||
status: { state: 'working' as const, modified: new Date() },
|
status: { state: 'working' as const, modified: new Date() },
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
},
|
},
|
||||||
agentDef: { id: 'test-agent-def', name: 'test', handlerConfig: {} },
|
agentDef: { id: 'test-agent-def', name: 'test', agentFrameworkConfig: {} },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
},
|
},
|
||||||
pluginConfig: historyPlugin! as unknown as IPromptConcatPlugin, // Type cast for JSON import
|
toolConfig: historyPlugin! as unknown as IPromptConcatTool, // Type cast for JSON import
|
||||||
prompts: testPrompts,
|
prompts: testPrompts,
|
||||||
messages,
|
messages,
|
||||||
};
|
};
|
||||||
|
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
fullReplacementPlugin(hooks);
|
fullReplacementTool(hooks);
|
||||||
|
|
||||||
await hooks.processPrompts.promise(context);
|
await hooks.processPrompts.promise(context);
|
||||||
|
|
||||||
|
|
@ -308,8 +308,8 @@ describe('Full Replacement Plugin - Duration Mechanism', () => {
|
||||||
describe('LLM Response Source Type', () => {
|
describe('LLM Response Source Type', () => {
|
||||||
it('should verify LLM response replacement config exists', () => {
|
it('should verify LLM response replacement config exists', () => {
|
||||||
// Verify the real config has LLM response replacement
|
// Verify the real config has LLM response replacement
|
||||||
const llmResponsePlugin = realHandlerConfig.plugins.find(
|
const llmResponsePlugin = realAgentFrameworkConfig.plugins.find(
|
||||||
p => p.pluginId === 'fullReplacement' && p.fullReplacementParam?.sourceType === 'llmResponse',
|
p => p.toolId === 'fullReplacement' && p.fullReplacementParam?.sourceType === 'llmResponse',
|
||||||
);
|
);
|
||||||
expect(llmResponsePlugin).toBeDefined();
|
expect(llmResponsePlugin).toBeDefined();
|
||||||
expect(llmResponsePlugin!.fullReplacementParam!.targetId).toBe('default-response');
|
expect(llmResponsePlugin!.fullReplacementParam!.targetId).toBe('default-response');
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* Deep integration tests for messageManagementPlugin with real SQLite database
|
* Deep integration tests for messageManagementTool with real SQLite database
|
||||||
* Tests actual message persistence scenarios using defaultAgents.json configuration
|
* Tests actual message persistence scenarios using taskAgents.json configuration
|
||||||
*/
|
*/
|
||||||
import { container } from '@services/container';
|
import { container } from '@services/container';
|
||||||
import type { IDatabaseService } from '@services/database/interface';
|
import type { IDatabaseService } from '@services/database/interface';
|
||||||
|
|
@ -8,20 +8,20 @@ import { AgentDefinitionEntity, AgentInstanceEntity, AgentInstanceMessageEntity
|
||||||
import serviceIdentifier from '@services/serviceIdentifier';
|
import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import defaultAgents from '../../buildInAgentHandlers/defaultAgents.json';
|
import defaultAgents from '../../agentFrameworks/taskAgents.json';
|
||||||
import type { AgentInstanceMessage, IAgentInstanceService } from '../../interface';
|
import type { AgentInstanceMessage, IAgentInstanceService } from '../../interface';
|
||||||
import { createHandlerHooks } from '../index';
|
import { createAgentFrameworkHooks } from '../index';
|
||||||
import { messageManagementPlugin } from '../messageManagementPlugin';
|
import { messageManagementTool } from '../messageManagement';
|
||||||
import type { ToolExecutionContext, UserMessageContext } from '../types';
|
import type { ToolExecutionContext, UserMessageContext } from '../types';
|
||||||
|
|
||||||
// Use the real agent config from defaultAgents.json
|
// Use the real agent config from taskAgents.json
|
||||||
const exampleAgent = defaultAgents[0];
|
const exampleAgent = defaultAgents[0];
|
||||||
|
|
||||||
describe('Message Management Plugin - Real Database Integration', () => {
|
describe('Message Management Plugin - Real Database Integration', () => {
|
||||||
let testAgentId: string;
|
let testAgentId: string;
|
||||||
// agentInstanceServiceImpl available to test blocks
|
// agentInstanceServiceImpl available to test blocks
|
||||||
let agentInstanceServiceImpl: IAgentInstanceService;
|
let agentInstanceServiceImpl: IAgentInstanceService;
|
||||||
let hooks: ReturnType<typeof createHandlerHooks>;
|
let hooks: ReturnType<typeof createAgentFrameworkHooks>;
|
||||||
let realDataSource: DataSource;
|
let realDataSource: DataSource;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|
@ -69,15 +69,15 @@ describe('Message Management Plugin - Real Database Integration', () => {
|
||||||
await agentInstanceServiceImpl.initialize();
|
await agentInstanceServiceImpl.initialize();
|
||||||
|
|
||||||
// Initialize plugin
|
// Initialize plugin
|
||||||
hooks = createHandlerHooks();
|
hooks = createAgentFrameworkHooks();
|
||||||
messageManagementPlugin(hooks);
|
messageManagementTool(hooks);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
// Clean up is handled automatically by beforeEach for each test
|
// Clean up is handled automatically by beforeEach for each test
|
||||||
});
|
});
|
||||||
|
|
||||||
const createHandlerContext = (messages: AgentInstanceMessage[] = []) => ({
|
const createAgentFrameworkContext = (messages: AgentInstanceMessage[] = []) => ({
|
||||||
agent: {
|
agent: {
|
||||||
id: testAgentId,
|
id: testAgentId,
|
||||||
agentDefId: exampleAgent.id,
|
agentDefId: exampleAgent.id,
|
||||||
|
|
@ -90,19 +90,19 @@ describe('Message Management Plugin - Real Database Integration', () => {
|
||||||
name: exampleAgent.name,
|
name: exampleAgent.name,
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
capabilities: [],
|
capabilities: [],
|
||||||
handlerConfig: exampleAgent.handlerConfig,
|
agentFrameworkConfig: exampleAgent.agentFrameworkConfig,
|
||||||
},
|
},
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Real Wiki Search Scenario - The Missing Tool Result Bug', () => {
|
describe('Real Wiki Search Scenario - The Missing Tool Result Bug', () => {
|
||||||
it('should persist all messages in wiki search flow: user query → AI tool call → tool result → AI final response', async () => {
|
it('should persist all messages in wiki search flow: user query → AI tool call → tool result → AI final response', async () => {
|
||||||
const handlerContext = createHandlerContext();
|
const agentFrameworkContext = createAgentFrameworkContext();
|
||||||
|
|
||||||
// Step 1: User asks to search wiki
|
// Step 1: User asks to search wiki
|
||||||
const userMessageId = `user-msg-${Date.now()}`;
|
const userMessageId = `user-msg-${Date.now()}`;
|
||||||
const userContext: UserMessageContext = {
|
const userContext: UserMessageContext = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
content: { text: '搜索 wiki 中的 Index 条目并解释' },
|
content: { text: '搜索 wiki 中的 Index 条目并解释' },
|
||||||
messageId: userMessageId,
|
messageId: userMessageId,
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
|
|
@ -133,10 +133,10 @@ describe('Message Management Plugin - Real Database Integration', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
await agentInstanceServiceImpl.saveUserMessage(aiToolCallMessage);
|
await agentInstanceServiceImpl.saveUserMessage(aiToolCallMessage);
|
||||||
handlerContext.agent.messages.push(aiToolCallMessage);
|
agentFrameworkContext.agent.messages.push(aiToolCallMessage);
|
||||||
|
|
||||||
// Step 3: Tool result message (THIS IS THE MISSING PIECE!)
|
// Step 3: Tool result message (THIS IS THE MISSING PIECE!)
|
||||||
// This simulates what wikiSearchPlugin does when tool execution completes
|
// This simulates what wikiSearchTool does when tool execution completes
|
||||||
const toolResultMessage: AgentInstanceMessage = {
|
const toolResultMessage: AgentInstanceMessage = {
|
||||||
id: `tool-result-${Date.now()}`,
|
id: `tool-result-${Date.now()}`,
|
||||||
agentId: testAgentId,
|
agentId: testAgentId,
|
||||||
|
|
@ -164,11 +164,11 @@ Result: 在wiki中找到了名为"Index"的条目。这个条目包含以下内
|
||||||
duration: 10, // Tool results might have expiration
|
duration: 10, // Tool results might have expiration
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add tool result to agent messages (simulating what wikiSearchPlugin does)
|
// Add tool result to agent messages (simulating what wikiSearchTool does)
|
||||||
handlerContext.agent.messages.push(toolResultMessage);
|
agentFrameworkContext.agent.messages.push(toolResultMessage);
|
||||||
|
|
||||||
const toolContext: ToolExecutionContext = {
|
const toolContext: ToolExecutionContext = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
toolResult: {
|
toolResult: {
|
||||||
success: true,
|
success: true,
|
||||||
data: 'Wiki search completed successfully',
|
data: 'Wiki search completed successfully',
|
||||||
|
|
@ -202,7 +202,7 @@ Result: 在wiki中找到了名为"Index"的条目。这个条目包含以下内
|
||||||
expect(savedToolResult?.duration).toBe(10);
|
expect(savedToolResult?.duration).toBe(10);
|
||||||
|
|
||||||
// Verify isPersisted flag was updated
|
// Verify isPersisted flag was updated
|
||||||
const toolMessageInMemory = handlerContext.agent.messages.find(
|
const toolMessageInMemory = agentFrameworkContext.agent.messages.find(
|
||||||
(m) => m.metadata?.isToolResult,
|
(m) => m.metadata?.isToolResult,
|
||||||
);
|
);
|
||||||
expect(toolMessageInMemory?.metadata?.isPersisted).toBe(true);
|
expect(toolMessageInMemory?.metadata?.isPersisted).toBe(true);
|
||||||
|
|
@ -249,7 +249,7 @@ Result: 在wiki中找到了名为"Index"的条目。这个条目包含以下内
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle multiple tool results in one execution', async () => {
|
it('should handle multiple tool results in one execution', async () => {
|
||||||
const handlerContext = createHandlerContext();
|
const agentFrameworkContext = createAgentFrameworkContext();
|
||||||
|
|
||||||
// Add multiple tool result messages
|
// Add multiple tool result messages
|
||||||
const toolResult1: AgentInstanceMessage = {
|
const toolResult1: AgentInstanceMessage = {
|
||||||
|
|
@ -282,10 +282,10 @@ Result: 在wiki中找到了名为"Index"的条目。这个条目包含以下内
|
||||||
duration: 3,
|
duration: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
handlerContext.agent.messages.push(toolResult1, toolResult2);
|
agentFrameworkContext.agent.messages.push(toolResult1, toolResult2);
|
||||||
|
|
||||||
const toolContext: ToolExecutionContext = {
|
const toolContext: ToolExecutionContext = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
toolResult: {
|
toolResult: {
|
||||||
success: true,
|
success: true,
|
||||||
data: 'Multiple tool search completed',
|
data: 'Multiple tool search completed',
|
||||||
|
|
@ -316,7 +316,7 @@ Result: 在wiki中找到了名为"Index"的条目。这个条目包含以下内
|
||||||
|
|
||||||
it('should maintain message integrity when reloading from database (simulating page refresh)', async () => {
|
it('should maintain message integrity when reloading from database (simulating page refresh)', async () => {
|
||||||
// This test simulates the issue where tool results are missing after page refresh
|
// This test simulates the issue where tool results are missing after page refresh
|
||||||
const handlerContext = createHandlerContext();
|
const agentFrameworkContext = createAgentFrameworkContext();
|
||||||
|
|
||||||
// Step 1: Complete chat flow with user message → AI tool call → tool result → AI response
|
// Step 1: Complete chat flow with user message → AI tool call → tool result → AI response
|
||||||
const userMessage: AgentInstanceMessage = {
|
const userMessage: AgentInstanceMessage = {
|
||||||
|
|
@ -372,9 +372,9 @@ Result: 在wiki中找到了名为"Index"的条目。这个条目包含以下内
|
||||||
await agentInstanceServiceImpl.saveUserMessage(aiToolCallMessage);
|
await agentInstanceServiceImpl.saveUserMessage(aiToolCallMessage);
|
||||||
|
|
||||||
// Add tool result to context and trigger persistence via toolExecuted hook
|
// Add tool result to context and trigger persistence via toolExecuted hook
|
||||||
handlerContext.agent.messages.push(toolResultMessage);
|
agentFrameworkContext.agent.messages.push(toolResultMessage);
|
||||||
const toolContext: ToolExecutionContext = {
|
const toolContext: ToolExecutionContext = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
toolResult: { success: true, data: 'Search completed' },
|
toolResult: { success: true, data: 'Search completed' },
|
||||||
toolInfo: { toolId: 'wiki-search', parameters: {} },
|
toolInfo: { toolId: 'wiki-search', parameters: {} },
|
||||||
};
|
};
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Tests for wikiOperationPlugin
|
* Tests for wikiOperationTool
|
||||||
*/
|
*/
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
|
@ -11,16 +11,16 @@ import type { IWikiService } from '@services/wiki/interface';
|
||||||
// Removed logger import as it is unused
|
// Removed logger import as it is unused
|
||||||
|
|
||||||
import { matchToolCalling } from '@services/agentDefinition/responsePatternUtility';
|
import { matchToolCalling } from '@services/agentDefinition/responsePatternUtility';
|
||||||
import type { IPromptConcatPlugin } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
import type { IPromptConcatTool } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
import type { IPrompt } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
import type { IPrompt } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
import type { AIStreamResponse } from '@services/externalAPI/interface';
|
import type { AIStreamResponse } from '@services/externalAPI/interface';
|
||||||
import type { IWorkspaceService } from '@services/workspaces/interface';
|
import type { IWorkspaceService } from '@services/workspaces/interface';
|
||||||
import type { AgentHandlerContext } from '../../buildInAgentHandlers/type';
|
import type { AgentFrameworkContext } from '../../agentFrameworks/utilities/type';
|
||||||
import type { AgentInstance } from '../../interface';
|
import type { AgentInstance } from '../../interface';
|
||||||
import { createHandlerHooks } from '../index';
|
import { createAgentFrameworkHooks } from '../index';
|
||||||
import type { AIResponseContext, PluginActions, PromptConcatHookContext } from '../types';
|
import type { AIResponseContext, PromptConcatHookContext, ToolActions } from '../types';
|
||||||
import { wikiOperationPlugin } from '../wikiOperationPlugin';
|
import { wikiOperationTool } from '../wikiOperation';
|
||||||
import { workspacesListPlugin } from '../workspacesListPlugin';
|
import { workspacesListTool } from '../workspacesList';
|
||||||
|
|
||||||
// Mock i18n
|
// Mock i18n
|
||||||
vi.mock('@services/libs/i18n', () => ({
|
vi.mock('@services/libs/i18n', () => ({
|
||||||
|
|
@ -50,8 +50,8 @@ vi.mock('@services/libs/i18n', () => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Helper to construct a complete AgentHandlerContext for tests
|
// Helper to construct a complete AgentagentFrameworkContext for tests
|
||||||
const makeHandlerContext = (agentId = 'test-agent'): AgentHandlerContext => ({
|
const makeAgentFrameworkContext = (agentId = 'test-agent'): AgentFrameworkContext => ({
|
||||||
agent: {
|
agent: {
|
||||||
id: agentId,
|
id: agentId,
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -59,11 +59,11 @@ const makeHandlerContext = (agentId = 'test-agent'): AgentHandlerContext => ({
|
||||||
status: { state: 'working', modified: new Date() },
|
status: { state: 'working', modified: new Date() },
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
} as unknown as AgentInstance,
|
} as unknown as AgentInstance,
|
||||||
agentDef: { id: 'test-agent-def', name: 'test-agent-def', handlerConfig: {} } as unknown as { id: string; name: string; handlerConfig: Record<string, unknown> },
|
agentDef: { id: 'test-agent-def', name: 'test-agent-def', agentFrameworkConfig: {} } as unknown as { id: string; name: string; agentFrameworkConfig: Record<string, unknown> },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('wikiOperationPlugin', () => {
|
describe('wikiOperationTool', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
@ -73,12 +73,12 @@ describe('wikiOperationPlugin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should inject wiki operation tool content when plugin is configured', async () => {
|
it('should inject wiki operation tool content when plugin is configured', async () => {
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
// First register workspacesListPlugin to inject available workspaces from the global mock
|
// First register workspacesListTool to inject available workspaces from the global mock
|
||||||
workspacesListPlugin(hooks);
|
workspacesListTool(hooks);
|
||||||
wikiOperationPlugin(hooks);
|
wikiOperationTool(hooks);
|
||||||
|
|
||||||
// Start with prompts and run workspacesList injection first (pluginConfig for workspacesList)
|
// Start with prompts and run workspacesList injection first (toolConfig for workspacesList)
|
||||||
const prompts: IPrompt[] = [
|
const prompts: IPrompt[] = [
|
||||||
{
|
{
|
||||||
id: 'target-prompt',
|
id: 'target-prompt',
|
||||||
|
|
@ -88,48 +88,48 @@ describe('wikiOperationPlugin', () => {
|
||||||
];
|
];
|
||||||
|
|
||||||
const workspacesContext: PromptConcatHookContext = {
|
const workspacesContext: PromptConcatHookContext = {
|
||||||
handlerContext: {
|
agentFrameworkContext: {
|
||||||
agent: { id: 'test-agent', messages: [], agentDefId: 'test', status: { state: 'working' as const, modified: new Date() }, created: new Date() },
|
agent: { id: 'test-agent', messages: [], agentDefId: 'test', status: { state: 'working' as const, modified: new Date() }, created: new Date() },
|
||||||
agentDef: { id: 'test', name: 'test', handlerConfig: {} },
|
agentDef: { id: 'test', name: 'test', agentFrameworkConfig: {} },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
},
|
},
|
||||||
messages: [],
|
messages: [],
|
||||||
prompts,
|
prompts,
|
||||||
pluginConfig: {
|
toolConfig: {
|
||||||
id: 'workspaces-plugin',
|
id: 'workspaces-plugin',
|
||||||
caption: 'Workspaces Plugin',
|
caption: 'Workspaces Plugin',
|
||||||
forbidOverrides: false,
|
forbidOverrides: false,
|
||||||
pluginId: 'workspacesList',
|
toolId: 'workspacesList',
|
||||||
workspacesListParam: {
|
workspacesListParam: {
|
||||||
targetId: 'target-prompt',
|
targetId: 'target-prompt',
|
||||||
position: 'after' as const,
|
position: 'after' as const,
|
||||||
},
|
},
|
||||||
} as unknown as IPromptConcatPlugin,
|
} as unknown as IPromptConcatTool,
|
||||||
};
|
};
|
||||||
|
|
||||||
await hooks.processPrompts.promise(workspacesContext);
|
await hooks.processPrompts.promise(workspacesContext);
|
||||||
|
|
||||||
// Then run wikiOperation injection which will append its tool content to the same prompt
|
// Then run wikiOperation injection which will append its tool content to the same prompt
|
||||||
const wikiOpContext: PromptConcatHookContext = {
|
const wikiOpContext: PromptConcatHookContext = {
|
||||||
handlerContext: workspacesContext.handlerContext,
|
agentFrameworkContext: workspacesContext.agentFrameworkContext,
|
||||||
messages: [],
|
messages: [],
|
||||||
prompts,
|
prompts,
|
||||||
pluginConfig: {
|
toolConfig: {
|
||||||
id: 'test-plugin',
|
id: 'test-plugin',
|
||||||
pluginId: 'wikiOperation',
|
toolId: 'wikiOperation',
|
||||||
wikiOperationParam: {
|
wikiOperationParam: {
|
||||||
toolListPosition: {
|
toolListPosition: {
|
||||||
targetId: 'target-prompt',
|
targetId: 'target-prompt',
|
||||||
position: 'after' as const,
|
position: 'after' as const,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as unknown as IPromptConcatPlugin,
|
} as unknown as IPromptConcatTool,
|
||||||
};
|
};
|
||||||
|
|
||||||
await hooks.processPrompts.promise(wikiOpContext);
|
await hooks.processPrompts.promise(wikiOpContext);
|
||||||
|
|
||||||
const targetPrompt = prompts[0];
|
const targetPrompt = prompts[0];
|
||||||
// workspacesListPlugin and wikiOperationPlugin may both add children; assert the combined children text contains expected snippets
|
// workspacesListTool and wikiOperationTool may both add children; assert the combined children text contains expected snippets
|
||||||
const childrenText = JSON.stringify(targetPrompt.children);
|
const childrenText = JSON.stringify(targetPrompt.children);
|
||||||
expect(childrenText).toContain('wiki-operation');
|
expect(childrenText).toContain('wiki-operation');
|
||||||
// Ensure the injected tool content documents the supported operations (enum values)
|
// Ensure the injected tool content documents the supported operations (enum values)
|
||||||
|
|
@ -147,17 +147,17 @@ describe('wikiOperationPlugin', () => {
|
||||||
|
|
||||||
describe('tool execution', () => {
|
describe('tool execution', () => {
|
||||||
it('should execute create operation successfully', async () => {
|
it('should execute create operation successfully', async () => {
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
wikiOperationPlugin(hooks);
|
wikiOperationTool(hooks);
|
||||||
|
|
||||||
const handlerContext = makeHandlerContext();
|
const agentFrameworkContext = makeAgentFrameworkContext();
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
handlerConfig: {
|
agentFrameworkConfig: {
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
pluginId: 'wikiOperation',
|
toolId: 'wikiOperation',
|
||||||
wikiOperationParam: {
|
wikiOperationParam: {
|
||||||
toolResultDuration: 1,
|
toolResultDuration: 1,
|
||||||
},
|
},
|
||||||
|
|
@ -183,9 +183,9 @@ describe('wikiOperationPlugin', () => {
|
||||||
context.response.content = `<tool_use name="wiki-operation">${JSON.stringify(createParams)}</tool_use>`;
|
context.response.content = `<tool_use name="wiki-operation">${JSON.stringify(createParams)}</tool_use>`;
|
||||||
|
|
||||||
// Add an assistant message containing the tool_use so the plugin can find it
|
// Add an assistant message containing the tool_use so the plugin can find it
|
||||||
handlerContext.agent.messages.push({
|
agentFrameworkContext.agent.messages.push({
|
||||||
id: `m-${Date.now()}`,
|
id: `m-${Date.now()}`,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
content: context.response.content,
|
content: context.response.content,
|
||||||
modified: new Date(),
|
modified: new Date(),
|
||||||
|
|
@ -209,13 +209,13 @@ describe('wikiOperationPlugin', () => {
|
||||||
expect(typeof wikiSvc.wikiOperationInServer).toBe('function');
|
expect(typeof wikiSvc.wikiOperationInServer).toBe('function');
|
||||||
|
|
||||||
const responseCtx: AIResponseContext = {
|
const responseCtx: AIResponseContext = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
pluginConfig: context.handlerConfig?.plugins?.[0] as unknown as IPromptConcatPlugin,
|
toolConfig: context.agentFrameworkConfig?.plugins?.[0] as unknown as IPromptConcatTool,
|
||||||
handlerConfig: context.handlerConfig as { plugins?: Array<{ pluginId: string; [key: string]: unknown }> },
|
agentFrameworkConfig: context.agentFrameworkConfig as { plugins?: Array<{ toolId: string; [key: string]: unknown }> },
|
||||||
response: { requestId: 'r-create', content: context.response.content, status: 'done' } as AIStreamResponse,
|
response: { requestId: 'r-create', content: context.response.content, status: 'done' } as AIStreamResponse,
|
||||||
requestId: 'r-create',
|
requestId: 'r-create',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
actions: {} as PluginActions,
|
actions: {} as ToolActions,
|
||||||
};
|
};
|
||||||
|
|
||||||
await hooks.responseComplete.promise(responseCtx);
|
await hooks.responseComplete.promise(responseCtx);
|
||||||
|
|
@ -227,7 +227,7 @@ describe('wikiOperationPlugin', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Verify a tool result message was added to agent history
|
// Verify a tool result message was added to agent history
|
||||||
const toolResultMessage = handlerContext.agent.messages.find(m => m.metadata?.isToolResult);
|
const toolResultMessage = agentFrameworkContext.agent.messages.find(m => m.metadata?.isToolResult);
|
||||||
expect(toolResultMessage).toBeTruthy();
|
expect(toolResultMessage).toBeTruthy();
|
||||||
expect(toolResultMessage?.content).toContain('<functions_result>');
|
expect(toolResultMessage?.content).toContain('<functions_result>');
|
||||||
// Check for general success wording and tiddler title
|
// Check for general success wording and tiddler title
|
||||||
|
|
@ -238,15 +238,15 @@ describe('wikiOperationPlugin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should execute update operation successfully', async () => {
|
it('should execute update operation successfully', async () => {
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
wikiOperationPlugin(hooks);
|
wikiOperationTool(hooks);
|
||||||
|
|
||||||
const handlerContext = makeHandlerContext();
|
const agentFrameworkContext = makeAgentFrameworkContext();
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
handlerConfig: {
|
agentFrameworkConfig: {
|
||||||
plugins: [{ pluginId: 'wikiOperation', wikiOperationParam: {} }],
|
plugins: [{ toolId: 'wikiOperation', wikiOperationParam: {} }],
|
||||||
},
|
},
|
||||||
response: {
|
response: {
|
||||||
status: 'done' as const,
|
status: 'done' as const,
|
||||||
|
|
@ -256,9 +256,9 @@ describe('wikiOperationPlugin', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add assistant message so plugin can detect the tool call
|
// Add assistant message so plugin can detect the tool call
|
||||||
handlerContext.agent.messages.push({
|
agentFrameworkContext.agent.messages.push({
|
||||||
id: `m-${Date.now()}`,
|
id: `m-${Date.now()}`,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
content: context.response.content,
|
content: context.response.content,
|
||||||
modified: new Date(),
|
modified: new Date(),
|
||||||
|
|
@ -274,11 +274,11 @@ describe('wikiOperationPlugin', () => {
|
||||||
context.response.content = `<tool_use name="wiki-operation">${JSON.stringify(updateParams)}</tool_use>`;
|
context.response.content = `<tool_use name="wiki-operation">${JSON.stringify(updateParams)}</tool_use>`;
|
||||||
|
|
||||||
const respCtx2: AIResponseContext = {
|
const respCtx2: AIResponseContext = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
pluginConfig: context.handlerConfig?.plugins?.[0] as unknown as IPromptConcatPlugin,
|
toolConfig: context.agentFrameworkConfig?.plugins?.[0] as unknown as IPromptConcatTool,
|
||||||
handlerConfig: context.handlerConfig as { plugins?: Array<{ pluginId: string; [key: string]: unknown }> },
|
agentFrameworkConfig: context.agentFrameworkConfig as { plugins?: Array<{ toolId: string; [key: string]: unknown }> },
|
||||||
response: { requestId: 'r-update', content: context.response.content, status: 'done' } as AIStreamResponse,
|
response: { requestId: 'r-update', content: context.response.content, status: 'done' } as AIStreamResponse,
|
||||||
actions: {} as PluginActions,
|
actions: {} as ToolActions,
|
||||||
requestId: 'r-update',
|
requestId: 'r-update',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
};
|
};
|
||||||
|
|
@ -291,22 +291,22 @@ describe('wikiOperationPlugin', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check general update success wording and tiddler title
|
// Check general update success wording and tiddler title
|
||||||
const updateResult = handlerContext.agent.messages.find(m => m.metadata?.isToolResult);
|
const updateResult = agentFrameworkContext.agent.messages.find(m => m.metadata?.isToolResult);
|
||||||
expect(updateResult).toBeTruthy();
|
expect(updateResult).toBeTruthy();
|
||||||
expect(updateResult?.content).toContain('成功在Wiki工作空间');
|
expect(updateResult?.content).toContain('成功在Wiki工作空间');
|
||||||
expect(updateResult?.content).toContain('Existing Note');
|
expect(updateResult?.content).toContain('Existing Note');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should execute delete operation successfully', async () => {
|
it('should execute delete operation successfully', async () => {
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
wikiOperationPlugin(hooks);
|
wikiOperationTool(hooks);
|
||||||
|
|
||||||
const handlerContext = makeHandlerContext();
|
const agentFrameworkContext = makeAgentFrameworkContext();
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
handlerConfig: {
|
agentFrameworkConfig: {
|
||||||
plugins: [{ pluginId: 'wikiOperation', wikiOperationParam: {} }],
|
plugins: [{ toolId: 'wikiOperation', wikiOperationParam: {} }],
|
||||||
},
|
},
|
||||||
response: {
|
response: {
|
||||||
status: 'done' as const,
|
status: 'done' as const,
|
||||||
|
|
@ -316,9 +316,9 @@ describe('wikiOperationPlugin', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add assistant message so plugin can detect the tool call
|
// Add assistant message so plugin can detect the tool call
|
||||||
handlerContext.agent.messages.push({
|
agentFrameworkContext.agent.messages.push({
|
||||||
id: `m-${Date.now()}`,
|
id: `m-${Date.now()}`,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
content: context.response.content,
|
content: context.response.content,
|
||||||
modified: new Date(),
|
modified: new Date(),
|
||||||
|
|
@ -333,11 +333,11 @@ describe('wikiOperationPlugin', () => {
|
||||||
context.response.content = `<tool_use name="wiki-operation">${JSON.stringify(deleteParams)}</tool_use>`;
|
context.response.content = `<tool_use name="wiki-operation">${JSON.stringify(deleteParams)}</tool_use>`;
|
||||||
|
|
||||||
const respCtx3: AIResponseContext = {
|
const respCtx3: AIResponseContext = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
pluginConfig: context.handlerConfig?.plugins?.[0] as unknown as IPromptConcatPlugin,
|
toolConfig: context.agentFrameworkConfig?.plugins?.[0] as unknown as IPromptConcatTool,
|
||||||
handlerConfig: context.handlerConfig as { plugins?: Array<{ pluginId: string; [key: string]: unknown }> },
|
agentFrameworkConfig: context.agentFrameworkConfig as { plugins?: Array<{ toolId: string; [key: string]: unknown }> },
|
||||||
response: { requestId: 'r-delete', content: context.response.content, status: 'done' } as AIStreamResponse,
|
response: { requestId: 'r-delete', content: context.response.content, status: 'done' } as AIStreamResponse,
|
||||||
actions: {} as PluginActions,
|
actions: {} as ToolActions,
|
||||||
requestId: 'r-delete',
|
requestId: 'r-delete',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
};
|
};
|
||||||
|
|
@ -349,22 +349,22 @@ describe('wikiOperationPlugin', () => {
|
||||||
['Note to Delete'],
|
['Note to Delete'],
|
||||||
);
|
);
|
||||||
|
|
||||||
const deleteResult = handlerContext.agent.messages.find(m => m.metadata?.isToolResult);
|
const deleteResult = agentFrameworkContext.agent.messages.find(m => m.metadata?.isToolResult);
|
||||||
expect(deleteResult).toBeTruthy();
|
expect(deleteResult).toBeTruthy();
|
||||||
expect(deleteResult?.content).toContain('成功从Wiki工作空间');
|
expect(deleteResult?.content).toContain('成功从Wiki工作空间');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle workspace not found error', async () => {
|
it('should handle workspace not found error', async () => {
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
wikiOperationPlugin(hooks);
|
wikiOperationTool(hooks);
|
||||||
|
|
||||||
// Use an actual tool_use payload with a nonexistent workspace
|
// Use an actual tool_use payload with a nonexistent workspace
|
||||||
const handlerContext = makeHandlerContext();
|
const agentFrameworkContext = makeAgentFrameworkContext();
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
handlerConfig: {
|
agentFrameworkConfig: {
|
||||||
plugins: [{ pluginId: 'wikiOperation', wikiOperationParam: {} }],
|
plugins: [{ toolId: 'wikiOperation', wikiOperationParam: {} }],
|
||||||
},
|
},
|
||||||
response: {
|
response: {
|
||||||
status: 'done',
|
status: 'done',
|
||||||
|
|
@ -374,9 +374,9 @@ describe('wikiOperationPlugin', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add assistant message so plugin can detect the tool call
|
// Add assistant message so plugin can detect the tool call
|
||||||
handlerContext.agent.messages.push({
|
agentFrameworkContext.agent.messages.push({
|
||||||
id: `m-${Date.now()}`,
|
id: `m-${Date.now()}`,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
content: context.response.content,
|
content: context.response.content,
|
||||||
modified: new Date(),
|
modified: new Date(),
|
||||||
|
|
@ -390,17 +390,17 @@ describe('wikiOperationPlugin', () => {
|
||||||
context.response.content = `<tool_use name="wiki-operation">${JSON.stringify(badParams)}</tool_use>`;
|
context.response.content = `<tool_use name="wiki-operation">${JSON.stringify(badParams)}</tool_use>`;
|
||||||
|
|
||||||
const respCtx4: AIResponseContext = {
|
const respCtx4: AIResponseContext = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
pluginConfig: context.handlerConfig?.plugins?.[0] as unknown as IPromptConcatPlugin,
|
toolConfig: context.agentFrameworkConfig?.plugins?.[0] as unknown as IPromptConcatTool,
|
||||||
handlerConfig: context.handlerConfig as { plugins?: Array<{ pluginId: string; [key: string]: unknown }> },
|
agentFrameworkConfig: context.agentFrameworkConfig as { plugins?: Array<{ toolId: string; [key: string]: unknown }> },
|
||||||
response: { requestId: 'r-error', content: context.response.content, status: 'done' } as AIStreamResponse,
|
response: { requestId: 'r-error', content: context.response.content, status: 'done' } as AIStreamResponse,
|
||||||
actions: {} as PluginActions,
|
actions: {} as ToolActions,
|
||||||
requestId: 'r-error',
|
requestId: 'r-error',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
};
|
};
|
||||||
await hooks.responseComplete.promise(respCtx4);
|
await hooks.responseComplete.promise(respCtx4);
|
||||||
|
|
||||||
const errResult = handlerContext.agent.messages.find(m => m.metadata?.isToolResult);
|
const errResult = agentFrameworkContext.agent.messages.find(m => m.metadata?.isToolResult);
|
||||||
expect(errResult).toBeTruthy();
|
expect(errResult).toBeTruthy();
|
||||||
expect(errResult?.content).toContain('工作空间名称或ID');
|
expect(errResult?.content).toContain('工作空间名称或ID');
|
||||||
// Ensure control is yielded to self on error so AI gets the next round
|
// Ensure control is yielded to self on error so AI gets the next round
|
||||||
|
|
@ -408,17 +408,17 @@ describe('wikiOperationPlugin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not execute when tool call is not found', async () => {
|
it('should not execute when tool call is not found', async () => {
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
wikiOperationPlugin(hooks);
|
wikiOperationTool(hooks);
|
||||||
|
|
||||||
// No tool_use in response
|
// No tool_use in response
|
||||||
|
|
||||||
const handlerContext = makeHandlerContext();
|
const agentFrameworkContext = makeAgentFrameworkContext();
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
handlerConfig: {
|
agentFrameworkConfig: {
|
||||||
plugins: [{ pluginId: 'wikiOperation', wikiOperationParam: {} }],
|
plugins: [{ toolId: 'wikiOperation', wikiOperationParam: {} }],
|
||||||
},
|
},
|
||||||
response: {
|
response: {
|
||||||
status: 'done' as const,
|
status: 'done' as const,
|
||||||
|
|
@ -428,18 +428,18 @@ describe('wikiOperationPlugin', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
await hooks.responseComplete.promise({
|
await hooks.responseComplete.promise({
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
pluginConfig: context.handlerConfig?.plugins?.[0] as unknown as IPromptConcatPlugin,
|
toolConfig: context.agentFrameworkConfig?.plugins?.[0] as unknown as IPromptConcatTool,
|
||||||
handlerConfig: context.handlerConfig as { plugins?: Array<{ pluginId: string; [key: string]: unknown }> },
|
agentFrameworkConfig: context.agentFrameworkConfig as { plugins?: Array<{ toolId: string; [key: string]: unknown }> },
|
||||||
response: { requestId: 'r-none', content: context.response.content, status: 'done' } as AIStreamResponse,
|
response: { requestId: 'r-none', content: context.response.content, status: 'done' } as AIStreamResponse,
|
||||||
actions: {} as PluginActions,
|
actions: {} as ToolActions,
|
||||||
requestId: 'r-none',
|
requestId: 'r-none',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const wikiLocalAssert = container.get<Partial<IWikiService>>(serviceIdentifier.Wiki);
|
const wikiLocalAssert = container.get<Partial<IWikiService>>(serviceIdentifier.Wiki);
|
||||||
expect(wikiLocalAssert.wikiOperationInServer).not.toHaveBeenCalled();
|
expect(wikiLocalAssert.wikiOperationInServer).not.toHaveBeenCalled();
|
||||||
expect(handlerContext.agent.messages).toHaveLength(0);
|
expect(agentFrameworkContext.agent.messages).toHaveLength(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -13,15 +13,15 @@ import type { AIResponseContext, YieldNextRoundTarget } from '../types';
|
||||||
import { WikiChannel } from '@/constants/channels';
|
import { WikiChannel } from '@/constants/channels';
|
||||||
import serviceIdentifier from '@services/serviceIdentifier';
|
import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
|
|
||||||
import type { AgentHandlerContext } from '@services/agentInstance/buildInAgentHandlers/type';
|
import type { AgentFrameworkContext } from '@services/agentInstance/agentFrameworks/utilities/type';
|
||||||
import { AgentPromptDescription } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
import { AgentPromptDescription } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
import type { IPrompt } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
import type { IPrompt } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
import type { IPromptConcatPlugin } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
import type { IPromptConcatTool } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import defaultAgents from '../../buildInAgentHandlers/defaultAgents.json';
|
import defaultAgents from '../../agentFrameworks/taskAgents.json';
|
||||||
import { createHandlerHooks, PromptConcatHookContext } from '../index';
|
import { createAgentFrameworkHooks, PromptConcatHookContext } from '../index';
|
||||||
import { messageManagementPlugin } from '../messageManagementPlugin';
|
import { messageManagementTool } from '../messageManagement';
|
||||||
import { wikiSearchPlugin } from '../wikiSearchPlugin';
|
import { wikiSearchTool } from '../wikiSearch';
|
||||||
|
|
||||||
// Mock i18n
|
// Mock i18n
|
||||||
vi.mock('@services/libs/i18n', () => ({
|
vi.mock('@services/libs/i18n', () => ({
|
||||||
|
|
@ -53,7 +53,7 @@ vi.mock('@services/libs/i18n', () => ({
|
||||||
|
|
||||||
// Use the real agent config
|
// Use the real agent config
|
||||||
const exampleAgent = defaultAgents[0];
|
const exampleAgent = defaultAgents[0];
|
||||||
const handlerConfig = exampleAgent.handlerConfig as AgentPromptDescription['handlerConfig'];
|
const agentFrameworkConfig = (exampleAgent.agentFrameworkConfig || {}) as AgentPromptDescription['agentFrameworkConfig'];
|
||||||
|
|
||||||
// Services will be retrieved from container on demand inside each test/describe
|
// Services will be retrieved from container on demand inside each test/describe
|
||||||
|
|
||||||
|
|
@ -77,7 +77,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
|
|
||||||
it('should inject wiki tools into prompts when configured', async () => {
|
it('should inject wiki tools into prompts when configured', async () => {
|
||||||
// Find the wiki search plugin config, make sure our default config
|
// Find the wiki search plugin config, make sure our default config
|
||||||
const wikiPlugin = handlerConfig.plugins.find((p: unknown): p is IPromptConcatPlugin => (p as IPromptConcatPlugin).pluginId === 'wikiSearch');
|
const wikiPlugin = agentFrameworkConfig.plugins.find((p: unknown): p is IPromptConcatTool => (p as IPromptConcatTool).toolId === 'wikiSearch');
|
||||||
expect(wikiPlugin).toBeDefined();
|
expect(wikiPlugin).toBeDefined();
|
||||||
if (!wikiPlugin) {
|
if (!wikiPlugin) {
|
||||||
// throw error to keep ts believe the plugin exists
|
// throw error to keep ts believe the plugin exists
|
||||||
|
|
@ -89,7 +89,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
expect(wikiPlugin.wikiSearchParam?.toolListPosition).toBeDefined();
|
expect(wikiPlugin.wikiSearchParam?.toolListPosition).toBeDefined();
|
||||||
|
|
||||||
// Create a copy of prompts to test modification
|
// Create a copy of prompts to test modification
|
||||||
const prompts = cloneDeep(handlerConfig.prompts);
|
const prompts = cloneDeep(agentFrameworkConfig.prompts);
|
||||||
const messages = [
|
const messages = [
|
||||||
{
|
{
|
||||||
id: 'user-1',
|
id: 'user-1',
|
||||||
|
|
@ -102,19 +102,19 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
];
|
];
|
||||||
|
|
||||||
const context: PromptConcatHookContext = {
|
const context: PromptConcatHookContext = {
|
||||||
handlerContext: {
|
agentFrameworkContext: {
|
||||||
agent: { id: 'test', messages: [], agentDefId: 'test', status: { state: 'working' as const, modified: new Date() }, created: new Date() },
|
agent: { id: 'test', messages: [], agentDefId: 'test', status: { state: 'working' as const, modified: new Date() }, created: new Date() },
|
||||||
agentDef: { id: 'test', name: 'test', handlerConfig: {} },
|
agentDef: { id: 'test', name: 'test', agentFrameworkConfig: {} },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
},
|
},
|
||||||
pluginConfig: wikiPlugin,
|
toolConfig: wikiPlugin,
|
||||||
prompts: prompts,
|
prompts: prompts,
|
||||||
messages,
|
messages,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use real hooks from the plugin system
|
// Use real hooks from the plugin system
|
||||||
const promptHooks = createHandlerHooks();
|
const promptHooks = createAgentFrameworkHooks();
|
||||||
wikiSearchPlugin(promptHooks);
|
wikiSearchTool(promptHooks);
|
||||||
|
|
||||||
// Execute the processPrompts hook
|
// Execute the processPrompts hook
|
||||||
await promptHooks.processPrompts.promise(context);
|
await promptHooks.processPrompts.promise(context);
|
||||||
|
|
@ -130,7 +130,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
// Create a plugin config with trigger that won't match
|
// Create a plugin config with trigger that won't match
|
||||||
const wikiPlugin = {
|
const wikiPlugin = {
|
||||||
id: 'test-wiki-plugin',
|
id: 'test-wiki-plugin',
|
||||||
pluginId: 'wikiSearch' as const,
|
toolId: 'wikiSearch' as const,
|
||||||
forbidOverrides: false,
|
forbidOverrides: false,
|
||||||
retrievalAugmentedGenerationParam: {
|
retrievalAugmentedGenerationParam: {
|
||||||
sourceType: 'wiki' as const,
|
sourceType: 'wiki' as const,
|
||||||
|
|
@ -144,11 +144,11 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const prompts = cloneDeep(defaultAgents[0].handlerConfig.prompts);
|
const prompts = cloneDeep(defaultAgents[0].agentFrameworkConfig.prompts);
|
||||||
const originalPromptsText = JSON.stringify(prompts);
|
const originalPromptsText = JSON.stringify(prompts);
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
pluginConfig: wikiPlugin,
|
toolConfig: wikiPlugin,
|
||||||
prompts,
|
prompts,
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
|
|
@ -162,10 +162,10 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
wikiSearchPlugin(hooks);
|
wikiSearchTool(hooks);
|
||||||
// build a minimal PromptConcatHookContext to run the plugin's processPrompts
|
// build a minimal PromptConcatHookContext to run the plugin's processPrompts
|
||||||
const handlerCtx: AgentHandlerContext = {
|
const handlerCtx: AgentFrameworkContext = {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test',
|
id: 'test',
|
||||||
agentDefId: 'test',
|
agentDefId: 'test',
|
||||||
|
|
@ -173,12 +173,12 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
status: { state: 'working' as const, modified: new Date() },
|
status: { state: 'working' as const, modified: new Date() },
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
} as AgentInstance,
|
} as AgentInstance,
|
||||||
agentDef: { id: 'test', name: 'test', handlerConfig: {} } as AgentDefinition,
|
agentDef: { id: 'test', name: 'test', agentFrameworkConfig: {} } as AgentDefinition,
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
};
|
};
|
||||||
const hookContext: PromptConcatHookContext = {
|
const hookContext: PromptConcatHookContext = {
|
||||||
handlerContext: handlerCtx,
|
agentFrameworkContext: handlerCtx,
|
||||||
pluginConfig: wikiPlugin as IPromptConcatPlugin,
|
toolConfig: wikiPlugin as IPromptConcatTool,
|
||||||
prompts: prompts as IPrompt[],
|
prompts: prompts as IPrompt[],
|
||||||
messages: context.messages as AgentInstanceMessage[],
|
messages: context.messages as AgentInstanceMessage[],
|
||||||
};
|
};
|
||||||
|
|
@ -221,12 +221,12 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should execute wiki search with correct duration=1 and trigger next round', async () => {
|
it('should execute wiki search with correct duration=1 and trigger next round', async () => {
|
||||||
// Find the real wikiSearch plugin config from defaultAgents.json
|
// Find the real wikiSearch plugin config from taskAgents.json
|
||||||
const wikiPlugin = handlerConfig.plugins.find((p: unknown): p is IPromptConcatPlugin => (p as IPromptConcatPlugin).pluginId === 'wikiSearch');
|
const wikiPlugin = agentFrameworkConfig.plugins.find((p: unknown): p is IPromptConcatTool => (p as IPromptConcatTool).toolId === 'wikiSearch');
|
||||||
expect(wikiPlugin).toBeDefined();
|
expect(wikiPlugin).toBeDefined();
|
||||||
expect(wikiPlugin!.wikiSearchParam).toBeDefined();
|
expect(wikiPlugin!.wikiSearchParam).toBeDefined();
|
||||||
|
|
||||||
const handlerContext = {
|
const agentFrameworkContext = {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -258,7 +258,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
agentDef: { id: 'test-agent-def', name: 'test', handlerConfig: {} },
|
agentDef: { id: 'test-agent-def', name: 'test', agentFrameworkConfig: {} },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -270,11 +270,11 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
response,
|
response,
|
||||||
requestId: 'test-request-123',
|
requestId: 'test-request-123',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
pluginConfig: wikiPlugin!,
|
toolConfig: wikiPlugin!,
|
||||||
prompts: [],
|
prompts: [],
|
||||||
messages: [],
|
messages: [],
|
||||||
llmResponse: response.content,
|
llmResponse: response.content,
|
||||||
|
|
@ -283,10 +283,10 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use real handler hooks
|
// Use real handler hooks
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
|
|
||||||
// Register the plugin
|
// Register the plugin
|
||||||
wikiSearchPlugin(hooks);
|
wikiSearchTool(hooks);
|
||||||
|
|
||||||
// Execute the response complete hook
|
// Execute the response complete hook
|
||||||
await hooks.responseComplete.promise(context);
|
await hooks.responseComplete.promise(context);
|
||||||
|
|
@ -307,15 +307,15 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check that AI tool call message now has duration=1 (should gray out immediately)
|
// Check that AI tool call message now has duration=1 (should gray out immediately)
|
||||||
const aiToolCallMessage = handlerContext.agent.messages[1] as AgentInstanceMessage;
|
const aiToolCallMessage = agentFrameworkContext.agent.messages[1] as AgentInstanceMessage;
|
||||||
expect(aiToolCallMessage.id).toBe('ai-tool-call-msg');
|
expect(aiToolCallMessage.id).toBe('ai-tool-call-msg');
|
||||||
expect(aiToolCallMessage.duration).toBe(1); // Should be 1 to gray out immediately
|
expect(aiToolCallMessage.duration).toBe(1); // Should be 1 to gray out immediately
|
||||||
expect(aiToolCallMessage.metadata?.containsToolCall).toBe(true);
|
expect(aiToolCallMessage.metadata?.containsToolCall).toBe(true);
|
||||||
expect(aiToolCallMessage.metadata?.toolId).toBe('wiki-search');
|
expect(aiToolCallMessage.metadata?.toolId).toBe('wiki-search');
|
||||||
|
|
||||||
// Verify tool result message was added to agent history with correct settings
|
// Verify tool result message was added to agent history with correct settings
|
||||||
expect(handlerContext.agent.messages.length).toBe(3); // user + ai + tool_result
|
expect(agentFrameworkContext.agent.messages.length).toBe(3); // user + ai + tool_result
|
||||||
const toolResultMessage = handlerContext.agent.messages[2] as AgentInstanceMessage;
|
const toolResultMessage = agentFrameworkContext.agent.messages[2] as AgentInstanceMessage;
|
||||||
expect(toolResultMessage.role).toBe('tool'); // Tool result message
|
expect(toolResultMessage.role).toBe('tool'); // Tool result message
|
||||||
expect(toolResultMessage.content).toContain('<functions_result>');
|
expect(toolResultMessage.content).toContain('<functions_result>');
|
||||||
expect(toolResultMessage.content).toContain('Tool: wiki-search');
|
expect(toolResultMessage.content).toContain('Tool: wiki-search');
|
||||||
|
|
@ -325,13 +325,13 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
expect(toolResultMessage.duration).toBe(1); // Tool result uses configurable toolResultDuration (default 1)
|
expect(toolResultMessage.duration).toBe(1); // Tool result uses configurable toolResultDuration (default 1)
|
||||||
|
|
||||||
// Check that previous user message is unchanged
|
// Check that previous user message is unchanged
|
||||||
const userMessage = handlerContext.agent.messages[0] as AgentInstanceMessage;
|
const userMessage = agentFrameworkContext.agent.messages[0] as AgentInstanceMessage;
|
||||||
expect(userMessage.id).toBe('user-msg-1');
|
expect(userMessage.id).toBe('user-msg-1');
|
||||||
expect(userMessage.duration).toBeUndefined(); // Should stay visible
|
expect(userMessage.duration).toBeUndefined(); // Should stay visible
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle wiki search errors gracefully and set duration=1 for both messages', async () => {
|
it('should handle wiki search errors gracefully and set duration=1 for both messages', async () => {
|
||||||
const handlerContext = {
|
const agentFrameworkContext = {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -352,7 +352,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
agentDef: { id: 'test-agent-def', name: 'test', handlerConfig: {} },
|
agentDef: { id: 'test-agent-def', name: 'test', agentFrameworkConfig: {} },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -364,13 +364,13 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
response,
|
response,
|
||||||
requestId: 'test-request-error',
|
requestId: 'test-request-error',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
pluginConfig: {
|
toolConfig: {
|
||||||
id: 'test-plugin',
|
id: 'test-plugin',
|
||||||
pluginId: 'wikiSearch' as const,
|
toolId: 'wikiSearch' as const,
|
||||||
forbidOverrides: false,
|
forbidOverrides: false,
|
||||||
},
|
},
|
||||||
prompts: [],
|
prompts: [],
|
||||||
|
|
@ -383,8 +383,8 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
wikiSearchPlugin(hooks);
|
wikiSearchTool(hooks);
|
||||||
|
|
||||||
await hooks.responseComplete.promise(context);
|
await hooks.responseComplete.promise(context);
|
||||||
|
|
||||||
|
|
@ -392,14 +392,14 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
expect(context.actions.yieldNextRoundTo).toBe('self');
|
expect(context.actions.yieldNextRoundTo).toBe('self');
|
||||||
|
|
||||||
// Check that AI tool call message has duration=1 even after error (should gray out immediately)
|
// Check that AI tool call message has duration=1 even after error (should gray out immediately)
|
||||||
const aiToolCallMessage = handlerContext.agent.messages[0] as AgentInstanceMessage;
|
const aiToolCallMessage = agentFrameworkContext.agent.messages[0] as AgentInstanceMessage;
|
||||||
expect(aiToolCallMessage.id).toBe('ai-error-tool-call');
|
expect(aiToolCallMessage.id).toBe('ai-error-tool-call');
|
||||||
expect(aiToolCallMessage.duration).toBe(1); // Should be 1 to gray out immediately
|
expect(aiToolCallMessage.duration).toBe(1); // Should be 1 to gray out immediately
|
||||||
expect(aiToolCallMessage.metadata?.containsToolCall).toBe(true);
|
expect(aiToolCallMessage.metadata?.containsToolCall).toBe(true);
|
||||||
|
|
||||||
// Verify error message was added to agent history
|
// Verify error message was added to agent history
|
||||||
expect(handlerContext.agent.messages.length).toBe(2); // tool_call + error_result
|
expect(agentFrameworkContext.agent.messages.length).toBe(2); // tool_call + error_result
|
||||||
const errorResultMessage = handlerContext.agent.messages[1] as AgentInstanceMessage;
|
const errorResultMessage = agentFrameworkContext.agent.messages[1] as AgentInstanceMessage;
|
||||||
expect(errorResultMessage.role).toBe('tool'); // Tool error message
|
expect(errorResultMessage.role).toBe('tool'); // Tool error message
|
||||||
expect(errorResultMessage.content).toContain('<functions_result>');
|
expect(errorResultMessage.content).toContain('<functions_result>');
|
||||||
expect(errorResultMessage.content).toContain('Error:');
|
expect(errorResultMessage.content).toContain('Error:');
|
||||||
|
|
@ -411,7 +411,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not modify duration of unrelated messages', async () => {
|
it('should not modify duration of unrelated messages', async () => {
|
||||||
const handlerContext = {
|
const agentFrameworkContext = {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -450,7 +450,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
agentDef: { id: 'test-agent-def', name: 'test', handlerConfig: {} },
|
agentDef: { id: 'test-agent-def', name: 'test', agentFrameworkConfig: {} },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -461,13 +461,13 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
response,
|
response,
|
||||||
requestId: 'test-request-selective',
|
requestId: 'test-request-selective',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
pluginConfig: {
|
toolConfig: {
|
||||||
id: 'test-plugin',
|
id: 'test-plugin',
|
||||||
pluginId: 'wikiSearch' as const,
|
toolId: 'wikiSearch' as const,
|
||||||
forbidOverrides: false,
|
forbidOverrides: false,
|
||||||
},
|
},
|
||||||
prompts: [],
|
prompts: [],
|
||||||
|
|
@ -480,20 +480,20 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
wikiSearchPlugin(hooks);
|
wikiSearchTool(hooks);
|
||||||
|
|
||||||
await hooks.responseComplete.promise(context);
|
await hooks.responseComplete.promise(context);
|
||||||
|
|
||||||
// Check that unrelated messages were not modified
|
// Check that unrelated messages were not modified
|
||||||
const unrelatedUserMsg = handlerContext.agent.messages[0] as AgentInstanceMessage;
|
const unrelatedUserMsg = agentFrameworkContext.agent.messages[0] as AgentInstanceMessage;
|
||||||
expect(unrelatedUserMsg.duration).toBe(5); // Should remain unchanged
|
expect(unrelatedUserMsg.duration).toBe(5); // Should remain unchanged
|
||||||
|
|
||||||
const unrelatedAiMsg = handlerContext.agent.messages[1] as AgentInstanceMessage;
|
const unrelatedAiMsg = agentFrameworkContext.agent.messages[1] as AgentInstanceMessage;
|
||||||
expect(unrelatedAiMsg.duration).toBeUndefined(); // Should remain unchanged
|
expect(unrelatedAiMsg.duration).toBeUndefined(); // Should remain unchanged
|
||||||
|
|
||||||
// Check that only the tool call message was modified
|
// Check that only the tool call message was modified
|
||||||
const toolCallMsg = handlerContext.agent.messages[2] as AgentInstanceMessage;
|
const toolCallMsg = agentFrameworkContext.agent.messages[2] as AgentInstanceMessage;
|
||||||
expect(toolCallMsg.duration).toBe(1); // Should be set to 1
|
expect(toolCallMsg.duration).toBe(1); // Should be set to 1
|
||||||
expect(toolCallMsg.metadata?.containsToolCall).toBe(true);
|
expect(toolCallMsg.metadata?.containsToolCall).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
@ -507,20 +507,20 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
status: { state: 'working' as const, modified: new Date() },
|
status: { state: 'working' as const, modified: new Date() },
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
} as AgentInstance,
|
} as AgentInstance,
|
||||||
agentDef: { id: 'test-agent-def', name: 'test', handlerConfig: {} } as AgentDefinition,
|
agentDef: { id: 'test-agent-def', name: 'test', agentFrameworkConfig: {} } as AgentDefinition,
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const context: AIResponseContext = {
|
const context: AIResponseContext = {
|
||||||
handlerContext: handlerCtx,
|
agentFrameworkContext: handlerCtx,
|
||||||
pluginConfig: { id: 'test-plugin', pluginId: 'wikiSearch' } as IPromptConcatPlugin,
|
toolConfig: { id: 'test-plugin', toolId: 'wikiSearch' } as IPromptConcatTool,
|
||||||
response: { requestId: 'test-request-345', content: 'Just a regular response without any tool calls', status: 'done' },
|
response: { requestId: 'test-request-345', content: 'Just a regular response without any tool calls', status: 'done' },
|
||||||
requestId: 'test-request',
|
requestId: 'test-request',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
wikiSearchPlugin(hooks);
|
wikiSearchTool(hooks);
|
||||||
|
|
||||||
await hooks.responseComplete.promise(context);
|
await hooks.responseComplete.promise(context);
|
||||||
|
|
||||||
|
|
@ -597,7 +597,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
}) as unknown as IWikiService['wikiOperationInServer'],
|
}) as unknown as IWikiService['wikiOperationInServer'],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handlerContext = {
|
const agentFrameworkContext = {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -627,7 +627,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
agentDef: { id: 'test-agent-def', name: 'test', handlerConfig: {} },
|
agentDef: { id: 'test-agent-def', name: 'test', agentFrameworkConfig: {} },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -638,13 +638,13 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
response,
|
response,
|
||||||
requestId: 'test-request-vector',
|
requestId: 'test-request-vector-error',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
pluginConfig: {
|
toolConfig: {
|
||||||
id: 'test-plugin',
|
id: 'test-plugin',
|
||||||
pluginId: 'wikiSearch' as const,
|
toolId: 'wikiSearch' as const,
|
||||||
forbidOverrides: false,
|
forbidOverrides: false,
|
||||||
},
|
},
|
||||||
prompts: [],
|
prompts: [],
|
||||||
|
|
@ -654,8 +654,8 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
actions: {} as ActionBag,
|
actions: {} as ActionBag,
|
||||||
};
|
};
|
||||||
|
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
wikiSearchPlugin(hooks);
|
wikiSearchTool(hooks);
|
||||||
|
|
||||||
await hooks.responseComplete.promise(context);
|
await hooks.responseComplete.promise(context);
|
||||||
|
|
||||||
|
|
@ -676,9 +676,9 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
|
|
||||||
// Verify results were processed
|
// Verify results were processed
|
||||||
expect(context.actions.yieldNextRoundTo).toBe('self');
|
expect(context.actions.yieldNextRoundTo).toBe('self');
|
||||||
expect(handlerContext.agent.messages.length).toBe(2);
|
expect(agentFrameworkContext.agent.messages.length).toBe(2);
|
||||||
|
|
||||||
const toolResultMessage = handlerContext.agent.messages[1] as AgentInstanceMessage;
|
const toolResultMessage = agentFrameworkContext.agent.messages[1] as AgentInstanceMessage;
|
||||||
expect(toolResultMessage.content).toContain('<functions_result>');
|
expect(toolResultMessage.content).toContain('<functions_result>');
|
||||||
expect(toolResultMessage.content).toContain('Vector Result 1');
|
expect(toolResultMessage.content).toContain('Vector Result 1');
|
||||||
expect(toolResultMessage.content).toContain('Vector Result 2');
|
expect(toolResultMessage.content).toContain('Vector Result 2');
|
||||||
|
|
@ -696,7 +696,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
new Error('Vector database not initialized'),
|
new Error('Vector database not initialized'),
|
||||||
);
|
);
|
||||||
|
|
||||||
const handlerContext = {
|
const agentFrameworkContext = {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -724,7 +724,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
agentDef: { id: 'test-agent-def', name: 'test', handlerConfig: {} },
|
agentDef: { id: 'test-agent-def', name: 'test', agentFrameworkConfig: {} },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -735,13 +735,13 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
response,
|
response,
|
||||||
requestId: 'test-request-vector-error',
|
requestId: 'test-request-vector-error',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
pluginConfig: {
|
toolConfig: {
|
||||||
id: 'test-plugin',
|
id: 'test-plugin',
|
||||||
pluginId: 'wikiSearch' as const,
|
toolId: 'wikiSearch' as const,
|
||||||
forbidOverrides: false,
|
forbidOverrides: false,
|
||||||
},
|
},
|
||||||
prompts: [],
|
prompts: [],
|
||||||
|
|
@ -751,15 +751,15 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
actions: {} as ActionBag,
|
actions: {} as ActionBag,
|
||||||
};
|
};
|
||||||
|
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
wikiSearchPlugin(hooks);
|
wikiSearchTool(hooks);
|
||||||
|
|
||||||
await hooks.responseComplete.promise(context);
|
await hooks.responseComplete.promise(context);
|
||||||
|
|
||||||
// Should still set up next round with error message
|
// Should still set up next round with error message
|
||||||
expect(context.actions.yieldNextRoundTo).toBe('self');
|
expect(context.actions.yieldNextRoundTo).toBe('self');
|
||||||
|
|
||||||
const errorResultMessage = handlerContext.agent.messages[1] as AgentInstanceMessage;
|
const errorResultMessage = agentFrameworkContext.agent.messages[1] as AgentInstanceMessage;
|
||||||
expect(errorResultMessage.content).toContain('Error:');
|
expect(errorResultMessage.content).toContain('Error:');
|
||||||
// Error message contains i18n key or actual error
|
// Error message contains i18n key or actual error
|
||||||
expect(errorResultMessage.content).toMatch(/Vector database not initialized|Tool\.WikiSearch\.Error\.VectorSearchFailed/);
|
expect(errorResultMessage.content).toMatch(/Vector database not initialized|Tool\.WikiSearch\.Error\.VectorSearchFailed/);
|
||||||
|
|
@ -767,7 +767,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require query parameter for vector search', async () => {
|
it('should require query parameter for vector search', async () => {
|
||||||
const handlerContext = {
|
const agentFrameworkContext = {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -795,7 +795,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
agentDef: { id: 'test-agent-def', name: 'test', handlerConfig: {} },
|
agentDef: { id: 'test-agent-def', name: 'test', agentFrameworkConfig: {} },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -806,13 +806,13 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
response,
|
response,
|
||||||
requestId: 'test-request-no-query',
|
requestId: 'test-request-no-query',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
pluginConfig: {
|
toolConfig: {
|
||||||
id: 'test-plugin',
|
id: 'test-plugin',
|
||||||
pluginId: 'wikiSearch' as const,
|
toolId: 'wikiSearch' as const,
|
||||||
forbidOverrides: false,
|
forbidOverrides: false,
|
||||||
},
|
},
|
||||||
prompts: [],
|
prompts: [],
|
||||||
|
|
@ -822,13 +822,13 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
actions: {} as ActionBag,
|
actions: {} as ActionBag,
|
||||||
};
|
};
|
||||||
|
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
wikiSearchPlugin(hooks);
|
wikiSearchTool(hooks);
|
||||||
|
|
||||||
await hooks.responseComplete.promise(context);
|
await hooks.responseComplete.promise(context);
|
||||||
|
|
||||||
// Should return error about missing query
|
// Should return error about missing query
|
||||||
const errorMessage = handlerContext.agent.messages[1] as AgentInstanceMessage;
|
const errorMessage = agentFrameworkContext.agent.messages[1] as AgentInstanceMessage;
|
||||||
expect(errorMessage.content).toContain('Error:');
|
expect(errorMessage.content).toContain('Error:');
|
||||||
// Error message contains i18n key or translated text
|
// Error message contains i18n key or translated text
|
||||||
expect(errorMessage.content).toMatch(/query|Tool\.WikiSearch\.Error\.VectorSearchRequiresQuery/);
|
expect(errorMessage.content).toMatch(/query|Tool\.WikiSearch\.Error\.VectorSearchRequiresQuery/);
|
||||||
|
|
@ -836,9 +836,9 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Message Persistence Integration', () => {
|
describe('Message Persistence Integration', () => {
|
||||||
it('should work with messageManagementPlugin for complete persistence flow', async () => {
|
it('should work with messageManagementTool for complete persistence flow', async () => {
|
||||||
// This test ensures wikiSearchPlugin works well with messageManagementPlugin
|
// This test ensures wikiSearchTool works well with messageManagementTool
|
||||||
const handlerContext = {
|
const agentFrameworkContext = {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -859,7 +859,7 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
agentDef: { id: 'test-agent-def', name: 'test', handlerConfig: {} },
|
agentDef: { id: 'test-agent-def', name: 'test', agentFrameworkConfig: {} },
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -870,13 +870,13 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
response,
|
response,
|
||||||
requestId: 'test-request-integration',
|
requestId: 'test-request-integration',
|
||||||
isFinal: true,
|
isFinal: true,
|
||||||
pluginConfig: {
|
toolConfig: {
|
||||||
id: 'test-plugin',
|
id: 'test-plugin',
|
||||||
pluginId: 'wikiSearch' as const,
|
toolId: 'wikiSearch' as const,
|
||||||
forbidOverrides: false,
|
forbidOverrides: false,
|
||||||
},
|
},
|
||||||
prompts: [],
|
prompts: [],
|
||||||
|
|
@ -889,19 +889,19 @@ describe('Wiki Search Plugin - Comprehensive Tests', () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
wikiSearchPlugin(hooks);
|
wikiSearchTool(hooks);
|
||||||
messageManagementPlugin(hooks);
|
messageManagementTool(hooks);
|
||||||
|
|
||||||
await hooks.responseComplete.promise(context);
|
await hooks.responseComplete.promise(context);
|
||||||
|
|
||||||
// Verify integration works
|
// Verify integration works
|
||||||
expect(context.actions.yieldNextRoundTo).toBe('self');
|
expect(context.actions.yieldNextRoundTo).toBe('self');
|
||||||
expect(handlerContext.agent.messages.length).toBe(2); // original + tool result
|
expect(agentFrameworkContext.agent.messages.length).toBe(2); // original + tool result
|
||||||
|
|
||||||
const toolResultMessage = handlerContext.agent.messages[1] as AgentInstanceMessage;
|
const toolResultMessage = agentFrameworkContext.agent.messages[1] as AgentInstanceMessage;
|
||||||
expect(toolResultMessage.metadata?.isToolResult).toBe(true);
|
expect(toolResultMessage.metadata?.isToolResult).toBe(true);
|
||||||
expect(toolResultMessage.metadata?.isPersisted).toBe(true); // Should be true after messageManagementPlugin processing
|
expect(toolResultMessage.metadata?.isPersisted).toBe(true); // Should be true after messageManagementTool processing
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should prevent regression: tool result not filtered in second round', async () => {
|
it('should prevent regression: tool result not filtered in second round', async () => {
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
/**
|
/**
|
||||||
* Tests for workspacesListPlugin
|
* Tests for workspacesListTool
|
||||||
*/
|
*/
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
// Note: global mocks from src/__tests__/setup-vitest.ts provide container and logger
|
// Note: global mocks from src/__tests__/setup-vitest.ts provide container and logger
|
||||||
import type { IPromptConcatPlugin } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
import type { IPromptConcatTool } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
import { container } from '@services/container';
|
import { container } from '@services/container';
|
||||||
import { logger } from '@services/libs/log';
|
import { logger } from '@services/libs/log';
|
||||||
import serviceIdentifier from '@services/serviceIdentifier';
|
import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
import type { AgentHandlerContext } from '../../buildInAgentHandlers/type';
|
import type { AgentFrameworkContext } from '../../agentFrameworks/utilities/type';
|
||||||
import type { AgentInstance } from '../../interface';
|
import type { AgentInstance } from '../../interface';
|
||||||
import type { PromptConcatHookContext } from '../types';
|
import type { PromptConcatHookContext } from '../types';
|
||||||
|
|
||||||
import type { IWorkspaceService } from '@services/workspaces/interface';
|
import type { IWorkspaceService } from '@services/workspaces/interface';
|
||||||
import { createHandlerHooks } from '../index';
|
import { createAgentFrameworkHooks } from '../index';
|
||||||
import { workspacesListPlugin } from '../workspacesListPlugin';
|
import { workspacesListTool } from '../workspacesList';
|
||||||
|
|
||||||
describe('workspacesListPlugin', () => {
|
describe('workspacesListTool', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
@ -27,11 +27,11 @@ describe('workspacesListPlugin', () => {
|
||||||
|
|
||||||
describe('workspaces list injection', () => {
|
describe('workspaces list injection', () => {
|
||||||
it('should inject workspaces list when plugin is configured', async () => {
|
it('should inject workspaces list when plugin is configured', async () => {
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
workspacesListPlugin(hooks);
|
workspacesListTool(hooks);
|
||||||
|
|
||||||
const context: PromptConcatHookContext = {
|
const context: PromptConcatHookContext = {
|
||||||
handlerContext: {
|
agentFrameworkContext: {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -41,7 +41,7 @@ describe('workspacesListPlugin', () => {
|
||||||
} as AgentInstance,
|
} as AgentInstance,
|
||||||
agentDef: { id: 'test-agent-def', name: 'test-agent-def' } as unknown,
|
agentDef: { id: 'test-agent-def', name: 'test-agent-def' } as unknown,
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
} as AgentHandlerContext,
|
} as AgentFrameworkContext,
|
||||||
messages: [],
|
messages: [],
|
||||||
prompts: [
|
prompts: [
|
||||||
{
|
{
|
||||||
|
|
@ -50,16 +50,16 @@ describe('workspacesListPlugin', () => {
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
pluginConfig: {
|
toolConfig: {
|
||||||
id: 'test-plugin',
|
id: 'test-plugin',
|
||||||
caption: 'Test Plugin',
|
caption: 'Test Plugin',
|
||||||
forbidOverrides: false,
|
forbidOverrides: false,
|
||||||
pluginId: 'workspacesList',
|
toolId: 'workspacesList',
|
||||||
workspacesListParam: {
|
workspacesListParam: {
|
||||||
targetId: 'target-prompt',
|
targetId: 'target-prompt',
|
||||||
position: 'after' as const,
|
position: 'after' as const,
|
||||||
},
|
},
|
||||||
} as unknown as IPromptConcatPlugin,
|
} as unknown as IPromptConcatTool,
|
||||||
};
|
};
|
||||||
|
|
||||||
await hooks.processPrompts.promise(context);
|
await hooks.processPrompts.promise(context);
|
||||||
|
|
@ -74,11 +74,11 @@ describe('workspacesListPlugin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should inject workspaces list when position is before', async () => {
|
it('should inject workspaces list when position is before', async () => {
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
workspacesListPlugin(hooks);
|
workspacesListTool(hooks);
|
||||||
|
|
||||||
const context: PromptConcatHookContext = {
|
const context: PromptConcatHookContext = {
|
||||||
handlerContext: {
|
agentFrameworkContext: {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -88,7 +88,7 @@ describe('workspacesListPlugin', () => {
|
||||||
} as AgentInstance,
|
} as AgentInstance,
|
||||||
agentDef: { id: 'test-agent-def', name: 'test-agent-def' } as unknown,
|
agentDef: { id: 'test-agent-def', name: 'test-agent-def' } as unknown,
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
} as AgentHandlerContext,
|
} as AgentFrameworkContext,
|
||||||
messages: [],
|
messages: [],
|
||||||
prompts: [
|
prompts: [
|
||||||
{
|
{
|
||||||
|
|
@ -97,16 +97,16 @@ describe('workspacesListPlugin', () => {
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
pluginConfig: {
|
toolConfig: {
|
||||||
id: 'test-plugin',
|
id: 'test-plugin',
|
||||||
caption: 'Test Plugin',
|
caption: 'Test Plugin',
|
||||||
forbidOverrides: false,
|
forbidOverrides: false,
|
||||||
pluginId: 'workspacesList',
|
toolId: 'workspacesList',
|
||||||
workspacesListParam: {
|
workspacesListParam: {
|
||||||
targetId: 'target-prompt',
|
targetId: 'target-prompt',
|
||||||
position: 'before' as const,
|
position: 'before' as const,
|
||||||
},
|
},
|
||||||
} as unknown as IPromptConcatPlugin,
|
} as unknown as IPromptConcatTool,
|
||||||
};
|
};
|
||||||
|
|
||||||
await hooks.processPrompts.promise(context);
|
await hooks.processPrompts.promise(context);
|
||||||
|
|
@ -118,11 +118,11 @@ describe('workspacesListPlugin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not inject content when plugin is not configured', async () => {
|
it('should not inject content when plugin is not configured', async () => {
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
workspacesListPlugin(hooks);
|
workspacesListTool(hooks);
|
||||||
|
|
||||||
const context: PromptConcatHookContext = {
|
const context: PromptConcatHookContext = {
|
||||||
handlerContext: {
|
agentFrameworkContext: {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -132,7 +132,7 @@ describe('workspacesListPlugin', () => {
|
||||||
} as AgentInstance,
|
} as AgentInstance,
|
||||||
agentDef: { id: 'test-agent-def', name: 'test-agent-def' } as unknown,
|
agentDef: { id: 'test-agent-def', name: 'test-agent-def' } as unknown,
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
} as AgentHandlerContext,
|
} as AgentFrameworkContext,
|
||||||
messages: [],
|
messages: [],
|
||||||
prompts: [
|
prompts: [
|
||||||
{
|
{
|
||||||
|
|
@ -141,7 +141,7 @@ describe('workspacesListPlugin', () => {
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
pluginConfig: { id: 'test-plugin', pluginId: 'otherPlugin', forbidOverrides: false } as unknown as IPromptConcatPlugin,
|
toolConfig: { id: 'test-plugin', toolId: 'otherPlugin', forbidOverrides: false } as unknown as IPromptConcatTool,
|
||||||
};
|
};
|
||||||
|
|
||||||
await hooks.processPrompts.promise(context);
|
await hooks.processPrompts.promise(context);
|
||||||
|
|
@ -155,11 +155,11 @@ describe('workspacesListPlugin', () => {
|
||||||
const workspaceService = container.get<Partial<IWorkspaceService>>(serviceIdentifier.Workspace);
|
const workspaceService = container.get<Partial<IWorkspaceService>>(serviceIdentifier.Workspace);
|
||||||
workspaceService.getWorkspacesAsList = vi.fn().mockResolvedValue([]) as unknown as IWorkspaceService['getWorkspacesAsList'];
|
workspaceService.getWorkspacesAsList = vi.fn().mockResolvedValue([]) as unknown as IWorkspaceService['getWorkspacesAsList'];
|
||||||
|
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
workspacesListPlugin(hooks);
|
workspacesListTool(hooks);
|
||||||
|
|
||||||
const context: PromptConcatHookContext = {
|
const context: PromptConcatHookContext = {
|
||||||
handlerContext: {
|
agentFrameworkContext: {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -169,7 +169,7 @@ describe('workspacesListPlugin', () => {
|
||||||
} as AgentInstance,
|
} as AgentInstance,
|
||||||
agentDef: { id: 'test-agent-def', name: 'test-agent-def' } as unknown,
|
agentDef: { id: 'test-agent-def', name: 'test-agent-def' } as unknown,
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
} as AgentHandlerContext,
|
} as AgentFrameworkContext,
|
||||||
messages: [],
|
messages: [],
|
||||||
prompts: [
|
prompts: [
|
||||||
{
|
{
|
||||||
|
|
@ -178,16 +178,16 @@ describe('workspacesListPlugin', () => {
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
pluginConfig: {
|
toolConfig: {
|
||||||
id: 'test-plugin',
|
id: 'test-plugin',
|
||||||
caption: 'Test Plugin',
|
caption: 'Test Plugin',
|
||||||
forbidOverrides: false,
|
forbidOverrides: false,
|
||||||
pluginId: 'workspacesList',
|
toolId: 'workspacesList',
|
||||||
workspacesListParam: {
|
workspacesListParam: {
|
||||||
targetId: 'target-prompt',
|
targetId: 'target-prompt',
|
||||||
position: 'after' as const,
|
position: 'after' as const,
|
||||||
},
|
},
|
||||||
} as unknown as IPromptConcatPlugin,
|
} as unknown as IPromptConcatTool,
|
||||||
};
|
};
|
||||||
|
|
||||||
await hooks.processPrompts.promise(context);
|
await hooks.processPrompts.promise(context);
|
||||||
|
|
@ -195,16 +195,16 @@ describe('workspacesListPlugin', () => {
|
||||||
const targetPrompt = context.prompts[0];
|
const targetPrompt = context.prompts[0];
|
||||||
expect(targetPrompt.children).toHaveLength(0);
|
expect(targetPrompt.children).toHaveLength(0);
|
||||||
expect(logger.debug).toHaveBeenCalledWith('No wiki workspaces found to inject', {
|
expect(logger.debug).toHaveBeenCalledWith('No wiki workspaces found to inject', {
|
||||||
pluginId: 'test-plugin',
|
toolId: 'test-plugin',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should warn when target prompt is not found', async () => {
|
it('should warn when target prompt is not found', async () => {
|
||||||
const hooks = createHandlerHooks();
|
const hooks = createAgentFrameworkHooks();
|
||||||
workspacesListPlugin(hooks);
|
workspacesListTool(hooks);
|
||||||
|
|
||||||
const context: PromptConcatHookContext = {
|
const context: PromptConcatHookContext = {
|
||||||
handlerContext: {
|
agentFrameworkContext: {
|
||||||
agent: {
|
agent: {
|
||||||
id: 'test-agent',
|
id: 'test-agent',
|
||||||
agentDefId: 'test-agent-def',
|
agentDefId: 'test-agent-def',
|
||||||
|
|
@ -214,7 +214,7 @@ describe('workspacesListPlugin', () => {
|
||||||
} as AgentInstance,
|
} as AgentInstance,
|
||||||
agentDef: { id: 'test-agent-def', name: 'test-agent-def' } as unknown,
|
agentDef: { id: 'test-agent-def', name: 'test-agent-def' } as unknown,
|
||||||
isCancelled: () => false,
|
isCancelled: () => false,
|
||||||
} as AgentHandlerContext,
|
} as AgentFrameworkContext,
|
||||||
messages: [],
|
messages: [],
|
||||||
prompts: [
|
prompts: [
|
||||||
{
|
{
|
||||||
|
|
@ -223,23 +223,23 @@ describe('workspacesListPlugin', () => {
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
pluginConfig: {
|
toolConfig: {
|
||||||
id: 'test-plugin',
|
id: 'test-plugin',
|
||||||
caption: 'Test Plugin',
|
caption: 'Test Plugin',
|
||||||
forbidOverrides: false,
|
forbidOverrides: false,
|
||||||
pluginId: 'workspacesList',
|
toolId: 'workspacesList',
|
||||||
workspacesListParam: {
|
workspacesListParam: {
|
||||||
targetId: 'non-existent-prompt',
|
targetId: 'non-existent-prompt',
|
||||||
position: 'after' as const,
|
position: 'after' as const,
|
||||||
},
|
},
|
||||||
} as unknown as IPromptConcatPlugin,
|
} as unknown as IPromptConcatTool,
|
||||||
};
|
};
|
||||||
|
|
||||||
await hooks.processPrompts.promise(context);
|
await hooks.processPrompts.promise(context);
|
||||||
|
|
||||||
expect(logger.warn).toHaveBeenCalledWith('Workspaces list target prompt not found', {
|
expect(logger.warn).toHaveBeenCalledWith('Workspaces list target prompt not found', {
|
||||||
targetId: 'non-existent-prompt',
|
targetId: 'non-existent-prompt',
|
||||||
pluginId: 'test-plugin',
|
toolId: 'test-plugin',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
190
src/services/agentInstance/tools/index.ts
Normal file
190
src/services/agentInstance/tools/index.ts
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
import { logger } from '@services/libs/log';
|
||||||
|
import { AsyncSeriesHook, AsyncSeriesWaterfallHook } from 'tapable';
|
||||||
|
import { registerToolParameterSchema } from './schemaRegistry';
|
||||||
|
import { AgentResponse, PromptConcatHookContext, PromptConcatHooks, PromptConcatTool, ResponseHookContext } from './types';
|
||||||
|
|
||||||
|
// Re-export types for convenience
|
||||||
|
export type { AgentResponse, PromptConcatHookContext, PromptConcatHooks, PromptConcatTool, ResponseHookContext };
|
||||||
|
// Backward compatibility aliases
|
||||||
|
export type { PromptConcatTool as PromptConcatPlugin };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry for built-in framework tools
|
||||||
|
*/
|
||||||
|
export const builtInTools = new Map<string, PromptConcatTool>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create unified hooks instance for the complete agent framework tool system
|
||||||
|
*/
|
||||||
|
export function createAgentFrameworkHooks(): PromptConcatHooks {
|
||||||
|
return {
|
||||||
|
// Prompt processing hooks
|
||||||
|
processPrompts: new AsyncSeriesWaterfallHook(['context']),
|
||||||
|
finalizePrompts: new AsyncSeriesWaterfallHook(['context']),
|
||||||
|
postProcess: new AsyncSeriesWaterfallHook(['context']),
|
||||||
|
// Agent lifecycle hooks
|
||||||
|
userMessageReceived: new AsyncSeriesHook(['context']),
|
||||||
|
agentStatusChanged: new AsyncSeriesHook(['context']),
|
||||||
|
toolExecuted: new AsyncSeriesHook(['context']),
|
||||||
|
responseUpdate: new AsyncSeriesHook(['context']),
|
||||||
|
responseComplete: new AsyncSeriesHook(['context']),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all available tools
|
||||||
|
*/
|
||||||
|
async function getAllTools() {
|
||||||
|
const [
|
||||||
|
promptToolsModule,
|
||||||
|
wikiSearchModule,
|
||||||
|
wikiOperationModule,
|
||||||
|
workspacesListModule,
|
||||||
|
messageManagementModule,
|
||||||
|
] = await Promise.all([
|
||||||
|
import('./prompt'),
|
||||||
|
import('./wikiSearch'),
|
||||||
|
import('./wikiOperation'),
|
||||||
|
import('./workspacesList'),
|
||||||
|
import('./messageManagement'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
messageManagement: messageManagementModule.messageManagementTool,
|
||||||
|
fullReplacement: promptToolsModule.fullReplacementTool,
|
||||||
|
wikiSearch: wikiSearchModule.wikiSearchTool,
|
||||||
|
wikiOperation: wikiOperationModule.wikiOperationTool,
|
||||||
|
workspacesList: workspacesListModule.workspacesListTool,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register tools to hooks based on framework configuration
|
||||||
|
* @param hooks - The hooks instance to register tools to
|
||||||
|
* @param agentFrameworkConfig - The framework configuration containing tool settings
|
||||||
|
*/
|
||||||
|
export async function registerToolsToHooksFromConfig(
|
||||||
|
hooks: PromptConcatHooks,
|
||||||
|
agentFrameworkConfig: { plugins?: Array<{ toolId: string; [key: string]: unknown }> },
|
||||||
|
): Promise<void> {
|
||||||
|
// Always register core tools that are needed for basic functionality
|
||||||
|
const messageManagementModule = await import('./messageManagement');
|
||||||
|
messageManagementModule.messageManagementTool(hooks);
|
||||||
|
logger.debug('Registered messageManagementTool to hooks');
|
||||||
|
|
||||||
|
// Register tools based on framework configuration
|
||||||
|
if (agentFrameworkConfig.plugins) {
|
||||||
|
for (const toolConfig of agentFrameworkConfig.plugins) {
|
||||||
|
const { toolId } = toolConfig;
|
||||||
|
|
||||||
|
// Get tool from global registry (supports both built-in and dynamic tools)
|
||||||
|
const tool = builtInTools.get(toolId);
|
||||||
|
if (tool) {
|
||||||
|
tool(hooks);
|
||||||
|
logger.debug(`Registered tool ${toolId} to hooks`);
|
||||||
|
} else {
|
||||||
|
logger.warn(`Tool not found in registry: ${toolId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize tool system - register all built-in tools to global registry
|
||||||
|
* This should be called once during service initialization
|
||||||
|
*/
|
||||||
|
export async function initializeToolSystem(): Promise<void> {
|
||||||
|
// Import tool schemas and register them
|
||||||
|
const [
|
||||||
|
promptToolsModule,
|
||||||
|
wikiSearchModule,
|
||||||
|
wikiOperationModule,
|
||||||
|
workspacesListModule,
|
||||||
|
modelContextProtocolModule,
|
||||||
|
] = await Promise.all([
|
||||||
|
import('./prompt'),
|
||||||
|
import('./wikiSearch'),
|
||||||
|
import('./wikiOperation'),
|
||||||
|
import('./workspacesList'),
|
||||||
|
import('./modelContextProtocol'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Register tool parameter schemas
|
||||||
|
registerToolParameterSchema(
|
||||||
|
'fullReplacement',
|
||||||
|
promptToolsModule.getFullReplacementParameterSchema(),
|
||||||
|
{
|
||||||
|
displayName: 'Full Replacement',
|
||||||
|
description: 'Replace target content with content from specified source',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
registerToolParameterSchema(
|
||||||
|
'dynamicPosition',
|
||||||
|
promptToolsModule.getDynamicPositionParameterSchema(),
|
||||||
|
{
|
||||||
|
displayName: 'Dynamic Position',
|
||||||
|
description: 'Insert content at a specific position relative to a target element',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
registerToolParameterSchema(
|
||||||
|
'wikiSearch',
|
||||||
|
wikiSearchModule.getWikiSearchParameterSchema(),
|
||||||
|
{
|
||||||
|
displayName: 'Wiki Search',
|
||||||
|
description: 'Search content in wiki workspaces and manage vector embeddings',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
registerToolParameterSchema(
|
||||||
|
'wikiOperation',
|
||||||
|
wikiOperationModule.getWikiOperationParameterSchema(),
|
||||||
|
{
|
||||||
|
displayName: 'Wiki Operation',
|
||||||
|
description: 'Perform operations on wiki workspaces (create, update, delete tiddlers)',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
registerToolParameterSchema(
|
||||||
|
'workspacesList',
|
||||||
|
workspacesListModule.getWorkspacesListParameterSchema(),
|
||||||
|
{
|
||||||
|
displayName: 'Workspaces List',
|
||||||
|
description: 'Inject available wiki workspaces list into prompts',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
registerToolParameterSchema(
|
||||||
|
'modelContextProtocol',
|
||||||
|
modelContextProtocolModule.getModelContextProtocolParameterSchema(),
|
||||||
|
{
|
||||||
|
displayName: 'Model Context Protocol',
|
||||||
|
description: 'MCP (Model Context Protocol) integration',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const tools = await getAllTools();
|
||||||
|
// Register all built-in tools to global registry for discovery
|
||||||
|
builtInTools.set('messageManagement', tools.messageManagement);
|
||||||
|
builtInTools.set('fullReplacement', tools.fullReplacement);
|
||||||
|
builtInTools.set('wikiSearch', tools.wikiSearch);
|
||||||
|
builtInTools.set('wikiOperation', tools.wikiOperation);
|
||||||
|
builtInTools.set('workspacesList', tools.workspacesList);
|
||||||
|
logger.debug('All built-in tools and schemas registered successfully');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create hooks and register tools based on framework configuration
|
||||||
|
* This creates a new hooks instance and registers tools for that specific context
|
||||||
|
*/
|
||||||
|
export async function createHooksWithTools(
|
||||||
|
agentFrameworkConfig: { plugins?: Array<{ toolId: string; [key: string]: unknown }> },
|
||||||
|
): Promise<{ hooks: PromptConcatHooks; toolConfigs: Array<{ toolId: string; [key: string]: unknown }> }> {
|
||||||
|
const hooks = createAgentFrameworkHooks();
|
||||||
|
await registerToolsToHooksFromConfig(hooks, agentFrameworkConfig);
|
||||||
|
return {
|
||||||
|
hooks,
|
||||||
|
toolConfigs: agentFrameworkConfig.plugins || [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -8,20 +8,20 @@ import { logger } from '@services/libs/log';
|
||||||
import serviceIdentifier from '@services/serviceIdentifier';
|
import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
import type { IAgentInstanceService } from '../interface';
|
import type { IAgentInstanceService } from '../interface';
|
||||||
import { createAgentMessage } from '../utilities';
|
import { createAgentMessage } from '../utilities';
|
||||||
import type { AgentStatusContext, AIResponseContext, PromptConcatPlugin, ToolExecutionContext, UserMessageContext } from './types';
|
import type { AgentStatusContext, AIResponseContext, PromptConcatTool, ToolExecutionContext, UserMessageContext } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message management plugin
|
* Message management plugin
|
||||||
* Handles all message-related operations: persistence, streaming, UI updates, and duration-based filtering
|
* Handles all message-related operations: persistence, streaming, UI updates, and duration-based filtering
|
||||||
*/
|
*/
|
||||||
export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
export const messageManagementTool: PromptConcatTool = (hooks) => {
|
||||||
// Handle user message persistence
|
// Handle user message persistence
|
||||||
hooks.userMessageReceived.tapAsync('messageManagementPlugin', async (context: UserMessageContext, callback) => {
|
hooks.userMessageReceived.tapAsync('messageManagementTool', async (context: UserMessageContext, callback) => {
|
||||||
try {
|
try {
|
||||||
const { handlerContext, content, messageId } = context;
|
const { agentFrameworkContext, content, messageId } = context;
|
||||||
|
|
||||||
// Create user message using the helper function
|
// Create user message using the helper function
|
||||||
const userMessage = createAgentMessage(messageId, handlerContext.agent.id, {
|
const userMessage = createAgentMessage(messageId, agentFrameworkContext.agent.id, {
|
||||||
role: 'user',
|
role: 'user',
|
||||||
content: content.text,
|
content: content.text,
|
||||||
contentType: 'text/plain',
|
contentType: 'text/plain',
|
||||||
|
|
@ -30,7 +30,7 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add message to the agent's message array for immediate use (do this before persistence so plugins see it)
|
// Add message to the agent's message array for immediate use (do this before persistence so plugins see it)
|
||||||
handlerContext.agent.messages.push(userMessage);
|
agentFrameworkContext.agent.messages.push(userMessage);
|
||||||
|
|
||||||
// Get the agent instance service to access repositories
|
// Get the agent instance service to access repositories
|
||||||
const agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
const agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
||||||
|
|
@ -40,7 +40,7 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
|
|
||||||
logger.debug('User message persisted to database', {
|
logger.debug('User message persisted to database', {
|
||||||
messageId,
|
messageId,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
contentLength: content.text.length,
|
contentLength: content.text.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -49,30 +49,30 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
logger.error('Message management plugin error in userMessageReceived', {
|
logger.error('Message management plugin error in userMessageReceived', {
|
||||||
error,
|
error,
|
||||||
messageId: context.messageId,
|
messageId: context.messageId,
|
||||||
agentId: context.handlerContext.agent.id,
|
agentId: context.agentFrameworkContext.agent.id,
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle agent status persistence
|
// Handle agent status persistence
|
||||||
hooks.agentStatusChanged.tapAsync('messageManagementPlugin', async (context: AgentStatusContext, callback) => {
|
hooks.agentStatusChanged.tapAsync('messageManagementTool', async (context: AgentStatusContext, callback) => {
|
||||||
try {
|
try {
|
||||||
const { handlerContext, status } = context;
|
const { agentFrameworkContext, status } = context;
|
||||||
|
|
||||||
// Get the agent instance service to update status
|
// Get the agent instance service to update status
|
||||||
const agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
const agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
||||||
|
|
||||||
// Update agent status in database
|
// Update agent status in database
|
||||||
await agentInstanceService.updateAgent(handlerContext.agent.id, {
|
await agentInstanceService.updateAgent(agentFrameworkContext.agent.id, {
|
||||||
status,
|
status,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update the agent object for immediate use
|
// Update the agent object for immediate use
|
||||||
handlerContext.agent.status = status;
|
agentFrameworkContext.agent.status = status;
|
||||||
|
|
||||||
logger.debug('Agent status updated in database', {
|
logger.debug('Agent status updated in database', {
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
state: status.state,
|
state: status.state,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -80,7 +80,7 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Message management plugin error in agentStatusChanged', {
|
logger.error('Message management plugin error in agentStatusChanged', {
|
||||||
error,
|
error,
|
||||||
agentId: context.handlerContext.agent.id,
|
agentId: context.agentFrameworkContext.agent.id,
|
||||||
status: context.status,
|
status: context.status,
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
|
|
@ -88,13 +88,13 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle AI response updates during streaming
|
// Handle AI response updates during streaming
|
||||||
hooks.responseUpdate.tapAsync('messageManagementPlugin', async (context: AIResponseContext, callback) => {
|
hooks.responseUpdate.tapAsync('messageManagementTool', async (context: AIResponseContext, callback) => {
|
||||||
try {
|
try {
|
||||||
const { handlerContext, response } = context;
|
const { agentFrameworkContext, response } = context;
|
||||||
|
|
||||||
if (response.status === 'update' && response.content) {
|
if (response.status === 'update' && response.content) {
|
||||||
// Find or create AI response message in agent's message array
|
// Find or create AI response message in agent's message array
|
||||||
let aiMessage = handlerContext.agent.messages.find(
|
let aiMessage = agentFrameworkContext.agent.messages.find(
|
||||||
(message) => message.role === 'assistant' && !message.metadata?.isComplete,
|
(message) => message.role === 'assistant' && !message.metadata?.isComplete,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -103,7 +103,7 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
aiMessage = {
|
aiMessage = {
|
||||||
id: `ai-response-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
id: `ai-response-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
content: response.content,
|
content: response.content,
|
||||||
created: now,
|
created: now,
|
||||||
|
|
@ -111,7 +111,7 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
metadata: { isComplete: false },
|
metadata: { isComplete: false },
|
||||||
duration: undefined, // AI responses persist indefinitely by default
|
duration: undefined, // AI responses persist indefinitely by default
|
||||||
};
|
};
|
||||||
handlerContext.agent.messages.push(aiMessage);
|
agentFrameworkContext.agent.messages.push(aiMessage);
|
||||||
// Persist immediately so DB timestamp reflects conversation order
|
// Persist immediately so DB timestamp reflects conversation order
|
||||||
try {
|
try {
|
||||||
const agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
const agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
||||||
|
|
@ -132,7 +132,7 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
// Update UI using the agent instance service
|
// Update UI using the agent instance service
|
||||||
try {
|
try {
|
||||||
const agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
const agentInstanceService = container.get<IAgentInstanceService>(serviceIdentifier.AgentInstance);
|
||||||
agentInstanceService.debounceUpdateMessage(aiMessage, handlerContext.agent.id);
|
agentInstanceService.debounceUpdateMessage(aiMessage, agentFrameworkContext.agent.id);
|
||||||
} catch (serviceError) {
|
} catch (serviceError) {
|
||||||
logger.warn('Failed to update UI for streaming message', {
|
logger.warn('Failed to update UI for streaming message', {
|
||||||
error: serviceError,
|
error: serviceError,
|
||||||
|
|
@ -150,13 +150,13 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle AI response completion
|
// Handle AI response completion
|
||||||
hooks.responseComplete.tapAsync('messageManagementPlugin', async (context: AIResponseContext, callback) => {
|
hooks.responseComplete.tapAsync('messageManagementTool', async (context: AIResponseContext, callback) => {
|
||||||
try {
|
try {
|
||||||
const { handlerContext, response } = context;
|
const { agentFrameworkContext, response } = context;
|
||||||
|
|
||||||
if (response.status === 'done' && response.content) {
|
if (response.status === 'done' && response.content) {
|
||||||
// Find and finalize AI response message
|
// Find and finalize AI response message
|
||||||
let aiMessage = handlerContext.agent.messages.find(
|
let aiMessage = agentFrameworkContext.agent.messages.find(
|
||||||
(message) => message.role === 'assistant' && !message.metadata?.isComplete && !message.metadata?.isToolResult,
|
(message) => message.role === 'assistant' && !message.metadata?.isComplete && !message.metadata?.isToolResult,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -170,7 +170,7 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
const nowFinal = new Date();
|
const nowFinal = new Date();
|
||||||
aiMessage = {
|
aiMessage = {
|
||||||
id: `ai-response-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
id: `ai-response-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
content: response.content,
|
content: response.content,
|
||||||
created: nowFinal,
|
created: nowFinal,
|
||||||
|
|
@ -180,7 +180,7 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
},
|
},
|
||||||
duration: undefined, // Default duration for AI responses
|
duration: undefined, // Default duration for AI responses
|
||||||
};
|
};
|
||||||
handlerContext.agent.messages.push(aiMessage);
|
agentFrameworkContext.agent.messages.push(aiMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the agent instance service for persistence and UI updates
|
// Get the agent instance service for persistence and UI updates
|
||||||
|
|
@ -191,7 +191,7 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
|
|
||||||
// Final UI update
|
// Final UI update
|
||||||
try {
|
try {
|
||||||
agentInstanceService.debounceUpdateMessage(aiMessage, handlerContext.agent.id);
|
agentInstanceService.debounceUpdateMessage(aiMessage, agentFrameworkContext.agent.id);
|
||||||
} catch (serviceError) {
|
} catch (serviceError) {
|
||||||
logger.warn('Failed to update UI for completed message', {
|
logger.warn('Failed to update UI for completed message', {
|
||||||
error: serviceError,
|
error: serviceError,
|
||||||
|
|
@ -215,12 +215,12 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle tool result messages persistence and UI updates
|
// Handle tool result messages persistence and UI updates
|
||||||
hooks.toolExecuted.tapAsync('messageManagementPlugin', async (context: ToolExecutionContext, callback) => {
|
hooks.toolExecuted.tapAsync('messageManagementTool', async (context: ToolExecutionContext, callback) => {
|
||||||
try {
|
try {
|
||||||
const { handlerContext } = context;
|
const { agentFrameworkContext } = context;
|
||||||
|
|
||||||
// Find newly added tool result messages that need to be persisted
|
// Find newly added tool result messages that need to be persisted
|
||||||
const newToolResultMessages = handlerContext.agent.messages.filter(
|
const newToolResultMessages = agentFrameworkContext.agent.messages.filter(
|
||||||
(message) => message.metadata?.isToolResult && !message.metadata.isPersisted,
|
(message) => message.metadata?.isToolResult && !message.metadata.isPersisted,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -233,7 +233,7 @@ export const messageManagementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
await agentInstanceService.saveUserMessage(message);
|
await agentInstanceService.saveUserMessage(message);
|
||||||
|
|
||||||
// Update UI
|
// Update UI
|
||||||
agentInstanceService.debounceUpdateMessage(message, handlerContext.agent.id);
|
agentInstanceService.debounceUpdateMessage(message, agentFrameworkContext.agent.id);
|
||||||
|
|
||||||
// Mark as persisted to avoid duplicate saves
|
// Mark as persisted to avoid duplicate saves
|
||||||
message.metadata = { ...message.metadata, isPersisted: true, uiUpdated: true };
|
message.metadata = { ...message.metadata, isPersisted: true, uiUpdated: true };
|
||||||
|
|
@ -10,7 +10,7 @@ import { findPromptById } from '../promptConcat/promptConcat';
|
||||||
import type { IPrompt } from '../promptConcat/promptConcatSchema';
|
import type { IPrompt } from '../promptConcat/promptConcatSchema';
|
||||||
import { filterMessagesByDuration } from '../utilities/messageDurationFilter';
|
import { filterMessagesByDuration } from '../utilities/messageDurationFilter';
|
||||||
import { normalizeRole } from '../utilities/normalizeRole';
|
import { normalizeRole } from '../utilities/normalizeRole';
|
||||||
import { AgentResponse, PromptConcatPlugin, ResponseHookContext } from './types';
|
import { AgentResponse, PromptConcatTool, ResponseHookContext } from './types';
|
||||||
|
|
||||||
const t = identity;
|
const t = identity;
|
||||||
|
|
||||||
|
|
@ -76,17 +76,17 @@ export function getDynamicPositionParameterSchema() {
|
||||||
* Full replacement plugin
|
* Full replacement plugin
|
||||||
* Replaces target content with content from specified source
|
* Replaces target content with content from specified source
|
||||||
*/
|
*/
|
||||||
export const fullReplacementPlugin: PromptConcatPlugin = (hooks) => {
|
export const fullReplacementTool: PromptConcatTool = (hooks) => {
|
||||||
// Normalize an AgentInstanceMessage role to Prompt role
|
// Normalize an AgentInstanceMessage role to Prompt role
|
||||||
hooks.processPrompts.tapAsync('fullReplacementPlugin', async (context, callback) => {
|
hooks.processPrompts.tapAsync('fullReplacementTool', async (context, callback) => {
|
||||||
const { pluginConfig, prompts, messages } = context;
|
const { toolConfig, prompts, messages } = context;
|
||||||
|
|
||||||
if (pluginConfig.pluginId !== 'fullReplacement' || !pluginConfig.fullReplacementParam) {
|
if (toolConfig.toolId !== 'fullReplacement' || !toolConfig.fullReplacementParam) {
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullReplacementConfig = pluginConfig.fullReplacementParam;
|
const fullReplacementConfig = toolConfig.fullReplacementParam;
|
||||||
if (!fullReplacementConfig) {
|
if (!fullReplacementConfig) {
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
|
|
@ -98,7 +98,7 @@ export const fullReplacementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
if (!found) {
|
if (!found) {
|
||||||
logger.warn('Target prompt not found for fullReplacement', {
|
logger.warn('Target prompt not found for fullReplacement', {
|
||||||
targetId,
|
targetId,
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
|
|
@ -173,16 +173,16 @@ export const fullReplacementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle response phase for llmResponse source type
|
// Handle response phase for llmResponse source type
|
||||||
hooks.postProcess.tapAsync('fullReplacementPlugin', async (context, callback) => {
|
hooks.postProcess.tapAsync('fullReplacementTool', async (context, callback) => {
|
||||||
const responseContext = context as ResponseHookContext;
|
const responseContext = context as ResponseHookContext;
|
||||||
const { pluginConfig, llmResponse, responses } = responseContext;
|
const { toolConfig, llmResponse, responses } = responseContext;
|
||||||
|
|
||||||
if (pluginConfig.pluginId !== 'fullReplacement' || !pluginConfig.fullReplacementParam) {
|
if (toolConfig.toolId !== 'fullReplacement' || !toolConfig.fullReplacementParam) {
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullReplacementParameter = pluginConfig.fullReplacementParam;
|
const fullReplacementParameter = toolConfig.fullReplacementParam;
|
||||||
if (!fullReplacementParameter) {
|
if (!fullReplacementParameter) {
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
|
|
@ -202,7 +202,7 @@ export const fullReplacementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
if (!found) {
|
if (!found) {
|
||||||
logger.warn('Full replacement target not found in responses', {
|
logger.warn('Full replacement target not found in responses', {
|
||||||
targetId,
|
targetId,
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
|
|
@ -212,7 +212,7 @@ export const fullReplacementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
logger.debug('Replacing target with LLM response', {
|
logger.debug('Replacing target with LLM response', {
|
||||||
targetId,
|
targetId,
|
||||||
responseLength: llmResponse.length,
|
responseLength: llmResponse.length,
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
found.text = llmResponse;
|
found.text = llmResponse;
|
||||||
|
|
@ -220,6 +220,7 @@ export const fullReplacementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
logger.debug('Full replacement completed in response phase', {
|
logger.debug('Full replacement completed in response phase', {
|
||||||
targetId,
|
targetId,
|
||||||
sourceType,
|
sourceType,
|
||||||
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
|
|
@ -230,16 +231,16 @@ export const fullReplacementPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
* Dynamic position plugin
|
* Dynamic position plugin
|
||||||
* Inserts content at a specific position relative to a target element
|
* Inserts content at a specific position relative to a target element
|
||||||
*/
|
*/
|
||||||
export const dynamicPositionPlugin: PromptConcatPlugin = (hooks) => {
|
export const dynamicPositionTool: PromptConcatTool = (hooks) => {
|
||||||
hooks.processPrompts.tapAsync('dynamicPositionPlugin', async (context, callback) => {
|
hooks.processPrompts.tapAsync('dynamicPositionTool', async (context, callback) => {
|
||||||
const { pluginConfig, prompts } = context;
|
const { toolConfig, prompts } = context;
|
||||||
|
|
||||||
if (pluginConfig.pluginId !== 'dynamicPosition' || !pluginConfig.dynamicPositionParam || !pluginConfig.content) {
|
if (toolConfig.toolId !== 'dynamicPosition' || !toolConfig.dynamicPositionParam || !toolConfig.content) {
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dynamicPositionConfig = pluginConfig.dynamicPositionParam;
|
const dynamicPositionConfig = toolConfig.dynamicPositionParam;
|
||||||
if (!dynamicPositionConfig) {
|
if (!dynamicPositionConfig) {
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
|
|
@ -251,7 +252,7 @@ export const dynamicPositionPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
if (!found) {
|
if (!found) {
|
||||||
logger.warn('Target prompt not found for dynamicPosition', {
|
logger.warn('Target prompt not found for dynamicPosition', {
|
||||||
targetId,
|
targetId,
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
|
|
@ -259,9 +260,9 @@ export const dynamicPositionPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
|
|
||||||
// Create new prompt part
|
// Create new prompt part
|
||||||
const newPart: IPrompt = {
|
const newPart: IPrompt = {
|
||||||
id: `dynamic-${pluginConfig.id}-${Date.now()}`,
|
id: `dynamic-${toolConfig.id}-${Date.now()}`,
|
||||||
caption: pluginConfig.caption || 'Dynamic Content',
|
caption: toolConfig.caption || 'Dynamic Content',
|
||||||
text: pluginConfig.content,
|
text: toolConfig.content,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Insert based on position
|
// Insert based on position
|
||||||
|
|
@ -288,7 +289,8 @@ export const dynamicPositionPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
logger.debug('Dynamic position insertion completed', {
|
logger.debug('Dynamic position insertion completed', {
|
||||||
targetId,
|
targetId,
|
||||||
position,
|
position,
|
||||||
contentLength: pluginConfig.content.length,
|
contentLength: toolConfig.content.length,
|
||||||
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
165
src/services/agentInstance/tools/schemaRegistry.ts
Normal file
165
src/services/agentInstance/tools/schemaRegistry.ts
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
/**
|
||||||
|
* Tool Schema Registry
|
||||||
|
*
|
||||||
|
* This system allows tools to register their parameter schemas dynamically,
|
||||||
|
* enabling dynamic tool loading while maintaining type safety and validation.
|
||||||
|
*/
|
||||||
|
import { identity } from 'lodash';
|
||||||
|
import { z } from 'zod/v4';
|
||||||
|
|
||||||
|
const t = identity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry for tool parameter schemas
|
||||||
|
*/
|
||||||
|
const toolSchemas = new Map<string, z.ZodType>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry for tool metadata
|
||||||
|
*/
|
||||||
|
const toolMetadata = new Map<string, {
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a tool parameter schema
|
||||||
|
* @param toolId The tool ID (should match toolId enum values)
|
||||||
|
* @param schema The Zod schema for this tool's parameters
|
||||||
|
* @param metadata Optional metadata for display purposes
|
||||||
|
*/
|
||||||
|
export function registerToolParameterSchema(
|
||||||
|
toolId: string,
|
||||||
|
schema: z.ZodType,
|
||||||
|
metadata?: {
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
},
|
||||||
|
): void {
|
||||||
|
toolSchemas.set(toolId, schema);
|
||||||
|
if (metadata) {
|
||||||
|
toolMetadata.set(toolId, metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a tool parameter schema by ID
|
||||||
|
* @param toolId The tool ID
|
||||||
|
* @returns The schema or undefined if not found
|
||||||
|
*/
|
||||||
|
export function getToolParameterSchema(toolId: string): z.ZodType | undefined {
|
||||||
|
return toolSchemas.get(toolId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all registered tool IDs
|
||||||
|
* @returns Array of all registered tool IDs
|
||||||
|
*/
|
||||||
|
export function getAllRegisteredToolIds(): string[] {
|
||||||
|
return Array.from(toolSchemas.keys());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get tool metadata
|
||||||
|
* @param toolId The tool ID
|
||||||
|
* @returns Tool metadata or undefined if not found
|
||||||
|
*/
|
||||||
|
export function getToolMetadata(toolId: string): { displayName: string; description: string } | undefined {
|
||||||
|
return toolMetadata.get(toolId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamically create the PromptConcatToolSchema based on registered tools
|
||||||
|
* This is called whenever the schema is needed, ensuring it includes all registered tools
|
||||||
|
*/
|
||||||
|
export function createDynamicPromptConcatToolSchema(): z.ZodType {
|
||||||
|
// Base tool configuration without parameter-specific fields
|
||||||
|
const baseToolSchema = z.object({
|
||||||
|
id: z.string().meta({
|
||||||
|
title: t('Schema.Tool.IdTitle'),
|
||||||
|
description: t('Schema.Tool.Id'),
|
||||||
|
}),
|
||||||
|
caption: z.string().optional().meta({
|
||||||
|
title: t('Schema.Tool.CaptionTitle'),
|
||||||
|
description: t('Schema.Tool.Caption'),
|
||||||
|
}),
|
||||||
|
content: z.string().optional().meta({
|
||||||
|
title: t('Schema.Tool.ContentTitle'),
|
||||||
|
description: t('Schema.Tool.Content'),
|
||||||
|
}),
|
||||||
|
forbidOverrides: z.boolean().optional().default(false).meta({
|
||||||
|
title: t('Schema.Tool.ForbidOverridesTitle'),
|
||||||
|
description: t('Schema.Tool.ForbidOverrides'),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get all registered tool IDs
|
||||||
|
const registeredToolIds = getAllRegisteredToolIds();
|
||||||
|
|
||||||
|
if (registeredToolIds.length === 0) {
|
||||||
|
// Fallback to a basic schema if no tools are registered yet
|
||||||
|
return baseToolSchema.extend({
|
||||||
|
toolId: z.string().meta({
|
||||||
|
title: t('Schema.Tool.ToolIdTitle'),
|
||||||
|
description: t('Schema.Tool.ToolId'),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create enum from registered tool IDs
|
||||||
|
const toolIdEnum = z.enum(registeredToolIds as [string, ...string[]]).meta({
|
||||||
|
title: t('Schema.Tool.ToolIdTitle'),
|
||||||
|
description: t('Schema.Tool.ToolId'),
|
||||||
|
enumOptions: registeredToolIds.map(toolId => {
|
||||||
|
const metadata = getToolMetadata(toolId);
|
||||||
|
return {
|
||||||
|
value: toolId,
|
||||||
|
label: metadata?.displayName || toolId,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create parameter schema object with all registered tools
|
||||||
|
const parameterSchema: Record<string, z.ZodType> = {};
|
||||||
|
|
||||||
|
for (const toolId of registeredToolIds) {
|
||||||
|
const schema = getToolParameterSchema(toolId);
|
||||||
|
if (schema) {
|
||||||
|
const metadata = getToolMetadata(toolId);
|
||||||
|
parameterSchema[`${toolId}Param`] = schema.optional().meta({
|
||||||
|
title: metadata?.displayName || toolId,
|
||||||
|
description: metadata?.description || `Parameters for ${toolId} tool`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine base schema with tool ID and parameters
|
||||||
|
return baseToolSchema.extend({
|
||||||
|
toolId: toolIdEnum,
|
||||||
|
...parameterSchema,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the type of a tool's parameters
|
||||||
|
* @param toolId The tool ID
|
||||||
|
* @returns The inferred TypeScript type of the tool's parameters
|
||||||
|
*/
|
||||||
|
export type ToolParameterType<T extends string> = T extends keyof ReturnType<typeof createToolParameterTypes> ? ReturnType<typeof createToolParameterTypes>[T] : never;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create type definitions for all registered tool parameters
|
||||||
|
* This is used internally for type inference
|
||||||
|
*/
|
||||||
|
export function createToolParameterTypes() {
|
||||||
|
const types: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
for (const toolId of getAllRegisteredToolIds()) {
|
||||||
|
const schema = getToolParameterSchema(toolId);
|
||||||
|
if (schema) {
|
||||||
|
types[toolId] = schema;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return types as Record<string, z.ZodType>;
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { ToolCallingMatch } from '@services/agentDefinition/interface';
|
import { ToolCallingMatch } from '@services/agentDefinition/interface';
|
||||||
import { AgentHandlerContext } from '@services/agentInstance/buildInAgentHandlers/type';
|
import { AgentFrameworkContext } from '@services/agentInstance/agentFrameworks/utilities/type';
|
||||||
import { AgentInstanceMessage } from '@services/agentInstance/interface';
|
import { AgentInstanceMessage } from '@services/agentInstance/interface';
|
||||||
import { AIStreamResponse } from '@services/externalAPI/interface';
|
import { AIStreamResponse } from '@services/externalAPI/interface';
|
||||||
import { AsyncSeriesHook, AsyncSeriesWaterfallHook } from 'tapable';
|
import { AsyncSeriesHook, AsyncSeriesWaterfallHook } from 'tapable';
|
||||||
import type { IPrompt, IPromptConcatPlugin } from '../promptConcat/promptConcatSchema/';
|
import type { IPrompt, IPromptConcatTool } from '../promptConcat/promptConcatSchema';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Next round target options
|
* Next round target options
|
||||||
|
|
@ -11,9 +11,9 @@ import type { IPrompt, IPromptConcatPlugin } from '../promptConcat/promptConcatS
|
||||||
export type YieldNextRoundTarget = 'human' | 'self' | `agent:${string}`; // allows for future agent IDs like "agent:agent-id"
|
export type YieldNextRoundTarget = 'human' | 'self' | `agent:${string}`; // allows for future agent IDs like "agent:agent-id"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unified actions interface for all plugin hooks
|
* Unified actions interface for all tool hooks
|
||||||
*/
|
*/
|
||||||
export interface PluginActions {
|
export interface ToolActions {
|
||||||
/** Whether to yield next round to continue processing */
|
/** Whether to yield next round to continue processing */
|
||||||
yieldNextRoundTo?: YieldNextRoundTarget;
|
yieldNextRoundTo?: YieldNextRoundTarget;
|
||||||
/** New user message to append */
|
/** New user message to append */
|
||||||
|
|
@ -23,27 +23,27 @@ export interface PluginActions {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base context interface for all plugin hooks
|
* Base context interface for all tool hooks
|
||||||
*/
|
*/
|
||||||
export interface BasePluginContext {
|
export interface BaseToolContext {
|
||||||
/** Handler context */
|
/** Framework context */
|
||||||
handlerContext: AgentHandlerContext;
|
agentFrameworkContext: AgentFrameworkContext;
|
||||||
/** Additional context data */
|
/** Additional context data */
|
||||||
metadata?: Record<string, unknown>;
|
metadata?: Record<string, unknown>;
|
||||||
/** Actions set by plugins during processing */
|
/** Actions set by tools during processing */
|
||||||
actions?: PluginActions;
|
actions?: ToolActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context for prompt processing hooks (processPrompts, finalizePrompts)
|
* Context for prompt processing hooks (processPrompts, finalizePrompts)
|
||||||
*/
|
*/
|
||||||
export interface PromptConcatHookContext extends BasePluginContext {
|
export interface PromptConcatHookContext extends BaseToolContext {
|
||||||
/** Array of agent instance messages for context */
|
/** Array of agent instance messages for context */
|
||||||
messages: AgentInstanceMessage[];
|
messages: AgentInstanceMessage[];
|
||||||
/** Current prompt tree */
|
/** Current prompt tree */
|
||||||
prompts: IPrompt[];
|
prompts: IPrompt[];
|
||||||
/** Plugin configuration */
|
/** Tool configuration */
|
||||||
pluginConfig: IPromptConcatPlugin;
|
toolConfig: IPromptConcatTool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -59,11 +59,11 @@ export interface PostProcessContext extends PromptConcatHookContext {
|
||||||
/**
|
/**
|
||||||
* Context for AI response hooks (responseUpdate, responseComplete)
|
* Context for AI response hooks (responseUpdate, responseComplete)
|
||||||
*/
|
*/
|
||||||
export interface AIResponseContext extends BasePluginContext {
|
export interface AIResponseContext extends BaseToolContext {
|
||||||
/** Plugin configuration - for backward compatibility */
|
/** Tool configuration - for backward compatibility */
|
||||||
pluginConfig: IPromptConcatPlugin;
|
toolConfig: IPromptConcatTool;
|
||||||
/** Complete handler configuration - allows plugins to access all configs */
|
/** Complete framework configuration - allows tools to access all configs */
|
||||||
handlerConfig?: { plugins?: Array<{ pluginId: string; [key: string]: unknown }> };
|
agentFrameworkConfig?: { plugins?: Array<{ toolId: string; [key: string]: unknown }> };
|
||||||
/** AI streaming response */
|
/** AI streaming response */
|
||||||
response: AIStreamResponse;
|
response: AIStreamResponse;
|
||||||
/** Current request ID */
|
/** Current request ID */
|
||||||
|
|
@ -75,7 +75,7 @@ export interface AIResponseContext extends BasePluginContext {
|
||||||
/**
|
/**
|
||||||
* Context for user message hooks
|
* Context for user message hooks
|
||||||
*/
|
*/
|
||||||
export interface UserMessageContext extends BasePluginContext {
|
export interface UserMessageContext extends BaseToolContext {
|
||||||
/** User message content */
|
/** User message content */
|
||||||
content: { text: string; file?: File };
|
content: { text: string; file?: File };
|
||||||
/** Generated message ID */
|
/** Generated message ID */
|
||||||
|
|
@ -87,7 +87,7 @@ export interface UserMessageContext extends BasePluginContext {
|
||||||
/**
|
/**
|
||||||
* Context for agent status hooks
|
* Context for agent status hooks
|
||||||
*/
|
*/
|
||||||
export interface AgentStatusContext extends BasePluginContext {
|
export interface AgentStatusContext extends BaseToolContext {
|
||||||
/** New status state */
|
/** New status state */
|
||||||
status: {
|
status: {
|
||||||
state: 'working' | 'completed' | 'failed' | 'canceled';
|
state: 'working' | 'completed' | 'failed' | 'canceled';
|
||||||
|
|
@ -98,7 +98,7 @@ export interface AgentStatusContext extends BasePluginContext {
|
||||||
/**
|
/**
|
||||||
* Context for tool execution hooks
|
* Context for tool execution hooks
|
||||||
*/
|
*/
|
||||||
export interface ToolExecutionContext extends BasePluginContext {
|
export interface ToolExecutionContext extends BaseToolContext {
|
||||||
/** Tool execution result */
|
/** Tool execution result */
|
||||||
toolResult: {
|
toolResult: {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
|
@ -136,7 +136,7 @@ export interface ResponseHookContext extends PromptConcatHookContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler hooks for unified plugin system
|
* Framework hooks for unified tool system
|
||||||
* Handles both prompt processing and agent lifecycle events
|
* Handles both prompt processing and agent lifecycle events
|
||||||
*/
|
*/
|
||||||
export interface PromptConcatHooks {
|
export interface PromptConcatHooks {
|
||||||
|
|
@ -159,6 +159,6 @@ export interface PromptConcatHooks {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Universal plugin function interface - can register handlers for any hooks
|
* Universal tool function interface - can register handlers for any hooks
|
||||||
*/
|
*/
|
||||||
export type PromptConcatPlugin = (hooks: PromptConcatHooks) => void;
|
export type PromptConcatTool = (hooks: PromptConcatHooks) => void;
|
||||||
|
|
@ -16,7 +16,7 @@ import { z } from 'zod/v4';
|
||||||
import type { AgentInstanceMessage, IAgentInstanceService } from '../interface';
|
import type { AgentInstanceMessage, IAgentInstanceService } from '../interface';
|
||||||
import { findPromptById } from '../promptConcat/promptConcat';
|
import { findPromptById } from '../promptConcat/promptConcat';
|
||||||
import { schemaToToolContent } from '../utilities/schemaToToolContent';
|
import { schemaToToolContent } from '../utilities/schemaToToolContent';
|
||||||
import type { PromptConcatPlugin } from './types';
|
import type { PromptConcatTool } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wiki Operation Parameter Schema
|
* Wiki Operation Parameter Schema
|
||||||
|
|
@ -101,17 +101,17 @@ const WikiOperationToolParameterSchema = z.object({
|
||||||
* Wiki Operation plugin - Prompt processing
|
* Wiki Operation plugin - Prompt processing
|
||||||
* Handles tool list injection for wiki operation functionality
|
* Handles tool list injection for wiki operation functionality
|
||||||
*/
|
*/
|
||||||
export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
export const wikiOperationTool: PromptConcatTool = (hooks) => {
|
||||||
// First tapAsync: Tool list injection
|
// First tapAsync: Tool list injection
|
||||||
hooks.processPrompts.tapAsync('wikiOperationPlugin-toolList', async (context, callback) => {
|
hooks.processPrompts.tapAsync('wikiOperationTool-toolList', async (context, callback) => {
|
||||||
const { pluginConfig, prompts } = context;
|
const { toolConfig, prompts } = context;
|
||||||
|
|
||||||
if (pluginConfig.pluginId !== 'wikiOperation' || !pluginConfig.wikiOperationParam) {
|
if (toolConfig.toolId !== 'wikiOperation' || !toolConfig.wikiOperationParam) {
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wikiOperationParameter = pluginConfig.wikiOperationParam;
|
const wikiOperationParameter = toolConfig.wikiOperationParam;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Handle tool list injection if toolListPosition is configured
|
// Handle tool list injection if toolListPosition is configured
|
||||||
|
|
@ -121,7 +121,7 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
if (!toolListTarget) {
|
if (!toolListTarget) {
|
||||||
logger.warn('Tool list target prompt not found', {
|
logger.warn('Tool list target prompt not found', {
|
||||||
targetId: toolListPosition.targetId,
|
targetId: toolListPosition.targetId,
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
|
|
@ -140,7 +140,7 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
}
|
}
|
||||||
const insertIndex = toolListTarget.prompt.children.length;
|
const insertIndex = toolListTarget.prompt.children.length;
|
||||||
toolListTarget.prompt.children.splice(insertIndex, 0, {
|
toolListTarget.prompt.children.splice(insertIndex, 0, {
|
||||||
id: `wiki-operation-tool-${pluginConfig.id}`,
|
id: `wiki-operation-tool-${toolConfig.id}`,
|
||||||
caption: 'Wiki Operation Tool',
|
caption: 'Wiki Operation Tool',
|
||||||
text: wikiOperationToolContent,
|
text: wikiOperationToolContent,
|
||||||
});
|
});
|
||||||
|
|
@ -149,7 +149,7 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
toolListTarget.prompt.children = [];
|
toolListTarget.prompt.children = [];
|
||||||
}
|
}
|
||||||
toolListTarget.prompt.children.unshift({
|
toolListTarget.prompt.children.unshift({
|
||||||
id: `wiki-operation-tool-${pluginConfig.id}`,
|
id: `wiki-operation-tool-${toolConfig.id}`,
|
||||||
caption: 'Wiki Operation Tool',
|
caption: 'Wiki Operation Tool',
|
||||||
text: wikiOperationToolContent,
|
text: wikiOperationToolContent,
|
||||||
});
|
});
|
||||||
|
|
@ -161,7 +161,7 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
logger.debug('Wiki operation tool list injected', {
|
logger.debug('Wiki operation tool list injected', {
|
||||||
targetId: toolListPosition.targetId,
|
targetId: toolListPosition.targetId,
|
||||||
position: toolListPosition.position,
|
position: toolListPosition.position,
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,20 +169,20 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error in wiki operation tool list injection', {
|
logger.error('Error in wiki operation tool list injection', {
|
||||||
error,
|
error,
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Tool execution when AI response is complete
|
// 2. Tool execution when AI response is complete
|
||||||
hooks.responseComplete.tapAsync('wikiOperationPlugin-handler', async (context, callback) => {
|
hooks.responseComplete.tapAsync('wikiOperationTool-handler', async (context, callback) => {
|
||||||
try {
|
try {
|
||||||
const { handlerContext, response, handlerConfig } = context;
|
const { agentFrameworkContext, response, agentFrameworkConfig } = context;
|
||||||
|
|
||||||
// Find this plugin's configuration from handlerConfig
|
// Find this plugin's configuration import { AgentFrameworkConfig }
|
||||||
const wikiOperationPluginConfig = handlerConfig?.plugins?.find(p => p.pluginId === 'wikiOperation');
|
const wikiOperationToolConfig = agentFrameworkConfig?.plugins?.find((p: { toolId: string; [key: string]: unknown }) => p.toolId === 'wikiOperation');
|
||||||
const wikiOperationParameter = wikiOperationPluginConfig?.wikiOperationParam as { toolResultDuration?: number } | undefined;
|
const wikiOperationParameter = wikiOperationToolConfig?.wikiOperationParam as { toolResultDuration?: number } | undefined;
|
||||||
const toolResultDuration = wikiOperationParameter?.toolResultDuration || 1; // Default to 1 round
|
const toolResultDuration = wikiOperationParameter?.toolResultDuration || 1; // Default to 1 round
|
||||||
|
|
||||||
if (response.status !== 'done' || !response.content) {
|
if (response.status !== 'done' || !response.content) {
|
||||||
|
|
@ -200,19 +200,19 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
|
|
||||||
logger.debug('Wiki operation tool call detected', {
|
logger.debug('Wiki operation tool call detected', {
|
||||||
toolId: toolMatch.toolId,
|
toolId: toolMatch.toolId,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set duration=1 for the AI message containing the tool call
|
// Set duration=1 for the AI message containing the tool call
|
||||||
// Find the most recent AI message (should be the one containing the tool call)
|
// Find the most recent AI message (should be the one containing the tool call)
|
||||||
const aiMessages = handlerContext.agent.messages.filter(message => message.role === 'assistant');
|
const aiMessages = agentFrameworkContext.agent.messages.filter((message: AgentInstanceMessage) => message.role === 'assistant');
|
||||||
if (aiMessages.length > 0) {
|
if (aiMessages.length > 0) {
|
||||||
const latestAiMessage = aiMessages[aiMessages.length - 1];
|
const latestAiMessage = aiMessages[aiMessages.length - 1];
|
||||||
latestAiMessage.duration = toolResultDuration;
|
latestAiMessage.duration = toolResultDuration;
|
||||||
logger.debug('Set AI message duration for tool call', {
|
logger.debug('Set AI message duration for tool call', {
|
||||||
messageId: latestAiMessage.id,
|
messageId: latestAiMessage.id,
|
||||||
duration: toolResultDuration,
|
duration: toolResultDuration,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,7 +220,7 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
try {
|
try {
|
||||||
logger.debug('Parsing wiki operation tool parameters', {
|
logger.debug('Parsing wiki operation tool parameters', {
|
||||||
toolMatch: toolMatch.parameters,
|
toolMatch: toolMatch.parameters,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Use parameters returned by matchToolCalling directly. Let zod schema validate.
|
// Use parameters returned by matchToolCalling directly. Let zod schema validate.
|
||||||
|
|
@ -253,7 +253,7 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
workspaceName,
|
workspaceName,
|
||||||
operation,
|
operation,
|
||||||
title,
|
title,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
let result: string;
|
let result: string;
|
||||||
|
|
@ -293,7 +293,7 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
workspaceID,
|
workspaceID,
|
||||||
operation,
|
operation,
|
||||||
title,
|
title,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Format the tool result for display
|
// Format the tool result for display
|
||||||
|
|
@ -307,8 +307,8 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
|
|
||||||
logger.debug('Wiki operation setting yieldNextRoundTo=self', {
|
logger.debug('Wiki operation setting yieldNextRoundTo=self', {
|
||||||
toolId: 'wiki-operation',
|
toolId: 'wiki-operation',
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
messageCount: handlerContext.agent.messages.length,
|
messageCount: agentFrameworkContext.agent.messages.length,
|
||||||
toolResultPreview: toolResultText.slice(0, 200),
|
toolResultPreview: toolResultText.slice(0, 200),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -316,7 +316,7 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
const toolResultTime = new Date();
|
const toolResultTime = new Date();
|
||||||
const toolResultMessage: AgentInstanceMessage = {
|
const toolResultMessage: AgentInstanceMessage = {
|
||||||
id: `tool-result-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
id: `tool-result-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
role: 'tool', // Tool result message
|
role: 'tool', // Tool result message
|
||||||
content: toolResultText,
|
content: toolResultText,
|
||||||
modified: toolResultTime,
|
modified: toolResultTime,
|
||||||
|
|
@ -331,7 +331,7 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
artificialOrder: Date.now() + 10, // Additional ordering hint
|
artificialOrder: Date.now() + 10, // Additional ordering hint
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
handlerContext.agent.messages.push(toolResultMessage);
|
agentFrameworkContext.agent.messages.push(toolResultMessage);
|
||||||
|
|
||||||
// Persist tool result immediately so DB ordering matches in-memory order
|
// Persist tool result immediately so DB ordering matches in-memory order
|
||||||
try {
|
try {
|
||||||
|
|
@ -347,7 +347,7 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
|
|
||||||
// Signal that tool was executed AFTER adding and persisting the message
|
// Signal that tool was executed AFTER adding and persisting the message
|
||||||
await hooks.toolExecuted.promise({
|
await hooks.toolExecuted.promise({
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
toolResult: {
|
toolResult: {
|
||||||
success: true,
|
success: true,
|
||||||
data: result,
|
data: result,
|
||||||
|
|
@ -370,7 +370,7 @@ export const wikiOperationPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Wiki operation tool execution failed', {
|
logger.error('Wiki operation tool execution failed', {
|
||||||
error,
|
error,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
toolParameters: toolMatch.parameters,
|
toolParameters: toolMatch.parameters,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -389,7 +389,7 @@ Error: ${error instanceof Error ? error.message : String(error)}
|
||||||
const errorResultTime = new Date();
|
const errorResultTime = new Date();
|
||||||
const errorResultMessage: AgentInstanceMessage = {
|
const errorResultMessage: AgentInstanceMessage = {
|
||||||
id: `tool-error-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
id: `tool-error-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
role: 'tool', // Tool error message
|
role: 'tool', // Tool error message
|
||||||
content: errorMessage,
|
content: errorMessage,
|
||||||
modified: errorResultTime,
|
modified: errorResultTime,
|
||||||
|
|
@ -402,11 +402,11 @@ Error: ${error instanceof Error ? error.message : String(error)}
|
||||||
isComplete: true, // Mark as complete to prevent messageManagementPlugin from overwriting content
|
isComplete: true, // Mark as complete to prevent messageManagementPlugin from overwriting content
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
handlerContext.agent.messages.push(errorResultMessage);
|
agentFrameworkContext.agent.messages.push(errorResultMessage);
|
||||||
|
|
||||||
// Signal that tool was executed (with error) AFTER adding the message
|
// Signal that tool was executed (with error) AFTER adding the message
|
||||||
await hooks.toolExecuted.promise({
|
await hooks.toolExecuted.promise({
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
toolResult: {
|
toolResult: {
|
||||||
success: false,
|
success: false,
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
|
@ -19,7 +19,7 @@ import { findPromptById } from '../promptConcat/promptConcat';
|
||||||
import type { AiAPIConfig } from '../promptConcat/promptConcatSchema';
|
import type { AiAPIConfig } from '../promptConcat/promptConcatSchema';
|
||||||
import type { IPrompt } from '../promptConcat/promptConcatSchema';
|
import type { IPrompt } from '../promptConcat/promptConcatSchema';
|
||||||
import { schemaToToolContent } from '../utilities/schemaToToolContent';
|
import { schemaToToolContent } from '../utilities/schemaToToolContent';
|
||||||
import type { PromptConcatPlugin } from './types';
|
import type { PromptConcatTool } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wiki Search Parameter Schema
|
* Wiki Search Parameter Schema
|
||||||
|
|
@ -488,17 +488,17 @@ async function executeWikiUpdateEmbeddingsTool(
|
||||||
* Wiki Search plugin - Prompt processing
|
* Wiki Search plugin - Prompt processing
|
||||||
* Handles tool list injection for wiki search and update embeddings functionality
|
* Handles tool list injection for wiki search and update embeddings functionality
|
||||||
*/
|
*/
|
||||||
export const wikiSearchPlugin: PromptConcatPlugin = (hooks) => {
|
export const wikiSearchTool: PromptConcatTool = (hooks) => {
|
||||||
// First tapAsync: Tool list injection
|
// First tapAsync: Tool list injection
|
||||||
hooks.processPrompts.tapAsync('wikiSearchPlugin-toolList', async (context, callback) => {
|
hooks.processPrompts.tapAsync('wikiSearchTool-toolList', async (context, callback) => {
|
||||||
const { pluginConfig, prompts } = context;
|
const { toolConfig, prompts } = context;
|
||||||
|
|
||||||
if (pluginConfig.pluginId !== 'wikiSearch' || !pluginConfig.wikiSearchParam) {
|
if (toolConfig.toolId !== 'wikiSearch' || !toolConfig.wikiSearchParam) {
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wikiSearchParameter = pluginConfig.wikiSearchParam;
|
const wikiSearchParameter = toolConfig.wikiSearchParam;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Handle tool list injection if toolListPosition is configured
|
// Handle tool list injection if toolListPosition is configured
|
||||||
|
|
@ -508,7 +508,7 @@ export const wikiSearchPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
if (!toolListTarget) {
|
if (!toolListTarget) {
|
||||||
logger.warn('Tool list target prompt not found', {
|
logger.warn('Tool list target prompt not found', {
|
||||||
targetId: toolListPosition.targetId,
|
targetId: toolListPosition.targetId,
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
|
|
@ -544,7 +544,7 @@ export const wikiSearchPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
targetId: toolListPosition.targetId,
|
targetId: toolListPosition.targetId,
|
||||||
position: toolListPosition.position,
|
position: toolListPosition.position,
|
||||||
toolCount: 2, // wiki-search and wiki-update-embeddings
|
toolCount: 2, // wiki-search and wiki-update-embeddings
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -552,20 +552,20 @@ export const wikiSearchPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error in wiki search tool list injection', {
|
logger.error('Error in wiki search tool list injection', {
|
||||||
error,
|
error,
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Tool execution when AI response is complete
|
// 2. Tool execution when AI response is complete
|
||||||
hooks.responseComplete.tapAsync('wikiSearchPlugin-handler', async (context, callback) => {
|
hooks.responseComplete.tapAsync('wikiSearchTool-handler', async (context, callback) => {
|
||||||
try {
|
try {
|
||||||
const { handlerContext, response, handlerConfig } = context;
|
const { agentFrameworkContext, response, agentFrameworkConfig } = context;
|
||||||
|
|
||||||
// Find this plugin's configuration from handlerConfig
|
// Find this plugin's configuration import { AgentFrameworkConfig }
|
||||||
const wikiSearchPluginConfig = handlerConfig?.plugins?.find(p => p.pluginId === 'wikiSearch');
|
const wikiSearchToolConfig = agentFrameworkConfig?.plugins?.find((p: { toolId: string; [key: string]: unknown }) => p.toolId === 'wikiSearch');
|
||||||
const wikiSearchParameter = wikiSearchPluginConfig?.wikiSearchParam as { toolResultDuration?: number } | undefined;
|
const wikiSearchParameter = wikiSearchToolConfig?.wikiSearchParam as { toolResultDuration?: number } | undefined;
|
||||||
const toolResultDuration = wikiSearchParameter?.toolResultDuration || 1; // Default to 1 round
|
const toolResultDuration = wikiSearchParameter?.toolResultDuration || 1; // Default to 1 round
|
||||||
|
|
||||||
if (response.status !== 'done' || !response.content) {
|
if (response.status !== 'done' || !response.content) {
|
||||||
|
|
@ -583,12 +583,12 @@ export const wikiSearchPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
|
|
||||||
logger.debug('Wiki tool call detected', {
|
logger.debug('Wiki tool call detected', {
|
||||||
toolId: toolMatch.toolId,
|
toolId: toolMatch.toolId,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set duration=1 for the AI message containing the tool call
|
// Set duration=1 for the AI message containing the tool call
|
||||||
// Find the most recent AI message (should be the one containing the tool call)
|
// Find the most recent AI message (should be the one containing the tool call)
|
||||||
const aiMessages = handlerContext.agent.messages.filter(message => message.role === 'assistant');
|
const aiMessages = agentFrameworkContext.agent.messages.filter((message: AgentInstanceMessage) => message.role === 'assistant');
|
||||||
if (aiMessages.length > 0) {
|
if (aiMessages.length > 0) {
|
||||||
const latestAiMessage = aiMessages[aiMessages.length - 1];
|
const latestAiMessage = aiMessages[aiMessages.length - 1];
|
||||||
if (latestAiMessage.content === response.content) {
|
if (latestAiMessage.content === response.content) {
|
||||||
|
|
@ -614,7 +614,7 @@ export const wikiSearchPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also update UI immediately
|
// Also update UI immediately
|
||||||
agentInstanceService.debounceUpdateMessage(latestAiMessage, handlerContext.agent.id, 0); // No delay
|
agentInstanceService.debounceUpdateMessage(latestAiMessage, agentFrameworkContext.agent.id, 0); // No delay
|
||||||
|
|
||||||
logger.debug('Set duration=1 for AI tool call message', {
|
logger.debug('Set duration=1 for AI tool call message', {
|
||||||
messageId: latestAiMessage.id,
|
messageId: latestAiMessage.id,
|
||||||
|
|
@ -626,10 +626,10 @@ export const wikiSearchPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
// Execute the appropriate tool
|
// Execute the appropriate tool
|
||||||
try {
|
try {
|
||||||
// Check if cancelled before starting tool execution
|
// Check if cancelled before starting tool execution
|
||||||
if (handlerContext.isCancelled()) {
|
if (agentFrameworkContext.isCancelled()) {
|
||||||
logger.debug('Wiki tool cancelled before execution', {
|
logger.debug('Wiki tool cancelled before execution', {
|
||||||
toolId: toolMatch.toolId,
|
toolId: toolMatch.toolId,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
|
|
@ -644,9 +644,9 @@ export const wikiSearchPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
result = await executeWikiSearchTool(
|
result = await executeWikiSearchTool(
|
||||||
validatedParameters,
|
validatedParameters,
|
||||||
{
|
{
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
messageId: handlerContext.agent.messages[handlerContext.agent.messages.length - 1]?.id,
|
messageId: agentFrameworkContext.agent.messages[agentFrameworkContext.agent.messages.length - 1]?.id,
|
||||||
config: handlerContext.agent.aiApiConfig as AiAPIConfig | undefined,
|
config: agentFrameworkContext.agent.aiApiConfig as AiAPIConfig | undefined,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -655,18 +655,18 @@ export const wikiSearchPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
result = await executeWikiUpdateEmbeddingsTool(
|
result = await executeWikiUpdateEmbeddingsTool(
|
||||||
validatedParameters,
|
validatedParameters,
|
||||||
{
|
{
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
messageId: handlerContext.agent.messages[handlerContext.agent.messages.length - 1]?.id,
|
messageId: agentFrameworkContext.agent.messages[agentFrameworkContext.agent.messages.length - 1]?.id,
|
||||||
aiConfig: handlerContext.agent.aiApiConfig,
|
aiConfig: agentFrameworkContext.agent.aiApiConfig,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if cancelled after tool execution
|
// Check if cancelled after tool execution
|
||||||
if (handlerContext.isCancelled()) {
|
if (agentFrameworkContext.isCancelled()) {
|
||||||
logger.debug('Wiki tool cancelled after execution', {
|
logger.debug('Wiki tool cancelled after execution', {
|
||||||
toolId: toolMatch.toolId,
|
toolId: toolMatch.toolId,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
|
|
@ -692,8 +692,8 @@ export const wikiSearchPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
|
|
||||||
logger.debug('Wiki search setting yieldNextRoundTo=self', {
|
logger.debug('Wiki search setting yieldNextRoundTo=self', {
|
||||||
toolId: 'wiki-search',
|
toolId: 'wiki-search',
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
messageCount: handlerContext.agent.messages.length,
|
messageCount: agentFrameworkContext.agent.messages.length,
|
||||||
toolResultPreview: toolResultText.slice(0, 200),
|
toolResultPreview: toolResultText.slice(0, 200),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -701,7 +701,7 @@ export const wikiSearchPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
const nowTool = new Date();
|
const nowTool = new Date();
|
||||||
const toolResultMessage: AgentInstanceMessage = {
|
const toolResultMessage: AgentInstanceMessage = {
|
||||||
id: `tool-result-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
id: `tool-result-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
role: 'tool', // Tool result message
|
role: 'tool', // Tool result message
|
||||||
content: toolResultText,
|
content: toolResultText,
|
||||||
created: nowTool,
|
created: nowTool,
|
||||||
|
|
@ -717,13 +717,13 @@ export const wikiSearchPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
artificialOrder: Date.now() + 10, // Additional ordering hint
|
artificialOrder: Date.now() + 10, // Additional ordering hint
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
handlerContext.agent.messages.push(toolResultMessage);
|
agentFrameworkContext.agent.messages.push(toolResultMessage);
|
||||||
|
|
||||||
// Do not persist immediately here. Let messageManagementPlugin handle persistence
|
// Do not persist immediately here. Let messageManagementPlugin handle persistence
|
||||||
|
|
||||||
// Signal that tool was executed AFTER adding and persisting the message
|
// Signal that tool was executed AFTER adding and persisting the message
|
||||||
await hooks.toolExecuted.promise({
|
await hooks.toolExecuted.promise({
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
toolResult: {
|
toolResult: {
|
||||||
success: true,
|
success: true,
|
||||||
data: result.success ? result.data : result.error,
|
data: result.success ? result.data : result.error,
|
||||||
|
|
@ -765,7 +765,7 @@ Error: ${error instanceof Error ? error.message : String(error)}
|
||||||
const nowError = new Date();
|
const nowError = new Date();
|
||||||
const errorResultMessage: AgentInstanceMessage = {
|
const errorResultMessage: AgentInstanceMessage = {
|
||||||
id: `tool-error-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
id: `tool-error-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
||||||
agentId: handlerContext.agent.id,
|
agentId: agentFrameworkContext.agent.id,
|
||||||
role: 'tool', // Tool error message
|
role: 'tool', // Tool error message
|
||||||
content: errorMessage,
|
content: errorMessage,
|
||||||
created: nowError,
|
created: nowError,
|
||||||
|
|
@ -779,11 +779,11 @@ Error: ${error instanceof Error ? error.message : String(error)}
|
||||||
isComplete: true, // Mark as complete to prevent messageManagementPlugin from overwriting content
|
isComplete: true, // Mark as complete to prevent messageManagementPlugin from overwriting content
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
handlerContext.agent.messages.push(errorResultMessage);
|
agentFrameworkContext.agent.messages.push(errorResultMessage);
|
||||||
|
|
||||||
// Do not persist immediately; let messageManagementPlugin handle it during toolExecuted
|
// Do not persist immediately; let messageManagementPlugin handle it during toolExecuted
|
||||||
await hooks.toolExecuted.promise({
|
await hooks.toolExecuted.promise({
|
||||||
handlerContext,
|
agentFrameworkContext,
|
||||||
toolResult: {
|
toolResult: {
|
||||||
success: false,
|
success: false,
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
|
@ -45,23 +45,23 @@ import type { IWorkspaceService } from '@services/workspaces/interface';
|
||||||
import { isWikiWorkspace } from '@services/workspaces/interface';
|
import { isWikiWorkspace } from '@services/workspaces/interface';
|
||||||
|
|
||||||
import { findPromptById } from '../promptConcat/promptConcat';
|
import { findPromptById } from '../promptConcat/promptConcat';
|
||||||
import type { PromptConcatPlugin } from './types';
|
import type { PromptConcatTool } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Workspaces List plugin - Prompt processing
|
* Workspaces List plugin - Prompt processing
|
||||||
* Handles injection of available wiki workspaces list
|
* Handles injection of available wiki workspaces list
|
||||||
*/
|
*/
|
||||||
export const workspacesListPlugin: PromptConcatPlugin = (hooks) => {
|
export const workspacesListTool: PromptConcatTool = (hooks) => {
|
||||||
// Tool list injection
|
// Tool list injection
|
||||||
hooks.processPrompts.tapAsync('workspacesListPlugin-injection', async (context, callback) => {
|
hooks.processPrompts.tapAsync('workspacesListTool-injection', async (context, callback) => {
|
||||||
const { pluginConfig, prompts } = context;
|
const { toolConfig, prompts } = context;
|
||||||
|
|
||||||
if (pluginConfig.pluginId !== 'workspacesList' || !pluginConfig.workspacesListParam) {
|
if (toolConfig.toolId !== 'workspacesList' || !toolConfig.workspacesListParam) {
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const workspacesListParameter = pluginConfig.workspacesListParam;
|
const workspacesListParameter = toolConfig.workspacesListParam;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Handle workspaces list injection if targetId is configured
|
// Handle workspaces list injection if targetId is configured
|
||||||
|
|
@ -70,7 +70,7 @@ export const workspacesListPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
if (!target) {
|
if (!target) {
|
||||||
logger.warn('Workspaces list target prompt not found', {
|
logger.warn('Workspaces list target prompt not found', {
|
||||||
targetId: workspacesListParameter.targetId,
|
targetId: workspacesListParameter.targetId,
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
|
|
@ -96,7 +96,7 @@ export const workspacesListPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
}
|
}
|
||||||
const insertIndex = target.prompt.children.length;
|
const insertIndex = target.prompt.children.length;
|
||||||
target.prompt.children.splice(insertIndex, 0, {
|
target.prompt.children.splice(insertIndex, 0, {
|
||||||
id: `workspaces-list-${pluginConfig.id}`,
|
id: `workspaces-list-${toolConfig.id}`,
|
||||||
caption: 'Available Workspaces',
|
caption: 'Available Workspaces',
|
||||||
text: workspacesListContent,
|
text: workspacesListContent,
|
||||||
});
|
});
|
||||||
|
|
@ -105,7 +105,7 @@ export const workspacesListPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
target.prompt.children = [];
|
target.prompt.children = [];
|
||||||
}
|
}
|
||||||
target.prompt.children.unshift({
|
target.prompt.children.unshift({
|
||||||
id: `workspaces-list-${pluginConfig.id}`,
|
id: `workspaces-list-${toolConfig.id}`,
|
||||||
caption: 'Available Workspaces',
|
caption: 'Available Workspaces',
|
||||||
text: workspacesListContent,
|
text: workspacesListContent,
|
||||||
});
|
});
|
||||||
|
|
@ -117,12 +117,12 @@ export const workspacesListPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
logger.debug('Workspaces list injected successfully', {
|
logger.debug('Workspaces list injected successfully', {
|
||||||
targetId: workspacesListParameter.targetId,
|
targetId: workspacesListParameter.targetId,
|
||||||
position: workspacesListParameter.position,
|
position: workspacesListParameter.position,
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
workspaceCount: wikiWorkspaces.length,
|
workspaceCount: wikiWorkspaces.length,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
logger.debug('No wiki workspaces found to inject', {
|
logger.debug('No wiki workspaces found to inject', {
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,7 +131,7 @@ export const workspacesListPlugin: PromptConcatPlugin = (hooks) => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error in workspaces list injection', {
|
logger.error('Error in workspaces list injection', {
|
||||||
error,
|
error,
|
||||||
pluginId: pluginConfig.id,
|
toolId: toolConfig.id,
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
|
@ -14,8 +14,8 @@ export function createAgentInstanceData(agentDefinition: {
|
||||||
name: string;
|
name: string;
|
||||||
avatarUrl?: string;
|
avatarUrl?: string;
|
||||||
aiApiConfig?: Record<string, unknown>;
|
aiApiConfig?: Record<string, unknown>;
|
||||||
handlerConfig?: Record<string, unknown>;
|
agentFrameworkConfig?: Record<string, unknown>;
|
||||||
handlerID?: string;
|
agentFrameworkID?: string;
|
||||||
}): {
|
}): {
|
||||||
instanceData: Omit<AgentInstance, 'created' | 'modified'>;
|
instanceData: Omit<AgentInstance, 'created' | 'modified'>;
|
||||||
instanceId: string;
|
instanceId: string;
|
||||||
|
|
@ -31,7 +31,7 @@ export function createAgentInstanceData(agentDefinition: {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Extract necessary fields from agent definition
|
// Extract necessary fields from agent definition
|
||||||
const { avatarUrl, aiApiConfig, handlerID } = agentDefinition;
|
const { avatarUrl, aiApiConfig, agentFrameworkID } = agentDefinition;
|
||||||
|
|
||||||
const instanceData = {
|
const instanceData = {
|
||||||
id: instanceId,
|
id: instanceId,
|
||||||
|
|
@ -40,9 +40,9 @@ export function createAgentInstanceData(agentDefinition: {
|
||||||
status: initialStatus,
|
status: initialStatus,
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
aiApiConfig,
|
aiApiConfig,
|
||||||
// Don't copy handlerConfig to instance - it should fallback to definition
|
// Don't copy agentFrameworkConfig to instance - it should fallback to definition
|
||||||
handlerConfig: undefined,
|
agentFrameworkConfig: undefined,
|
||||||
handlerID,
|
agentFrameworkID,
|
||||||
messages: [],
|
messages: [],
|
||||||
closed: false,
|
closed: false,
|
||||||
};
|
};
|
||||||
|
|
@ -119,6 +119,6 @@ export const AGENT_INSTANCE_FIELDS = [
|
||||||
'modified',
|
'modified',
|
||||||
'avatarUrl',
|
'avatarUrl',
|
||||||
'aiApiConfig',
|
'aiApiConfig',
|
||||||
'handlerConfig',
|
'agentFrameworkConfig',
|
||||||
'closed',
|
'closed',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,11 @@ export class AgentDefinitionEntity implements Partial<AgentDefinition> {
|
||||||
|
|
||||||
/** Agent handler function ID, nullable indicates using default handler */
|
/** Agent handler function ID, nullable indicates using default handler */
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
handlerID?: string;
|
agentFrameworkID?: string;
|
||||||
|
|
||||||
/** Agent handler configuration parameters, stored as JSON */
|
/** Agent handler configuration parameters, stored as JSON */
|
||||||
@Column({ type: 'simple-json', nullable: true })
|
@Column({ type: 'simple-json', nullable: true })
|
||||||
handlerConfig?: Record<string, unknown>;
|
agentFrameworkConfig?: Record<string, unknown>;
|
||||||
|
|
||||||
/** Agent's AI API configuration, can override global default config */
|
/** Agent's AI API configuration, can override global default config */
|
||||||
@Column({ type: 'simple-json', nullable: true })
|
@Column({ type: 'simple-json', nullable: true })
|
||||||
|
|
@ -88,7 +88,7 @@ export class AgentInstanceEntity implements Partial<AgentInstance> {
|
||||||
|
|
||||||
/** Agent handler configuration parameters, inherited from AgentDefinition */
|
/** Agent handler configuration parameters, inherited from AgentDefinition */
|
||||||
@Column({ type: 'simple-json', nullable: true })
|
@Column({ type: 'simple-json', nullable: true })
|
||||||
handlerConfig?: Record<string, unknown>;
|
agentFrameworkConfig?: Record<string, unknown>;
|
||||||
|
|
||||||
@Column({ default: false })
|
@Column({ default: false })
|
||||||
closed: boolean = false;
|
closed: boolean = false;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { AgentDefinition } from '@services/agentDefinition/interface';
|
import type { AgentDefinition } from '@services/agentDefinition/interface';
|
||||||
import defaultAgents from '@services/agentInstance/buildInAgentHandlers/defaultAgents.json';
|
import defaultAgents from '@services/agentInstance/agentFrameworks/taskAgents.json';
|
||||||
import { container } from '@services/container';
|
import { container } from '@services/container';
|
||||||
import type { IDatabaseService } from '@services/database/interface';
|
import type { IDatabaseService } from '@services/database/interface';
|
||||||
import { AgentDefinitionEntity } from '@services/database/schema/agent';
|
import { AgentDefinitionEntity } from '@services/database/schema/agent';
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ export async function registerMenu(): Promise<void> {
|
||||||
dir: activeWorkspace.wikiFolderLocation,
|
dir: activeWorkspace.wikiFolderLocation,
|
||||||
commitOnly: false,
|
commitOnly: false,
|
||||||
commitMessage: i18n.t('LOG.CommitBackupMessage'),
|
commitMessage: i18n.t('LOG.CommitBackupMessage'),
|
||||||
remoteUrl: activeWorkspace.gitUrl,
|
remoteUrl: activeWorkspace.gitUrl ?? undefined,
|
||||||
userInfo,
|
userInfo,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +127,7 @@ export async function registerMenu(): Promise<void> {
|
||||||
dir: activeWorkspace.wikiFolderLocation,
|
dir: activeWorkspace.wikiFolderLocation,
|
||||||
commitOnly: false,
|
commitOnly: false,
|
||||||
// Don't provide commitMessage to trigger AI generation
|
// Don't provide commitMessage to trigger AI generation
|
||||||
remoteUrl: activeWorkspace.gitUrl,
|
remoteUrl: activeWorkspace.gitUrl ?? undefined,
|
||||||
userInfo,
|
userInfo,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -151,7 +151,7 @@ export async function registerMenu(): Promise<void> {
|
||||||
dir: activeWorkspace.wikiFolderLocation,
|
dir: activeWorkspace.wikiFolderLocation,
|
||||||
commitOnly: false,
|
commitOnly: false,
|
||||||
commitMessage: i18n.t('LOG.CommitBackupMessage'),
|
commitMessage: i18n.t('LOG.CommitBackupMessage'),
|
||||||
remoteUrl: activeWorkspace.gitUrl,
|
remoteUrl: activeWorkspace.gitUrl ?? undefined,
|
||||||
userInfo,
|
userInfo,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
import { AgentFrameworkConfig } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
interface useAgentFrameworkConfigManagementProps {
|
||||||
|
agentDefId?: string;
|
||||||
|
agentId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UseAgentFrameworkConfigManagementResult {
|
||||||
|
loading: boolean;
|
||||||
|
config: AgentFrameworkConfig | undefined;
|
||||||
|
schema?: Record<string, unknown>;
|
||||||
|
handleConfigChange: (newConfig: AgentFrameworkConfig) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAgentFrameworkConfigManagement = ({ agentDefId, agentId }: useAgentFrameworkConfigManagementProps = {}): UseAgentFrameworkConfigManagementResult => {
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [config, setConfig] = useState<AgentFrameworkConfig | undefined>(undefined);
|
||||||
|
const [schema, setSchema] = useState<Record<string, unknown> | undefined>(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchConfig = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
let finalConfig: AgentFrameworkConfig | undefined;
|
||||||
|
let agentFrameworkID: string | undefined;
|
||||||
|
|
||||||
|
if (agentId) {
|
||||||
|
const agentInstance = await window.service.agentInstance.getAgent(agentId);
|
||||||
|
let agentDefinition: Awaited<ReturnType<typeof window.service.agentDefinition.getAgentDef>> | undefined;
|
||||||
|
if (agentInstance?.agentDefId) {
|
||||||
|
agentDefinition = await window.service.agentDefinition.getAgentDef(agentInstance.agentDefId);
|
||||||
|
}
|
||||||
|
// Use instance config if available, otherwise fallback to definition config
|
||||||
|
if (agentInstance?.agentFrameworkConfig && Object.keys(agentInstance.agentFrameworkConfig).length > 0) {
|
||||||
|
finalConfig = agentInstance.agentFrameworkConfig as AgentFrameworkConfig;
|
||||||
|
} else if (agentDefinition?.agentFrameworkConfig) {
|
||||||
|
finalConfig = agentDefinition.agentFrameworkConfig as AgentFrameworkConfig;
|
||||||
|
}
|
||||||
|
// Use agentFrameworkID from instance, fallback to definition
|
||||||
|
agentFrameworkID = agentInstance?.agentFrameworkID || agentDefinition?.agentFrameworkID;
|
||||||
|
} else if (agentDefId) {
|
||||||
|
const agentDefinition = await window.service.agentDefinition.getAgentDef(agentDefId);
|
||||||
|
if (agentDefinition?.agentFrameworkConfig) {
|
||||||
|
finalConfig = agentDefinition.agentFrameworkConfig as AgentFrameworkConfig;
|
||||||
|
}
|
||||||
|
agentFrameworkID = agentDefinition?.agentFrameworkID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (agentFrameworkID) {
|
||||||
|
try {
|
||||||
|
const frameworkSchema = await window.service.agentInstance.getFrameworkConfigSchema(agentFrameworkID);
|
||||||
|
setSchema(frameworkSchema);
|
||||||
|
} catch (error) {
|
||||||
|
void window.service.native.log('error', 'Failed to load framework schema', { function: 'useAgentFrameworkConfigManagement.fetchConfig', error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setConfig(finalConfig);
|
||||||
|
setLoading(false);
|
||||||
|
} catch (error) {
|
||||||
|
void window.service.native.log('error', 'Failed to load framework configuration', { function: 'useAgentFrameworkConfigManagement.fetchConfig', error });
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void fetchConfig();
|
||||||
|
}, [agentDefId, agentId]);
|
||||||
|
|
||||||
|
const handleConfigChange = useCallback(async (newConfig: AgentFrameworkConfig) => {
|
||||||
|
try {
|
||||||
|
setConfig(newConfig);
|
||||||
|
|
||||||
|
if (agentId) {
|
||||||
|
await window.service.agentInstance.updateAgent(agentId, {
|
||||||
|
agentFrameworkConfig: newConfig,
|
||||||
|
});
|
||||||
|
} else if (agentDefId) {
|
||||||
|
const agentDefinition = await window.service.agentDefinition.getAgentDef(agentDefId);
|
||||||
|
if (agentDefinition) {
|
||||||
|
await window.service.agentDefinition.updateAgentDef({
|
||||||
|
...agentDefinition,
|
||||||
|
agentFrameworkConfig: newConfig,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
void window.service.native.log('error', 'No agent ID or definition ID provided for updating handler config', {
|
||||||
|
function: 'useAgentFrameworkConfigManagement.handleConfigChange',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
void window.service.native.log('error', 'Failed to update framework configuration', { function: 'useAgentFrameworkConfigManagement.handleConfigChange', error });
|
||||||
|
}
|
||||||
|
}, [agentId, agentDefId]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
config,
|
||||||
|
schema,
|
||||||
|
handleConfigChange,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
import { HandlerConfig } from '@services/agentInstance/promptConcat/promptConcatSchema';
|
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
interface UseHandlerConfigManagementProps {
|
|
||||||
agentDefId?: string;
|
|
||||||
agentId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UseHandlerConfigManagementResult {
|
|
||||||
loading: boolean;
|
|
||||||
config: HandlerConfig | undefined;
|
|
||||||
schema?: Record<string, unknown>;
|
|
||||||
handleConfigChange: (newConfig: HandlerConfig) => Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useHandlerConfigManagement = ({ agentDefId, agentId }: UseHandlerConfigManagementProps = {}): UseHandlerConfigManagementResult => {
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [config, setConfig] = useState<HandlerConfig | undefined>(undefined);
|
|
||||||
const [schema, setSchema] = useState<Record<string, unknown> | undefined>(undefined);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchConfig = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
let finalConfig: HandlerConfig | undefined;
|
|
||||||
let handlerID: string | undefined;
|
|
||||||
|
|
||||||
if (agentId) {
|
|
||||||
const agentInstance = await window.service.agentInstance.getAgent(agentId);
|
|
||||||
let agentDefinition: Awaited<ReturnType<typeof window.service.agentDefinition.getAgentDef>> | undefined;
|
|
||||||
if (agentInstance?.agentDefId) {
|
|
||||||
agentDefinition = await window.service.agentDefinition.getAgentDef(agentInstance.agentDefId);
|
|
||||||
}
|
|
||||||
// Use instance config if available, otherwise fallback to definition config
|
|
||||||
if (agentInstance?.handlerConfig && Object.keys(agentInstance.handlerConfig).length > 0) {
|
|
||||||
finalConfig = agentInstance.handlerConfig as HandlerConfig;
|
|
||||||
} else if (agentDefinition?.handlerConfig) {
|
|
||||||
finalConfig = agentDefinition.handlerConfig as HandlerConfig;
|
|
||||||
}
|
|
||||||
// Use handlerID from instance, fallback to definition
|
|
||||||
handlerID = agentInstance?.handlerID || agentDefinition?.handlerID;
|
|
||||||
} else if (agentDefId) {
|
|
||||||
const agentDefinition = await window.service.agentDefinition.getAgentDef(agentDefId);
|
|
||||||
if (agentDefinition?.handlerConfig) {
|
|
||||||
finalConfig = agentDefinition.handlerConfig as HandlerConfig;
|
|
||||||
}
|
|
||||||
handlerID = agentDefinition?.handlerID;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handlerID) {
|
|
||||||
try {
|
|
||||||
const handlerSchema = await window.service.agentInstance.getHandlerConfigSchema(handlerID);
|
|
||||||
setSchema(handlerSchema);
|
|
||||||
} catch (error) {
|
|
||||||
void window.service.native.log('error', 'Failed to load handler schema', { function: 'useHandlerConfigManagement.fetchConfig', error });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setConfig(finalConfig);
|
|
||||||
setLoading(false);
|
|
||||||
} catch (error) {
|
|
||||||
void window.service.native.log('error', 'Failed to load handler configuration', { function: 'useHandlerConfigManagement.fetchConfig', error });
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void fetchConfig();
|
|
||||||
}, [agentDefId, agentId]);
|
|
||||||
|
|
||||||
const handleConfigChange = useCallback(async (newConfig: HandlerConfig) => {
|
|
||||||
try {
|
|
||||||
setConfig(newConfig);
|
|
||||||
|
|
||||||
if (agentId) {
|
|
||||||
await window.service.agentInstance.updateAgent(agentId, {
|
|
||||||
handlerConfig: newConfig,
|
|
||||||
});
|
|
||||||
} else if (agentDefId) {
|
|
||||||
const agentDefinition = await window.service.agentDefinition.getAgentDef(agentDefId);
|
|
||||||
if (agentDefinition) {
|
|
||||||
await window.service.agentDefinition.updateAgentDef({
|
|
||||||
...agentDefinition,
|
|
||||||
handlerConfig: newConfig,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
void window.service.native.log('error', 'No agent ID or definition ID provided for updating handler config', { function: 'useHandlerConfigManagement.handleConfigChange' });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
void window.service.native.log('error', 'Failed to update handler configuration', { function: 'useHandlerConfigManagement.handleConfigChange', error });
|
|
||||||
}
|
|
||||||
}, [agentId, agentDefId]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
loading,
|
|
||||||
config,
|
|
||||||
schema,
|
|
||||||
handleConfigChange,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue