@zodic/shared 0.0.313 → 0.0.314

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.
@@ -3,6 +3,7 @@ import { inject, injectable } from 'inversify';
3
3
  import { ChatMessages, Composition, schema } from '../..';
4
4
  import { KVArchetype, ZodiacSignSlug } from '../../types/scopes/legacy';
5
5
  import { generateArchetypePrompt } from '../../utils/archetypeNamesMessages';
6
+ import { duplicatedArchetypeNamePrompt } from '../../utils/duplicatedArchetypeNamePrompt';
6
7
  import { buildCosmicMirrorArchetypeKVKey } from '../../utils/KVKeysBuilders';
7
8
  import { AppContext } from '../base';
8
9
 
@@ -751,6 +752,195 @@ export class ArchetypeService {
751
752
  );
752
753
  }
753
754
 
755
+ async regenerateDuplicateArchetypeNamesBatch(
756
+ entriesByCombination: Record<string, any[]>
757
+ ) {
758
+ const db = this.context.drizzle();
759
+ console.log(
760
+ `[RegenerateDuplicatesBatch] Processing batch with ${
761
+ Object.keys(entriesByCombination).length
762
+ } combinations`
763
+ );
764
+
765
+ // Process each combination in the batch
766
+ for (const [combination, entries] of Object.entries(entriesByCombination)) {
767
+ console.log(
768
+ `[RegenerateDuplicatesBatch] Processing combination: ${combination}`
769
+ );
770
+
771
+ // Fetch all existing English names for this combination (across all archetypeIndex, gender, language)
772
+ const existingNamesResult = await db
773
+ .selectDistinct({ name: schema.archetypesData.name })
774
+ .from(schema.archetypesData)
775
+ .where(
776
+ and(
777
+ eq(schema.archetypesData.combination, combination),
778
+ eq(schema.archetypesData.language, 'en-us')
779
+ )
780
+ )
781
+ .execute();
782
+
783
+ let existingNames = existingNamesResult.map((r) => r.name);
784
+ console.log(
785
+ `[RegenerateDuplicatesBatch] Existing names for ${combination}: ${existingNames.join(
786
+ ', '
787
+ )}`
788
+ );
789
+
790
+ // Process each entry with the duplicate name in this combination
791
+ for (const entry of entries) {
792
+ const { archetypeIndex, essenceLine } = entry;
793
+ console.log(
794
+ `[RegenerateDuplicatesBatch] Regenerating name for ${combination} (archetypeIndex: ${archetypeIndex})`
795
+ );
796
+
797
+ let newName: {
798
+ name_en: string;
799
+ name_pt_male: string;
800
+ name_pt_female: string;
801
+ } | null = null;
802
+ let attempts = 0;
803
+ const maxAttempts = 5;
804
+
805
+ // Generate a new name until a unique one is found
806
+ while (attempts < maxAttempts) {
807
+ attempts++;
808
+ console.log(
809
+ `[RegenerateDuplicatesBatch] Attempt ${attempts} to generate a new name for ${combination} (archetypeIndex: ${archetypeIndex})`
810
+ );
811
+
812
+ // Build the prompt
813
+ const prompt = duplicatedArchetypeNamePrompt({
814
+ combination,
815
+ essenceLine: essenceLine || 'A unique and meaningful archetype', // Fallback for null essenceLine
816
+ existingNames,
817
+ });
818
+
819
+ // Send the request to the AI
820
+ const messages: ChatMessages = [{ role: 'user', content: prompt }];
821
+ const response = await this.context
822
+ .api()
823
+ .callTogether.single(messages, {});
824
+
825
+ if (!response) {
826
+ console.error(
827
+ `[RegenerateDuplicatesBatch] No response from model for ${combination} (archetypeIndex: ${archetypeIndex})`
828
+ );
829
+ continue;
830
+ }
831
+
832
+ // Parse the response
833
+ try {
834
+ newName = JSON.parse(response);
835
+ console.log(
836
+ `[RegenerateDuplicatesBatch] Generated new name: ${JSON.stringify(
837
+ newName,
838
+ null,
839
+ 2
840
+ )}`
841
+ );
842
+
843
+ // Validate the response format
844
+ if (
845
+ !newName ||
846
+ !newName.name_en ||
847
+ !newName.name_pt_male ||
848
+ !newName.name_pt_female
849
+ ) {
850
+ console.error(
851
+ `[RegenerateDuplicatesBatch] Invalid response format for ${combination} (archetypeIndex: ${archetypeIndex}): ${response}`
852
+ );
853
+ newName = null;
854
+ continue;
855
+ }
856
+ } catch (error) {
857
+ console.error(
858
+ `[RegenerateDuplicatesBatch] Failed to parse AI response for ${combination} (archetypeIndex: ${archetypeIndex}): ${response}`,
859
+ error
860
+ );
861
+ continue;
862
+ }
863
+
864
+ // Check if the new name is unique (globally across all combinations)
865
+ const isDuplicate = await db
866
+ .select({ name: schema.archetypesData.name })
867
+ .from(schema.archetypesData)
868
+ .where(
869
+ and(
870
+ eq(schema.archetypesData.language, 'en-us'),
871
+ eq(schema.archetypesData.name, newName.name_en)
872
+ )
873
+ )
874
+ .limit(1)
875
+ .execute();
876
+
877
+ if (isDuplicate.length > 0) {
878
+ console.warn(
879
+ `[RegenerateDuplicatesBatch] Generated name "${newName.name_en}" is already used. Adding to existing names and retrying.`
880
+ );
881
+ existingNames.push(newName.name_en);
882
+ newName = null;
883
+ continue;
884
+ }
885
+
886
+ // If we reach here, the name is unique
887
+ break;
888
+ }
889
+
890
+ if (!newName) {
891
+ console.error(
892
+ `[RegenerateDuplicatesBatch] Failed to generate a unique name for ${combination} (archetypeIndex: ${archetypeIndex}) after ${maxAttempts} attempts`
893
+ );
894
+ continue;
895
+ }
896
+
897
+ // Update the database with the new names
898
+ for (const gender of ['male', 'female']) {
899
+ const enId = `${combination}:${gender}:${archetypeIndex}`;
900
+ const ptId = `${combination}:${gender}:${archetypeIndex}:pt`;
901
+
902
+ // Update English entry
903
+ await db
904
+ .update(schema.archetypesData)
905
+ .set({
906
+ name: newName.name_en,
907
+ updatedAt: new Date().getTime(),
908
+ })
909
+ .where(
910
+ and(
911
+ eq(schema.archetypesData.id, enId),
912
+ eq(schema.archetypesData.language, 'en-us')
913
+ )
914
+ )
915
+ .execute();
916
+
917
+ // Update Portuguese entry
918
+ const ptName =
919
+ gender === 'female' ? newName.name_pt_female : newName.name_pt_male;
920
+ await db
921
+ .update(schema.archetypesData)
922
+ .set({
923
+ name: ptName,
924
+ updatedAt: new Date().getTime(),
925
+ })
926
+ .where(
927
+ and(
928
+ eq(schema.archetypesData.id, ptId),
929
+ eq(schema.archetypesData.language, 'pt-br')
930
+ )
931
+ )
932
+ .execute();
933
+
934
+ console.log(
935
+ `[RegenerateDuplicatesBatch] Updated ${combination} (archetypeIndex: ${archetypeIndex}, gender: ${gender}) with new name - English: ${newName.name_en}, Portuguese: ${ptName}`
936
+ );
937
+ }
938
+ }
939
+ }
940
+
941
+ console.log(`✅ [RegenerateDuplicatesBatch] Finished processing batch`);
942
+ }
943
+
754
944
  async recycleArchetypeNameDumpsBatch(
755
945
  compositions: Composition[]
756
946
  ): Promise<void> {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zodic/shared",
3
- "version": "0.0.313",
3
+ "version": "0.0.314",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -0,0 +1,47 @@
1
+ type ArchetypePromptInputs = {
2
+ combination: string;
3
+ essenceLine: string;
4
+ existingNames: string[];
5
+ };
6
+
7
+ export function duplicatedArchetypeNamePrompt({
8
+ combination,
9
+ essenceLine,
10
+ existingNames,
11
+ }: ArchetypePromptInputs): string {
12
+ return `
13
+ You are a creative naming assistant for a symbolic astrology project. Each archetype is defined by a unique combination of three zodiac signs (Sun, Moon, Ascendant), and has an associated *essence line* that captures its spiritual and psychological meaning. Your task is to generate a new, poetic and meaningful name for the archetype, ensuring it is unique among previously used names.
14
+
15
+ ---
16
+
17
+ Input:
18
+
19
+ - Combination: \`${combination}\`
20
+ - Essence Line: \`${essenceLine}\`
21
+ - Existing Names: \`${JSON.stringify(existingNames)}\`
22
+
23
+ ---
24
+
25
+ Important Instructions:
26
+
27
+ 1. Create a new archetype name that poetically reflects the given essence line.
28
+ 2. The name should follow the same style as existing archetypes (e.g., \`The Wandering Alchemist\`, \`The Radiant Mask\`, \`The Harmonious Builder\`).
29
+ 3. The name must not appear in the list of existing names.
30
+ 4. Also generate Portuguese translations of the name:
31
+ - One in masculine form.
32
+ - One in feminine form.
33
+ 5. Ensure all outputs are semantically coherent and well-written.
34
+ 6. Use the following output format precisely.
35
+
36
+ ---
37
+
38
+ Output Format (exactly like this, do not change it):
39
+
40
+ {
41
+ "name_en": <Name in English>,
42
+ "name_pt_male": <Name in Portuguese Male>,
43
+ "name_pt_female": <Name in Portuguese Female>,
44
+ }
45
+
46
+ `;
47
+ }