roleManager.ts•7.16 kB
import { MemoryManager } from "../memory/memoryManager";
import { OpenAIClient } from "../ai/openaiClient";
import { ContextManager } from "../context/contextManager";
import { config } from "../config";
import { ImportanceLevel, MemoryType } from "../memory/types";
import { ContextType } from "../context/types";
// Types for role management
export interface Role {
id: string;
name: string;
description: string;
instructions: string;
domains: string[];
tone: string;
systemPrompt: string;
customInstructions?: string;
createdAt: Date;
updatedAt: Date;
}
export interface ToneProfile {
description: string;
modifiers: string;
}
export class RoleManager {
private roles: Map<string, Role> = new Map();
private toneProfiles: Map<string, ToneProfile> = new Map();
private memoryManager: MemoryManager;
private aiClient: OpenAIClient;
private contextManager: ContextManager;
constructor(memoryManager: MemoryManager, aiClient: OpenAIClient, contextManager: ContextManager) {
this.memoryManager = memoryManager;
this.aiClient = aiClient;
this.contextManager = contextManager;
this.initializeDefaultRoles();
this.initializeToneProfiles();
}
private initializeDefaultRoles(): void {
for (const roleConfig of config.roles.defaults) {
const role: Role = {
...roleConfig,
createdAt: new Date(),
updatedAt: new Date()
};
this.roles.set(role.id, role);
}
}
private initializeToneProfiles(): void {
for (const [tone, profile] of Object.entries(config.roles.toneProfiles)) {
this.toneProfiles.set(tone, profile as ToneProfile);
}
}
/**
* Get all available roles
*/
public getAllRoles(): Role[] {
return Array.from(this.roles.values());
}
/**
* Get a specific role by ID
*/
public getRole(roleId: string): Role | undefined {
return this.roles.get(roleId);
}
/**
* Create a new custom role
*/
public createRole(roleData: Omit<Role, 'createdAt' | 'updatedAt'>): Role {
if (this.roles.has(roleData.id)) {
throw new Error(`Role with ID ${roleData.id} already exists`);
}
const role: Role = {
...roleData,
createdAt: new Date(),
updatedAt: new Date()
};
this.roles.set(role.id, role);
return role;
}
/**
* Update an existing role
*/
public updateRole(roleId: string, updates: Partial<Omit<Role, 'id' | 'createdAt' | 'updatedAt'>>): Role {
const existingRole = this.roles.get(roleId);
if (!existingRole) {
throw new Error(`Role with ID ${roleId} not found`);
}
const updatedRole: Role = {
...existingRole,
...updates,
updatedAt: new Date()
};
this.roles.set(roleId, updatedRole);
return updatedRole;
}
/**
* Delete a role
*/
public deleteRole(roleId: string): boolean {
// Don't allow deletion of default roles
const isDefaultRole = config.roles.defaults.some((r: any) => r.id === roleId);
if (isDefaultRole) {
throw new Error(`Cannot delete default role: ${roleId}`);
}
const deleted = this.roles.delete(roleId);
if (deleted) {
// Clean up associated memories
this.memoryManager.clearRoleMemories(roleId);
}
return deleted;
}
/**
* Get all available tone profiles
*/
public getAllToneProfiles(): Map<string, ToneProfile> {
return this.toneProfiles;
}
/**
* Get a specific tone profile
*/
public getToneProfile(tone: string): ToneProfile | undefined {
return this.toneProfiles.get(tone);
}
/**
* Change a role's tone
*/
public async changeRoleTone(roleId: string, newTone: string): Promise<Role> {
if (!this.toneProfiles.has(newTone)) {
throw new Error(`Tone profile '${newTone}' not found`);
}
const role = this.updateRole(roleId, { tone: newTone });
// Also update the context tone if this role is active
await this.contextManager.switchContext({
agentId: roleId,
contextType: ContextType.TONE,
contextValue: newTone,
metadata: { source: 'role_tone_change' }
});
return role;
}
/**
* Generate a complete system prompt for a role, including tone modifiers
*/
public generateCompletePrompt(roleId: string, customInstructions?: string): string {
const role = this.getRole(roleId);
if (!role) {
throw new Error(`Role with ID ${roleId} not found`);
}
const toneProfile = this.getToneProfile(role.tone);
if (!toneProfile) {
throw new Error(`Tone profile '${role.tone}' not found`);
}
let prompt = role.systemPrompt;
// Add tone modifiers
prompt += `\n\nTone and Style Guidelines: ${toneProfile.modifiers}`;
// Add domain knowledge context
prompt += `\n\nYour expertise is specifically in: ${role.domains.join(', ')}.`;
// Add role-specific instructions
prompt += `\n\nSpecific Instructions: ${role.instructions}`;
// Add custom instructions if provided
if (customInstructions || role.customInstructions) {
prompt += `\n\nAdditional Context: ${customInstructions || role.customInstructions}`;
}
return prompt;
}
/**
* Get relevant memories for a role and query
*/
public async getRelevantContext(roleId: string, query: string): Promise<string> {
const role = this.getRole(roleId);
if (!role) {
throw new Error(`Role with ID ${roleId} not found`);
}
// Get relevant memories for this role and query
const memories = await this.memoryManager.getRelevantMemories({
roleId,
query,
limit: 10
});
if (memories.length === 0) {
return "";
}
// Format memories as context
return `Relevant context from previous interactions:\n\n${memories.map(m =>
`- ${m.content} (${new Date(m.timestamp).toLocaleString()})`
).join('\n')}`;
}
/**
* Process a query using a specific role
*/
public async processQuery(roleId: string, query: string, customInstructions?: string): Promise<string> {
const role = this.getRole(roleId);
if (!role) {
throw new Error(`Role with ID ${roleId} not found`);
}
// Check for context triggers in the query
await this.contextManager.checkAndApplyTriggers(roleId, query);
// Generate the complete system prompt
const systemPrompt = this.generateCompletePrompt(roleId, customInstructions);
// Get relevant context from role-specific memory
const contextMemories = await this.getRelevantContext(roleId, query);
// Get context-aware modifiers
const contextModifiers = this.contextManager.generateContextPromptModifiers(roleId);
// Process with AI
const response = await this.aiClient.generateResponse(
systemPrompt + contextModifiers,
query,
contextMemories
);
// Store this interaction in memory
await this.memoryManager.storeMemory({
roleId,
content: `Q: ${query}\nA: ${response}`,
type: MemoryType.SESSION,
importance: ImportanceLevel.MEDIUM,
metadata: { type: 'interaction' }
});
return response;
}
}