@zodic/shared 0.0.224 → 0.0.226

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