@zodic/shared 0.0.307 → 0.0.309
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.
- package/app/services/ArchetypeService.ts +268 -65
- package/package.json +1 -1
- package/utils/archetypeNamesMessages.ts +18 -10
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import { and, eq, sql } from 'drizzle-orm';
|
|
3
2
|
import { inject, injectable } from 'inversify';
|
|
4
3
|
import { ChatMessages, Composition, schema } from '../..';
|
|
@@ -313,7 +312,8 @@ export class ArchetypeService {
|
|
|
313
312
|
return;
|
|
314
313
|
}
|
|
315
314
|
|
|
316
|
-
const { english: englishNames, portuguese: portugueseVariants } =
|
|
315
|
+
const { english: englishNames, portuguese: portugueseVariants } =
|
|
316
|
+
this.parseArchetypeNameBlocks(response);
|
|
317
317
|
|
|
318
318
|
async function isEnglishNameDuplicate(name: string): Promise<boolean> {
|
|
319
319
|
const result = await db
|
|
@@ -610,7 +610,12 @@ export class ArchetypeService {
|
|
|
610
610
|
return;
|
|
611
611
|
}
|
|
612
612
|
|
|
613
|
-
|
|
613
|
+
// Clean up the response by removing unexpected suffixes
|
|
614
|
+
const cleanedResponse = response
|
|
615
|
+
.replace(/###|---\s*###|-\s*###/g, '')
|
|
616
|
+
.trim();
|
|
617
|
+
|
|
618
|
+
const blocks = cleanedResponse
|
|
614
619
|
.split(/Composition \d+/)
|
|
615
620
|
.slice(1)
|
|
616
621
|
.map((b) => b.trim());
|
|
@@ -623,13 +628,13 @@ export class ArchetypeService {
|
|
|
623
628
|
|
|
624
629
|
console.log(`🔄 [Batch] Processing: ${combination}`);
|
|
625
630
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
console.error(`❌ [Batch] Parsing failed for: ${combination}
|
|
631
|
+
const { english, portuguese } = this.parseArchetypeNameBlocks(block);
|
|
632
|
+
|
|
633
|
+
if (
|
|
634
|
+
english.length !== indexes.length ||
|
|
635
|
+
portuguese.length !== indexes.length
|
|
636
|
+
) {
|
|
637
|
+
console.error(`❌ [Batch] Parsing failed for: ${combination}`);
|
|
633
638
|
await this.context
|
|
634
639
|
.drizzle()
|
|
635
640
|
.insert(schema.archetypeNameDumps)
|
|
@@ -643,37 +648,27 @@ export class ArchetypeService {
|
|
|
643
648
|
continue;
|
|
644
649
|
}
|
|
645
650
|
|
|
646
|
-
const english = en
|
|
647
|
-
.split(/\n\d\.\s*\n?/)
|
|
648
|
-
.filter(Boolean)
|
|
649
|
-
.map((line) => ({
|
|
650
|
-
name: line.match(/• Name:\s*(.+)/)?.[1]?.trim() || '',
|
|
651
|
-
essenceLine: line.match(/• Essence:\s*(.+)/)?.[1]?.trim() || '',
|
|
652
|
-
}));
|
|
653
|
-
|
|
654
|
-
const portuguese = pt
|
|
655
|
-
.split(/\n\d\.\s*\n?/)
|
|
656
|
-
.filter(Boolean)
|
|
657
|
-
.map((line) => ({
|
|
658
|
-
masc: line.match(/• Masculino:\s*(.+)/)?.[1]?.trim() || '',
|
|
659
|
-
fem: line.match(/• Feminino:\s*(.+)/)?.[1]?.trim() || '',
|
|
660
|
-
essenceLine: line.match(/• Essência:\s*(.+)/)?.[1]?.trim() || '',
|
|
661
|
-
}));
|
|
662
|
-
|
|
663
651
|
for (const index of indexes) {
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
652
|
+
const idx = index - 1;
|
|
653
|
+
const englishEntry = english[idx];
|
|
654
|
+
const ptEntry = portuguese[idx];
|
|
655
|
+
|
|
656
|
+
if (!englishEntry || !ptEntry) {
|
|
657
|
+
await this.context
|
|
658
|
+
.drizzle()
|
|
659
|
+
.insert(schema.archetypeNameDumps)
|
|
660
|
+
.values({
|
|
661
|
+
id: `${combination}:${index}:${Date.now()}`,
|
|
662
|
+
combination,
|
|
663
|
+
rawText: block,
|
|
664
|
+
parsedSuccessfully: 0,
|
|
665
|
+
createdAt: Date.now(),
|
|
666
|
+
});
|
|
667
|
+
console.warn(
|
|
668
|
+
`⚠️ [Batch] Skipping index ${index} for ${combination} due to missing parsed block`
|
|
669
|
+
);
|
|
673
670
|
continue;
|
|
674
671
|
}
|
|
675
|
-
const englishEntry = english[index - 1];
|
|
676
|
-
const ptEntry = portuguese[index - 1];
|
|
677
672
|
|
|
678
673
|
for (const gender of ['male', 'female']) {
|
|
679
674
|
const enId = `${combination}:${gender}:${index}`;
|
|
@@ -737,11 +732,188 @@ export class ArchetypeService {
|
|
|
737
732
|
`🎉 [Batch] Completed regenerating ${compositions.length} combinations`
|
|
738
733
|
);
|
|
739
734
|
}
|
|
740
|
-
|
|
735
|
+
|
|
736
|
+
async recycleArchetypeNameDumpsBatch(
|
|
737
|
+
compositions: Composition[]
|
|
738
|
+
): Promise<void> {
|
|
739
|
+
console.log('🔁 [Recycle] Starting recycling of archetype name dumps');
|
|
740
|
+
|
|
741
|
+
if (compositions.length === 0) {
|
|
742
|
+
console.log('✅ [Recycle] No compositions provided for recycling');
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
console.log(
|
|
747
|
+
`📦 [Recycle] Total compositions to recycle: ${compositions.length}`
|
|
748
|
+
);
|
|
749
|
+
|
|
750
|
+
// Step 1: Process each composition
|
|
751
|
+
const db = this.context.drizzle();
|
|
752
|
+
for (const comp of compositions) {
|
|
753
|
+
const combination = [comp.sun, comp.ascendant, comp.moon].join('-');
|
|
754
|
+
const indexes = comp.indexesToGenerate ?? [1, 2, 3];
|
|
755
|
+
|
|
756
|
+
console.log(`🔄 [Recycle] Processing: ${combination}`);
|
|
757
|
+
|
|
758
|
+
// Fetch all dump entries for this combination
|
|
759
|
+
const dumpEntries = await db
|
|
760
|
+
.select({
|
|
761
|
+
id: schema.archetypeNameDumps.id,
|
|
762
|
+
rawText: schema.archetypeNameDumps.rawText,
|
|
763
|
+
})
|
|
764
|
+
.from(schema.archetypeNameDumps)
|
|
765
|
+
.where(eq(schema.archetypeNameDumps.combination, combination))
|
|
766
|
+
.execute();
|
|
767
|
+
|
|
768
|
+
if (dumpEntries.length === 0) {
|
|
769
|
+
console.log(
|
|
770
|
+
`⚠️ [Recycle] No dump entries found for combination: ${combination}`
|
|
771
|
+
);
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Step 2: Process each missing index
|
|
776
|
+
for (const index of indexes) {
|
|
777
|
+
// Find a dump entry that contains the specific index
|
|
778
|
+
let rawText: string | null = null;
|
|
779
|
+
for (const entry of dumpEntries) {
|
|
780
|
+
if (entry.rawText.includes(`-EN ${index}.`)) {
|
|
781
|
+
rawText = entry.rawText;
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (!rawText) {
|
|
787
|
+
console.log(
|
|
788
|
+
`⚠️ [Recycle] No dump entry contains index ${index} for combination: ${combination}`
|
|
789
|
+
);
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// Clean up the raw text
|
|
794
|
+
const cleanedText = rawText
|
|
795
|
+
.replace(/###|---\s*###|-\s*###/g, '')
|
|
796
|
+
.trim();
|
|
797
|
+
|
|
798
|
+
// Extract the specific index sections
|
|
799
|
+
const enSectionMatch = cleanedText.match(
|
|
800
|
+
new RegExp(`-EN ${index}\\.\\s*[^-]*(?=-EN|-PT|$)`, 's')
|
|
801
|
+
);
|
|
802
|
+
const ptSectionMatch = cleanedText.match(
|
|
803
|
+
new RegExp(`-PT ${index}\\.\\s*[^-]*(?=-EN|-PT|$)`, 's')
|
|
804
|
+
);
|
|
805
|
+
|
|
806
|
+
if (!enSectionMatch || !ptSectionMatch) {
|
|
807
|
+
console.log(
|
|
808
|
+
`⚠️ [Recycle] Could not extract index ${index} sections for combination: ${combination}`
|
|
809
|
+
);
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Combine the extracted sections into a block for parsing
|
|
814
|
+
const block = `-EN\n${enSectionMatch[0]
|
|
815
|
+
.replace(/-EN\s*/, '')
|
|
816
|
+
.trim()}\n\n-PT\n${ptSectionMatch[0].replace(/-PT\s*/, '').trim()}`;
|
|
817
|
+
|
|
818
|
+
// Step 3: Parse the block
|
|
819
|
+
const { english, portuguese } = this.parseArchetypeNameBlocks(block);
|
|
820
|
+
|
|
821
|
+
if (english.length === 0 || portuguese.length === 0) {
|
|
822
|
+
console.error(
|
|
823
|
+
`❌ [Recycle] Parsing failed for index ${index} of combination: ${combination}`
|
|
824
|
+
);
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
const englishEntry = english[0];
|
|
829
|
+
const ptEntry = portuguese[0];
|
|
830
|
+
|
|
831
|
+
if (!englishEntry || !ptEntry) {
|
|
832
|
+
console.warn(
|
|
833
|
+
`⚠️ [Recycle] Skipping index ${index} for ${combination} due to missing parsed data`
|
|
834
|
+
);
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// Step 4: Insert the parsed data into archetypes_data
|
|
839
|
+
for (const gender of ['male', 'female']) {
|
|
840
|
+
const enId = `${combination}:${gender}:${index}`;
|
|
841
|
+
const ptId = `${combination}:${gender}:${index}:pt`;
|
|
842
|
+
const ptName = gender === 'female' ? ptEntry.fem : ptEntry.masc;
|
|
843
|
+
|
|
844
|
+
await this.context
|
|
845
|
+
.drizzle()
|
|
846
|
+
.insert(schema.archetypesData)
|
|
847
|
+
.values({
|
|
848
|
+
id: enId,
|
|
849
|
+
combination,
|
|
850
|
+
gender,
|
|
851
|
+
archetypeIndex: index.toString(),
|
|
852
|
+
language: 'en-us',
|
|
853
|
+
name: englishEntry.name,
|
|
854
|
+
essenceLine: englishEntry.essenceLine,
|
|
855
|
+
status: 'idle',
|
|
856
|
+
})
|
|
857
|
+
.onConflictDoUpdate({
|
|
858
|
+
target: [schema.archetypesData.id],
|
|
859
|
+
set: {
|
|
860
|
+
name: englishEntry.name,
|
|
861
|
+
essenceLine: englishEntry.essenceLine,
|
|
862
|
+
updatedAt: new Date().getTime(),
|
|
863
|
+
},
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
await this.context
|
|
867
|
+
.drizzle()
|
|
868
|
+
.insert(schema.archetypesData)
|
|
869
|
+
.values({
|
|
870
|
+
id: ptId,
|
|
871
|
+
combination,
|
|
872
|
+
gender,
|
|
873
|
+
archetypeIndex: index.toString(),
|
|
874
|
+
language: 'pt-br',
|
|
875
|
+
name: ptName,
|
|
876
|
+
essenceLine: ptEntry.essenceLine,
|
|
877
|
+
status: 'idle',
|
|
878
|
+
})
|
|
879
|
+
.onConflictDoUpdate({
|
|
880
|
+
target: [schema.archetypesData.id],
|
|
881
|
+
set: {
|
|
882
|
+
name: ptName,
|
|
883
|
+
essenceLine: ptEntry.essenceLine,
|
|
884
|
+
updatedAt: new Date().getTime(),
|
|
885
|
+
},
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
await db
|
|
889
|
+
.delete(schema.archetypeNameDumps)
|
|
890
|
+
.where(
|
|
891
|
+
eq(
|
|
892
|
+
schema.archetypeNameDumps.id,
|
|
893
|
+
dumpEntries.find((e) => e.rawText === rawText)?.id || ''
|
|
894
|
+
)
|
|
895
|
+
);
|
|
896
|
+
|
|
897
|
+
console.log(
|
|
898
|
+
`✅ [Recycle] Saved archetype ${index} (${gender}) for ${combination}`
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
console.log(`🏁 [Recycle] Finished combination: ${combination}`);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
console.log(
|
|
907
|
+
`🎉 [Recycle] Completed recycling for ${compositions.length} combinations`
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
|
|
741
911
|
async fetchMissingArchetypeCompositions(): Promise<Composition[]> {
|
|
742
912
|
const db = this.context.drizzle();
|
|
743
913
|
const incomplete = await this.findIncompleteCombinations();
|
|
744
|
-
console.log(
|
|
914
|
+
console.log(
|
|
915
|
+
`🔎 [Fetch] Retrieved ${incomplete.length} incomplete combinations`
|
|
916
|
+
);
|
|
745
917
|
const limited = incomplete.slice(0, 400);
|
|
746
918
|
|
|
747
919
|
const compositions: Composition[] = [];
|
|
@@ -776,13 +948,18 @@ export class ArchetypeService {
|
|
|
776
948
|
moon,
|
|
777
949
|
indexesToGenerate,
|
|
778
950
|
});
|
|
779
|
-
console.log(
|
|
951
|
+
console.log(
|
|
952
|
+
`✅ [Fetch] Will regenerate indexes [${indexesToGenerate.join(
|
|
953
|
+
', '
|
|
954
|
+
)}] for ${combination}`
|
|
955
|
+
);
|
|
780
956
|
}
|
|
781
957
|
}
|
|
782
958
|
|
|
783
|
-
console.log(
|
|
784
|
-
|
|
785
|
-
|
|
959
|
+
console.log(
|
|
960
|
+
`📦 [Fetch] Total compositions to regenerate (limited to 400): ${compositions.length}`
|
|
961
|
+
);
|
|
962
|
+
|
|
786
963
|
return compositions;
|
|
787
964
|
}
|
|
788
965
|
|
|
@@ -794,43 +971,69 @@ export class ArchetypeService {
|
|
|
794
971
|
english: [] as { name: string; essenceLine: string }[],
|
|
795
972
|
portuguese: [] as { masc: string; fem: string; essenceLine: string }[],
|
|
796
973
|
};
|
|
797
|
-
|
|
974
|
+
|
|
798
975
|
try {
|
|
799
|
-
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
976
|
+
// Normalize the block by replacing multiple spaces with a single space and ensuring newlines
|
|
977
|
+
const normalizedBlock = block
|
|
978
|
+
.replace(/\s+/g, ' ')
|
|
979
|
+
.replace(/-EN\s*/, '-EN\n')
|
|
980
|
+
.replace(/-PT\s*/, '\n-PT\n')
|
|
981
|
+
.trim();
|
|
982
|
+
|
|
983
|
+
// Split into EN and PT sections
|
|
984
|
+
const sections = normalizedBlock.split(/\n?-PT\n/);
|
|
985
|
+
const enSection = sections[0]?.replace(/-EN\n/, '').trim() ?? '';
|
|
986
|
+
const ptSection = sections[1]?.trim() ?? '';
|
|
987
|
+
|
|
988
|
+
// Parse English entries
|
|
989
|
+
result.english = enSection
|
|
990
|
+
.split(/\n?\d+\.\s*\n?/)
|
|
807
991
|
.filter(Boolean)
|
|
808
992
|
.map((entry, i) => {
|
|
809
|
-
const
|
|
810
|
-
const
|
|
993
|
+
const nameMatch = entry.match(/• Name:\s*([^•]+)/);
|
|
994
|
+
const essenceMatch = entry.match(/• Essence:\s*([^•]+)/);
|
|
995
|
+
const name = nameMatch?.[1]?.trim() ?? '';
|
|
996
|
+
const essenceLine = essenceMatch?.[1]?.trim() ?? '';
|
|
811
997
|
if (!name || !essenceLine) {
|
|
812
|
-
console.warn(
|
|
998
|
+
console.warn(
|
|
999
|
+
`⚠️ [Parse] Incomplete English entry ${i + 1}:`,
|
|
1000
|
+
entry
|
|
1001
|
+
);
|
|
813
1002
|
}
|
|
814
1003
|
return { name, essenceLine };
|
|
815
1004
|
});
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
1005
|
+
|
|
1006
|
+
// Parse Portuguese entries
|
|
1007
|
+
result.portuguese = ptSection
|
|
1008
|
+
.split(/\n?\d+\.\s*\n?/)
|
|
819
1009
|
.filter(Boolean)
|
|
820
1010
|
.map((entry, i) => {
|
|
821
|
-
const
|
|
822
|
-
const
|
|
823
|
-
const
|
|
1011
|
+
const mascMatch = entry.match(/• Masculino:\s*([^•]+)/);
|
|
1012
|
+
const femMatch = entry.match(/• Feminino:\s*([^•]+)/);
|
|
1013
|
+
const essenceMatch = entry.match(/• Essência:\s*([^•]+)/);
|
|
1014
|
+
const masc = mascMatch?.[1]?.trim() ?? '';
|
|
1015
|
+
const fem = femMatch?.[1]?.trim() ?? '';
|
|
1016
|
+
const essenceLine = essenceMatch?.[1]?.trim() ?? '';
|
|
824
1017
|
if (!masc || !fem || !essenceLine) {
|
|
825
|
-
console.warn(
|
|
1018
|
+
console.warn(
|
|
1019
|
+
`⚠️ [Parse] Incomplete Portuguese entry ${i + 1}:`,
|
|
1020
|
+
entry
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
1023
|
+
// Validate gender consistency
|
|
1024
|
+
if (masc.startsWith('A ') && fem.startsWith('O ')) {
|
|
1025
|
+
console.warn(
|
|
1026
|
+
`⚠️ [Parse] Gender mismatch in Portuguese entry ${i + 1}:`,
|
|
1027
|
+
entry
|
|
1028
|
+
);
|
|
1029
|
+
return { masc: fem, fem: masc, essenceLine }; // Swap to correct the mismatch
|
|
826
1030
|
}
|
|
827
1031
|
return { masc, fem, essenceLine };
|
|
828
1032
|
});
|
|
829
|
-
|
|
830
1033
|
} catch (error) {
|
|
831
1034
|
console.error('❌ [Parse] Failed to parse block:', error);
|
|
832
1035
|
}
|
|
833
|
-
|
|
1036
|
+
|
|
834
1037
|
return result;
|
|
835
1038
|
}
|
|
836
1039
|
}
|
package/package.json
CHANGED
|
@@ -211,6 +211,14 @@ For every composition, return:
|
|
|
211
211
|
Avoid names that sound like objects, titles, or abstract forces (e.g., The Lantern, The Eclipse, The Veil).
|
|
212
212
|
Instead, use names that suggest a personified being or living archetype, such as The Masked Oracle, The Starborn Seeker, or The Dancer of the Falling Sky.
|
|
213
213
|
|
|
214
|
+
**Formatting Rules**:
|
|
215
|
+
- Each composition block must start with "Composition <number>" on its own line.
|
|
216
|
+
- Within each composition block, the English section must start with "-EN" on its own line, followed by the entries.
|
|
217
|
+
- The Portuguese section must start with "-PT" on its own line, followed by the entries.
|
|
218
|
+
- Each entry (e.g., "1.", "2.", "3.") must be on its own line, with its sub-items (e.g., "• Name: ...", "• Masculino: ...") on separate lines.
|
|
219
|
+
- Do not include any extra characters, suffixes, or separators (e.g., "###", "---", or extra newlines) at the end of the response or between composition blocks.
|
|
220
|
+
- Ensure that Portuguese names correctly match the gender: "Masculino" names should use masculine forms (e.g., "O Sábio"), and "Feminino" names should use feminine forms (e.g., "A Sábia").
|
|
221
|
+
|
|
214
222
|
Do not include any commentary or explanations outside the defined output structure. Only return the output block for each composition, exactly as described:`;
|
|
215
223
|
|
|
216
224
|
const compositionBlocks = compositions.map((comp, i) => {
|
|
@@ -219,7 +227,7 @@ Do not include any commentary or explanations outside the defined output structu
|
|
|
219
227
|
const moon = influenceMap.moon[comp.moon];
|
|
220
228
|
const indexes = comp.indexesToGenerate ?? [1, 2, 3];
|
|
221
229
|
|
|
222
|
-
const influences = `\n\
|
|
230
|
+
const influences = `\n\nComposition ${i + 1}
|
|
223
231
|
|
|
224
232
|
• First Influence – ${sun.label}: ${sun.description}
|
|
225
233
|
• Second Influence – ${asc.label}: ${asc.description}
|
|
@@ -228,28 +236,28 @@ Do not include any commentary or explanations outside the defined output structu
|
|
|
228
236
|
const enLines = indexes
|
|
229
237
|
.map(
|
|
230
238
|
(idx) => `${idx}.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
239
|
+
• Name: [Name that emphasizes the ${
|
|
240
|
+
['first', 'second', 'third'][idx - 1]
|
|
241
|
+
} influence, while blending the other two]
|
|
242
|
+
• Essence: [Short poetic description line in English]`
|
|
235
243
|
)
|
|
236
244
|
.join('\n');
|
|
237
245
|
|
|
238
246
|
const ptLines = indexes
|
|
239
247
|
.map(
|
|
240
248
|
(idx) => `${idx}.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
249
|
+
• Masculino: [Portuguese name (masc)]
|
|
250
|
+
• Feminino: [Portuguese name (fem)]
|
|
251
|
+
• Essência: [Short poetic description line in Portuguese]`
|
|
244
252
|
)
|
|
245
253
|
.join('\n');
|
|
246
254
|
|
|
247
255
|
return `${influences}
|
|
248
256
|
|
|
249
|
-
\`\n-EN
|
|
257
|
+
\`\n-EN
|
|
250
258
|
${enLines}
|
|
251
259
|
|
|
252
|
-
-PT
|
|
260
|
+
-PT
|
|
253
261
|
${ptLines}
|
|
254
262
|
\``;
|
|
255
263
|
});
|