Skip to content

Commit

Permalink
Merge pull request #2159 from bark-ruffalo/inheritance
Browse files Browse the repository at this point in the history
inheritance of character from parent using extends key
  • Loading branch information
monilpat authored Jan 11, 2025
2 parents 1ca639e + 8d831fa commit ea154a0
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 26 deletions.
82 changes: 56 additions & 26 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,61 @@ function tryLoadFile(filePath: string): string | null {
return null;
}
}
function mergeCharacters(base: Character, child: Character): Character {
const mergeObjects = (baseObj: any, childObj: any) => {
const result: any = {};
const keys = new Set([...Object.keys(baseObj || {}), ...Object.keys(childObj || {})]);
keys.forEach(key => {
if (typeof baseObj[key] === 'object' && typeof childObj[key] === 'object' && !Array.isArray(baseObj[key]) && !Array.isArray(childObj[key])) {
result[key] = mergeObjects(baseObj[key], childObj[key]);
} else if (Array.isArray(baseObj[key]) || Array.isArray(childObj[key])) {
result[key] = [...(baseObj[key] || []), ...(childObj[key] || [])];
} else {
result[key] = childObj[key] !== undefined ? childObj[key] : baseObj[key];
}
});
return result;
};
return mergeObjects(base, child);
}
async function loadCharacter(filePath: string): Promise<Character> {
const content = tryLoadFile(filePath);
if (!content) {
throw new Error(`Character file not found: ${filePath}`);
}
let character = JSON.parse(content);
validateCharacterConfig(character);

// .id isn't really valid
const characterId = character.id || character.name;
const characterPrefix = `CHARACTER.${characterId.toUpperCase().replace(/ /g, "_")}.`;
const characterSettings = Object.entries(process.env)
.filter(([key]) => key.startsWith(characterPrefix))
.reduce((settings, [key, value]) => {
const settingKey = key.slice(characterPrefix.length);
return { ...settings, [settingKey]: value };
}, {});
if (Object.keys(characterSettings).length > 0) {
character.settings = character.settings || {};
character.settings.secrets = {
...characterSettings,
...character.settings.secrets,
};
}
// Handle plugins
character.plugins = await handlePluginImporting(
character.plugins
);
if (character.extends) {
elizaLogger.info(`Merging ${character.name} character with parent characters`);
for (const extendPath of character.extends) {
const baseCharacter = await loadCharacter(path.resolve(path.dirname(filePath), extendPath));
character = mergeCharacters(baseCharacter, character);
elizaLogger.info(`Merged ${character.name} with ${baseCharacter.name}`);
}
}
return character;
}

export async function loadCharacters(
charactersArg: string
Expand Down Expand Up @@ -211,32 +266,7 @@ export async function loadCharacters(
}

try {
const character = JSON.parse(content);
validateCharacterConfig(character);

// .id isn't really valid
const characterId = character.id || character.name;
const characterPrefix = `CHARACTER.${characterId.toUpperCase().replace(/ /g, "_")}.`;

const characterSettings = Object.entries(process.env)
.filter(([key]) => key.startsWith(characterPrefix))
.reduce((settings, [key, value]) => {
const settingKey = key.slice(characterPrefix.length);
return { ...settings, [settingKey]: value };
}, {});

if (Object.keys(characterSettings).length > 0) {
character.settings = character.settings || {};
character.settings.secrets = {
...characterSettings,
...character.settings.secrets,
};
}

// Handle plugins
character.plugins = await handlePluginImporting(
character.plugins
);
const character: Character = await loadCharacter(resolvedPath);

loadedCharacters.push(character);
elizaLogger.info(
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/defaultCharacter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,4 +527,5 @@ export const defaultCharacter: Character = {
"meticulous",
"provocative",
],
extends: [],
};
1 change: 1 addition & 0 deletions packages/core/src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export const CharacterSchema = z.object({
prompt: z.string().optional(),
})
.optional(),
extends: z.array(z.string()).optional(),
});

// Type inference
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,8 @@ export type Character = {
nft?: {
prompt: string;
};
/**Optinal Parent characters to inherit information from */
extends?: string[];
};

/**
Expand Down

0 comments on commit ea154a0

Please sign in to comment.