@zodic/shared 0.0.373 → 0.0.374

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