@zodic/shared 0.0.223 → 0.0.225

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.
@@ -1034,44 +1034,48 @@ export class ConceptService {
1034
1034
  conceptSlug: Concept,
1035
1035
  maxEntries?: number
1036
1036
  ) {
1037
- console.log(
1038
- `🔄 [START] Regenerating duplicate names for concept: ${conceptSlug}...`
1039
- );
1040
-
1037
+ console.log(`🔄 [START] Regenerating duplicate names for concept: ${conceptSlug}...`);
1038
+ const startTime = Date.now();
1039
+
1041
1040
  const db = drizzle(this.context.env.DB);
1042
-
1041
+
1043
1042
  // ✅ Step 1: Fetch duplicate entries filtered by conceptSlug
1043
+ console.log(`📡 Fetching duplicate entries for concept: ${conceptSlug}...`);
1044
+
1044
1045
  const duplicateEntries = await db
1045
- .select({
1046
- id: conceptsData.id,
1047
- name: conceptsData.name,
1048
- description: conceptsData.description,
1049
- combination: conceptsData.combination,
1050
- })
1051
- .from(conceptsData)
1052
- .where(
1053
- and(
1054
- eq(conceptsData.language, 'en-us'), // English version only
1055
- eq(conceptsData.conceptSlug, conceptSlug), // ✅ Filter by concept
1056
- sql`name IN (
1057
- SELECT name FROM concepts_data
1046
+ .select({
1047
+ id: conceptsData.id,
1048
+ name: conceptsData.name,
1049
+ description: conceptsData.description,
1050
+ combination: conceptsData.combination,
1051
+ })
1052
+ .from(conceptsData)
1053
+ .where(
1054
+ and(
1055
+ eq(conceptsData.language, 'en-us'), // English only
1056
+ eq(conceptsData.conceptSlug, conceptSlug), // ✅ Filter by concept
1057
+ sql`
1058
+ name IN (
1059
+ SELECT name
1060
+ FROM concepts_data
1058
1061
  WHERE language = 'en-us' AND concept_slug = ${conceptSlug}
1059
- GROUP BY name HAVING COUNT(*) > 1
1060
- )`
1061
- )
1062
+ GROUP BY name
1063
+ HAVING COUNT(*) > 1
1064
+ )
1065
+ `
1062
1066
  )
1063
- .limit(maxEntries || 50) // ✅ Apply optional limit
1064
- .execute();
1065
-
1067
+ )
1068
+ .orderBy(conceptsData.name) // ✅ Order by name for better grouping
1069
+ .limit(maxEntries || 50) // ✅ Apply optional limit
1070
+ .execute();
1071
+
1066
1072
  if (!duplicateEntries.length) {
1067
1073
  console.log(`🎉 No duplicates found for ${conceptSlug}.`);
1068
1074
  return { message: `No duplicates detected for ${conceptSlug}.` };
1069
1075
  }
1070
-
1071
- console.log(
1072
- `🔍 Found ${duplicateEntries.length} duplicate entries to process.`
1073
- );
1074
-
1076
+
1077
+ console.log(`🔍 Found ${duplicateEntries.length} duplicate entries to process.`);
1078
+
1075
1079
  // ✅ Group duplicates by name
1076
1080
  const duplicateGroups = new Map<string, typeof duplicateEntries>();
1077
1081
  for (const entry of duplicateEntries) {
@@ -1080,59 +1084,50 @@ export class ConceptService {
1080
1084
  }
1081
1085
  duplicateGroups.get(entry.name)!.push(entry);
1082
1086
  }
1083
-
1087
+
1084
1088
  let recentDuplicates = new Set<string>(); // ✅ Track newly generated names
1085
1089
  let updatedEntries = 0;
1086
-
1090
+ let skippedEntries = 0;
1091
+ let failedEntries = 0;
1092
+
1087
1093
  for (const [duplicateName, entries] of duplicateGroups.entries()) {
1088
- console.log(
1089
- `🔄 Processing duplicate group: "${duplicateName}" with ${entries.length} occurrences`
1090
- );
1091
-
1094
+ console.log(`🔄 Processing duplicate group: "${duplicateName}" with ${entries.length} occurrences`);
1095
+
1092
1096
  // ✅ Leave the first entry unchanged and only process the extras
1093
1097
  const [keepEntry, ...entriesToProcess] = entries;
1094
1098
  console.log(`✅ Keeping original name for ID: ${keepEntry.id}`);
1095
-
1099
+
1096
1100
  for (const entry of entriesToProcess) {
1097
1101
  const { id, description, combination } = entry;
1098
-
1102
+
1099
1103
  console.log(`🎭 Processing duplicate ID ${id}: "${duplicateName}"`);
1100
-
1104
+
1101
1105
  let newNameEN: string | null = null;
1102
1106
  let newNamePT: string | null = null;
1103
1107
  let attempts = 0;
1104
1108
  const maxAttempts = 3;
1105
-
1109
+
1106
1110
  while (attempts < maxAttempts) {
1107
1111
  attempts++;
1108
-
1109
- console.log(
1110
- `📡 Attempt ${attempts}/${maxAttempts} - Requesting AI for new name...`
1111
- );
1112
-
1112
+ console.log(`📡 Attempt ${attempts}/${maxAttempts} - Requesting AI for a new name...`);
1113
+
1113
1114
  // ✅ Generate a new name, passing dynamically tracked duplicates
1114
- const messages = this.context
1115
- .buildLLMMessages()
1116
- .regenerateConceptName({
1117
- conceptSlug,
1118
- oldNameEN: duplicateName,
1119
- description,
1120
- recentNames: Array.from(recentDuplicates),
1121
- });
1122
-
1123
- const aiResponse = await this.context
1124
- .api()
1125
- .callTogether.single(messages, {});
1126
-
1115
+ const messages = this.context.buildLLMMessages().regenerateConceptName({
1116
+ conceptSlug,
1117
+ oldNameEN: duplicateName,
1118
+ description,
1119
+ recentNames: Array.from(recentDuplicates),
1120
+ });
1121
+
1122
+ const aiResponse = await this.context.api().callTogether.single(messages, {});
1123
+
1127
1124
  if (!aiResponse) {
1128
- console.warn(
1129
- `⚠️ AI failed to generate a new name for ID ${id}, skipping.`
1130
- );
1125
+ console.warn(`⚠️ AI failed to generate a new name for ID ${id}, skipping.`);
1131
1126
  break;
1132
1127
  }
1133
-
1128
+
1134
1129
  console.log(`📝 Raw AI Response:\n${aiResponse}`);
1135
-
1130
+
1136
1131
  // ✅ Mapping for concept names with proper PT articles
1137
1132
  const conceptMapping = {
1138
1133
  amulet: { en: 'Amulet', pt: 'Amuleto', articlePT: 'O' },
@@ -1142,7 +1137,7 @@ export class ConceptService {
1142
1137
  ring: { en: 'Ring', pt: 'Anel', articlePT: 'O' },
1143
1138
  orb: { en: 'Orb', pt: 'Orbe', articlePT: 'O' },
1144
1139
  };
1145
-
1140
+
1146
1141
  const {
1147
1142
  en: conceptEN,
1148
1143
  pt: conceptPT,
@@ -1152,90 +1147,81 @@ export class ConceptService {
1152
1147
  pt: conceptSlug,
1153
1148
  articlePT: 'O', // Default to "O" if concept is missing
1154
1149
  };
1155
-
1150
+
1156
1151
  // ✅ Strict regex matching for English and Portuguese
1157
- const enMatch = aiResponse.match(
1158
- new RegExp(`EN:\\s*The ${conceptEN} of (.+)`)
1159
- );
1160
- const ptMatch = aiResponse.match(
1161
- new RegExp(`PT:\\s*${articlePT} ${conceptPT} de (.+)`)
1162
- );
1163
-
1152
+ const enMatch = aiResponse.match(new RegExp(`EN:\\s*The ${conceptEN} of (.+)`));
1153
+ const ptMatch = aiResponse.match(new RegExp(`PT:\\s*${articlePT} ${conceptPT} de (.+)`));
1154
+
1164
1155
  if (!enMatch || !ptMatch) {
1165
- console.error(
1166
- `❌ Invalid AI response format for ID ${id}, retrying...`
1167
- );
1156
+ console.error(`❌ Invalid AI response format for ID ${id}, retrying...`);
1168
1157
  continue;
1169
1158
  }
1170
-
1159
+
1171
1160
  newNameEN = `The ${conceptEN} of ${enMatch[1].trim()}`;
1172
1161
  newNamePT = `${articlePT} ${conceptPT} de ${ptMatch[1].trim()}`;
1173
-
1174
- console.log(
1175
- `🎨 Extracted Names: EN - "${newNameEN}", PT - "${newNamePT}"`
1176
- );
1177
-
1162
+
1163
+ console.log(`🎨 Extracted Names: EN - "${newNameEN}", PT - "${newNamePT}"`);
1164
+
1178
1165
  // ✅ Check if the new name is already in use in D1 before updating
1179
1166
  const existingEntry = await db
1180
1167
  .select({ id: conceptsData.id })
1181
1168
  .from(conceptsData)
1182
1169
  .where(eq(conceptsData.name, newNameEN))
1183
1170
  .execute();
1184
-
1171
+
1185
1172
  if (existingEntry.length > 0 || recentDuplicates.has(newNameEN)) {
1186
1173
  console.warn(`⚠️ Duplicate detected: "${newNameEN}", retrying...`);
1187
1174
  continue;
1188
1175
  }
1189
-
1176
+
1190
1177
  recentDuplicates.add(newNameEN);
1191
1178
  break; // ✅ Found a valid new name
1192
1179
  }
1193
-
1180
+
1194
1181
  if (!newNameEN || !newNamePT) {
1195
- console.error(
1196
- `🚨 Failed to generate a unique name for ID ${id} after ${maxAttempts} attempts.`
1197
- );
1182
+ console.error(`🚨 Failed to generate a unique name for ID ${id} after ${maxAttempts} attempts.`);
1183
+ failedEntries++;
1198
1184
  continue;
1199
1185
  }
1200
-
1201
- console.log(
1202
- `✅ Updating names: EN - "${newNameEN}", PT - "${newNamePT}"`
1203
- );
1204
-
1186
+
1187
+ console.log(`✅ Updating names: EN - "${newNameEN}", PT - "${newNamePT}"`);
1188
+
1205
1189
  // ✅ Update English name using the ID
1206
1190
  await db
1207
1191
  .update(conceptsData)
1208
1192
  .set({ name: newNameEN })
1209
- .where(
1210
- and(eq(conceptsData.id, id), eq(conceptsData.language, 'en-us'))
1211
- )
1193
+ .where(and(eq(conceptsData.id, id), eq(conceptsData.language, 'en-us')))
1212
1194
  .execute();
1213
-
1195
+
1214
1196
  // ✅ Convert English ID to Portuguese ID by replacing `:en-us:` with `:pt-br:`
1215
1197
  const ptId = id.replace(':en-us:', ':pt-br:');
1216
-
1198
+
1217
1199
  // ✅ Update Portuguese name using the derived PT ID
1218
1200
  await db
1219
1201
  .update(conceptsData)
1220
1202
  .set({ name: newNamePT })
1221
- .where(
1222
- and(eq(conceptsData.id, ptId), eq(conceptsData.language, 'pt-br'))
1223
- )
1203
+ .where(and(eq(conceptsData.id, ptId), eq(conceptsData.language, 'pt-br')))
1224
1204
  .execute();
1225
-
1226
- console.log(
1227
- `📝 Updated IDs: EN (${id}) -> "${newNameEN}", PT (${ptId}) -> "${newNamePT}"`
1228
- );
1229
-
1205
+
1206
+ console.log(`📝 Updated IDs: EN (${id}) -> "${newNameEN}", PT (${ptId}) -> "${newNamePT}"`);
1207
+
1230
1208
  updatedEntries++;
1231
1209
  }
1232
1210
  }
1233
-
1234
- console.log(
1235
- `🎉 [COMPLETE] Regenerated ${updatedEntries} duplicate names for concept: ${conceptSlug}.`
1236
- );
1211
+
1212
+ const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(2);
1213
+
1214
+ console.log(`🎉 [COMPLETE] Regeneration finished in ${elapsedTime}s.`);
1215
+ console.log(`✅ Successfully updated: ${updatedEntries}`);
1216
+ console.log(`⚠️ Skipped (already unique): ${skippedEntries}`);
1217
+ console.log(`🚨 Failed attempts: ${failedEntries}`);
1218
+
1237
1219
  return {
1238
1220
  message: `Duplicate names processed and regenerated for ${conceptSlug}.`,
1221
+ updated: updatedEntries,
1222
+ skipped: skippedEntries,
1223
+ failed: failedEntries,
1224
+ elapsedTime,
1239
1225
  };
1240
1226
  }
1241
1227
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zodic/shared",
3
- "version": "0.0.223",
3
+ "version": "0.0.225",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "publishConfig": {