@zodic/shared 0.0.373 → 0.0.375

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,7 +29,6 @@ export class ArchetypeService {
29
29
  context: Record<string, any> = {}
30
30
  ) {
31
31
  const logMessage = `[${new Date().toISOString()}] [${level.toUpperCase()}] ${message}`;
32
-
33
32
  console[level](logMessage, context);
34
33
  }
35
34
 
@@ -49,6 +48,7 @@ export class ArchetypeService {
49
48
  .select()
50
49
  .from(schema.archetypesData)
51
50
  .where(eq(schema.archetypesData.combination, combinationString))
51
+ .orderBy(schema.archetypesData.archetypeIndex)
52
52
  .execute();
53
53
 
54
54
  if (
@@ -134,7 +134,7 @@ export class ArchetypeService {
134
134
  continue;
135
135
  }
136
136
 
137
- for (const gender of ['male', 'female']) {
137
+ for (const gender of ['male', 'female'] as Gender[]) {
138
138
  for (const lang of ['en-us', 'pt-br']) {
139
139
  const id = `${combinationString}:${gender}:${index}${
140
140
  lang === 'pt-br' ? ':pt' : ''
@@ -153,6 +153,7 @@ export class ArchetypeService {
153
153
  await this.log('debug', `Skipping existing archetype ${id}`, {
154
154
  name: existing.name,
155
155
  essenceLine: existing.essenceLine,
156
+ archetypeIndex: index,
156
157
  });
157
158
  continue;
158
159
  }
@@ -160,6 +161,9 @@ export class ArchetypeService {
160
161
  await this.log('debug', `Saving archetype name for ${id}`, {
161
162
  name,
162
163
  essenceLine,
164
+ archetypeIndex: index,
165
+ gender,
166
+ language: lang,
163
167
  });
164
168
 
165
169
  await db
@@ -210,6 +214,13 @@ export class ArchetypeService {
210
214
  blocksCount: blocks.length,
211
215
  });
212
216
 
217
+ if (blocks.length !== 3) {
218
+ this.log('error', 'Expected exactly 3 archetype blocks', {
219
+ blocksCount: blocks.length,
220
+ });
221
+ throw new Error(`Expected 3 archetype blocks, got ${blocks.length}`);
222
+ }
223
+
213
224
  const english: Array<{ name: string; essenceLine: string }> = [];
214
225
  const portuguese: Array<{
215
226
  masc: string;
@@ -217,7 +228,7 @@ export class ArchetypeService {
217
228
  essenceLine: string;
218
229
  }> = [];
219
230
 
220
- for (const block of blocks) {
231
+ blocks.forEach((block, index) => {
221
232
  const nameMatch = block.match(/- Name: (.+)/);
222
233
  const essenceLineMatch = block.match(/- Essence Line: (.+)/);
223
234
  const ptMascMatch = block.match(/- Portuguese \(Masc\): (.+)/);
@@ -240,10 +251,21 @@ export class ArchetypeService {
240
251
  fem: ptFemMatch[1].trim(),
241
252
  essenceLine: ptEssenceLineMatch[1].trim(),
242
253
  });
254
+ this.log('debug', `Parsed block for archetypeIndex ${index + 1}`, {
255
+ english: english[index],
256
+ portuguese: portuguese[index],
257
+ });
243
258
  } else {
244
- this.log('warn', 'Malformed archetype name block', { block });
259
+ this.log(
260
+ 'error',
261
+ `Malformed archetype name block for index ${index + 1}`,
262
+ { block }
263
+ );
264
+ throw new Error(
265
+ `Malformed archetype name block for index ${index + 1}`
266
+ );
245
267
  }
246
- }
268
+ });
247
269
 
248
270
  this.log('info', 'Completed parseArchetypeNameBlocks', {
249
271
  english,
@@ -254,7 +276,7 @@ export class ArchetypeService {
254
276
 
255
277
  async generateDescriptionsAndVirtues(
256
278
  combinationString: string,
257
- gender: Gender,
279
+ gender: Gender, // Used for compatibility, but processes both genders
258
280
  language: string,
259
281
  override: boolean = false
260
282
  ): Promise<ParsedDescriptionAndVirtues[]> {
@@ -266,48 +288,70 @@ export class ArchetypeService {
266
288
  });
267
289
 
268
290
  const db = this.context.drizzle();
269
- const archetypes = await db
270
- .select()
271
- .from(schema.archetypesData)
272
- .where(
273
- and(
274
- eq(schema.archetypesData.combination, combinationString),
275
- eq(schema.archetypesData.gender, gender),
276
- eq(schema.archetypesData.language, language)
291
+ const allGenders: Gender[] = ['male', 'female'];
292
+ const archetypesByGender: Record<Gender, any[]> = { male: [], female: [] };
293
+
294
+ for (const g of allGenders) {
295
+ const archetypes = await db
296
+ .select()
297
+ .from(schema.archetypesData)
298
+ .where(
299
+ and(
300
+ eq(schema.archetypesData.combination, combinationString),
301
+ eq(schema.archetypesData.gender, g),
302
+ eq(schema.archetypesData.language, language)
303
+ )
277
304
  )
278
- )
279
- .execute();
305
+ .orderBy(schema.archetypesData.archetypeIndex)
306
+ .execute();
307
+ archetypesByGender[g] = archetypes;
308
+ }
280
309
 
281
- if (
282
- !override &&
283
- archetypes.every((a) => a.description && a.virtues !== '[]')
284
- ) {
310
+ const needsGeneration = allGenders.some(
311
+ (g) =>
312
+ archetypesByGender[g].length !== 3 ||
313
+ archetypesByGender[g].some((a) => !a.description || a.virtues === '[]')
314
+ );
315
+
316
+ if (!override && !needsGeneration) {
285
317
  await this.log('info', 'Skipping descriptions and virtues generation', {
286
318
  reason: 'Descriptions and virtues already exist for all archetypes',
287
319
  });
288
- return archetypes.map((a) => ({
320
+ return archetypesByGender['male'].map((a, i) => ({
289
321
  descriptionEN: a.description || '',
290
- descriptionPTM: a.description || '',
291
- descriptionPTF: a.description || '',
322
+ descriptionPTM: archetypesByGender['male'][i].description || '',
323
+ descriptionPTF: archetypesByGender['female'][i].description || '',
292
324
  virtuesEN: JSON.parse(a.virtues || '[]'),
293
325
  virtuesPT: JSON.parse(a.virtues || '[]'),
294
326
  }));
295
327
  }
296
328
 
297
329
  await this.log('info', 'Fetched archetypes for description generation', {
298
- archetypesCount: archetypes.length,
299
- names: archetypes.map((a) => a.name),
330
+ maleArchetypes: archetypesByGender['male'].map((a) => ({
331
+ id: a.id,
332
+ name: a.name,
333
+ index: a.archetypeIndex,
334
+ })),
335
+ femaleArchetypes: archetypesByGender['female'].map((a) => ({
336
+ id: a.id,
337
+ name: a.name,
338
+ index: a.archetypeIndex,
339
+ })),
300
340
  });
301
341
 
302
342
  const descVirtueMessages = this.context
303
343
  .buildLLMMessages()
304
344
  .generateCosmicMirrorDescriptionAndVirtues({
305
345
  combination: combinationString,
306
- names: [archetypes[0].name, archetypes[1].name, archetypes[2].name],
307
- essenceLines: [
308
- archetypes[0].essenceLine,
309
- archetypes[1].essenceLine,
310
- archetypes[2].essenceLine,
346
+ names: archetypesByGender['male'].map((a) => a.name) as [
347
+ string,
348
+ string,
349
+ string
350
+ ],
351
+ essenceLines: archetypesByGender['male'].map((a) => a.essenceLine) as [
352
+ string,
353
+ string,
354
+ string
311
355
  ],
312
356
  });
313
357
 
@@ -317,7 +361,7 @@ export class ArchetypeService {
317
361
  const descVirtueResponse = await this.context
318
362
  .api()
319
363
  .callTogether.single(descVirtueMessages, {
320
- options: { temperature: 0.5 },
364
+ options: { temperature: 0.3 },
321
365
  });
322
366
  if (!descVirtueResponse) {
323
367
  await this.log('error', 'No response for descriptions and virtues', {
@@ -339,106 +383,78 @@ export class ArchetypeService {
339
383
 
340
384
  for (let i = 0; i < 3; i++) {
341
385
  const index = (i + 1).toString();
342
- const enId = `${combinationString}:${gender}:${index}`;
343
- const ptId = `${combinationString}:${gender}:${index}:pt`;
344
-
345
- // Update English entry (en-us)
346
- if (language === 'en-us') {
347
- const enExisting = archetypes.find((a) => a.id === enId);
348
- if (
349
- !override &&
350
- enExisting?.description &&
351
- enExisting.virtues !== '[]'
352
- ) {
386
+ for (const g of allGenders) {
387
+ const archetype = archetypesByGender[g][i];
388
+ if (!archetype || archetype.archetypeIndex !== index) {
353
389
  await this.log(
354
- 'debug',
355
- `Skipping existing description and virtues for ${enId}`,
390
+ 'error',
391
+ `Archetype index mismatch for gender ${g} at position ${i + 1}`,
356
392
  {
357
- description: enExisting.description,
358
- virtues: enExisting.virtues,
393
+ expectedIndex: index,
394
+ actualIndex: archetype?.archetypeIndex,
395
+ archetypeId: archetype?.id,
359
396
  }
360
397
  );
361
- } else {
362
- await this.log(
363
- 'debug',
364
- `Updating description and virtues for ${enId}`,
365
- {
366
- description: parsedDescVirtues[i].descriptionEN,
367
- virtues: parsedDescVirtues[i].virtuesEN,
368
- }
398
+ throw new Error(
399
+ `Archetype index mismatch for gender ${g}: expected ${index}, got ${archetype?.archetypeIndex}`
369
400
  );
370
- await db
371
- .update(schema.archetypesData)
372
- .set({
373
- description: parsedDescVirtues[i].descriptionEN,
374
- virtues: JSON.stringify(parsedDescVirtues[i].virtuesEN),
375
- updatedAt: new Date().getTime(),
376
- })
377
- .where(
378
- and(
379
- eq(schema.archetypesData.id, enId),
380
- eq(schema.archetypesData.language, 'en-us')
381
- )
382
- )
383
- .execute();
384
- await this.log('info', `Updated description and virtues for ${enId}`);
385
401
  }
386
- }
387
402
 
388
- // Update Portuguese entries (pt-br)
389
- if (language === 'pt-br') {
390
- for (const ptGender of ['male', 'female']) {
391
- const ptExisting = archetypes.find(
392
- (a) => a.id === ptId && a.gender === ptGender
393
- );
394
- if (
395
- !override &&
396
- ptExisting?.description &&
397
- ptExisting.virtues !== '[]'
398
- ) {
399
- await this.log(
400
- 'debug',
401
- `Skipping existing description and virtues for ${ptId} (${ptGender})`,
402
- {
403
- description: ptExisting.description,
404
- virtues: ptExisting.virtues,
405
- }
406
- );
407
- continue;
408
- }
409
-
410
- const description =
411
- ptGender === 'male'
412
- ? parsedDescVirtues[i].descriptionPTM
413
- : parsedDescVirtues[i].descriptionPTF;
403
+ const id = `${combinationString}:${g}:${index}${
404
+ language === 'pt-br' ? ':pt' : ''
405
+ }`;
406
+ if (!override && archetype.description && archetype.virtues !== '[]') {
414
407
  await this.log(
415
408
  'debug',
416
- `Updating description and virtues for ${ptId} (${ptGender})`,
409
+ `Skipping existing description and virtues for ${id}`,
417
410
  {
418
- description,
419
- virtues: parsedDescVirtues[i].virtuesPT,
411
+ description: archetype.description,
412
+ virtues: archetype.virtues,
413
+ archetypeIndex: index,
414
+ gender: g,
415
+ language,
420
416
  }
421
417
  );
422
- await db
423
- .update(schema.archetypesData)
424
- .set({
425
- description,
426
- virtues: JSON.stringify(parsedDescVirtues[i].virtuesPT),
427
- updatedAt: new Date().getTime(),
428
- })
429
- .where(
430
- and(
431
- eq(schema.archetypesData.id, ptId),
432
- eq(schema.archetypesData.language, 'pt-br'),
433
- eq(schema.archetypesData.gender, ptGender)
434
- )
435
- )
436
- .execute();
437
- await this.log(
438
- 'info',
439
- `Updated description and virtues for ${ptId} (${ptGender})`
440
- );
418
+ continue;
441
419
  }
420
+
421
+ const description =
422
+ language === 'en-us'
423
+ ? parsedDescVirtues[i].descriptionEN
424
+ : g === 'male'
425
+ ? parsedDescVirtues[i].descriptionPTM
426
+ : parsedDescVirtues[i].descriptionPTF;
427
+ const virtues =
428
+ language === 'en-us'
429
+ ? parsedDescVirtues[i].virtuesEN
430
+ : parsedDescVirtues[i].virtuesPT;
431
+
432
+ await this.log('debug', `Updating description and virtues for ${id}`, {
433
+ description,
434
+ virtues,
435
+ archetypeIndex: index,
436
+ gender: g,
437
+ language,
438
+ });
439
+
440
+ await db
441
+ .update(schema.archetypesData)
442
+ .set({
443
+ description,
444
+ virtues: JSON.stringify(virtues),
445
+ updatedAt: new Date().getTime(),
446
+ })
447
+ .where(
448
+ and(
449
+ eq(schema.archetypesData.id, id),
450
+ eq(schema.archetypesData.language, language),
451
+ eq(schema.archetypesData.gender, g),
452
+ eq(schema.archetypesData.archetypeIndex, index)
453
+ )
454
+ )
455
+ .execute();
456
+
457
+ await this.log('info', `Updated description and virtues for ${id}`);
442
458
  }
443
459
  }
444
460
 
@@ -488,6 +504,22 @@ export class ArchetypeService {
488
504
  lines,
489
505
  });
490
506
 
507
+ if (lines[0] !== `${entryIndex + 1}.`) {
508
+ this.log(
509
+ 'error',
510
+ `Invalid archetype index in entry ${entryIndex + 1}`,
511
+ {
512
+ expected: `${entryIndex + 1}.`,
513
+ found: lines[0],
514
+ }
515
+ );
516
+ throw new Error(
517
+ `Invalid archetype index in entry ${entryIndex + 1}: expected ${
518
+ entryIndex + 1
519
+ }, got ${lines[0]}`
520
+ );
521
+ }
522
+
491
523
  let descriptionEN = '';
492
524
  let descriptionPTM = '';
493
525
  let descriptionPTF = '';
@@ -495,18 +527,10 @@ export class ArchetypeService {
495
527
  let virtuesPT: string[] = [];
496
528
  let currentField = '';
497
529
 
498
- for (let i = 0; i < lines.length; i++) {
530
+ for (let i = 1; i < lines.length; i++) {
499
531
  let line = lines[i];
500
532
  line = line.replace(/\*\*(.*?)\*\*/g, '$1');
501
533
 
502
- if (
503
- line.startsWith('1.') ||
504
- line.startsWith('2.') ||
505
- line.startsWith('3.')
506
- ) {
507
- continue;
508
- }
509
-
510
534
  if (line.startsWith('• Description EN:')) {
511
535
  currentField = 'descriptionEN';
512
536
  descriptionEN = line.split('• Description EN:')[1]?.trim() || '';
@@ -612,7 +636,7 @@ export class ArchetypeService {
612
636
  virtuesPT.length !== 3
613
637
  ) {
614
638
  this.log(
615
- 'warn',
639
+ 'error',
616
640
  `Malformed description and virtues response for entry ${
617
641
  entryIndex + 1
618
642
  }`,
@@ -632,13 +656,19 @@ export class ArchetypeService {
632
656
  );
633
657
  }
634
658
 
635
- return {
659
+ const parsed = {
636
660
  descriptionEN,
637
661
  descriptionPTM,
638
662
  descriptionPTF,
639
663
  virtuesEN,
640
664
  virtuesPT,
641
665
  };
666
+ this.log(
667
+ 'debug',
668
+ `Parsed description and virtues for archetypeIndex ${entryIndex + 1}`,
669
+ { parsed }
670
+ );
671
+ return parsed;
642
672
  });
643
673
 
644
674
  this.log('info', 'Completed parseDescriptionAndVirtuesResponse', {
@@ -649,7 +679,7 @@ export class ArchetypeService {
649
679
 
650
680
  async generateContent(
651
681
  combinationString: string,
652
- gender: Gender,
682
+ gender: Gender, // Used for compatibility, but processes both genders
653
683
  language: string,
654
684
  descriptions: Array<{ descriptionEN: string }>,
655
685
  override: boolean = false
@@ -662,19 +692,32 @@ export class ArchetypeService {
662
692
  });
663
693
 
664
694
  const db = this.context.drizzle();
665
- const archetypes = await db
666
- .select()
667
- .from(schema.archetypesData)
668
- .where(
669
- and(
670
- eq(schema.archetypesData.combination, combinationString),
671
- eq(schema.archetypesData.gender, gender),
672
- eq(schema.archetypesData.language, language)
695
+ const allGenders: Gender[] = ['male', 'female'];
696
+ const archetypesByGender: Record<Gender, any[]> = { male: [], female: [] };
697
+
698
+ for (const g of allGenders) {
699
+ const archetypes = await db
700
+ .select()
701
+ .from(schema.archetypesData)
702
+ .where(
703
+ and(
704
+ eq(schema.archetypesData.combination, combinationString),
705
+ eq(schema.archetypesData.gender, g),
706
+ eq(schema.archetypesData.language, language)
707
+ )
673
708
  )
674
- )
675
- .execute();
709
+ .orderBy(schema.archetypesData.archetypeIndex)
710
+ .execute();
711
+ archetypesByGender[g] = archetypes;
712
+ }
676
713
 
677
- if (!override && archetypes.every((a) => a.content !== '[]')) {
714
+ const needsGeneration = allGenders.some(
715
+ (g) =>
716
+ archetypesByGender[g].length !== 3 ||
717
+ archetypesByGender[g].some((a) => a.content === '[]')
718
+ );
719
+
720
+ if (!override && !needsGeneration) {
678
721
  await this.log('info', 'Skipping content generation', {
679
722
  reason: 'Content already exists for all archetypes',
680
723
  });
@@ -682,122 +725,159 @@ export class ArchetypeService {
682
725
  }
683
726
 
684
727
  await this.log('info', 'Fetched archetypes for content generation', {
685
- archetypesCount: archetypes.length,
686
- names: archetypes.map((a) => a.name),
728
+ maleArchetypes: archetypesByGender['male'].map((a) => ({
729
+ id: a.id,
730
+ name: a.name,
731
+ index: a.archetypeIndex,
732
+ })),
733
+ femaleArchetypes: archetypesByGender['female'].map((a) => ({
734
+ id: a.id,
735
+ name: a.name,
736
+ index: a.archetypeIndex,
737
+ })),
687
738
  });
688
739
 
689
740
  const contentResults = [];
690
741
  for (let i = 0; i < 3; i++) {
691
742
  const index = (i + 1).toString();
692
- const id = `${combinationString}:${gender}:${index}${
693
- language === 'pt-br' ? ':pt' : ''
694
- }`;
695
- const existing = archetypes.find((a) => a.id === id);
696
-
697
- if (!override && existing && existing?.content !== '[]') {
698
- await this.log('debug', `Skipping existing content for ${id}`, {
699
- content: existing.content,
700
- });
701
- contentResults.push({}); // Placeholder
702
- continue;
703
- }
743
+ for (const g of allGenders) {
744
+ const archetype = archetypesByGender[g][i];
745
+ if (!archetype || archetype.archetypeIndex !== index) {
746
+ await this.log(
747
+ 'error',
748
+ `Archetype index mismatch for gender ${g} at position ${i + 1}`,
749
+ {
750
+ expectedIndex: index,
751
+ actualIndex: archetype?.archetypeIndex,
752
+ archetypeId: archetype?.id,
753
+ }
754
+ );
755
+ throw new Error(
756
+ `Archetype index mismatch for gender ${g}: expected ${index}, got ${archetype?.archetypeIndex}`
757
+ );
758
+ }
704
759
 
705
- await this.log('debug', `Generating content for archetype ${i + 1}`, {
706
- name: archetypes[i].name,
707
- });
708
- const contentMessages = this.context
709
- .buildLLMMessages()
710
- .generateCosmicMirrorArchetypeContent({
711
- combination: combinationString,
712
- name: archetypes[i].name,
713
- description: descriptions[i].descriptionEN,
714
- });
760
+ const id = `${combinationString}:${g}:${index}${
761
+ language === 'pt-br' ? ':pt' : ''
762
+ }`;
763
+ if (!override && archetype.content !== '[]') {
764
+ await this.log('debug', `Skipping existing content for ${id}`, {
765
+ content: archetype.content,
766
+ archetypeIndex: index,
767
+ gender: g,
768
+ language,
769
+ });
770
+ if (g === 'male') contentResults.push({}); // Placeholder for male only
771
+ continue;
772
+ }
715
773
 
716
- await this.log('debug', `Calling API for content of archetype ${i + 1}`, {
717
- messages: contentMessages,
718
- });
719
- const contentResponse = await this.context
720
- .api()
721
- .callTogether.single(contentMessages, {});
722
- if (!contentResponse) {
723
774
  await this.log(
724
- 'error',
725
- `No response for content of archetype ${i + 1}`,
775
+ 'debug',
776
+ `Generating content for archetype ${index} (${g})`,
777
+ { name: archetype.name }
778
+ );
779
+ const contentMessages = this.context
780
+ .buildLLMMessages()
781
+ .generateCosmicMirrorArchetypeContent({
782
+ combination: combinationString,
783
+ name: archetype.name,
784
+ description: descriptions[i].descriptionEN,
785
+ });
786
+
787
+ await this.log(
788
+ 'debug',
789
+ `Calling API for content of archetype ${index} (${g})`,
790
+ { messages: contentMessages }
791
+ );
792
+ const contentResponse = await this.context
793
+ .api()
794
+ .callTogether.single(contentMessages, {});
795
+ if (!contentResponse) {
796
+ await this.log(
797
+ 'error',
798
+ `No response for content of archetype ${index} (${g})`,
799
+ {
800
+ combinationString,
801
+ archetype: archetype.name,
802
+ }
803
+ );
804
+ throw new Error(
805
+ `Failed to generate content for archetype ${index} (${g}) of: ${combinationString}`
806
+ );
807
+ }
808
+ await this.log(
809
+ 'info',
810
+ `Received content response for archetype ${index} (${g})`,
726
811
  {
727
- combinationString,
728
- archetype: archetypes[i].name,
812
+ responseLength: contentResponse.length,
813
+ rawResponse: contentResponse, // Log raw response for debugging
729
814
  }
730
815
  );
731
- throw new Error(
732
- `Failed to generate content for archetype ${
733
- i + 1
734
- } of: ${combinationString}`
816
+
817
+ await this.log(
818
+ 'debug',
819
+ `Parsing content response for archetype ${index} (${g})`
735
820
  );
736
- }
737
- await this.log(
738
- 'info',
739
- `Received content response for archetype ${i + 1}`,
740
- {
741
- responseLength: contentResponse.length,
742
- }
743
- );
821
+ const parsedContent = this.parseContentResponse(contentResponse);
822
+ if (g === 'male') contentResults.push(parsedContent);
823
+ await this.log('info', `Parsed content for archetype ${index} (${g})`, {
824
+ parsedContent,
825
+ });
744
826
 
745
- await this.log(
746
- 'debug',
747
- `Parsing content response for archetype ${i + 1}`
748
- );
749
- const parsedContent = this.parseContentResponse(contentResponse);
750
- contentResults.push(parsedContent);
751
- await this.log('info', `Parsed content for archetype ${i + 1}`, {
752
- parsedContent,
753
- });
827
+ const contentLangIndex =
828
+ language === 'en-us' ? 0 : g === 'male' ? 1 : 2;
829
+ const content = [
830
+ {
831
+ section: 'voiceOfTheSoul',
832
+ text: parsedContent[contentLangIndex].voiceOfTheSoul,
833
+ },
834
+ {
835
+ section: 'giftsYouBear',
836
+ text: parsedContent[contentLangIndex].giftsYouBear,
837
+ },
838
+ {
839
+ section: 'shadowsYouFace',
840
+ text: parsedContent[contentLangIndex].shadowsYouFace,
841
+ },
842
+ {
843
+ section: 'rhythmOfYourDays',
844
+ text: parsedContent[contentLangIndex].rhythmOfYourDays,
845
+ },
846
+ {
847
+ section: 'tiesThatBind',
848
+ text: parsedContent[contentLangIndex].tiesThatBind,
849
+ },
850
+ {
851
+ section: 'lightWithin',
852
+ text: parsedContent[contentLangIndex].lightWithin,
853
+ },
854
+ ];
855
+
856
+ await this.log('debug', `Updating content for ${id}`, {
857
+ content,
858
+ archetypeIndex: index,
859
+ gender: g,
860
+ language,
861
+ });
754
862
 
755
- const contentLangIndex =
756
- language === 'en-us' ? 0 : gender === 'male' ? 1 : 2;
757
- const content = [
758
- {
759
- section: 'voiceOfTheSoul',
760
- text: parsedContent[contentLangIndex].voiceOfTheSoul,
761
- },
762
- {
763
- section: 'giftsYouBear',
764
- text: parsedContent[contentLangIndex].giftsYouBear,
765
- },
766
- {
767
- section: 'shadowsYouFace',
768
- text: parsedContent[contentLangIndex].shadowsYouFace,
769
- },
770
- {
771
- section: 'rhythmOfYourDays',
772
- text: parsedContent[contentLangIndex].rhythmOfYourDays,
773
- },
774
- {
775
- section: 'tiesThatBind',
776
- text: parsedContent[contentLangIndex].tiesThatBind,
777
- },
778
- {
779
- section: 'lightWithin',
780
- text: parsedContent[contentLangIndex].lightWithin,
781
- },
782
- ];
783
-
784
- await this.log('debug', `Updating content for ${id}`, { content });
785
-
786
- await db
787
- .update(schema.archetypesData)
788
- .set({
789
- content: JSON.stringify(content),
790
- updatedAt: new Date().getTime(),
791
- })
792
- .where(
793
- and(
794
- eq(schema.archetypesData.id, id),
795
- eq(schema.archetypesData.language, language)
863
+ await db
864
+ .update(schema.archetypesData)
865
+ .set({
866
+ content: JSON.stringify(content),
867
+ updatedAt: new Date().getTime(),
868
+ })
869
+ .where(
870
+ and(
871
+ eq(schema.archetypesData.id, id),
872
+ eq(schema.archetypesData.language, language),
873
+ eq(schema.archetypesData.gender, g),
874
+ eq(schema.archetypesData.archetypeIndex, index)
875
+ )
796
876
  )
797
- )
798
- .execute();
877
+ .execute();
799
878
 
800
- await this.log('info', `Updated content for ${id}`);
879
+ await this.log('info', `Updated content for ${id}`);
880
+ }
801
881
  }
802
882
 
803
883
  await this.log('info', 'Completed generateContent', {
@@ -821,6 +901,14 @@ export class ArchetypeService {
821
901
  sectionsCount: sections.length,
822
902
  });
823
903
 
904
+ if (sections.length !== 3) {
905
+ this.log('error', 'Expected exactly 3 content sections', {
906
+ sectionsCount: sections.length,
907
+ response,
908
+ });
909
+ throw new Error(`Expected 3 content sections, got ${sections.length}`);
910
+ }
911
+
824
912
  const result = sections.map((section, sectionIndex) => {
825
913
  this.log('debug', `Processing section ${sectionIndex + 1}`, { section });
826
914
 
@@ -830,24 +918,28 @@ export class ArchetypeService {
830
918
  lines,
831
919
  });
832
920
 
921
+ const expectedHeader =
922
+ sectionIndex === 0 ? 'EN:' : sectionIndex === 1 ? 'PT-M:' : 'PT-F:';
923
+ const actualHeader = lines[0].trim(); // Trim the header to remove trailing spaces
924
+ if (actualHeader !== expectedHeader) {
925
+ this.log(
926
+ 'error',
927
+ `Invalid language header in section ${sectionIndex + 1}`,
928
+ {
929
+ expected: expectedHeader,
930
+ found: actualHeader,
931
+ }
932
+ );
933
+ throw new Error(
934
+ `Invalid language header in section ${sectionIndex + 1}`
935
+ );
936
+ }
937
+
833
938
  const content: Record<string, string> = {};
834
939
  let currentSection = '';
835
940
  let currentText: string[] = [];
836
941
 
837
- for (const line of lines) {
838
- if (
839
- line.startsWith('EN:') ||
840
- line.startsWith('PT-M:') ||
841
- line.startsWith('PT-F:')
842
- ) {
843
- this.log(
844
- 'debug',
845
- `Skipping language header in section ${sectionIndex + 1}`,
846
- { line }
847
- );
848
- continue;
849
- }
850
-
942
+ for (const line of lines.slice(1)) {
851
943
  if (line.startsWith('#')) {
852
944
  if (currentSection && currentText.length > 0) {
853
945
  const key = currentSection
@@ -913,15 +1005,21 @@ export class ArchetypeService {
913
1005
 
914
1006
  if (Object.values(parsedContent).some((value) => !value)) {
915
1007
  this.log(
916
- 'warn',
1008
+ 'error',
917
1009
  `Malformed content response for section ${sectionIndex + 1}`,
918
1010
  {
919
1011
  parsedContent,
920
1012
  response: section,
921
1013
  }
922
1014
  );
1015
+ throw new Error(
1016
+ `Malformed content response for section ${sectionIndex + 1}`
1017
+ );
923
1018
  }
924
1019
 
1020
+ this.log('debug', `Parsed content for section ${sectionIndex + 1}`, {
1021
+ parsedContent,
1022
+ });
925
1023
  return parsedContent;
926
1024
  });
927
1025
 
@@ -931,7 +1029,7 @@ export class ArchetypeService {
931
1029
 
932
1030
  async generateLeonardoPrompts(
933
1031
  combinationString: string,
934
- gender: Gender,
1032
+ gender: Gender, // Used for compatibility, but processes both genders
935
1033
  language: string,
936
1034
  descriptions: Array<{ descriptionEN: string }>,
937
1035
  override: boolean = false
@@ -944,9 +1042,8 @@ export class ArchetypeService {
944
1042
  });
945
1043
 
946
1044
  const db = this.context.drizzle();
947
- type Binary = 'male' | 'female';
948
- const allGenders: Binary[] = ['male', 'female'];
949
- const archetypesByGender: Record<Binary, any[]> = { male: [], female: [] };
1045
+ const allGenders: Gender[] = ['male', 'female'];
1046
+ const archetypesByGender: Record<Gender, any[]> = { male: [], female: [] };
950
1047
 
951
1048
  for (const g of allGenders) {
952
1049
  const archetypes = await db
@@ -959,30 +1056,18 @@ export class ArchetypeService {
959
1056
  eq(schema.archetypesData.language, language)
960
1057
  )
961
1058
  )
1059
+ .orderBy(schema.archetypesData.archetypeIndex)
962
1060
  .execute();
963
1061
  archetypesByGender[g] = archetypes;
964
1062
  }
965
1063
 
966
- for (const g of allGenders) {
967
- if (archetypesByGender[g].length !== 3) {
968
- await this.log(
969
- 'error',
970
- `Expected 3 archetypes for gender ${g}, but found ${archetypesByGender[g].length}`,
971
- {
972
- combinationString,
973
- gender: g,
974
- }
975
- );
976
- throw new Error(
977
- `Expected 3 archetypes for gender ${g}, but found ${archetypesByGender[g].length}`
978
- );
979
- }
980
- }
1064
+ const needsGeneration = allGenders.some(
1065
+ (g) =>
1066
+ archetypesByGender[g].length !== 3 ||
1067
+ archetypesByGender[g].some((a) => !a.leonardoPrompt)
1068
+ );
981
1069
 
982
- if (
983
- !override &&
984
- archetypesByGender[gender as Binary].every((a) => a.leonardoPrompt)
985
- ) {
1070
+ if (!override && !needsGeneration) {
986
1071
  await this.log('info', 'Skipping Leonardo prompts generation', {
987
1072
  reason: 'Leonardo prompts already exist for all archetypes',
988
1073
  });
@@ -990,12 +1075,16 @@ export class ArchetypeService {
990
1075
  }
991
1076
 
992
1077
  await this.log('info', 'Fetched archetypes for Leonardo prompts', {
993
- maleArchetypesCount: archetypesByGender['male'].length,
994
- femaleArchetypesCount: archetypesByGender['female'].length,
995
- names: {
996
- male: archetypesByGender['male'].map((a) => a.name),
997
- female: archetypesByGender['female'].map((a) => a.name),
998
- },
1078
+ maleArchetypes: archetypesByGender['male'].map((a) => ({
1079
+ id: a.id,
1080
+ name: a.name,
1081
+ index: a.archetypeIndex,
1082
+ })),
1083
+ femaleArchetypes: archetypesByGender['female'].map((a) => ({
1084
+ id: a.id,
1085
+ name: a.name,
1086
+ index: a.archetypeIndex,
1087
+ })),
999
1088
  });
1000
1089
 
1001
1090
  const promptMessages = this.context
@@ -1030,46 +1119,71 @@ export class ArchetypeService {
1030
1119
 
1031
1120
  for (const g of allGenders) {
1032
1121
  for (let i = 0; i < 3; i++) {
1122
+ const archetype = archetypesByGender[g][i];
1033
1123
  const index = (i + 1).toString();
1124
+ if (!archetype || archetype.archetypeIndex !== index) {
1125
+ await this.log(
1126
+ 'error',
1127
+ `Archetype index mismatch for gender ${g} at position ${i + 1}`,
1128
+ {
1129
+ expectedIndex: index,
1130
+ actualIndex: archetype?.archetypeIndex,
1131
+ archetypeId: archetype?.id,
1132
+ }
1133
+ );
1134
+ throw new Error(
1135
+ `Archetype index mismatch for gender ${g}: expected ${index}, got ${archetype?.archetypeIndex}`
1136
+ );
1137
+ }
1138
+
1034
1139
  const leonardoPrompt =
1035
1140
  g === 'male'
1036
1141
  ? parsedPrompts[i].malePrompt
1037
1142
  : parsedPrompts[i].femalePrompt;
1038
-
1039
1143
  const id = `${combinationString}:${g}:${index}${
1040
1144
  language === 'pt-br' ? ':pt' : ''
1041
1145
  }`;
1042
- const existing = archetypesByGender[g].find((a) => a.id === id);
1043
- if (!override && existing?.leonardoPrompt) {
1146
+
1147
+ if (!override && archetype.leonardoPrompt) {
1044
1148
  await this.log(
1045
1149
  'debug',
1046
1150
  `Skipping existing Leonardo prompt for ${id}`,
1047
1151
  {
1048
- leonardoPrompt: existing.leonardoPrompt,
1152
+ leonardoPrompt: archetype.leonardoPrompt,
1153
+ archetypeIndex: index,
1154
+ gender: g,
1155
+ language,
1049
1156
  }
1050
1157
  );
1051
- } else {
1052
- await this.log('debug', `Updating Leonardo prompt for ${id}`, {
1158
+ continue;
1159
+ }
1160
+
1161
+ await this.log('debug', `Updating Leonardo prompt for ${id}`, {
1162
+ leonardoPrompt,
1163
+ archetypeIndex: index,
1164
+ gender: g,
1165
+ language,
1166
+ });
1167
+
1168
+ const updateResult = await db
1169
+ .update(schema.archetypesData)
1170
+ .set({
1053
1171
  leonardoPrompt,
1054
- });
1055
- const updateResult = await db
1056
- .update(schema.archetypesData)
1057
- .set({
1058
- leonardoPrompt,
1059
- updatedAt: new Date().getTime(),
1060
- })
1061
- .where(
1062
- and(
1063
- eq(schema.archetypesData.id, id),
1064
- eq(schema.archetypesData.language, language),
1065
- eq(schema.archetypesData.gender, g)
1066
- )
1172
+ updatedAt: new Date().getTime(),
1173
+ })
1174
+ .where(
1175
+ and(
1176
+ eq(schema.archetypesData.id, id),
1177
+ eq(schema.archetypesData.language, language),
1178
+ eq(schema.archetypesData.gender, g),
1179
+ eq(schema.archetypesData.archetypeIndex, index)
1067
1180
  )
1068
- .execute();
1069
- await this.log('info', `Updated Leonardo prompt for ${id}`, {
1070
- rowsAffected: updateResult.rowsAffected,
1071
- });
1072
- }
1181
+ )
1182
+ .execute();
1183
+
1184
+ await this.log('info', `Updated Leonardo prompt for ${id}`, {
1185
+ rowsAffected: updateResult.rowsAffected,
1186
+ });
1073
1187
  }
1074
1188
  }
1075
1189
 
@@ -1092,38 +1206,45 @@ export class ArchetypeService {
1092
1206
  lines,
1093
1207
  });
1094
1208
 
1095
- const prompts: LeonardoPrompt[] = [];
1096
- let currentArchetype = 0;
1209
+ if (lines.length !== 12) {
1210
+ this.log('error', 'Expected exactly 12 lines for 3 archetype prompts', {
1211
+ linesCount: lines.length,
1212
+ response,
1213
+ });
1214
+ throw new Error(
1215
+ `Expected 12 lines for 3 archetype prompts, got ${lines.length}`
1216
+ );
1217
+ }
1097
1218
 
1219
+ const prompts: LeonardoPrompt[] = [];
1098
1220
  for (let i = 0; i < lines.length; i += 4) {
1221
+ const archetypeIndex = i / 4 + 1;
1099
1222
  const maleLabel = lines[i];
1100
1223
  const malePromptLine = lines[i + 1];
1101
1224
  const femaleLabel = lines[i + 2];
1102
1225
  const femalePromptLine = lines[i + 3];
1103
1226
 
1104
- currentArchetype++;
1105
- const expectedMaleLabel = `${currentArchetype}.m`;
1106
- const expectedFemaleLabel = `${currentArchetype}.f`;
1227
+ const expectedMaleLabel = `${archetypeIndex}.m`;
1228
+ const expectedFemaleLabel = `${archetypeIndex}.f`;
1107
1229
 
1108
1230
  if (
1109
1231
  !maleLabel?.startsWith(expectedMaleLabel) ||
1110
1232
  !femaleLabel?.startsWith(expectedFemaleLabel)
1111
1233
  ) {
1112
1234
  this.log(
1113
- 'warn',
1114
- `Malformed Leonardo prompt format for archetype ${currentArchetype}`,
1235
+ 'error',
1236
+ `Malformed Leonardo prompt format for archetype ${archetypeIndex}`,
1115
1237
  {
1116
1238
  maleLabel,
1117
1239
  malePromptLine,
1118
1240
  femaleLabel,
1119
1241
  femalePromptLine,
1120
- lines,
1242
+ expectedMaleLabel,
1243
+ expectedFemaleLabel,
1121
1244
  }
1122
1245
  );
1123
1246
  throw new Error(
1124
- `Expected ${expectedMaleLabel} and ${expectedFemaleLabel} at lines ${i} and ${
1125
- i + 2
1126
- }`
1247
+ `Malformed prompt format for archetype ${archetypeIndex}`
1127
1248
  );
1128
1249
  }
1129
1250
 
@@ -1132,42 +1253,30 @@ export class ArchetypeService {
1132
1253
 
1133
1254
  if (!malePrompt || !femalePrompt) {
1134
1255
  this.log(
1135
- 'warn',
1136
- `Empty Leonardo prompt for archetype ${currentArchetype}`,
1256
+ 'error',
1257
+ `Empty Leonardo prompt for archetype ${archetypeIndex}`,
1137
1258
  {
1138
1259
  malePrompt,
1139
1260
  femalePrompt,
1140
- malePromptLine,
1141
- femalePromptLine,
1142
1261
  }
1143
1262
  );
1144
- throw new Error(`Empty prompt for archetype ${currentArchetype}`);
1263
+ throw new Error(`Empty prompt for archetype ${archetypeIndex}`);
1145
1264
  }
1146
1265
 
1147
1266
  prompts.push({ malePrompt, femalePrompt });
1148
- this.log('debug', `Parsed prompts for archetype ${currentArchetype}`, {
1267
+ this.log('debug', `Parsed prompts for archetypeIndex ${archetypeIndex}`, {
1149
1268
  malePrompt,
1150
1269
  femalePrompt,
1151
1270
  });
1152
1271
  }
1153
1272
 
1154
- if (prompts.length !== 3) {
1155
- this.log('error', 'Expected exactly 3 archetype prompts', {
1156
- promptsCount: prompts.length,
1157
- response,
1158
- });
1159
- throw new Error(
1160
- `Expected exactly 3 archetype prompts, but got ${prompts.length}`
1161
- );
1162
- }
1163
-
1164
1273
  this.log('info', 'Completed parseLeonardoPromptResponse', { prompts });
1165
1274
  return prompts;
1166
1275
  }
1167
1276
 
1168
1277
  async fetchArchetypesFromDB(
1169
1278
  combinationString: string,
1170
- gender: Gender,
1279
+ gender: Gender | null, // null to fetch both genders
1171
1280
  language: string
1172
1281
  ) {
1173
1282
  await this.log('debug', 'Executing fetchArchetypesFromDB', {
@@ -1176,20 +1285,23 @@ export class ArchetypeService {
1176
1285
  language,
1177
1286
  });
1178
1287
  const db = this.context.drizzle();
1288
+ const conditions = [
1289
+ eq(schema.archetypesData.combination, combinationString),
1290
+ eq(schema.archetypesData.language, language),
1291
+ ];
1292
+ if (gender) {
1293
+ conditions.push(eq(schema.archetypesData.gender, gender));
1294
+ }
1179
1295
  const result = await db
1180
1296
  .select()
1181
1297
  .from(schema.archetypesData)
1182
- .where(
1183
- and(
1184
- eq(schema.archetypesData.combination, combinationString),
1185
- eq(schema.archetypesData.gender, gender),
1186
- eq(schema.archetypesData.language, language)
1187
- )
1188
- )
1298
+ .where(and(...conditions))
1299
+ .orderBy(schema.archetypesData.archetypeIndex)
1189
1300
  .execute();
1190
1301
  await this.log('debug', 'fetchArchetypesFromDB result', {
1191
1302
  resultCount: result.length,
1192
- result,
1303
+ indexes: result.map((r) => r.archetypeIndex),
1304
+ genders: result.map((r) => r.gender),
1193
1305
  });
1194
1306
  return result;
1195
1307
  }