@zodic/shared 0.0.218 → 0.0.220
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/ConceptService.ts +68 -83
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
KVNamespaceListKey,
|
|
3
3
|
KVNamespaceListResult,
|
|
4
4
|
} from '@cloudflare/workers-types';
|
|
5
|
-
import { and, eq } from 'drizzle-orm';
|
|
5
|
+
import { and, eq, sql } from 'drizzle-orm';
|
|
6
6
|
import { inject, injectable } from 'inversify';
|
|
7
7
|
import 'reflect-metadata';
|
|
8
8
|
import { v4 as uuidv4 } from 'uuid';
|
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
import { leonardoInitImages } from '../../utils/initImages';
|
|
17
17
|
import { buildConceptKVKey } from '../../utils/KVKeysBuilders';
|
|
18
18
|
import { AppContext } from '../base/AppContext';
|
|
19
|
+
import { drizzle } from 'drizzle-orm/d1';
|
|
20
|
+
import { conceptsData } from '../../db/schema';
|
|
19
21
|
|
|
20
22
|
@injectable()
|
|
21
23
|
export class ConceptService {
|
|
@@ -1028,41 +1030,48 @@ export class ConceptService {
|
|
|
1028
1030
|
};
|
|
1029
1031
|
}
|
|
1030
1032
|
|
|
1031
|
-
async regenerateDuplicateNamesForConcept(conceptSlug: Concept) {
|
|
1032
|
-
console.log(`🔄 [START] Regenerating duplicate names for concept: ${conceptSlug}
|
|
1033
|
+
async regenerateDuplicateNamesForConcept(conceptSlug: Concept, maxEntries?: number) {
|
|
1034
|
+
console.log(`🔄 [START] Regenerating duplicate names for concept: ${conceptSlug}...`);
|
|
1033
1035
|
|
|
1034
|
-
const
|
|
1035
|
-
const kvStore = this.context.env.KV_CONCEPTS;
|
|
1036
|
+
const db = drizzle(this.context.env.DB);
|
|
1036
1037
|
|
|
1037
|
-
// ✅
|
|
1038
|
-
const
|
|
1039
|
-
|
|
1038
|
+
// ✅ Step 1: Fetch duplicate entries filtered by conceptSlug
|
|
1039
|
+
const duplicateEntries = await db
|
|
1040
|
+
.select({
|
|
1041
|
+
id: conceptsData.id,
|
|
1042
|
+
name: conceptsData.name,
|
|
1043
|
+
description: conceptsData.description,
|
|
1044
|
+
combination: conceptsData.combination,
|
|
1045
|
+
})
|
|
1046
|
+
.from(conceptsData)
|
|
1047
|
+
.where(
|
|
1048
|
+
and(
|
|
1049
|
+
eq(conceptsData.language, 'en-us'), // English version only
|
|
1050
|
+
eq(conceptsData.conceptSlug, conceptSlug), // ✅ Filter by concept
|
|
1051
|
+
sql`name IN (
|
|
1052
|
+
SELECT name FROM concepts_data
|
|
1053
|
+
WHERE language = 'en-us' AND concept_slug = ${conceptSlug}
|
|
1054
|
+
GROUP BY name HAVING COUNT(*) > 1
|
|
1055
|
+
)`
|
|
1056
|
+
)
|
|
1057
|
+
)
|
|
1058
|
+
.limit(maxEntries || 50) // ✅ Apply optional limit
|
|
1059
|
+
.execute();
|
|
1040
1060
|
|
|
1041
|
-
if (
|
|
1061
|
+
if (!duplicateEntries.length) {
|
|
1042
1062
|
console.log(`🎉 No duplicates found for ${conceptSlug}.`);
|
|
1043
1063
|
return { message: `No duplicates detected for ${conceptSlug}.` };
|
|
1044
1064
|
}
|
|
1045
1065
|
|
|
1046
|
-
|
|
1047
|
-
let recentDuplicates = new Set<string>(); // ✅ Track newly generated names dynamically
|
|
1066
|
+
console.log(`🔍 Found ${duplicateEntries.length} duplicate entries to process.`);
|
|
1048
1067
|
|
|
1049
|
-
|
|
1068
|
+
let recentDuplicates = new Set<string>(); // ✅ Track newly generated names
|
|
1069
|
+
let updatedEntries = 0;
|
|
1050
1070
|
|
|
1051
|
-
for (const
|
|
1052
|
-
const
|
|
1071
|
+
for (const entry of duplicateEntries) {
|
|
1072
|
+
const { id, name, description, combination } = entry;
|
|
1053
1073
|
|
|
1054
|
-
|
|
1055
|
-
name: string;
|
|
1056
|
-
description: string;
|
|
1057
|
-
};
|
|
1058
|
-
const kvDataPT = (await kvStore.get(kvKeyPT, 'json')) as { name: string };
|
|
1059
|
-
|
|
1060
|
-
if (!kvDataEN || !kvDataEN.name) {
|
|
1061
|
-
console.warn(`⚠️ Missing data for key: ${kvKeyEN}, skipping.`);
|
|
1062
|
-
continue;
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
console.log(`🎭 Processing duplicate: ${kvKeyEN} (Old Name: "${kvDataEN.name}")`);
|
|
1074
|
+
console.log(`🎭 Processing duplicate ID ${id}: "${name}"`);
|
|
1066
1075
|
|
|
1067
1076
|
let newNameEN: string | null = null;
|
|
1068
1077
|
let newNamePT: string | null = null;
|
|
@@ -1077,21 +1086,21 @@ export class ConceptService {
|
|
|
1077
1086
|
// ✅ Generate a new name, passing dynamically tracked duplicates
|
|
1078
1087
|
const messages = this.context.buildLLMMessages().regenerateConceptName({
|
|
1079
1088
|
conceptSlug,
|
|
1080
|
-
oldNameEN:
|
|
1081
|
-
description
|
|
1082
|
-
recentNames: Array.from(recentDuplicates),
|
|
1089
|
+
oldNameEN: name,
|
|
1090
|
+
description,
|
|
1091
|
+
recentNames: Array.from(recentDuplicates),
|
|
1083
1092
|
});
|
|
1084
1093
|
|
|
1085
1094
|
const aiResponse = await this.context.api().callTogether.single(messages, {});
|
|
1086
1095
|
|
|
1087
1096
|
if (!aiResponse) {
|
|
1088
|
-
console.warn(`⚠️ AI failed to generate a new name for ${
|
|
1097
|
+
console.warn(`⚠️ AI failed to generate a new name for ID ${id}, skipping.`);
|
|
1089
1098
|
break;
|
|
1090
1099
|
}
|
|
1091
1100
|
|
|
1092
1101
|
console.log(`📝 Raw AI Response:\n${aiResponse}`);
|
|
1093
1102
|
|
|
1094
|
-
// ✅
|
|
1103
|
+
// ✅ Mapping for concept names with proper PT articles
|
|
1095
1104
|
const conceptMapping = {
|
|
1096
1105
|
amulet: { en: "Amulet", pt: "Amuleto", articlePT: "O" },
|
|
1097
1106
|
crown: { en: "Crown", pt: "Coroa", articlePT: "A" },
|
|
@@ -1108,83 +1117,59 @@ export class ConceptService {
|
|
|
1108
1117
|
};
|
|
1109
1118
|
|
|
1110
1119
|
// ✅ Strict regex matching for English and Portuguese
|
|
1111
|
-
const enMatch = aiResponse.match(new RegExp(`EN:\\s*The ${conceptEN} of
|
|
1112
|
-
const ptMatch = aiResponse.match(new RegExp(`PT:\\s*${articlePT} ${conceptPT} de
|
|
1120
|
+
const enMatch = aiResponse.match(new RegExp(`EN:\\s*The ${conceptEN} of (.+)`));
|
|
1121
|
+
const ptMatch = aiResponse.match(new RegExp(`PT:\\s*${articlePT} ${conceptPT} de (.+)`));
|
|
1113
1122
|
|
|
1114
1123
|
if (!enMatch || !ptMatch) {
|
|
1115
|
-
console.error(`❌ Invalid AI response format
|
|
1124
|
+
console.error(`❌ Invalid AI response format for ID ${id}, retrying...`);
|
|
1116
1125
|
continue;
|
|
1117
1126
|
}
|
|
1118
1127
|
|
|
1119
|
-
newNameEN = enMatch[
|
|
1120
|
-
newNamePT = ptMatch[
|
|
1128
|
+
newNameEN = `The ${conceptEN} of ${enMatch[1].trim()}`;
|
|
1129
|
+
newNamePT = `${articlePT} ${conceptPT} de ${ptMatch[1].trim()}`;
|
|
1121
1130
|
|
|
1122
1131
|
console.log(`🎨 Extracted Names: EN - "${newNameEN}", PT - "${newNamePT}"`);
|
|
1123
1132
|
|
|
1124
|
-
// ✅ Check
|
|
1125
|
-
if (
|
|
1126
|
-
|
|
1127
|
-
usedNamesPT.has(newNamePT) ||
|
|
1128
|
-
recentDuplicates.has(newNameEN)
|
|
1129
|
-
) {
|
|
1130
|
-
console.warn(
|
|
1131
|
-
`⚠️ Duplicate detected: EN - "${newNameEN}", PT - "${newNamePT}". Retrying...`
|
|
1132
|
-
);
|
|
1133
|
-
recentDuplicates.add(newNameEN);
|
|
1134
|
-
recentDuplicates.add(newNamePT);
|
|
1133
|
+
// ✅ Check for duplicate avoidance
|
|
1134
|
+
if (recentDuplicates.has(newNameEN)) {
|
|
1135
|
+
console.warn(`⚠️ Duplicate detected: "${newNameEN}", retrying...`);
|
|
1135
1136
|
continue;
|
|
1136
1137
|
}
|
|
1137
1138
|
|
|
1138
1139
|
recentDuplicates.add(newNameEN);
|
|
1139
|
-
|
|
1140
|
-
break; // ✅ Found unique names, exit retry loop
|
|
1140
|
+
break; // ✅ Found a valid new name
|
|
1141
1141
|
}
|
|
1142
1142
|
|
|
1143
1143
|
if (!newNameEN || !newNamePT) {
|
|
1144
|
-
console.error(`🚨 Failed to generate a unique name for ${
|
|
1144
|
+
console.error(`🚨 Failed to generate a unique name for ID ${id} after ${maxAttempts} attempts.`);
|
|
1145
1145
|
continue;
|
|
1146
1146
|
}
|
|
1147
1147
|
|
|
1148
|
-
console.log(`✅
|
|
1149
|
-
|
|
1150
|
-
// ✅ Update KV with new names (EN)
|
|
1151
|
-
Object.assign(kvDataEN, { name: newNameEN });
|
|
1152
|
-
await kvStore.put(kvKeyEN, JSON.stringify(kvDataEN));
|
|
1148
|
+
console.log(`✅ Updating names: EN - "${newNameEN}", PT - "${newNamePT}"`);
|
|
1153
1149
|
|
|
1154
|
-
// ✅ Update
|
|
1155
|
-
|
|
1156
|
-
|
|
1150
|
+
// ✅ Update English name using the ID
|
|
1151
|
+
await db
|
|
1152
|
+
.update(conceptsData)
|
|
1153
|
+
.set({ name: newNameEN })
|
|
1154
|
+
.where(and(eq(conceptsData.id, id), eq(conceptsData.language, 'en-us')))
|
|
1155
|
+
.execute();
|
|
1157
1156
|
|
|
1158
|
-
// ✅
|
|
1159
|
-
|
|
1160
|
-
usedNamesPT.add(newNamePT);
|
|
1157
|
+
// ✅ Convert English ID to Portuguese ID by replacing `:en-us:` with `:pt-br:`
|
|
1158
|
+
const ptId = id.replace(':en-us:', ':pt-br:');
|
|
1161
1159
|
|
|
1162
|
-
// ✅
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
let duplicateKeysList: string[] =
|
|
1169
|
-
(await kvCacheStore.get(`cache:duplicates:${conceptSlug}`, 'json')) || [];
|
|
1170
|
-
|
|
1171
|
-
console.log(`🗑️ Removing ${updatedKeys.length} resolved duplicates from cache.`);
|
|
1160
|
+
// ✅ Update Portuguese name using the derived PT ID
|
|
1161
|
+
await db
|
|
1162
|
+
.update(conceptsData)
|
|
1163
|
+
.set({ name: newNamePT })
|
|
1164
|
+
.where(and(eq(conceptsData.id, ptId), eq(conceptsData.language, 'pt-br')))
|
|
1165
|
+
.execute();
|
|
1172
1166
|
|
|
1173
|
-
|
|
1174
|
-
duplicateKeysList = duplicateKeysList.filter((key) => !updatedKeys.includes(key));
|
|
1167
|
+
console.log(`📝 Updated IDs: EN (${id}) -> "${newNameEN}", PT (${ptId}) -> "${newNamePT}"`);
|
|
1175
1168
|
|
|
1176
|
-
|
|
1177
|
-
console.log(`✅ All duplicates resolved for ${conceptSlug}, deleting cache entry.`);
|
|
1178
|
-
await kvCacheStore.delete(`cache:duplicates:${conceptSlug}`);
|
|
1179
|
-
} else {
|
|
1180
|
-
await kvCacheStore.put(
|
|
1181
|
-
`cache:duplicates:${conceptSlug}`,
|
|
1182
|
-
JSON.stringify(duplicateKeysList)
|
|
1183
|
-
);
|
|
1184
|
-
}
|
|
1169
|
+
updatedEntries++;
|
|
1185
1170
|
}
|
|
1186
1171
|
|
|
1187
|
-
console.log(`🎉 [COMPLETE]
|
|
1172
|
+
console.log(`🎉 [COMPLETE] Regenerated ${updatedEntries} duplicate names for concept: ${conceptSlug}.`);
|
|
1188
1173
|
return {
|
|
1189
1174
|
message: `Duplicate names processed and regenerated for ${conceptSlug}.`,
|
|
1190
1175
|
};
|