@zodic/shared 0.0.271 → 0.0.273

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.
@@ -323,25 +323,56 @@ export class ConceptService {
323
323
  conceptSlug: Concept,
324
324
  combinationString: string,
325
325
  override: boolean = false
326
- ): Promise<void> {
326
+ ): Promise<{ generatedPrompt: string }> {
327
327
  console.log(
328
- `🚀 Generating Leonardo prompt for concept: ${conceptSlug}, combination: ${combinationString}`
328
+ `🚀 Generating Leonardo prompt for concept: ${conceptSlug}, combination: ${combinationString}, override: ${override}`
329
329
  );
330
330
 
331
- // ✅ Build KV keys for both languages
332
- const kvKeyEN = buildConceptKVKey('en-us', conceptSlug, combinationString);
333
- const kvKeyPT = buildConceptKVKey('pt-br', conceptSlug, combinationString);
331
+ const db = drizzle(this.context.env.DB); // ✅ Initialize Drizzle with Cloudflare D1
332
+
333
+ // Fetch concept data from D1 for both languages
334
+ console.log(
335
+ `📡 Fetching concept data for ${conceptSlug}:${combinationString}`
336
+ );
337
+
338
+ const conceptEntries = await db
339
+ .select({
340
+ id: conceptsData.id,
341
+ language: conceptsData.language,
342
+ name: conceptsData.name,
343
+ description: conceptsData.description,
344
+ poem: conceptsData.poem,
345
+ leonardoPrompt: conceptsData.leonardoPrompt,
346
+ })
347
+ .from(conceptsData)
348
+ .where(
349
+ and(
350
+ eq(conceptsData.conceptSlug, conceptSlug),
351
+ eq(conceptsData.combination, combinationString),
352
+ inArray(conceptsData.language, ['en-us', 'pt-br']) // ✅ Fetch both languages
353
+ )
354
+ )
355
+ .execute();
356
+
357
+ if (conceptEntries.length !== 2) {
358
+ throw new Error(
359
+ `❌ Missing concept data for ${conceptSlug}:${combinationString}. Ensure basic info is populated first.`
360
+ );
361
+ }
362
+
363
+ const conceptEN = conceptEntries.find((c) => c.language === 'en-us');
364
+ const conceptPT = conceptEntries.find((c) => c.language === 'pt-br');
334
365
 
335
- // Retrieve existing KV data
336
- const conceptEN = await this.getKVConcept(kvKeyEN);
337
- const conceptPT = await this.getKVConcept(kvKeyPT);
366
+ if (!conceptEN || !conceptPT) {
367
+ throw new Error(`❌ Could not find both EN and PT versions.`);
368
+ }
338
369
 
339
- // ✅ Check if prompt already exists
370
+ // ✅ Check if prompt already exists and skip if override is false
340
371
  if (!override && conceptEN.leonardoPrompt && conceptPT.leonardoPrompt) {
341
372
  console.log(
342
373
  `⚡ Leonardo prompt already exists for ${conceptSlug}, skipping.`
343
374
  );
344
- return; // Skip regeneration
375
+ return { generatedPrompt: conceptEN.leonardoPrompt };
345
376
  }
346
377
 
347
378
  // ✅ Ensure basic info is present
@@ -374,18 +405,38 @@ export class ConceptService {
374
405
  );
375
406
  }
376
407
 
377
- // Store the generated prompt in both languages
378
- conceptEN.leonardoPrompt = response.trim();
379
- conceptPT.leonardoPrompt = response.trim();
408
+ const generatedPrompt = response.trim();
380
409
 
381
- await Promise.all([
382
- this.context.kvConceptsStore().put(kvKeyEN, JSON.stringify(conceptEN)),
383
- this.context.kvConceptsStore().put(kvKeyPT, JSON.stringify(conceptPT)),
410
+ // ✅ Store the generated prompt in D1 for both languages
411
+ console.log(
412
+ `💾 Storing Leonardo prompt for ${conceptSlug}:${combinationString} in D1...`
413
+ );
414
+ await db.batch([
415
+ db
416
+ .update(conceptsData)
417
+ .set({ leonardoPrompt: generatedPrompt })
418
+ .where(
419
+ and(
420
+ eq(conceptsData.id, conceptEN.id),
421
+ eq(conceptsData.language, 'en-us')
422
+ )
423
+ ),
424
+ db
425
+ .update(conceptsData)
426
+ .set({ leonardoPrompt: generatedPrompt })
427
+ .where(
428
+ and(
429
+ eq(conceptsData.id, conceptPT.id),
430
+ eq(conceptsData.language, 'pt-br')
431
+ )
432
+ ),
384
433
  ]);
385
434
 
386
435
  console.log(
387
436
  `✅ Leonardo prompt stored for concept: ${conceptSlug}, combination: ${combinationString}, in both languages.`
388
437
  );
438
+
439
+ return { generatedPrompt };
389
440
  }
390
441
 
391
442
  /**
@@ -795,432 +846,6 @@ export class ConceptService {
795
846
  }
796
847
  }
797
848
 
798
- // async parseStructuredContent(
799
- // conceptSlug: Concept,
800
- // response: string
801
- // ): Promise<{
802
- // structuredContentEN: StructuredConceptContent;
803
- // structuredContentPT: StructuredConceptContent;
804
- // }> {
805
- // console.log(
806
- // `📌 [START] Parsing structured content for ${conceptSlug} from ChatGPT response.`
807
- // );
808
-
809
- // // ✅ Step 1: Clean artifacts before processing
810
- // let cleanedResponse = response
811
- // .replace(/[*#•]/g, '') // Remove bullet points, bold markers, headings
812
- // .replace(/---+/g, '') // Remove dividers (like "---")
813
- // .replace(/\n\s*\n/g, '\n\n') // Normalize multiple line breaks
814
- // .replace(/\s+EN:\s*/g, '\nEN: ') // Ensure consistent "EN:" formatting
815
- // .replace(/\s+PT:\s*/g, '\nPT: ') // Ensure consistent "PT:" formatting
816
- // .trim();
817
-
818
- // console.log(
819
- // '🔹 [CLEANED RESPONSE]:',
820
- // cleanedResponse.slice(0, 500) + '...'
821
- // );
822
-
823
- // try {
824
- // // ✅ Step 2: Define Section Titles Per Concept
825
- // const conceptSections: Record<string, { en: string[]; pt: string[] }> = {
826
- // crown: {
827
- // en: [
828
- // 'Core Identity',
829
- // 'Strengths and Challenges',
830
- // 'Path to Fulfillment',
831
- // 'Emotional Depth',
832
- // 'Vision and Aspirations',
833
- // ],
834
- // pt: [
835
- // 'Identidade Essencial',
836
- // 'Forças e Desafios',
837
- // 'Caminho para a Plenitude',
838
- // 'Profundidade Emocional',
839
- // 'Visão e Aspirações',
840
- // ],
841
- // },
842
- // scepter: {
843
- // en: [
844
- // 'The Power of Expression',
845
- // 'The Dance of Action and Reaction',
846
- // 'Navigating Challenges',
847
- // 'The Mirror of Relations',
848
- // 'Impact on the World',
849
- // ],
850
- // pt: [
851
- // 'O Poder da Expressão',
852
- // 'A Dança da Ação e Reação',
853
- // 'Navegando Desafios',
854
- // 'O Espelho das Relações',
855
- // 'Impacto no Mundo',
856
- // ],
857
- // },
858
- // amulet: {
859
- // en: [
860
- // 'The Shield of Protection',
861
- // 'The Strength Within',
862
- // 'Nurturing Growth',
863
- // 'The Cycle of Renewal',
864
- // 'The Anchor of Wisdom',
865
- // ],
866
- // pt: [
867
- // 'O Escudo da Proteção',
868
- // 'A Força Interior',
869
- // 'Cultivando o Crescimento',
870
- // 'O Ciclo da Renovação',
871
- // 'A Âncora da Sabedoria',
872
- // ],
873
- // },
874
- // ring: {
875
- // en: [
876
- // 'Magnetic Pull',
877
- // 'Hidden Desires',
878
- // 'The Fated Dance',
879
- // 'Unveiling the Shadows',
880
- // 'Your Love Power',
881
- // ],
882
- // pt: [
883
- // 'Força Magnética',
884
- // 'Desejos Ocultos',
885
- // 'A Dança do Destino',
886
- // 'Revelando as Sombras',
887
- // 'Seu Poder no Amor',
888
- // ],
889
- // },
890
- // lantern: {
891
- // en: [
892
- // 'The Call to Awakening',
893
- // 'Walking Through Shadows',
894
- // 'The Inner Flame',
895
- // 'Revelations and Truths',
896
- // 'Becoming the Light',
897
- // ],
898
- // pt: [
899
- // 'O Chamado para o Despertar',
900
- // 'Caminhando Pelas Sombras',
901
- // 'A Chama Interior',
902
- // 'Revelações e Verdades',
903
- // 'Tornando-se a Luz',
904
- // ],
905
- // },
906
- // orb: {
907
- // en: [
908
- // 'The Path Unfolding',
909
- // 'A Calling Beyond the Self',
910
- // 'Turning Points of Fate',
911
- // 'Mastering the Journey',
912
- // 'Legacy and Impact',
913
- // ],
914
- // pt: [
915
- // 'O Caminho que se Revela',
916
- // 'Um Chamado Além de Si Mesmo',
917
- // 'Pontos de Virada do Destino',
918
- // 'Dominando a Jornada',
919
- // 'Legado e Impacto',
920
- // ],
921
- // },
922
- // };
923
-
924
- // const sectionsEN = conceptSections[conceptSlug]?.en;
925
- // const sectionsPT = conceptSections[conceptSlug]?.pt;
926
-
927
- // if (!sectionsEN || !sectionsPT) {
928
- // throw new Error(`Unknown concept: ${conceptSlug}`);
929
- // }
930
-
931
- // // ✅ Step 3: Detect if AI interleaved EN/PT sections
932
- // const interleavedMatches = cleanedResponse.match(
933
- // /(EN:\s*[\s\S]+?PT:\s*[\s\S]+?)(?=EN:|$)/
934
- // );
935
- // let extractedEN = '';
936
- // let extractedPT = '';
937
-
938
- // if (interleavedMatches) {
939
- // console.warn(
940
- // '⚠️ Detected interleaved EN/PT sections. Parsing accordingly.'
941
- // );
942
- // extractedEN = interleavedMatches
943
- // .map(
944
- // (pair) => pair.match(/EN:\s*([\s\S]+?)\s*PT:/)?.[1]?.trim() || ''
945
- // )
946
- // .join('\n\n');
947
- // extractedPT = interleavedMatches
948
- // .map((pair) => pair.match(/PT:\s*([\s\S]+)/)?.[1]?.trim() || '')
949
- // .join('\n\n');
950
- // } else {
951
- // console.log(
952
- // '✅ Standard format detected (EN block followed by PT block).'
953
- // );
954
- // extractedEN =
955
- // cleanedResponse.match(/EN:\s*([\s\S]+?)\s*PT:/)?.[1]?.trim() || '';
956
- // extractedPT =
957
- // cleanedResponse.match(/PT:\s*([\s\S]+)/)?.[1]?.trim() || '';
958
- // }
959
-
960
- // console.log(
961
- // '✅ [MATCH SUCCESS] Extracted English Content:',
962
- // extractedEN.slice(0, 500) + '...'
963
- // );
964
- // console.log(
965
- // '✅ [MATCH SUCCESS] Extracted Portuguese Content:',
966
- // extractedPT.slice(0, 500) + '...'
967
- // );
968
-
969
- // // ✅ Step 4: Extract structured sections dynamically
970
- // function extractSections(text: string, sectionTitles: string[]) {
971
- // return sectionTitles.map((title) => {
972
- // console.log(`🔍 [PROCESSING] Extracting section: "${title}"`);
973
-
974
- // const regex = new RegExp(
975
- // `\\d+\\.\\s*${title}:\\s*([\\s\\S]+?)(?=\\n\\d+\\.\\s*[A-Z]|$)`
976
- // );
977
- // const match = text.match(regex);
978
-
979
- // if (!match) {
980
- // throw new Error(`❌ Missing section: "${title}"`);
981
- // }
982
-
983
- // const paragraphs = match[1]
984
- // .trim()
985
- // .split(/\n{2,}/)
986
- // .map((p) => p.trim())
987
- // .filter((p) => p.length > 0);
988
-
989
- // console.log(
990
- // `✅ [EXTRACTED] "${title}" - Parsed Content:`,
991
- // paragraphs
992
- // );
993
- // return { type: 'section', title, content: paragraphs };
994
- // });
995
- // }
996
-
997
- // const structuredContentEN = extractSections(extractedEN, sectionsEN);
998
- // const structuredContentPT = extractSections(extractedPT, sectionsPT);
999
-
1000
- // console.log(
1001
- // '🎯 [FINAL RESULT] Parsed English Content:',
1002
- // JSON.stringify(structuredContentEN, null, 2)
1003
- // );
1004
- // console.log(
1005
- // '🎯 [FINAL RESULT] Parsed Portuguese Content:',
1006
- // JSON.stringify(structuredContentPT, null, 2)
1007
- // );
1008
-
1009
- // console.log(
1010
- // '✅ [COMPLETE] Structured content parsing finished successfully.'
1011
- // );
1012
-
1013
- // return {
1014
- // structuredContentEN,
1015
- // structuredContentPT,
1016
- // };
1017
- // } catch (error) {
1018
- // console.error(`❌ Parsing failed for ${conceptSlug}. Logging failure.`);
1019
-
1020
- // // ✅ Store failure in KV for debugging
1021
- // const kvFailuresStore = this.context.kvConceptFailuresStore();
1022
- // const failureKey = `failures:content:parsing:${conceptSlug}:${new Date().toISOString()}`;
1023
-
1024
- // await kvFailuresStore.put(
1025
- // failureKey,
1026
- // JSON.stringify({
1027
- // error: (error as Error).message,
1028
- // conceptSlug,
1029
- // cleanedResponse,
1030
- // timestamp: new Date().toISOString(),
1031
- // })
1032
- // );
1033
-
1034
- // console.error(`🚨 Failure logged in KV: ${failureKey}`);
1035
- // throw error;
1036
- // }
1037
- // }
1038
-
1039
- // private parseStructuredContent(
1040
- // conceptSlug: Concept,
1041
- // response: string
1042
- // ): {
1043
- // structuredContentEN: StructuredConceptContent;
1044
- // structuredContentPT: StructuredConceptContent;
1045
- // } {
1046
- // console.log(`📌 [START] Parsing structured content for ${conceptSlug} from ChatGPT response.`);
1047
-
1048
- // // ✅ Step 1: Clean artifacts before processing
1049
- // let cleanedResponse = response
1050
- // .replace(/[*#•]/g, '') // Remove bullet points, bold markers, headings
1051
- // .replace(/---+/g, '') // Remove dividers (like "---")
1052
- // .replace(/\n\s*\n/g, '\n\n') // Normalize multiple line breaks
1053
- // .replace(/\s+EN:\s*/g, '\nEN: ') // Ensure consistent "EN:" formatting
1054
- // .replace(/\s+PT:\s*/g, '\nPT: ') // Ensure consistent "PT:" formatting
1055
- // .trim();
1056
-
1057
- // console.log('🔹 [CLEANED RESPONSE]:', cleanedResponse.slice(0, 500) + '...');
1058
-
1059
- // // ✅ Step 2: Define Section Titles Per Concept
1060
- // const conceptSections: Record<string, { en: string[]; pt: string[] }> = {
1061
- // crown: {
1062
- // en: [
1063
- // 'Core Identity',
1064
- // 'Strengths and Challenges',
1065
- // 'Path to Fulfillment',
1066
- // 'Emotional Depth',
1067
- // 'Vision and Aspirations',
1068
- // ],
1069
- // pt: [
1070
- // 'Identidade Essencial',
1071
- // 'Forças e Desafios',
1072
- // 'Caminho para a Plenitude',
1073
- // 'Profundidade Emocional',
1074
- // 'Visão e Aspirações',
1075
- // ],
1076
- // },
1077
- // scepter: {
1078
- // en: [
1079
- // 'The Power of Expression',
1080
- // 'The Dance of Action and Reaction',
1081
- // 'Navigating Challenges',
1082
- // 'The Mirror of Relationships',
1083
- // 'Impact on the World',
1084
- // ],
1085
- // pt: [
1086
- // 'O Poder da Expressão',
1087
- // 'A Dança da Ação e Reação',
1088
- // 'Navegando Desafios',
1089
- // 'O Espelho dos Relacionamentos',
1090
- // 'Impacto no Mundo',
1091
- // ],
1092
- // },
1093
- // amulet: {
1094
- // en: [
1095
- // 'The Shield of Protection',
1096
- // 'The Strength Within',
1097
- // 'Nurturing Growth',
1098
- // 'The Cycle of Renewal',
1099
- // 'The Anchor of Wisdom',
1100
- // ],
1101
- // pt: [
1102
- // 'O Escudo da Proteção',
1103
- // 'A Força Interior',
1104
- // 'Cultivando o Crescimento',
1105
- // 'O Ciclo da Renovação',
1106
- // 'A Âncora da Sabedoria',
1107
- // ],
1108
- // },
1109
- // ring: {
1110
- // en: [
1111
- // 'Magnetic Pull',
1112
- // 'Hidden Desires',
1113
- // 'The Fated Dance',
1114
- // 'Unveiling the Shadows',
1115
- // 'Your Love Power',
1116
- // ],
1117
- // pt: [
1118
- // 'Força Magnética',
1119
- // 'Desejos Ocultos',
1120
- // 'A Dança do Destino',
1121
- // 'Revelando as Sombras',
1122
- // 'Seu Poder no Amor',
1123
- // ],
1124
- // },
1125
- // lantern: {
1126
- // en: [
1127
- // 'The Call to Awakening',
1128
- // 'Walking Through Shadows',
1129
- // 'The Inner Flame',
1130
- // 'Revelations and Truths',
1131
- // 'Becoming the Light',
1132
- // ],
1133
- // pt: [
1134
- // 'O Chamado para o Despertar',
1135
- // 'Caminhando Pelas Sombras',
1136
- // 'A Chama Interior',
1137
- // 'Revelações e Verdades',
1138
- // 'Tornando-se a Luz',
1139
- // ],
1140
- // },
1141
- // orb: {
1142
- // en: [
1143
- // 'The Path Unfolding',
1144
- // 'A Calling Beyond the Self',
1145
- // 'Turning Points of Fate',
1146
- // 'Mastering the Journey',
1147
- // 'Legacy and Impact',
1148
- // ],
1149
- // pt: [
1150
- // 'O Caminho que se Revela',
1151
- // 'Um Chamado Além de Si Mesmo',
1152
- // 'Pontos de Virada do Destino',
1153
- // 'Dominando a Jornada',
1154
- // 'Legado e Impacto',
1155
- // ],
1156
- // },
1157
- // };
1158
-
1159
- // const sectionsEN = conceptSections[conceptSlug]?.en;
1160
- // const sectionsPT = conceptSections[conceptSlug]?.pt;
1161
-
1162
- // if (!sectionsEN || !sectionsPT) {
1163
- // throw new Error(`❌ Unknown concept: ${conceptSlug}`);
1164
- // }
1165
-
1166
- // // ✅ Step 3: Extract English and Portuguese content separately
1167
- // const enMatches = cleanedResponse.match(/EN:\s*([\s\S]+?)\s*PT:/);
1168
- // const ptMatches = cleanedResponse.match(/PT:\s*([\s\S]+)/);
1169
-
1170
- // if (!enMatches || !ptMatches) {
1171
- // console.error('❌ [ERROR] Missing English or Portuguese content in response.');
1172
- // throw new Error('❌ Missing English or Portuguese content in response.');
1173
- // }
1174
-
1175
- // let enContent = enMatches[1].trim();
1176
- // let ptContent = ptMatches[1].trim();
1177
-
1178
- // console.log('✅ [MATCH SUCCESS] Extracted English Content:', enContent.slice(0, 500) + '...');
1179
- // console.log('✅ [MATCH SUCCESS] Extracted Portuguese Content:', ptContent.slice(0, 500) + '...');
1180
-
1181
- // // ✅ Step 4: Debug **why** PT content has "EN:"
1182
- // if (ptContent.includes('EN:')) {
1183
- // console.warn('⚠️ PT content contains "EN:". Check if parsing was incorrect.');
1184
- // }
1185
-
1186
- // // ✅ Step 5: Extract structured sections
1187
- // function extractSections(text: string, sectionTitles: string[]) {
1188
- // return sectionTitles.map((title) => {
1189
- // console.log(`🔍 [PROCESSING] Extracting section: "${title}"`);
1190
-
1191
- // const regex = new RegExp(`\\d+\\.\\s*${title}:\\s*([\\s\\S]+?)(?=\\n\\d+\\.\\s*[A-Z]|$)`);
1192
- // const match = text.match(regex);
1193
-
1194
- // if (!match) {
1195
- // console.warn(`⚠️ [WARNING] Missing section: "${title}"`);
1196
- // return { type: 'section', title, content: ['❌ Section Missing'] };
1197
- // }
1198
-
1199
- // const paragraphs = match[1]
1200
- // .trim()
1201
- // .split(/\n{2,}/)
1202
- // .map((p) => p.trim())
1203
- // .filter((p) => p.length > 0);
1204
-
1205
- // console.log(`✅ [EXTRACTED] "${title}" - Parsed Content:`, paragraphs);
1206
-
1207
- // return { type: 'section', title, content: paragraphs };
1208
- // });
1209
- // }
1210
-
1211
- // const structuredContentEN = extractSections(enContent, sectionsEN);
1212
- // const structuredContentPT = extractSections(ptContent, sectionsPT);
1213
-
1214
- // console.log('🎯 [FINAL RESULT] Parsed English Content:', JSON.stringify(structuredContentEN, null, 2));
1215
- // console.log('🎯 [FINAL RESULT] Parsed Portuguese Content:', JSON.stringify(structuredContentPT, null, 2));
1216
-
1217
- // console.log('✅ [COMPLETE] Structured content parsing finished successfully.');
1218
-
1219
- // return {
1220
- // structuredContentEN,
1221
- // structuredContentPT,
1222
- // };
1223
- // }
1224
849
  /**
1225
850
  * Queue image generation for a concept.
1226
851
  */
@@ -1229,23 +854,35 @@ export class ConceptService {
1229
854
  conceptSlug: Concept,
1230
855
  combinationString: string
1231
856
  ): Promise<void> {
1232
- const drizzle = this.context.drizzle();
857
+ const db = this.context.drizzle();
1233
858
  const { width, height } = sizes['alchemy']['post4:5'];
1234
859
  const { generations, conceptCombinations, concepts } = schema;
1235
860
 
1236
- const kvKey = buildConceptKVKey(language, conceptSlug, combinationString);
1237
861
  console.log(
1238
862
  `🚀 Queuing image generation for concept: ${conceptSlug}, combination: ${combinationString}, language: ${language}`
1239
863
  );
1240
864
 
1241
- const concept = await this.getKVConcept(kvKey);
1242
- if (!concept.leonardoPrompt) {
865
+ const conceptEntries = await db
866
+ .select({
867
+ leonardoPrompt: conceptsData.leonardoPrompt,
868
+ })
869
+ .from(conceptsData)
870
+ .where(
871
+ and(
872
+ eq(conceptsData.conceptSlug, conceptSlug),
873
+ eq(conceptsData.combination, combinationString),
874
+ eq(conceptsData.language, language) // ✅ Fetch both languages
875
+ )
876
+ )
877
+ .limit(1);
878
+
879
+ if (!conceptEntries[0].leonardoPrompt) {
1243
880
  throw new Error(
1244
881
  `❌ Leonardo prompt must be populated before generating images for concept: ${conceptSlug}`
1245
882
  );
1246
883
  }
1247
884
 
1248
- const conceptRecord = await drizzle
885
+ const conceptRecord = await db
1249
886
  .select({
1250
887
  id: concepts.id,
1251
888
  planet1: concepts.planet1,
@@ -1278,7 +915,7 @@ export class ConceptService {
1278
915
  `🔹 Extracted planet signs: ${planet1} -> ${planet1Sign}, ${planet2} -> ${planet2Sign}, ${planet3} -> ${planet3Sign}`
1279
916
  );
1280
917
 
1281
- let [conceptCombination] = await drizzle
918
+ let [conceptCombination] = await db
1282
919
  .select({ id: conceptCombinations.id })
1283
920
  .from(conceptCombinations)
1284
921
  .where(
@@ -1294,7 +931,7 @@ export class ConceptService {
1294
931
  `🔍 No existing conceptCombination found. Creating new entry for ${conceptSlug}:${combinationString}`
1295
932
  );
1296
933
 
1297
- const [insertedCombination] = await drizzle
934
+ const [insertedCombination] = await db
1298
935
  .insert(conceptCombinations)
1299
936
  .values({
1300
937
  id: uuidv4(),
@@ -1338,7 +975,7 @@ export class ConceptService {
1338
975
  const leonardoResponse = await this.context
1339
976
  .api()
1340
977
  .callLeonardo.generateImage({
1341
- prompt: concept.leonardoPrompt,
978
+ prompt: conceptEntries[0].leonardoPrompt,
1342
979
  width,
1343
980
  height,
1344
981
  ...(controlNets.length > 0 ? { controlNets } : {}),
@@ -1354,7 +991,7 @@ export class ConceptService {
1354
991
 
1355
992
  console.log(`✅ Leonardo Generation ID received: ${generationId}`);
1356
993
 
1357
- await drizzle.insert(generations).values({
994
+ await db.insert(generations).values({
1358
995
  id: generationId,
1359
996
  conceptCombinationId,
1360
997
  type: 'concept_image',
@@ -1467,140 +1104,6 @@ export class ConceptService {
1467
1104
  };
1468
1105
  }
1469
1106
 
1470
- // async regenerateDuplicateNames(limit?: number) {
1471
- // console.log(`🔄 Processing duplicate names for regeneration...`);
1472
-
1473
- // const { duplicateEntries, usedNamesEN, usedNamesPT } =
1474
- // await this.findDuplicateNamesAndUsedNames(limit);
1475
-
1476
- // if (duplicateEntries.length === 0) {
1477
- // console.log(`🎉 No duplicates found. Nothing to regenerate.`);
1478
- // return { message: 'No duplicates detected.' };
1479
- // }
1480
-
1481
- // for (const { name: oldNameEN, combinations } of duplicateEntries) {
1482
- // for (let i = 1; i < combinations.length; i++) {
1483
- // const [_, lang, conceptSlug, combinationString] =
1484
- // combinations[i].split(':');
1485
-
1486
- // if (!lang || !conceptSlug || !combinationString) {
1487
- // console.warn(`⚠️ Invalid KV key format: ${combinations[i]}`);
1488
- // continue;
1489
- // }
1490
-
1491
- // const kvKeyEN = buildConceptKVKey(
1492
- // 'en-us',
1493
- // conceptSlug as Concept,
1494
- // combinationString
1495
- // );
1496
- // const kvKeyPT = buildConceptKVKey(
1497
- // 'pt-br',
1498
- // conceptSlug as Concept,
1499
- // combinationString
1500
- // );
1501
-
1502
- // const kvDataEN = (await this.context.env.KV_CONCEPTS.get(
1503
- // kvKeyEN,
1504
- // 'json'
1505
- // )) as {
1506
- // name: string;
1507
- // description: string;
1508
- // };
1509
- // const kvDataPT = (await this.context.env.KV_CONCEPTS.get(
1510
- // kvKeyPT,
1511
- // 'json'
1512
- // )) as {
1513
- // name: string;
1514
- // };
1515
-
1516
- // console.log(
1517
- // `🎭 Regenerating name for: ${kvKeyEN} (Old Name: "${oldNameEN}")`
1518
- // );
1519
-
1520
- // let newNameEN: string | null = null;
1521
- // let newNamePT: string | null = null;
1522
- // let attempts = 0;
1523
- // const maxAttempts = 3;
1524
-
1525
- // while (attempts < maxAttempts) {
1526
- // attempts++;
1527
-
1528
- // const messages = this.context
1529
- // .buildLLMMessages()
1530
- // .regenerateConceptName({
1531
- // oldNameEN: kvDataEN.name,
1532
- // description: kvDataEN.description,
1533
- // usedNamesEN: Array.from(usedNamesEN), // ✅ Ensure array format
1534
- // });
1535
-
1536
- // const aiResponse = await this.context
1537
- // .api()
1538
- // .callTogether.single(messages, {});
1539
- // if (!aiResponse) {
1540
- // console.warn(
1541
- // `⚠️ AI failed to generate a new name for ${kvKeyEN}, skipping.`
1542
- // );
1543
- // break;
1544
- // }
1545
-
1546
- // // ✅ Parse AI response to extract both EN and PT names
1547
- // const [generatedEN, generatedPT] = aiResponse
1548
- // .split('\n')
1549
- // .map((s) => s.trim());
1550
-
1551
- // // ✅ Validate that the generated names are not duplicates
1552
- // if (!usedNamesEN.has(generatedEN) && !usedNamesPT.has(generatedPT)) {
1553
- // newNameEN = generatedEN;
1554
- // newNamePT = generatedPT;
1555
- // break; // ✅ Found unique names, exit retry loop
1556
- // }
1557
-
1558
- // console.warn(
1559
- // `⚠️ AI suggested a duplicate name: EN - "${generatedEN}", PT - "${generatedPT}". Retrying... (Attempt ${attempts}/${maxAttempts})`
1560
- // );
1561
- // }
1562
-
1563
- // if (!newNameEN || !newNamePT) {
1564
- // console.error(
1565
- // `🚨 Failed to generate a unique name for ${kvKeyEN} after ${maxAttempts} attempts.`
1566
- // );
1567
- // continue;
1568
- // }
1569
-
1570
- // console.log(
1571
- // `✅ Storing new names for ${kvKeyEN}: EN - "${newNameEN}", PT - "${newNamePT}"`
1572
- // );
1573
-
1574
- // // ✅ Update KV with new names (EN)
1575
- // Object.assign(kvDataEN, { name: newNameEN });
1576
- // await this.context.env.KV_CONCEPTS.put(
1577
- // kvKeyEN,
1578
- // JSON.stringify(kvDataEN)
1579
- // );
1580
-
1581
- // // ✅ Update KV with new names (PT)
1582
- // Object.assign(kvDataPT, { name: newNamePT });
1583
- // await this.context.env.KV_CONCEPTS.put(
1584
- // kvKeyPT,
1585
- // JSON.stringify(kvDataPT)
1586
- // );
1587
-
1588
- // // ✅ Add new names to the used names sets to prevent future reuse
1589
- // usedNamesEN.add(newNameEN);
1590
- // usedNamesPT.add(newNamePT);
1591
-
1592
- // // ✅ Stop if limit is reached
1593
- // if (limit && --limit <= 0) {
1594
- // console.log(`🎯 Limit reached, stopping further processing.`);
1595
- // return { message: 'Regeneration limit reached.' };
1596
- // }
1597
- // }
1598
- // }
1599
-
1600
- // console.log(`🎉 Duplicate names regenerated successfully.`);
1601
- // return { message: 'Duplicate names processed and regenerated.' };
1602
- // }
1603
-
1604
1107
  async findDuplicateNamesForConcept(conceptSlug: Concept) {
1605
1108
  console.log(
1606
1109
  `🔍 Fetching duplicate names from KV_CONCEPT_CACHE for ${conceptSlug}...`