Skip to content

Commit 4ef4b6a

Browse files
committed
inheritance of character from parent using extends key
1 parent 565f4e7 commit 4ef4b6a

File tree

4 files changed

+60
-26
lines changed

4 files changed

+60
-26
lines changed

agent/src/index.ts

+56-26
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,61 @@ function tryLoadFile(filePath: string): string | null {
144144
return null;
145145
}
146146
}
147+
function mergeCharacters(base: Character, child: Character): Character {
148+
const mergeObjects = (baseObj: any, childObj: any) => {
149+
const result: any = {};
150+
const keys = new Set([...Object.keys(baseObj || {}), ...Object.keys(childObj || {})]);
151+
keys.forEach(key => {
152+
if (typeof baseObj[key] === 'object' && typeof childObj[key] === 'object' && !Array.isArray(baseObj[key]) && !Array.isArray(childObj[key])) {
153+
result[key] = mergeObjects(baseObj[key], childObj[key]);
154+
} else if (Array.isArray(baseObj[key]) || Array.isArray(childObj[key])) {
155+
result[key] = [...(baseObj[key] || []), ...(childObj[key] || [])];
156+
} else {
157+
result[key] = childObj[key] !== undefined ? childObj[key] : baseObj[key];
158+
}
159+
});
160+
return result;
161+
};
162+
return mergeObjects(base, child);
163+
}
164+
async function loadCharacter(filePath: string): Promise<Character> {
165+
const content = tryLoadFile(filePath);
166+
if (!content) {
167+
throw new Error(`Character file not found: ${filePath}`);
168+
}
169+
let character = JSON.parse(content);
170+
validateCharacterConfig(character);
171+
172+
// .id isn't really valid
173+
const characterId = character.id || character.name;
174+
const characterPrefix = `CHARACTER.${characterId.toUpperCase().replace(/ /g, "_")}.`;
175+
const characterSettings = Object.entries(process.env)
176+
.filter(([key]) => key.startsWith(characterPrefix))
177+
.reduce((settings, [key, value]) => {
178+
const settingKey = key.slice(characterPrefix.length);
179+
return { ...settings, [settingKey]: value };
180+
}, {});
181+
if (Object.keys(characterSettings).length > 0) {
182+
character.settings = character.settings || {};
183+
character.settings.secrets = {
184+
...characterSettings,
185+
...character.settings.secrets,
186+
};
187+
}
188+
// Handle plugins
189+
character.plugins = await handlePluginImporting(
190+
character.plugins
191+
);
192+
if (character.extends) {
193+
elizaLogger.info(`Merging ${character.name} character with parent characters`);
194+
for (const extendPath of character.extends) {
195+
const baseCharacter = await loadCharacter(path.resolve(path.dirname(filePath), extendPath));
196+
character = mergeCharacters(baseCharacter, character);
197+
elizaLogger.info(`Merged ${character.name} with ${baseCharacter.name}`);
198+
}
199+
}
200+
return character;
201+
}
147202

148203
export async function loadCharacters(
149204
charactersArg: string
@@ -207,32 +262,7 @@ export async function loadCharacters(
207262
}
208263

209264
try {
210-
const character = JSON.parse(content);
211-
validateCharacterConfig(character);
212-
213-
// .id isn't really valid
214-
const characterId = character.id || character.name;
215-
const characterPrefix = `CHARACTER.${characterId.toUpperCase().replace(/ /g, "_")}.`;
216-
217-
const characterSettings = Object.entries(process.env)
218-
.filter(([key]) => key.startsWith(characterPrefix))
219-
.reduce((settings, [key, value]) => {
220-
const settingKey = key.slice(characterPrefix.length);
221-
return { ...settings, [settingKey]: value };
222-
}, {});
223-
224-
if (Object.keys(characterSettings).length > 0) {
225-
character.settings = character.settings || {};
226-
character.settings.secrets = {
227-
...characterSettings,
228-
...character.settings.secrets,
229-
};
230-
}
231-
232-
// Handle plugins
233-
character.plugins = await handlePluginImporting(
234-
character.plugins
235-
);
265+
const character: Character = await loadCharacter(resolvedPath);
236266

237267
loadedCharacters.push(character);
238268
elizaLogger.info(

packages/core/src/defaultCharacter.ts

+1
Original file line numberDiff line numberDiff line change
@@ -527,4 +527,5 @@ export const defaultCharacter: Character = {
527527
"meticulous",
528528
"provocative",
529529
],
530+
extends: [],
530531
};

packages/core/src/environment.ts

+1
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export const CharacterSchema = z.object({
135135
prompt: z.string().optional(),
136136
})
137137
.optional(),
138+
extends: z.array(z.string()).optional(),
138139
});
139140

140141
// Type inference

packages/core/src/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,8 @@ export type Character = {
871871
nft?: {
872872
prompt: string;
873873
};
874+
/**Optinal Parent characters to inherit information from */
875+
extends?: string[];
874876
};
875877

876878
/**

0 commit comments

Comments
 (0)