AI Agent handoffs (#13472)
This commit is contained in:
@@ -24,6 +24,7 @@ import { PostgresCredentials } from 'src/engine/core-modules/postgres-credential
|
||||
import { WorkspaceSSOIdentityProvider } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
|
||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { Webhook } from 'src/engine/core-modules/webhook/webhook.entity';
|
||||
import { AgentHandoffEntity } from 'src/engine/metadata-modules/agent/agent-handoff.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/agent/agent.entity';
|
||||
import { AgentDTO } from 'src/engine/metadata-modules/agent/dtos/agent.dto';
|
||||
import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
|
||||
@@ -130,6 +131,11 @@ export class Workspace {
|
||||
})
|
||||
agents: Relation<AgentEntity[]>;
|
||||
|
||||
@OneToMany(() => AgentHandoffEntity, (handoff) => handoff.workspace, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
agentHandoffs: Relation<AgentHandoffEntity[]>;
|
||||
|
||||
@OneToMany(() => Webhook, (webhook) => webhook.workspace)
|
||||
webhooks: Relation<Webhook[]>;
|
||||
|
||||
|
||||
+13
-39
@@ -3,8 +3,6 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Readable } from 'stream';
|
||||
|
||||
import { createAnthropic } from '@ai-sdk/anthropic';
|
||||
import { createOpenAI } from '@ai-sdk/openai';
|
||||
import {
|
||||
CoreMessage,
|
||||
CoreUserMessage,
|
||||
@@ -17,10 +15,7 @@ import {
|
||||
} from 'ai';
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
import {
|
||||
ModelId,
|
||||
ModelProvider,
|
||||
} from 'src/engine/core-modules/ai/constants/ai-models.const';
|
||||
import { ModelProvider } from 'src/engine/core-modules/ai/constants/ai-models.const';
|
||||
import { AiModelRegistryService } from 'src/engine/core-modules/ai/services/ai-model-registry.service';
|
||||
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
|
||||
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||
@@ -73,38 +68,6 @@ export class AgentExecutionService {
|
||||
private readonly fileRepository: Repository<FileEntity>,
|
||||
) {}
|
||||
|
||||
getModel = (modelId: ModelId, provider: ModelProvider) => {
|
||||
switch (provider) {
|
||||
case ModelProvider.OPENAI_COMPATIBLE: {
|
||||
const OpenAIProvider = createOpenAI({
|
||||
baseURL: this.twentyConfigService.get('OPENAI_COMPATIBLE_BASE_URL'),
|
||||
apiKey: this.twentyConfigService.get('OPENAI_COMPATIBLE_API_KEY'),
|
||||
});
|
||||
|
||||
return OpenAIProvider(modelId);
|
||||
}
|
||||
case ModelProvider.OPENAI: {
|
||||
const OpenAIProvider = createOpenAI({
|
||||
apiKey: this.twentyConfigService.get('OPENAI_API_KEY'),
|
||||
});
|
||||
|
||||
return OpenAIProvider(modelId);
|
||||
}
|
||||
case ModelProvider.ANTHROPIC: {
|
||||
const AnthropicProvider = createAnthropic({
|
||||
apiKey: this.twentyConfigService.get('ANTHROPIC_API_KEY'),
|
||||
});
|
||||
|
||||
return AnthropicProvider(modelId);
|
||||
}
|
||||
default:
|
||||
throw new AgentException(
|
||||
`Unsupported provider: ${provider}`,
|
||||
AgentExceptionCode.AGENT_EXECUTION_FAILED,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
private async validateApiKey(provider: ModelProvider): Promise<void> {
|
||||
let apiKey: string | undefined;
|
||||
|
||||
@@ -171,10 +134,21 @@ export class AgentExecutionService {
|
||||
|
||||
this.logger.log(`Generated ${Object.keys(tools).length} tools for agent`);
|
||||
|
||||
const registeredModel = this.aiModelRegistryService.getModel(
|
||||
aiModel.modelId,
|
||||
);
|
||||
|
||||
if (!registeredModel) {
|
||||
throw new AgentException(
|
||||
`Model ${aiModel.modelId} not found in registry`,
|
||||
AgentExceptionCode.AGENT_EXECUTION_FAILED,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
system,
|
||||
tools,
|
||||
model: this.getModel(aiModel.modelId, aiModel.provider),
|
||||
model: registeredModel.model,
|
||||
...(messages && { messages }),
|
||||
...(prompt && { prompt }),
|
||||
maxSteps: AGENT_CONFIG.MAX_STEPS,
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { generateText } from 'ai';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { AiModelRegistryService } from 'src/engine/core-modules/ai/services/ai-model-registry.service';
|
||||
|
||||
import { AgentHandoffService } from './agent-handoff.service';
|
||||
import { AgentEntity } from './agent.entity';
|
||||
import { AgentException, AgentExceptionCode } from './agent.exception';
|
||||
|
||||
export type HandoffRequest = {
|
||||
fromAgentId: string;
|
||||
toAgentId: string;
|
||||
workspaceId: string;
|
||||
reason: string;
|
||||
context?: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class AgentHandoffExecutorService {
|
||||
private readonly logger = new Logger(AgentHandoffExecutorService.name);
|
||||
|
||||
constructor(
|
||||
@InjectRepository(AgentEntity, 'core')
|
||||
private readonly agentRepository: Repository<AgentEntity>,
|
||||
private readonly agentHandoffService: AgentHandoffService,
|
||||
private readonly aiModelRegistryService: AiModelRegistryService,
|
||||
) {}
|
||||
|
||||
async executeHandoff(handoffRequest: HandoffRequest) {
|
||||
try {
|
||||
const { fromAgentId, toAgentId, workspaceId, reason } = handoffRequest;
|
||||
|
||||
const canHandoff = await this.agentHandoffService.canHandoffTo({
|
||||
fromAgentId,
|
||||
toAgentId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
if (!canHandoff) {
|
||||
throw new AgentException(
|
||||
`Agent ${fromAgentId} is not allowed to hand off to agent ${toAgentId}`,
|
||||
AgentExceptionCode.AGENT_EXECUTION_FAILED,
|
||||
);
|
||||
}
|
||||
|
||||
const targetAgent = await this.agentRepository.findOne({
|
||||
where: { id: toAgentId, workspaceId },
|
||||
});
|
||||
|
||||
if (!targetAgent) {
|
||||
throw new AgentException(
|
||||
`Target agent ${toAgentId} not found`,
|
||||
AgentExceptionCode.AGENT_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const registeredModel = this.aiModelRegistryService.getModel(
|
||||
targetAgent.modelId,
|
||||
);
|
||||
|
||||
if (!registeredModel) {
|
||||
throw new AgentException(
|
||||
`Model ${targetAgent.modelId} not found in registry`,
|
||||
AgentExceptionCode.AGENT_EXECUTION_FAILED,
|
||||
);
|
||||
}
|
||||
|
||||
const aiRequestConfig = {
|
||||
system: targetAgent.prompt,
|
||||
prompt: this.createHandoffPrompt(handoffRequest),
|
||||
model: registeredModel.model,
|
||||
};
|
||||
|
||||
const textResponse = await generateText(aiRequestConfig);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
newAgentId: toAgentId,
|
||||
newAgentName: targetAgent.name,
|
||||
message: `Successfully transferred to ${targetAgent.name}. ${reason}`,
|
||||
response: textResponse.text,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Handoff execution failed: ${error.message}`,
|
||||
error.stack,
|
||||
);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
newAgentId: handoffRequest.toAgentId,
|
||||
newAgentName: 'Unknown',
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private createHandoffPrompt(handoffRequest: HandoffRequest): string {
|
||||
const { reason, context } = handoffRequest;
|
||||
|
||||
const prompt = `
|
||||
You have received a handoff from another AI agent. This means the previous agent has determined that you are better suited to handle this conversation based on your specialized knowledge and capabilities.
|
||||
|
||||
The previous agent has transferred this conversation to you because: ${reason}
|
||||
|
||||
Additional context from the previous agent:
|
||||
${context || 'No additional context provided'}
|
||||
|
||||
Please continue the conversation naturally, acknowledging that you are taking over from the previous agent. Use your specialized knowledge to provide the best possible assistance to the user.
|
||||
`;
|
||||
|
||||
return prompt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
DeleteDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
|
||||
import { AgentEntity } from './agent.entity';
|
||||
|
||||
@Entity('agentHandoff')
|
||||
@Index('IDX_AGENT_HANDOFF_ID_DELETED_AT', ['id', 'deletedAt'])
|
||||
@Index(
|
||||
'IDX_AGENT_HANDOFF_FROM_TO_WORKSPACE_UNIQUE',
|
||||
['fromAgentId', 'toAgentId', 'workspaceId'],
|
||||
{
|
||||
unique: true,
|
||||
where: '"deletedAt" IS NULL',
|
||||
},
|
||||
)
|
||||
export class AgentHandoffEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
fromAgentId: string;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
toAgentId: string;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
workspaceId: string;
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
description: string;
|
||||
|
||||
@ManyToOne(() => AgentEntity, (agent) => agent.outgoingHandoffs, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'fromAgentId' })
|
||||
fromAgent: Relation<AgentEntity>;
|
||||
|
||||
@ManyToOne(() => AgentEntity, (agent) => agent.incomingHandoffs, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'toAgentId' })
|
||||
toAgent: Relation<AgentEntity>;
|
||||
|
||||
@ManyToOne(() => Workspace, (workspace) => workspace.agentHandoffs, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'workspaceId' })
|
||||
workspace: Relation<Workspace>;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ type: 'timestamptz' })
|
||||
updatedAt: Date;
|
||||
|
||||
@DeleteDateColumn({ type: 'timestamptz' })
|
||||
deletedAt?: Date;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { AgentHandoffEntity } from './agent-handoff.entity';
|
||||
import { AgentEntity } from './agent.entity';
|
||||
import { AgentException, AgentExceptionCode } from './agent.exception';
|
||||
|
||||
@Injectable()
|
||||
export class AgentHandoffService {
|
||||
constructor(
|
||||
@InjectRepository(AgentEntity, 'core')
|
||||
private readonly agentRepository: Repository<AgentEntity>,
|
||||
@InjectRepository(AgentHandoffEntity, 'core')
|
||||
private readonly agentHandoffRepository: Repository<AgentHandoffEntity>,
|
||||
) {}
|
||||
|
||||
async canHandoffTo({
|
||||
fromAgentId,
|
||||
toAgentId,
|
||||
workspaceId,
|
||||
}: {
|
||||
fromAgentId: string;
|
||||
toAgentId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<boolean> {
|
||||
const handoff = await this.agentHandoffRepository.findOne({
|
||||
where: {
|
||||
fromAgentId,
|
||||
toAgentId,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
return Boolean(handoff);
|
||||
}
|
||||
|
||||
async getHandoffTargets({
|
||||
fromAgentId,
|
||||
workspaceId,
|
||||
}: {
|
||||
fromAgentId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<AgentEntity[]> {
|
||||
const handoffs = await this.agentHandoffRepository.find({
|
||||
where: {
|
||||
fromAgentId,
|
||||
workspaceId,
|
||||
},
|
||||
relations: ['toAgent'],
|
||||
});
|
||||
|
||||
return handoffs.map((handoff) => handoff.toAgent);
|
||||
}
|
||||
|
||||
async createHandoff({
|
||||
fromAgentId,
|
||||
toAgentId,
|
||||
workspaceId,
|
||||
description,
|
||||
}: {
|
||||
fromAgentId: string;
|
||||
toAgentId: string;
|
||||
workspaceId: string;
|
||||
description?: string;
|
||||
}): Promise<AgentHandoffEntity> {
|
||||
const [fromAgent, toAgent] = await Promise.all([
|
||||
this.agentRepository.findOne({
|
||||
where: { id: fromAgentId, workspaceId },
|
||||
}),
|
||||
this.agentRepository.findOne({
|
||||
where: { id: toAgentId, workspaceId },
|
||||
}),
|
||||
]);
|
||||
|
||||
if (!fromAgent) {
|
||||
throw new AgentException(
|
||||
`Agent with id ${fromAgentId} not found in workspace`,
|
||||
AgentExceptionCode.AGENT_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (!toAgent) {
|
||||
throw new AgentException(
|
||||
`Agent with id ${toAgentId} not found in workspace`,
|
||||
AgentExceptionCode.AGENT_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const existingHandoff = await this.agentHandoffRepository.findOne({
|
||||
where: {
|
||||
fromAgentId,
|
||||
toAgentId,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingHandoff) {
|
||||
throw new AgentException(
|
||||
`Handoff from ${fromAgent.name} to ${toAgent.name} already exists`,
|
||||
AgentExceptionCode.HANDOFF_ALREADY_EXISTS,
|
||||
);
|
||||
}
|
||||
|
||||
const handoff = await this.agentHandoffRepository.save({
|
||||
fromAgentId,
|
||||
toAgentId,
|
||||
workspaceId,
|
||||
description,
|
||||
});
|
||||
|
||||
return handoff;
|
||||
}
|
||||
|
||||
async removeHandoff({
|
||||
fromAgentId,
|
||||
toAgentId,
|
||||
workspaceId,
|
||||
}: {
|
||||
fromAgentId: string;
|
||||
toAgentId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
await this.agentHandoffRepository.delete({
|
||||
fromAgentId,
|
||||
toAgentId,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
async getWorkspaceHandoffs(
|
||||
workspaceId: string,
|
||||
): Promise<AgentHandoffEntity[]> {
|
||||
return this.agentHandoffRepository.find({
|
||||
where: { workspaceId },
|
||||
relations: ['fromAgent', 'toAgent'],
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,22 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { ToolSet } from 'ai';
|
||||
import { Repository } from 'typeorm';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ToolAdapterService } from 'src/engine/core-modules/ai/services/tool-adapter.service';
|
||||
import { ToolService } from 'src/engine/core-modules/ai/services/tool.service';
|
||||
import { AgentHandoffExecutorService } from 'src/engine/metadata-modules/agent/agent-handoff-executor.service';
|
||||
import { AgentHandoffService } from 'src/engine/metadata-modules/agent/agent-handoff.service';
|
||||
import { AgentService } from 'src/engine/metadata-modules/agent/agent.service';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { camelCase } from 'src/utils/camel-case';
|
||||
|
||||
@Injectable()
|
||||
export class AgentToolService {
|
||||
constructor(
|
||||
private readonly agentService: AgentService,
|
||||
private readonly agentHandoffService: AgentHandoffService,
|
||||
private readonly agentHandoffExecutorService: AgentHandoffExecutorService,
|
||||
@InjectRepository(RoleEntity, 'core')
|
||||
private readonly roleRepository: Repository<RoleEntity>,
|
||||
private readonly toolService: ToolService,
|
||||
@@ -23,39 +29,88 @@ export class AgentToolService {
|
||||
agentId: string,
|
||||
workspaceId: string,
|
||||
): Promise<ToolSet> {
|
||||
try {
|
||||
const agent = await this.agentService.findOneAgent(agentId, workspaceId);
|
||||
const agent = await this.agentService.findOneAgent(agentId, workspaceId);
|
||||
|
||||
if (!agent.roleId) {
|
||||
const actionTools = await this.toolAdapterService.getTools();
|
||||
const handoffTools = await this.generateHandoffTools(agentId, workspaceId);
|
||||
|
||||
return actionTools;
|
||||
}
|
||||
if (!agent.roleId) {
|
||||
const actionTools = await this.toolAdapterService.getTools();
|
||||
|
||||
const role = await this.roleRepository.findOne({
|
||||
where: {
|
||||
id: agent.roleId,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
return { ...actionTools, ...handoffTools };
|
||||
}
|
||||
|
||||
if (!role) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const actionTools = await this.toolAdapterService.getTools(
|
||||
role.id,
|
||||
const role = await this.roleRepository.findOne({
|
||||
where: {
|
||||
id: agent.roleId,
|
||||
workspaceId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const databaseTools = await this.toolService.listTools(
|
||||
role.id,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return { ...databaseTools, ...actionTools };
|
||||
} catch (error) {
|
||||
if (!role) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const actionTools = await this.toolAdapterService.getTools(
|
||||
role.id,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const databaseTools = await this.toolService.listTools(
|
||||
role.id,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return { ...databaseTools, ...actionTools, ...handoffTools };
|
||||
}
|
||||
|
||||
private async generateHandoffTools(
|
||||
agentId: string,
|
||||
workspaceId: string,
|
||||
): Promise<ToolSet> {
|
||||
const handoffTargets = await this.agentHandoffService.getHandoffTargets({
|
||||
fromAgentId: agentId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const handoffTools = handoffTargets.reduce<ToolSet>(
|
||||
(tools, targetAgent) => {
|
||||
const toolName = `transfer_to_${camelCase(targetAgent.name)}`;
|
||||
|
||||
const handoffSchema = z.object({
|
||||
reason: z.string().describe('Reason for transferring to this agent'),
|
||||
context: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('Additional context to pass to the receiving agent'),
|
||||
});
|
||||
|
||||
tools[toolName] = {
|
||||
description: `Transfer this request to ${targetAgent.name} when you need their specialized expertise. Use this when the user's request is outside your capabilities or when ${targetAgent.name} would be better suited to handle the request.`,
|
||||
parameters: handoffSchema,
|
||||
execute: async ({ reason, context }) => {
|
||||
const result =
|
||||
await this.agentHandoffExecutorService.executeHandoff({
|
||||
fromAgentId: agentId,
|
||||
toAgentId: targetAgent.id,
|
||||
workspaceId,
|
||||
reason,
|
||||
context,
|
||||
});
|
||||
|
||||
return {
|
||||
success: result.success,
|
||||
message: result.message || `Transferred to ${targetAgent.name}`,
|
||||
newAgentId: result.newAgentId,
|
||||
newAgentName: result.newAgentName,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
return tools;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
return handoffTools;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { ModelId } from 'src/engine/core-modules/ai/constants/ai-models.const';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
|
||||
import { AgentChatThreadEntity } from './agent-chat-thread.entity';
|
||||
import { AgentHandoffEntity } from './agent-handoff.entity';
|
||||
|
||||
@Entity('agent')
|
||||
@Index('IDX_AGENT_ID_DELETED_AT', ['id', 'deletedAt'])
|
||||
@@ -62,6 +63,12 @@ export class AgentEntity {
|
||||
@OneToMany(() => AgentChatThreadEntity, (chatThread) => chatThread.agent)
|
||||
chatThreads: Relation<AgentChatThreadEntity[]>;
|
||||
|
||||
@OneToMany(() => AgentHandoffEntity, (handoff) => handoff.fromAgent)
|
||||
outgoingHandoffs: Relation<AgentHandoffEntity[]>;
|
||||
|
||||
@OneToMany(() => AgentHandoffEntity, (handoff) => handoff.toAgent)
|
||||
incomingHandoffs: Relation<AgentHandoffEntity[]>;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
|
||||
@@ -13,4 +13,5 @@ export enum AgentExceptionCode {
|
||||
API_KEY_NOT_CONFIGURED = 'API_KEY_NOT_CONFIGURED',
|
||||
USER_WORKSPACE_ID_NOT_FOUND = 'USER_WORKSPACE_ID_NOT_FOUND',
|
||||
ROLE_NOT_FOUND = 'ROLE_NOT_FOUND',
|
||||
HANDOFF_ALREADY_EXISTS = 'HANDOFF_ALREADY_EXISTS',
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ import { AgentChatThreadEntity } from './agent-chat-thread.entity';
|
||||
import { AgentChatResolver } from './agent-chat.resolver';
|
||||
import { AgentChatService } from './agent-chat.service';
|
||||
import { AgentExecutionService } from './agent-execution.service';
|
||||
import { AgentHandoffExecutorService } from './agent-handoff-executor.service';
|
||||
import { AgentHandoffEntity } from './agent-handoff.entity';
|
||||
import { AgentHandoffService } from './agent-handoff.service';
|
||||
import { AgentStreamingService } from './agent-streaming.service';
|
||||
import { AgentTitleGenerationService } from './agent-title-generation.service';
|
||||
import { AgentToolService } from './agent-tool.service';
|
||||
@@ -35,6 +38,7 @@ import { AgentService } from './agent.service';
|
||||
TypeOrmModule.forFeature(
|
||||
[
|
||||
AgentEntity,
|
||||
AgentHandoffEntity,
|
||||
RoleEntity,
|
||||
RoleTargetsEntity,
|
||||
AgentChatMessageEntity,
|
||||
@@ -66,6 +70,8 @@ import { AgentService } from './agent.service';
|
||||
AgentChatService,
|
||||
AgentStreamingService,
|
||||
AgentTitleGenerationService,
|
||||
AgentHandoffExecutorService,
|
||||
AgentHandoffService,
|
||||
],
|
||||
exports: [
|
||||
AgentService,
|
||||
@@ -78,6 +84,8 @@ import { AgentService } from './agent.service';
|
||||
[AgentEntity, AgentChatMessageEntity, AgentChatThreadEntity],
|
||||
'core',
|
||||
),
|
||||
AgentHandoffExecutorService,
|
||||
AgentHandoffService,
|
||||
],
|
||||
})
|
||||
export class AgentModule {}
|
||||
|
||||
Reference in New Issue
Block a user