@zodic/shared 0.0.167 → 0.0.168
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.
|
@@ -28,63 +28,63 @@ export class ConceptService {
|
|
|
28
28
|
console.log(
|
|
29
29
|
`🚀 Generating basic info for concept: ${conceptSlug}, combination: ${combinationString}, override: ${override}`
|
|
30
30
|
);
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
const kvStore = this.context.kvConceptsStore();
|
|
33
33
|
const kvFailuresStore = this.context.kvConceptFailuresStore();
|
|
34
|
-
const kvNamesStore = this.context.kvConceptNamesStore(); // ✅ New Durable Object
|
|
35
34
|
const kvKeyEN = buildConceptKVKey('en-us', conceptSlug, combinationString);
|
|
36
35
|
const kvKeyPT = buildConceptKVKey('pt-br', conceptSlug, combinationString);
|
|
37
36
|
const failureKey = `failures:basic-info:${conceptSlug}:${combinationString}`;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const existingEN = await this.getKVConcept(kvKeyEN);
|
|
42
|
-
const existingPT = await this.getKVConcept(kvKeyPT);
|
|
43
|
-
if (
|
|
44
|
-
existingEN.name &&
|
|
45
|
-
existingEN.description &&
|
|
46
|
-
Array.isArray(existingEN.poem) &&
|
|
47
|
-
existingEN.poem.length > 0 &&
|
|
48
|
-
existingPT.name &&
|
|
49
|
-
existingPT.description &&
|
|
50
|
-
Array.isArray(existingPT.poem) &&
|
|
51
|
-
existingPT.poem.length > 0
|
|
52
|
-
) {
|
|
53
|
-
console.log(
|
|
54
|
-
`⚡ Basic info already exists for ${conceptSlug}, skipping.`
|
|
55
|
-
);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
37
|
+
|
|
38
|
+
const doUrl = `${this.context.env.API_BASE_URL}/concept-names/${conceptSlug}`;
|
|
39
|
+
|
|
60
40
|
let attempts = 0;
|
|
61
41
|
const maxAttempts = 3;
|
|
62
|
-
|
|
42
|
+
|
|
63
43
|
while (attempts < maxAttempts) {
|
|
64
44
|
let phase = 'generation';
|
|
65
45
|
try {
|
|
66
46
|
attempts++;
|
|
67
47
|
console.log(`🔄 Attempt ${attempts} to generate basic info...`);
|
|
68
|
-
|
|
69
|
-
// ✅ Fetch the latest name list
|
|
70
|
-
const response = await fetch(
|
|
71
|
-
`${this.context.env.API_BASE_URL}/concept-names/${conceptSlug}`
|
|
72
|
-
);
|
|
48
|
+
|
|
49
|
+
// ✅ Fetch the latest name list from Durable Object
|
|
73
50
|
let allNamesEN: string[] = [];
|
|
74
51
|
let allNamesPT: string[] = [];
|
|
52
|
+
const response = await fetch(doUrl);
|
|
75
53
|
if (response.ok) {
|
|
76
|
-
const data = (await response.json()) as {
|
|
77
|
-
'en-us': string[];
|
|
78
|
-
'pt-br': string[];
|
|
79
|
-
};
|
|
54
|
+
const data = (await response.json()) as { 'en-us': string[]; 'pt-br': string[] };
|
|
80
55
|
allNamesEN = data['en-us'] || [];
|
|
81
56
|
allNamesPT = data['pt-br'] || [];
|
|
82
57
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
58
|
+
|
|
59
|
+
// ✅ Fetch existing KV data to backfill Durable Object if necessary
|
|
60
|
+
const existingEN = await this.getKVConcept(kvKeyEN);
|
|
61
|
+
const existingPT = await this.getKVConcept(kvKeyPT);
|
|
62
|
+
|
|
63
|
+
// 🔥 If names already exist in KV but not in DO, add them
|
|
64
|
+
if (existingEN.name && !allNamesEN.includes(existingEN.name)) {
|
|
65
|
+
console.log(`⚡ Backfilling existing name to DO: ${existingEN.name}`);
|
|
66
|
+
await fetch(`${doUrl}/add`, {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
body: JSON.stringify({ language: 'en-us', name: existingEN.name }),
|
|
69
|
+
headers: { 'Content-Type': 'application/json' },
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (existingPT.name && !allNamesPT.includes(existingPT.name)) {
|
|
73
|
+
console.log(`⚡ Backfilling existing name to DO: ${existingPT.name}`);
|
|
74
|
+
await fetch(`${doUrl}/add`, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
body: JSON.stringify({ language: 'pt-br', name: existingPT.name }),
|
|
77
|
+
headers: { 'Content-Type': 'application/json' },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ✅ Skip generation if basic info already exists and override is false
|
|
82
|
+
if (!override && existingEN.name && existingPT.name) {
|
|
83
|
+
console.log(`⚡ Basic info already exists for ${conceptSlug}, skipping.`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const recentNamesEN = allNamesEN.slice(-144);
|
|
88
88
|
const messages = this.context
|
|
89
89
|
.buildLLMMessages()
|
|
90
90
|
.generateConceptBasicInfo({
|
|
@@ -92,92 +92,79 @@ export class ConceptService {
|
|
|
92
92
|
conceptSlug,
|
|
93
93
|
existingNames: recentNamesEN,
|
|
94
94
|
});
|
|
95
|
-
|
|
96
|
-
let aiResponse = await this.context
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (!aiResponse) {
|
|
100
|
-
throw new Error(`❌ AI returned an empty response`);
|
|
101
|
-
}
|
|
102
|
-
|
|
95
|
+
|
|
96
|
+
let aiResponse = await this.context.api().callTogether.single(messages, {});
|
|
97
|
+
if (!aiResponse) throw new Error(`❌ AI returned an empty response`);
|
|
98
|
+
|
|
103
99
|
phase = 'cleaning';
|
|
104
100
|
aiResponse = this.cleanAIResponse(aiResponse);
|
|
105
|
-
|
|
101
|
+
|
|
106
102
|
phase = 'parsing';
|
|
107
|
-
|
|
108
103
|
let { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT } =
|
|
109
104
|
this.parseBasicInfoResponse(aiResponse, conceptSlug);
|
|
110
|
-
|
|
111
|
-
// ✅ Check
|
|
105
|
+
|
|
106
|
+
// ✅ Check uniqueness before storing
|
|
112
107
|
if (allNamesEN.includes(nameEN) || allNamesPT.includes(namePT)) {
|
|
113
|
-
console.warn(
|
|
114
|
-
|
|
115
|
-
);
|
|
116
|
-
if (attempts >= maxAttempts) {
|
|
117
|
-
throw new Error(
|
|
118
|
-
`🚨 Could not generate a unique name after ${maxAttempts} attempts.`
|
|
119
|
-
);
|
|
120
|
-
}
|
|
108
|
+
console.warn(`⚠️ Duplicate Name Detected: "${nameEN}" or "${namePT}"`);
|
|
109
|
+
if (attempts >= maxAttempts) throw new Error(`🚨 Could not generate a unique name`);
|
|
121
110
|
console.log('🔁 Retrying due to duplicate name...');
|
|
122
111
|
continue;
|
|
123
112
|
}
|
|
124
|
-
|
|
113
|
+
|
|
125
114
|
// ✅ **Immediately update Durable Object with the new name**
|
|
126
|
-
await fetch(
|
|
127
|
-
|
|
128
|
-
{
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
method: 'POST',
|
|
139
|
-
body: JSON.stringify({ language: 'pt-br', name: namePT }),
|
|
140
|
-
headers: { 'Content-Type': 'application/json' },
|
|
141
|
-
}
|
|
142
|
-
);
|
|
143
|
-
|
|
115
|
+
await fetch(`${doUrl}/add`, {
|
|
116
|
+
method: 'POST',
|
|
117
|
+
body: JSON.stringify({ language: 'en-us', name: nameEN }),
|
|
118
|
+
headers: { 'Content-Type': 'application/json' },
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
await fetch(`${doUrl}/add`, {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
body: JSON.stringify({ language: 'pt-br', name: namePT }),
|
|
124
|
+
headers: { 'Content-Type': 'application/json' },
|
|
125
|
+
});
|
|
126
|
+
|
|
144
127
|
// ✅ Store the generated basic info in KV
|
|
145
|
-
|
|
146
|
-
Object.assign(conceptEN, {
|
|
128
|
+
Object.assign(existingEN, {
|
|
147
129
|
name: nameEN,
|
|
148
130
|
description: descriptionEN,
|
|
149
131
|
poem: poemEN,
|
|
150
132
|
status: 'idle',
|
|
151
133
|
});
|
|
152
|
-
await kvStore.put(kvKeyEN, JSON.stringify(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
Object.assign(conceptPT, {
|
|
134
|
+
await kvStore.put(kvKeyEN, JSON.stringify(existingEN));
|
|
135
|
+
|
|
136
|
+
Object.assign(existingPT, {
|
|
156
137
|
name: namePT,
|
|
157
138
|
description: descriptionPT,
|
|
158
139
|
poem: poemPT,
|
|
159
140
|
status: 'idle',
|
|
160
141
|
});
|
|
161
|
-
await kvStore.put(kvKeyPT, JSON.stringify(
|
|
162
|
-
|
|
163
|
-
console.log(
|
|
164
|
-
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
return; // ✅ Exit loop if successful
|
|
142
|
+
await kvStore.put(kvKeyPT, JSON.stringify(existingPT));
|
|
143
|
+
|
|
144
|
+
console.log(`✅ Stored basic info for ${conceptSlug}, combination: ${combinationString}.`);
|
|
145
|
+
return;
|
|
168
146
|
} catch (error) {
|
|
169
|
-
console.error(
|
|
170
|
-
|
|
171
|
-
|
|
147
|
+
console.error(`❌ Attempt ${attempts} failed at phase: ${phase}`, (error as Error).message);
|
|
148
|
+
|
|
149
|
+
// ✅ Store failure details in KV for debugging
|
|
150
|
+
await kvFailuresStore.put(
|
|
151
|
+
failureKey,
|
|
152
|
+
JSON.stringify({
|
|
153
|
+
error: (error as Error).message,
|
|
154
|
+
attempt: attempts,
|
|
155
|
+
phase,
|
|
156
|
+
conceptSlug,
|
|
157
|
+
combinationString,
|
|
158
|
+
timestamp: new Date().toISOString(),
|
|
159
|
+
})
|
|
172
160
|
);
|
|
161
|
+
|
|
173
162
|
if (attempts >= maxAttempts) {
|
|
174
163
|
console.error(`🚨 All ${maxAttempts} attempts failed.`);
|
|
175
|
-
throw new Error(
|
|
176
|
-
`Failed to generate basic info after ${maxAttempts} attempts.`
|
|
177
|
-
);
|
|
164
|
+
throw new Error(`Failed to generate basic info after ${maxAttempts} attempts.`);
|
|
178
165
|
}
|
|
179
166
|
console.log('🔁 Retrying...');
|
|
180
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
167
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
181
168
|
}
|
|
182
169
|
}
|
|
183
170
|
}
|
package/package.json
CHANGED