@@ -148,6 +148,61 @@ function tryLoadFile(filePath: string): string | null {
148
148
return null ;
149
149
}
150
150
}
151
+ function mergeCharacters ( base : Character , child : Character ) : Character {
152
+ const mergeObjects = ( baseObj : any , childObj : any ) => {
153
+ const result : any = { } ;
154
+ const keys = new Set ( [ ...Object . keys ( baseObj || { } ) , ...Object . keys ( childObj || { } ) ] ) ;
155
+ keys . forEach ( key => {
156
+ if ( typeof baseObj [ key ] === 'object' && typeof childObj [ key ] === 'object' && ! Array . isArray ( baseObj [ key ] ) && ! Array . isArray ( childObj [ key ] ) ) {
157
+ result [ key ] = mergeObjects ( baseObj [ key ] , childObj [ key ] ) ;
158
+ } else if ( Array . isArray ( baseObj [ key ] ) || Array . isArray ( childObj [ key ] ) ) {
159
+ result [ key ] = [ ...( baseObj [ key ] || [ ] ) , ...( childObj [ key ] || [ ] ) ] ;
160
+ } else {
161
+ result [ key ] = childObj [ key ] !== undefined ? childObj [ key ] : baseObj [ key ] ;
162
+ }
163
+ } ) ;
164
+ return result ;
165
+ } ;
166
+ return mergeObjects ( base , child ) ;
167
+ }
168
+ async function loadCharacter ( filePath : string ) : Promise < Character > {
169
+ const content = tryLoadFile ( filePath ) ;
170
+ if ( ! content ) {
171
+ throw new Error ( `Character file not found: ${ filePath } ` ) ;
172
+ }
173
+ let character = JSON . parse ( content ) ;
174
+ validateCharacterConfig ( character ) ;
175
+
176
+ // .id isn't really valid
177
+ const characterId = character . id || character . name ;
178
+ const characterPrefix = `CHARACTER.${ characterId . toUpperCase ( ) . replace ( / / g, "_" ) } .` ;
179
+ const characterSettings = Object . entries ( process . env )
180
+ . filter ( ( [ key ] ) => key . startsWith ( characterPrefix ) )
181
+ . reduce ( ( settings , [ key , value ] ) => {
182
+ const settingKey = key . slice ( characterPrefix . length ) ;
183
+ return { ...settings , [ settingKey ] : value } ;
184
+ } , { } ) ;
185
+ if ( Object . keys ( characterSettings ) . length > 0 ) {
186
+ character . settings = character . settings || { } ;
187
+ character . settings . secrets = {
188
+ ...characterSettings ,
189
+ ...character . settings . secrets ,
190
+ } ;
191
+ }
192
+ // Handle plugins
193
+ character . plugins = await handlePluginImporting (
194
+ character . plugins
195
+ ) ;
196
+ if ( character . extends ) {
197
+ elizaLogger . info ( `Merging ${ character . name } character with parent characters` ) ;
198
+ for ( const extendPath of character . extends ) {
199
+ const baseCharacter = await loadCharacter ( path . resolve ( path . dirname ( filePath ) , extendPath ) ) ;
200
+ character = mergeCharacters ( baseCharacter , character ) ;
201
+ elizaLogger . info ( `Merged ${ character . name } with ${ baseCharacter . name } ` ) ;
202
+ }
203
+ }
204
+ return character ;
205
+ }
151
206
152
207
export async function loadCharacters (
153
208
charactersArg : string
@@ -211,32 +266,7 @@ export async function loadCharacters(
211
266
}
212
267
213
268
try {
214
- const character = JSON . parse ( content ) ;
215
- validateCharacterConfig ( character ) ;
216
-
217
- // .id isn't really valid
218
- const characterId = character . id || character . name ;
219
- const characterPrefix = `CHARACTER.${ characterId . toUpperCase ( ) . replace ( / / g, "_" ) } .` ;
220
-
221
- const characterSettings = Object . entries ( process . env )
222
- . filter ( ( [ key ] ) => key . startsWith ( characterPrefix ) )
223
- . reduce ( ( settings , [ key , value ] ) => {
224
- const settingKey = key . slice ( characterPrefix . length ) ;
225
- return { ...settings , [ settingKey ] : value } ;
226
- } , { } ) ;
227
-
228
- if ( Object . keys ( characterSettings ) . length > 0 ) {
229
- character . settings = character . settings || { } ;
230
- character . settings . secrets = {
231
- ...characterSettings ,
232
- ...character . settings . secrets ,
233
- } ;
234
- }
235
-
236
- // Handle plugins
237
- character . plugins = await handlePluginImporting (
238
- character . plugins
239
- ) ;
269
+ const character : Character = await loadCharacter ( resolvedPath ) ;
240
270
241
271
loadedCharacters . push ( character ) ;
242
272
elizaLogger . info (
0 commit comments