@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
- // Check if data already exists
40
- if (!override) {
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 **directly from Durable Object**
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
- const recentNamesEN = allNamesEN.slice(-144); // 🔥 Only pass the last 144 names
85
- const recentNamesPT = allNamesPT.slice(-144);
86
-
87
- // ✅ Generate content ensuring uniqueness
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
- .api()
98
- .callTogether.single(messages, {});
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 if generated names are unique **right after parsing**
105
+
106
+ // ✅ Check uniqueness before storing
112
107
  if (allNamesEN.includes(nameEN) || allNamesPT.includes(namePT)) {
113
- console.warn(
114
- `⚠️ Duplicate Name Detected: "${nameEN}" or "${namePT}"`
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
- `${this.context.env.API_BASE_URL}/concept-names/${conceptSlug}/add`,
128
- {
129
- method: 'POST',
130
- body: JSON.stringify({ language: 'en-us', name: nameEN }),
131
- headers: { 'Content-Type': 'application/json' },
132
- }
133
- );
134
-
135
- await fetch(
136
- `${this.context.env.API_BASE_URL}/concept-names/${conceptSlug}/add`,
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
- const conceptEN = await this.getKVConcept(kvKeyEN);
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(conceptEN));
153
-
154
- const conceptPT = await this.getKVConcept(kvKeyPT);
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(conceptPT));
162
-
163
- console.log(
164
- `✅ Stored basic info for ${conceptSlug}, combination: ${combinationString}.`
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
- `❌ Attempt ${attempts} failed at phase: ${phase}`,
171
- (error as Error).message
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)); // ⏳ Small delay before retrying
167
+ await new Promise((resolve) => setTimeout(resolve, 500));
181
168
  }
182
169
  }
183
170
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zodic/shared",
3
- "version": "0.0.167",
3
+ "version": "0.0.168",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  Ai,
3
3
  D1Database,
4
- DurableObject,
5
4
  DurableObjectNamespace,
6
5
  KVNamespace,
7
6
  Queue,