@zodic/shared 0.0.314 → 0.0.316
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/api/index.ts +114 -16
- package/app/services/ArchetypeService.ts +738 -1244
- package/app/services/LeonardoService.ts +187 -288
- package/app/workflow/ArchetypeWorkflow.ts +175 -141
- package/db/migrations/0014_green_marvel_apes.sql +10 -0
- package/db/migrations/meta/0014_snapshot.json +2723 -0
- package/db/migrations/meta/_journal.json +7 -0
- package/db/schema.ts +17 -0
- package/package.json +1 -1
- package/utils/buildMessages.ts +273 -59
|
@@ -1,431 +1,90 @@
|
|
|
1
|
-
import { and, eq
|
|
1
|
+
import { and, eq } from 'drizzle-orm';
|
|
2
2
|
import { inject, injectable } from 'inversify';
|
|
3
|
-
import { ChatMessages,
|
|
4
|
-
import {
|
|
3
|
+
import { ChatMessages, schema } from '../..';
|
|
4
|
+
import { Gender } from '../../types';
|
|
5
|
+
import { ZodiacSignSlug } from '../../types/scopes/legacy';
|
|
5
6
|
import { generateArchetypePrompt } from '../../utils/archetypeNamesMessages';
|
|
6
|
-
import { duplicatedArchetypeNamePrompt } from '../../utils/duplicatedArchetypeNamePrompt';
|
|
7
|
-
import { buildCosmicMirrorArchetypeKVKey } from '../../utils/KVKeysBuilders';
|
|
8
7
|
import { AppContext } from '../base';
|
|
9
8
|
|
|
10
9
|
@injectable()
|
|
11
10
|
export class ArchetypeService {
|
|
12
11
|
constructor(@inject(AppContext) private context: AppContext) {}
|
|
13
12
|
|
|
14
|
-
async
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
const messages = this.context
|
|
22
|
-
.buildLLMMessages()
|
|
23
|
-
.generateCosmicMirrorArchetypeBasicInfo({ sun, ascendant, moon });
|
|
24
|
-
|
|
25
|
-
const response = await this.context.api().callChatGPT.single(messages, {});
|
|
26
|
-
|
|
27
|
-
if (!response) {
|
|
28
|
-
throw new Error(
|
|
29
|
-
`Failed to generate basic info for archetypes: ${combinationString}`
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const archetypes = this.parseArchetypeResponse(response);
|
|
34
|
-
|
|
35
|
-
await Promise.all(
|
|
36
|
-
archetypes.map(async (archetype, index) => {
|
|
37
|
-
const archetypeIndex = index + 1;
|
|
38
|
-
|
|
39
|
-
const maleKey = buildCosmicMirrorArchetypeKVKey(
|
|
40
|
-
'en-us',
|
|
41
|
-
combinationString,
|
|
42
|
-
'male',
|
|
43
|
-
archetypeIndex
|
|
44
|
-
);
|
|
45
|
-
const femaleKey = buildCosmicMirrorArchetypeKVKey(
|
|
46
|
-
'en-us',
|
|
47
|
-
combinationString,
|
|
48
|
-
'female',
|
|
49
|
-
archetypeIndex
|
|
50
|
-
);
|
|
51
|
-
const nonBinaryKey = buildCosmicMirrorArchetypeKVKey(
|
|
52
|
-
'en-us',
|
|
53
|
-
combinationString,
|
|
54
|
-
'non-binary',
|
|
55
|
-
archetypeIndex
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
const kvDataMale: KVArchetype = {
|
|
59
|
-
name: archetype.name,
|
|
60
|
-
description: archetype.descriptionMale,
|
|
61
|
-
visualDescription: archetype.visualRepresentation,
|
|
62
|
-
content: '',
|
|
63
|
-
leonardoPrompt: '',
|
|
64
|
-
virtues: archetype.virtues,
|
|
65
|
-
images: [],
|
|
66
|
-
status: 'idle',
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const kvDataFemale: KVArchetype = {
|
|
70
|
-
name: archetype.name,
|
|
71
|
-
description: archetype.descriptionFemale,
|
|
72
|
-
visualDescription: archetype.visualRepresentation,
|
|
73
|
-
content: '',
|
|
74
|
-
leonardoPrompt: '',
|
|
75
|
-
virtues: archetype.virtues,
|
|
76
|
-
images: [],
|
|
77
|
-
status: 'idle',
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
await Promise.all([
|
|
81
|
-
this.context
|
|
82
|
-
.kvCosmicMirrorArchetypesStore()
|
|
83
|
-
.put(maleKey, JSON.stringify(kvDataMale)),
|
|
84
|
-
this.context
|
|
85
|
-
.kvCosmicMirrorArchetypesStore()
|
|
86
|
-
.put(femaleKey, JSON.stringify(kvDataFemale)),
|
|
87
|
-
this.context
|
|
88
|
-
.kvCosmicMirrorArchetypesStore()
|
|
89
|
-
.put(nonBinaryKey, JSON.stringify(kvDataFemale)),
|
|
90
|
-
]);
|
|
91
|
-
})
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
console.log(
|
|
95
|
-
`Basic info stored for archetypes of combination: ${combinationString}`
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
private parseArchetypeResponse(response: string): Array<{
|
|
100
|
-
name: string;
|
|
101
|
-
descriptionMale: string;
|
|
102
|
-
descriptionFemale: string;
|
|
103
|
-
visualRepresentation: string;
|
|
104
|
-
virtues: string[];
|
|
105
|
-
}> {
|
|
106
|
-
console.log('Parsing archetype response from ChatGPT');
|
|
107
|
-
|
|
108
|
-
const archetypes: Array<{
|
|
109
|
-
name: string;
|
|
110
|
-
descriptionMale: string;
|
|
111
|
-
descriptionFemale: string;
|
|
112
|
-
visualRepresentation: string;
|
|
113
|
-
virtues: string[];
|
|
114
|
-
}> = [];
|
|
115
|
-
|
|
116
|
-
const entries = response.split(/\n\d+\.\n/).filter(Boolean);
|
|
117
|
-
|
|
118
|
-
for (const entry of entries) {
|
|
119
|
-
const nameMatch = entry.match(/- Name: (.+)/);
|
|
120
|
-
const descriptionMaleMatch = entry.match(
|
|
121
|
-
/- Narrative Description \(Male\): (.+)/
|
|
122
|
-
);
|
|
123
|
-
const descriptionFemaleMatch = entry.match(
|
|
124
|
-
/- Narrative Description \(Female\): (.+)/
|
|
125
|
-
);
|
|
126
|
-
const visualRepresentationMatch = entry.match(
|
|
127
|
-
/- Visual Representation: (.+)/
|
|
128
|
-
);
|
|
129
|
-
const virtuesMatch = entry.match(/- Virtues: (.+)/);
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
nameMatch &&
|
|
133
|
-
descriptionMaleMatch &&
|
|
134
|
-
descriptionFemaleMatch &&
|
|
135
|
-
visualRepresentationMatch &&
|
|
136
|
-
virtuesMatch
|
|
137
|
-
) {
|
|
138
|
-
archetypes.push({
|
|
139
|
-
name: nameMatch[1].trim(),
|
|
140
|
-
descriptionMale: descriptionMaleMatch[1].trim(),
|
|
141
|
-
descriptionFemale: descriptionFemaleMatch[1].trim(),
|
|
142
|
-
visualRepresentation: visualRepresentationMatch[1].trim(),
|
|
143
|
-
virtues: virtuesMatch[1].split(',').map((virtue) => virtue.trim()),
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return archetypes;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async generatePrompts(combinationString: string): Promise<void> {
|
|
152
|
-
console.log(
|
|
153
|
-
`Generating Leonardo.ai prompts for archetypes of: ${combinationString}`
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
const kvStore = this.context.kvCosmicMirrorArchetypesStore();
|
|
157
|
-
|
|
158
|
-
const kvKeys = [1, 2, 3].map((index) =>
|
|
159
|
-
buildCosmicMirrorArchetypeKVKey('en-us', combinationString, 'male', index)
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
const archetypeEntries = await Promise.all(
|
|
163
|
-
kvKeys.map((key) => kvStore.get<KVArchetype>(key))
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
if (archetypeEntries.some((entry) => !entry)) {
|
|
167
|
-
throw new Error(
|
|
168
|
-
`Missing archetype data for combination: ${combinationString}`
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const messages = this.context
|
|
173
|
-
.buildLLMMessages()
|
|
174
|
-
.generateCosmicMirrorArchetypesLeonardoPrompts(
|
|
175
|
-
archetypeEntries.map((entry) => ({
|
|
176
|
-
name: entry!.name,
|
|
177
|
-
visualDescription: entry!.visualDescription, // Using visual description for prompt
|
|
178
|
-
}))
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
const response = await this.context.api().callChatGPT.single(messages, {});
|
|
182
|
-
if (!response) {
|
|
183
|
-
throw new Error(
|
|
184
|
-
`Failed to generate Leonardo prompts for: ${combinationString}`
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const parsedPrompts = this.parseLeonardoPromptResponse(response);
|
|
189
|
-
|
|
190
|
-
await Promise.all(
|
|
191
|
-
parsedPrompts.map(async (promptData, index) => {
|
|
192
|
-
const archetypeIndex = index + 1;
|
|
193
|
-
|
|
194
|
-
const maleKey = buildCosmicMirrorArchetypeKVKey(
|
|
195
|
-
'en-us',
|
|
196
|
-
combinationString,
|
|
197
|
-
'male',
|
|
198
|
-
archetypeIndex
|
|
199
|
-
);
|
|
200
|
-
const femaleKey = buildCosmicMirrorArchetypeKVKey(
|
|
201
|
-
'en-us',
|
|
202
|
-
combinationString,
|
|
203
|
-
'female',
|
|
204
|
-
archetypeIndex
|
|
205
|
-
);
|
|
206
|
-
const nonBinaryKey = buildCosmicMirrorArchetypeKVKey(
|
|
207
|
-
'en-us',
|
|
208
|
-
combinationString,
|
|
209
|
-
'non-binary',
|
|
210
|
-
archetypeIndex
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
const updateKV = async (key: string, updatedPrompt: string) => {
|
|
214
|
-
const existingEntry = await kvStore.get<KVArchetype>(key);
|
|
215
|
-
if (existingEntry) {
|
|
216
|
-
await kvStore.put(
|
|
217
|
-
key,
|
|
218
|
-
JSON.stringify({
|
|
219
|
-
...existingEntry,
|
|
220
|
-
leonardoPrompt: updatedPrompt,
|
|
221
|
-
})
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
await Promise.all([
|
|
227
|
-
updateKV(maleKey, promptData.malePrompt),
|
|
228
|
-
updateKV(femaleKey, promptData.femalePrompt),
|
|
229
|
-
updateKV(nonBinaryKey, promptData.nonBinaryPrompt),
|
|
230
|
-
]);
|
|
231
|
-
})
|
|
232
|
-
);
|
|
233
|
-
|
|
234
|
-
console.log(
|
|
235
|
-
`Leonardo prompts stored for archetypes of: ${combinationString}`
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
private parseLeonardoPromptResponse(response: string): Array<{
|
|
240
|
-
malePrompt: string;
|
|
241
|
-
femalePrompt: string;
|
|
242
|
-
nonBinaryPrompt: string;
|
|
243
|
-
}> {
|
|
244
|
-
console.log('Parsing Leonardo prompt response from ChatGPT');
|
|
245
|
-
|
|
246
|
-
const parsedPrompts: Array<{
|
|
247
|
-
malePrompt: string;
|
|
248
|
-
femalePrompt: string;
|
|
249
|
-
nonBinaryPrompt: string;
|
|
250
|
-
}> = [];
|
|
251
|
-
|
|
252
|
-
const entries = response.split(/\n\d+\.\w{1,2}\n/).filter(Boolean);
|
|
253
|
-
|
|
254
|
-
for (let i = 0; i < entries.length; i += 3) {
|
|
255
|
-
const malePrompt = entries[i]?.trim();
|
|
256
|
-
const femalePrompt = entries[i + 1]?.trim();
|
|
257
|
-
const nonBinaryPrompt = entries[i + 2]?.trim();
|
|
13
|
+
private async log(
|
|
14
|
+
level: 'info' | 'debug' | 'warn' | 'error',
|
|
15
|
+
message: string,
|
|
16
|
+
context: Record<string, any> = {}
|
|
17
|
+
) {
|
|
18
|
+
const logId = `archetype-service:${Date.now()}`;
|
|
19
|
+
const logMessage = `[${level.toUpperCase()}] ${message}`;
|
|
258
20
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
21
|
+
console[level](logMessage, context);
|
|
22
|
+
const db = this.context.drizzle();
|
|
23
|
+
try {
|
|
24
|
+
await db
|
|
25
|
+
.insert(schema.logs)
|
|
26
|
+
.values({
|
|
27
|
+
id: logId,
|
|
28
|
+
level,
|
|
29
|
+
message,
|
|
30
|
+
context: JSON.stringify(context),
|
|
31
|
+
createdAt: new Date().getTime(),
|
|
32
|
+
})
|
|
33
|
+
.execute();
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error('[ERROR] Failed to persist log to database:', {
|
|
36
|
+
error,
|
|
37
|
+
logId,
|
|
38
|
+
message,
|
|
39
|
+
context,
|
|
40
|
+
});
|
|
262
41
|
}
|
|
263
|
-
|
|
264
|
-
return parsedPrompts;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
async generateImages(combinationString: string): Promise<void> {
|
|
268
|
-
const kvBaseKey = `archetypes:${combinationString}`;
|
|
269
|
-
console.log(
|
|
270
|
-
`Generating images for archetypes of combination: ${combinationString}`
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
await Promise.all(
|
|
274
|
-
[1, 2, 3].map(async (index) => {
|
|
275
|
-
const kvKey = `${kvBaseKey}:${index}`;
|
|
276
|
-
const archetype = await this.context
|
|
277
|
-
.kvCosmicMirrorArchetypesStore()
|
|
278
|
-
.get<KVArchetype>(kvKey, 'json');
|
|
279
|
-
if (!archetype || !archetype.leonardoPrompt) {
|
|
280
|
-
throw new Error(
|
|
281
|
-
`Missing Leonardo prompt for archetype ${index} of combination: ${combinationString}`
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
// Add image generation logic
|
|
285
|
-
})
|
|
286
|
-
);
|
|
287
42
|
}
|
|
288
43
|
|
|
44
|
+
// Replace generateArchetypeNames and generateArchetypeNamesBatch
|
|
289
45
|
async generateArchetypeNames(
|
|
290
|
-
|
|
46
|
+
combinationString: string,
|
|
291
47
|
overrideExisting: boolean = false,
|
|
292
|
-
indexesToGenerate
|
|
48
|
+
indexesToGenerate: number[] = [1, 2, 3]
|
|
293
49
|
) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
50
|
+
await this.log('info', 'Starting generateArchetypeNames', {
|
|
51
|
+
combinationString,
|
|
52
|
+
overrideExisting,
|
|
53
|
+
indexesToGenerate,
|
|
54
|
+
});
|
|
298
55
|
|
|
299
|
-
const [sun, ascendant, moon] =
|
|
56
|
+
const [sun, ascendant, moon] = combinationString.split(
|
|
57
|
+
'-'
|
|
58
|
+
) as ZodiacSignSlug[];
|
|
300
59
|
const prompt = generateArchetypePrompt([
|
|
301
|
-
{
|
|
302
|
-
sun,
|
|
303
|
-
ascendant,
|
|
304
|
-
moon,
|
|
305
|
-
indexesToGenerate: indexesToGenerate ?? [1, 2, 3],
|
|
306
|
-
},
|
|
60
|
+
{ sun, ascendant, moon, indexesToGenerate },
|
|
307
61
|
]);
|
|
308
62
|
const messages: ChatMessages = [{ role: 'user', content: prompt }];
|
|
309
|
-
const response = await this.context.api().callTogether.single(messages, {});
|
|
310
63
|
|
|
64
|
+
await this.log('debug', 'Calling API to generate archetype names', {
|
|
65
|
+
messages,
|
|
66
|
+
});
|
|
67
|
+
const response = await this.context.api().callTogether.single(messages, {});
|
|
311
68
|
if (!response) {
|
|
312
|
-
|
|
313
|
-
|
|
69
|
+
await this.log('error', 'No response for archetype names generation', {
|
|
70
|
+
combinationString,
|
|
71
|
+
});
|
|
72
|
+
throw new Error(
|
|
73
|
+
`Failed to generate archetype names for: ${combinationString}`
|
|
74
|
+
);
|
|
314
75
|
}
|
|
76
|
+
await this.log('info', 'Received archetype names response', {
|
|
77
|
+
responseLength: response.length,
|
|
78
|
+
});
|
|
315
79
|
|
|
316
80
|
const { english: englishNames, portuguese: portugueseVariants } =
|
|
317
81
|
this.parseArchetypeNameBlocks(response);
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
.select({ name: schema.archetypesData.name })
|
|
322
|
-
.from(schema.archetypesData)
|
|
323
|
-
.where(
|
|
324
|
-
and(
|
|
325
|
-
eq(schema.archetypesData.language, 'en-us'),
|
|
326
|
-
eq(schema.archetypesData.name, name)
|
|
327
|
-
)
|
|
328
|
-
)
|
|
329
|
-
.limit(1)
|
|
330
|
-
.execute();
|
|
331
|
-
|
|
332
|
-
return result.length > 0;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
await Promise.all(
|
|
336
|
-
englishNames.map(async (entry, i) => {
|
|
337
|
-
const index = (i + 1).toString();
|
|
338
|
-
const ptVariant = portugueseVariants[i];
|
|
339
|
-
|
|
340
|
-
if (await isEnglishNameDuplicate(entry.name)) {
|
|
341
|
-
console.warn(`⚠️ [Single] Duplicate name skipped: ${entry.name}`);
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
for (const gender of ['male', 'female']) {
|
|
346
|
-
const enId = `${combination}:${gender}:${index}`;
|
|
347
|
-
const ptId = `${combination}:${gender}:${index}:pt`;
|
|
348
|
-
const ptName = gender === 'female' ? ptVariant.fem : ptVariant.masc;
|
|
349
|
-
|
|
350
|
-
await db
|
|
351
|
-
.insert(schema.archetypesData)
|
|
352
|
-
.values({
|
|
353
|
-
id: enId,
|
|
354
|
-
combination,
|
|
355
|
-
gender,
|
|
356
|
-
archetypeIndex: index,
|
|
357
|
-
language: 'en-us',
|
|
358
|
-
name: entry.name,
|
|
359
|
-
essenceLine: entry.essenceLine,
|
|
360
|
-
status: 'idle',
|
|
361
|
-
})
|
|
362
|
-
.onConflictDoUpdate({
|
|
363
|
-
target: [schema.archetypesData.id],
|
|
364
|
-
set: {
|
|
365
|
-
name: entry.name,
|
|
366
|
-
essenceLine: entry.essenceLine,
|
|
367
|
-
updatedAt: new Date().getTime(),
|
|
368
|
-
},
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
await db
|
|
372
|
-
.insert(schema.archetypesData)
|
|
373
|
-
.values({
|
|
374
|
-
id: ptId,
|
|
375
|
-
combination,
|
|
376
|
-
gender,
|
|
377
|
-
archetypeIndex: index,
|
|
378
|
-
language: 'pt-br',
|
|
379
|
-
name: ptName,
|
|
380
|
-
essenceLine: ptVariant.essenceLine,
|
|
381
|
-
status: 'idle',
|
|
382
|
-
})
|
|
383
|
-
.onConflictDoUpdate({
|
|
384
|
-
target: [schema.archetypesData.id],
|
|
385
|
-
set: {
|
|
386
|
-
name: ptName,
|
|
387
|
-
essenceLine: ptVariant.essenceLine,
|
|
388
|
-
updatedAt: new Date().getTime(),
|
|
389
|
-
},
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
console.log(
|
|
393
|
-
`✅ [Single] Saved archetype ${index} (${gender}) for ${combination}`
|
|
394
|
-
);
|
|
395
|
-
}
|
|
396
|
-
})
|
|
397
|
-
);
|
|
398
|
-
|
|
399
|
-
console.log(`🏁 [Single] Done generating names for ${combination}`);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
async generateArchetypeNamesBatch(
|
|
403
|
-
entries: Array<{ combination: string }>,
|
|
404
|
-
overrideExisting: boolean = false
|
|
405
|
-
) {
|
|
406
|
-
const db = this.context.drizzle();
|
|
407
|
-
console.log(
|
|
408
|
-
`🚀 [Batch] Starting generation for ${entries.length} combinations | Override: ${overrideExisting}`
|
|
409
|
-
);
|
|
410
|
-
|
|
411
|
-
const prompts = entries.map(({ combination }) => {
|
|
412
|
-
const [sun, ascendant, moon] = combination.split('-') as ZodiacSignSlug[];
|
|
413
|
-
return { sun, ascendant, moon };
|
|
82
|
+
await this.log('debug', 'Parsed archetype names', {
|
|
83
|
+
englishNames,
|
|
84
|
+
portugueseVariants,
|
|
414
85
|
});
|
|
415
86
|
|
|
416
|
-
const
|
|
417
|
-
const messages: ChatMessages = [{ role: 'user', content: prompt }];
|
|
418
|
-
const response = await this.context.api().callTogether.single(messages, {});
|
|
419
|
-
|
|
420
|
-
if (!response) {
|
|
421
|
-
console.error(`❌ [Batch] No response from model`);
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
const blocks = response
|
|
426
|
-
.split(/Composition \d+/)
|
|
427
|
-
.slice(1)
|
|
428
|
-
.map((b) => b.trim());
|
|
87
|
+
const db = this.context.drizzle();
|
|
429
88
|
|
|
430
89
|
async function isEnglishNameDuplicate(name: string): Promise<boolean> {
|
|
431
90
|
const result = await db
|
|
@@ -439,967 +98,802 @@ export class ArchetypeService {
|
|
|
439
98
|
)
|
|
440
99
|
.limit(1)
|
|
441
100
|
.execute();
|
|
442
|
-
|
|
443
101
|
return result.length > 0;
|
|
444
102
|
}
|
|
445
103
|
|
|
446
|
-
for (let i = 0; i <
|
|
447
|
-
const
|
|
448
|
-
const
|
|
449
|
-
|
|
104
|
+
for (let i = 0; i < englishNames.length; i++) {
|
|
105
|
+
const index = (i + 1).toString();
|
|
106
|
+
const entry = englishNames[i];
|
|
107
|
+
const ptVariant = portugueseVariants[i];
|
|
450
108
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
`[Batch] Parsing failed for ${combination}. Expected 3 entries, got English: ${english.length}, Portuguese: ${portuguese.length}`
|
|
109
|
+
if (!indexesToGenerate.includes(parseInt(index))) {
|
|
110
|
+
await this.log(
|
|
111
|
+
'debug',
|
|
112
|
+
`Skipping index ${index} as it is not in indexesToGenerate`,
|
|
113
|
+
{ index, indexesToGenerate }
|
|
457
114
|
);
|
|
458
|
-
continue;
|
|
115
|
+
continue;
|
|
459
116
|
}
|
|
460
117
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
console.warn(
|
|
469
|
-
`[Batch] Skipping index ${index} for ${combination} due to missing parsed data. English: ${JSON.stringify(
|
|
470
|
-
englishEntry
|
|
471
|
-
)}, Portuguese: ${JSON.stringify(ptEntry)}`
|
|
472
|
-
);
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// if (await isEnglishNameDuplicate(englishEntry.name)) {
|
|
477
|
-
// console.warn(
|
|
478
|
-
// `⚠️ [Batch] Duplicate name skipped: ${englishEntry.name}`
|
|
479
|
-
// );
|
|
480
|
-
// continue;
|
|
481
|
-
// }
|
|
482
|
-
|
|
483
|
-
for (const gender of ['male', 'female']) {
|
|
484
|
-
const enId = `${combination}:${gender}:${index}`;
|
|
485
|
-
const ptId = `${combination}:${gender}:${index}:pt`;
|
|
486
|
-
const ptName = gender === 'female' ? ptEntry.fem : ptEntry.masc;
|
|
118
|
+
if (await isEnglishNameDuplicate(entry.name)) {
|
|
119
|
+
await this.log('warn', `Duplicate name skipped: ${entry.name}`, {
|
|
120
|
+
combinationString,
|
|
121
|
+
index,
|
|
122
|
+
});
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
487
125
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
.
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
},
|
|
507
|
-
});
|
|
126
|
+
for (const gender of ['male', 'female']) {
|
|
127
|
+
for (const lang of ['en-us', 'pt-br']) {
|
|
128
|
+
const id = `${combinationString}:${gender}:${index}${
|
|
129
|
+
lang === 'pt-br' ? ':pt' : ''
|
|
130
|
+
}`;
|
|
131
|
+
const name =
|
|
132
|
+
lang === 'en-us'
|
|
133
|
+
? entry.name
|
|
134
|
+
: gender === 'female'
|
|
135
|
+
? ptVariant.fem
|
|
136
|
+
: ptVariant.masc;
|
|
137
|
+
const essenceLine =
|
|
138
|
+
lang === 'en-us' ? entry.essenceLine : ptVariant.essenceLine;
|
|
139
|
+
|
|
140
|
+
await this.log('debug', `Saving archetype name for ${id}`, {
|
|
141
|
+
name,
|
|
142
|
+
essenceLine,
|
|
143
|
+
});
|
|
508
144
|
|
|
509
145
|
await db
|
|
510
146
|
.insert(schema.archetypesData)
|
|
511
147
|
.values({
|
|
512
|
-
id
|
|
513
|
-
combination,
|
|
148
|
+
id,
|
|
149
|
+
combination: combinationString,
|
|
514
150
|
gender,
|
|
515
151
|
archetypeIndex: index,
|
|
516
|
-
language:
|
|
517
|
-
name
|
|
518
|
-
essenceLine
|
|
152
|
+
language: lang,
|
|
153
|
+
name,
|
|
154
|
+
essenceLine,
|
|
519
155
|
status: 'idle',
|
|
520
156
|
})
|
|
521
157
|
.onConflictDoUpdate({
|
|
522
158
|
target: [schema.archetypesData.id],
|
|
523
159
|
set: {
|
|
524
|
-
name
|
|
525
|
-
essenceLine
|
|
160
|
+
name,
|
|
161
|
+
essenceLine,
|
|
526
162
|
updatedAt: new Date().getTime(),
|
|
527
163
|
},
|
|
528
164
|
});
|
|
529
165
|
|
|
530
|
-
|
|
531
|
-
|
|
166
|
+
await this.log(
|
|
167
|
+
'info',
|
|
168
|
+
`Saved archetype ${index} (${gender}, ${lang}) for ${combinationString}`
|
|
532
169
|
);
|
|
533
170
|
}
|
|
534
171
|
}
|
|
535
|
-
|
|
536
|
-
console.log(`🏁 [Batch] Finished combination: ${combination}`);
|
|
537
172
|
}
|
|
538
173
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
);
|
|
174
|
+
await this.log('info', 'Completed generateArchetypeNames', {
|
|
175
|
+
combinationString,
|
|
176
|
+
});
|
|
542
177
|
}
|
|
543
178
|
|
|
544
|
-
|
|
545
|
-
Array<{
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
combination: schema.archetypesData.combination,
|
|
552
|
-
count: sql<number>`count(*)`.as('count'),
|
|
553
|
-
})
|
|
554
|
-
.from(schema.archetypesData)
|
|
555
|
-
.groupBy(schema.archetypesData.combination)
|
|
556
|
-
.having(sql`count(*) < 12`)
|
|
557
|
-
.limit(400)
|
|
558
|
-
.execute();
|
|
559
|
-
|
|
560
|
-
const typedResult = result.map((row) => ({
|
|
561
|
-
combination: row.combination,
|
|
562
|
-
count: Number(row.count),
|
|
563
|
-
}));
|
|
179
|
+
private parseArchetypeNameBlocks(response: string): {
|
|
180
|
+
english: Array<{ name: string; essenceLine: string }>;
|
|
181
|
+
portuguese: Array<{ masc: string; fem: string; essenceLine: string }>;
|
|
182
|
+
} {
|
|
183
|
+
this.log('debug', 'Starting parseArchetypeNameBlocks', {
|
|
184
|
+
responseLength: response.length,
|
|
185
|
+
});
|
|
564
186
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
187
|
+
const blocks = response.split(/\n\d+\.\n/).filter(Boolean);
|
|
188
|
+
this.log('debug', 'Split response into blocks', {
|
|
189
|
+
blocksCount: blocks.length,
|
|
190
|
+
});
|
|
568
191
|
|
|
569
|
-
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
for (const { combination, count } of incomplete) {
|
|
576
|
-
const indexesToGenerate: number[] = [];
|
|
577
|
-
|
|
578
|
-
const db = this.context.drizzle();
|
|
579
|
-
const existing = await db
|
|
580
|
-
.select({
|
|
581
|
-
archetypeIndex: schema.archetypesData.archetypeIndex,
|
|
582
|
-
language: schema.archetypesData.language,
|
|
583
|
-
gender: schema.archetypesData.gender,
|
|
584
|
-
})
|
|
585
|
-
.from(schema.archetypesData)
|
|
586
|
-
.where(eq(schema.archetypesData.combination, combination))
|
|
587
|
-
.execute();
|
|
192
|
+
const english: Array<{ name: string; essenceLine: string }> = [];
|
|
193
|
+
const portuguese: Array<{
|
|
194
|
+
masc: string;
|
|
195
|
+
fem: string;
|
|
196
|
+
essenceLine: string;
|
|
197
|
+
}> = [];
|
|
588
198
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
199
|
+
for (const block of blocks) {
|
|
200
|
+
const nameMatch = block.match(/- Name: (.+)/);
|
|
201
|
+
const essenceLineMatch = block.match(/- Essence Line: (.+)/);
|
|
202
|
+
const ptMascMatch = block.match(/- Portuguese \(Masc\): (.+)/);
|
|
203
|
+
const ptFemMatch = block.match(/- Portuguese \(Fem\): (.+)/);
|
|
204
|
+
const ptEssenceLineMatch = block.match(/- Portuguese Essence Line: (.+)/);
|
|
595
205
|
|
|
596
|
-
if (
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
);
|
|
206
|
+
if (
|
|
207
|
+
nameMatch &&
|
|
208
|
+
essenceLineMatch &&
|
|
209
|
+
ptMascMatch &&
|
|
210
|
+
ptFemMatch &&
|
|
211
|
+
ptEssenceLineMatch
|
|
212
|
+
) {
|
|
213
|
+
english.push({
|
|
214
|
+
name: nameMatch[1].trim(),
|
|
215
|
+
essenceLine: essenceLineMatch[1].trim(),
|
|
216
|
+
});
|
|
217
|
+
portuguese.push({
|
|
218
|
+
masc: ptMascMatch[1].trim(),
|
|
219
|
+
fem: ptFemMatch[1].trim(),
|
|
220
|
+
essenceLine: ptEssenceLineMatch[1].trim(),
|
|
221
|
+
});
|
|
222
|
+
} else {
|
|
223
|
+
this.log('warn', 'Malformed archetype name block', { block });
|
|
607
224
|
}
|
|
608
225
|
}
|
|
609
226
|
|
|
610
|
-
|
|
227
|
+
this.log('info', 'Completed parseArchetypeNameBlocks', {
|
|
228
|
+
english,
|
|
229
|
+
portuguese,
|
|
230
|
+
});
|
|
231
|
+
return { english, portuguese };
|
|
611
232
|
}
|
|
612
233
|
|
|
613
|
-
async
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
234
|
+
async generateDescriptionsAndVirtues(
|
|
235
|
+
combinationString: string,
|
|
236
|
+
gender: Gender,
|
|
237
|
+
language: string
|
|
238
|
+
) {
|
|
239
|
+
await this.log('info', 'Starting generateDescriptionsAndVirtues', {
|
|
240
|
+
combinationString,
|
|
241
|
+
gender,
|
|
242
|
+
language,
|
|
243
|
+
});
|
|
618
244
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
245
|
+
const db = this.context.drizzle();
|
|
246
|
+
const archetypes = await db
|
|
247
|
+
.select()
|
|
248
|
+
.from(schema.archetypesData)
|
|
249
|
+
.where(
|
|
250
|
+
and(
|
|
251
|
+
eq(schema.archetypesData.combination, combinationString),
|
|
252
|
+
eq(schema.archetypesData.gender, gender),
|
|
253
|
+
eq(schema.archetypesData.language, 'en-us')
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
.execute();
|
|
622
257
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
258
|
+
await this.log('info', 'Fetched archetypes for description generation', {
|
|
259
|
+
archetypesCount: archetypes.length,
|
|
260
|
+
names: archetypes.map((a) => a.name),
|
|
261
|
+
});
|
|
626
262
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
263
|
+
const descVirtueMessages = this.context
|
|
264
|
+
.buildLLMMessages()
|
|
265
|
+
.generateCosmicMirrorDescriptionAndVirtues({
|
|
266
|
+
combination: combinationString,
|
|
267
|
+
names: [archetypes[0].name, archetypes[1].name, archetypes[2].name],
|
|
268
|
+
essenceLines: [
|
|
269
|
+
archetypes[0].essenceLine,
|
|
270
|
+
archetypes[1].essenceLine,
|
|
271
|
+
archetypes[2].essenceLine,
|
|
272
|
+
],
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
await this.log('debug', 'Calling API for descriptions and virtues', {
|
|
276
|
+
messages: descVirtueMessages,
|
|
277
|
+
});
|
|
278
|
+
const descVirtueResponse = await this.context
|
|
279
|
+
.api()
|
|
280
|
+
.callTogether.single(descVirtueMessages, {});
|
|
281
|
+
if (!descVirtueResponse) {
|
|
282
|
+
await this.log('error', 'No response for descriptions and virtues', {
|
|
283
|
+
combinationString,
|
|
284
|
+
});
|
|
285
|
+
throw new Error(
|
|
286
|
+
`Failed to generate descriptions and virtues for: ${combinationString}`
|
|
287
|
+
);
|
|
630
288
|
}
|
|
289
|
+
await this.log('info', 'Received descriptions and virtues response', {
|
|
290
|
+
responseLength: descVirtueResponse.length,
|
|
291
|
+
});
|
|
631
292
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
const blocks = cleanedResponse
|
|
638
|
-
.split(/Composition \d+/)
|
|
639
|
-
.slice(1)
|
|
640
|
-
.map((b) => b.trim());
|
|
641
|
-
|
|
642
|
-
for (let i = 0; i < compositions.length; i++) {
|
|
643
|
-
const comp = compositions[i];
|
|
644
|
-
const combination = [comp.sun, comp.ascendant, comp.moon].join('-');
|
|
645
|
-
const block = blocks[i];
|
|
646
|
-
const indexes = comp.indexesToGenerate ?? [1, 2, 3];
|
|
647
|
-
|
|
648
|
-
console.log(`🔄 [Batch] Processing: ${combination}`);
|
|
649
|
-
|
|
650
|
-
const { english, portuguese } = this.parseArchetypeNameBlocks(block);
|
|
651
|
-
|
|
652
|
-
if (
|
|
653
|
-
english.length !== indexes.length ||
|
|
654
|
-
portuguese.length !== indexes.length
|
|
655
|
-
) {
|
|
656
|
-
console.error(`❌ [Batch] Parsing failed for: ${combination}`);
|
|
657
|
-
await this.context
|
|
658
|
-
.drizzle()
|
|
659
|
-
.insert(schema.archetypeNameDumps)
|
|
660
|
-
.values({
|
|
661
|
-
id: `${combination}:${Date.now()}`,
|
|
662
|
-
combination,
|
|
663
|
-
rawText: block,
|
|
664
|
-
parsedSuccessfully: 0,
|
|
665
|
-
createdAt: Date.now(),
|
|
666
|
-
});
|
|
667
|
-
continue;
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
for (const index of indexes) {
|
|
671
|
-
const idx = index - 1;
|
|
672
|
-
const englishEntry = english[idx];
|
|
673
|
-
const ptEntry = portuguese[idx];
|
|
674
|
-
|
|
675
|
-
if (!englishEntry || !ptEntry) {
|
|
676
|
-
await this.context
|
|
677
|
-
.drizzle()
|
|
678
|
-
.insert(schema.archetypeNameDumps)
|
|
679
|
-
.values({
|
|
680
|
-
id: `${combination}:${index}:${Date.now()}`,
|
|
681
|
-
combination,
|
|
682
|
-
rawText: block,
|
|
683
|
-
parsedSuccessfully: 0,
|
|
684
|
-
createdAt: Date.now(),
|
|
685
|
-
});
|
|
686
|
-
console.warn(
|
|
687
|
-
`⚠️ [Batch] Skipping index ${index} for ${combination} due to missing parsed block`
|
|
688
|
-
);
|
|
689
|
-
continue;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
for (const gender of ['male', 'female']) {
|
|
693
|
-
const enId = `${combination}:${gender}:${index}`;
|
|
694
|
-
const ptId = `${combination}:${gender}:${index}:pt`;
|
|
695
|
-
const ptName = gender === 'female' ? ptEntry.fem : ptEntry.masc;
|
|
293
|
+
const parsedDescVirtues =
|
|
294
|
+
this.parseDescriptionAndVirtuesResponse(descVirtueResponse);
|
|
295
|
+
await this.log('info', 'Parsed descriptions and virtues', {
|
|
296
|
+
parsedDescVirtues,
|
|
297
|
+
});
|
|
696
298
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
essenceLine: englishEntry.essenceLine,
|
|
715
|
-
updatedAt: new Date().getTime(),
|
|
716
|
-
},
|
|
717
|
-
});
|
|
299
|
+
for (let i = 0; i < 3; i++) {
|
|
300
|
+
const index = (i + 1).toString();
|
|
301
|
+
for (const lang of ['en-us', 'pt-br']) {
|
|
302
|
+
const descriptionField =
|
|
303
|
+
lang === 'en-us'
|
|
304
|
+
? parsedDescVirtues[i].descriptionEN
|
|
305
|
+
: gender === 'male'
|
|
306
|
+
? parsedDescVirtues[i].descriptionPTM
|
|
307
|
+
: parsedDescVirtues[i].descriptionPTF;
|
|
308
|
+
|
|
309
|
+
const id = `${combinationString}:${gender}:${index}${
|
|
310
|
+
lang === 'pt-br' ? ':pt' : ''
|
|
311
|
+
}`;
|
|
312
|
+
await this.log('debug', `Updating description and virtues for ${id}`, {
|
|
313
|
+
description: descriptionField,
|
|
314
|
+
virtues: parsedDescVirtues[i].virtues,
|
|
315
|
+
});
|
|
718
316
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
target: [schema.archetypesData.id],
|
|
734
|
-
set: {
|
|
735
|
-
name: ptName,
|
|
736
|
-
essenceLine: ptEntry.essenceLine,
|
|
737
|
-
updatedAt: new Date().getTime(),
|
|
738
|
-
},
|
|
739
|
-
});
|
|
317
|
+
await db
|
|
318
|
+
.update(schema.archetypesData)
|
|
319
|
+
.set({
|
|
320
|
+
description: descriptionField,
|
|
321
|
+
virtues: JSON.stringify(parsedDescVirtues[i].virtues),
|
|
322
|
+
updatedAt: new Date().getTime(),
|
|
323
|
+
})
|
|
324
|
+
.where(
|
|
325
|
+
and(
|
|
326
|
+
eq(schema.archetypesData.id, id),
|
|
327
|
+
eq(schema.archetypesData.language, lang)
|
|
328
|
+
)
|
|
329
|
+
)
|
|
330
|
+
.execute();
|
|
740
331
|
|
|
741
|
-
|
|
742
|
-
`✅ [Batch] Saved archetype ${index} (${gender}) for ${combination}`
|
|
743
|
-
);
|
|
744
|
-
}
|
|
332
|
+
await this.log('info', `Updated description and virtues for ${id}`);
|
|
745
333
|
}
|
|
746
|
-
|
|
747
|
-
console.log(`🏁 [Batch] Finished combination: ${combination}`);
|
|
748
334
|
}
|
|
749
335
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
336
|
+
await this.log('info', 'Completed generateDescriptionsAndVirtues', {
|
|
337
|
+
combinationString,
|
|
338
|
+
gender,
|
|
339
|
+
language,
|
|
340
|
+
});
|
|
341
|
+
return parsedDescVirtues;
|
|
753
342
|
}
|
|
754
343
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
console.log(
|
|
760
|
-
`[RegenerateDuplicatesBatch] Processing batch with ${
|
|
761
|
-
Object.keys(entriesByCombination).length
|
|
762
|
-
} combinations`
|
|
763
|
-
);
|
|
764
|
-
|
|
765
|
-
// Process each combination in the batch
|
|
766
|
-
for (const [combination, entries] of Object.entries(entriesByCombination)) {
|
|
767
|
-
console.log(
|
|
768
|
-
`[RegenerateDuplicatesBatch] Processing combination: ${combination}`
|
|
769
|
-
);
|
|
770
|
-
|
|
771
|
-
// Fetch all existing English names for this combination (across all archetypeIndex, gender, language)
|
|
772
|
-
const existingNamesResult = await db
|
|
773
|
-
.selectDistinct({ name: schema.archetypesData.name })
|
|
774
|
-
.from(schema.archetypesData)
|
|
775
|
-
.where(
|
|
776
|
-
and(
|
|
777
|
-
eq(schema.archetypesData.combination, combination),
|
|
778
|
-
eq(schema.archetypesData.language, 'en-us')
|
|
779
|
-
)
|
|
780
|
-
)
|
|
781
|
-
.execute();
|
|
344
|
+
private parseDescriptionAndVirtuesResponse(response: string) {
|
|
345
|
+
this.log('debug', 'Starting parseDescriptionAndVirtuesResponse', {
|
|
346
|
+
responseLength: response.length,
|
|
347
|
+
});
|
|
782
348
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
)}`
|
|
788
|
-
);
|
|
349
|
+
const entries = response
|
|
350
|
+
.split(/---/)
|
|
351
|
+
.map((block) => block.trim())
|
|
352
|
+
.filter((block) => block.length > 0);
|
|
789
353
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
console.log(
|
|
794
|
-
`[RegenerateDuplicatesBatch] Regenerating name for ${combination} (archetypeIndex: ${archetypeIndex})`
|
|
795
|
-
);
|
|
354
|
+
this.log('debug', 'Split response into entries', {
|
|
355
|
+
entriesCount: entries.length,
|
|
356
|
+
});
|
|
796
357
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
358
|
+
const result = entries.map((entry, entryIndex) => {
|
|
359
|
+
this.log('debug', `Processing entry ${entryIndex + 1}`, { entry });
|
|
360
|
+
|
|
361
|
+
const lines = entry.split('\n').filter((line) => line.trim());
|
|
362
|
+
this.log('debug', `Split entry ${entryIndex + 1} into lines`, {
|
|
363
|
+
linesCount: lines.length,
|
|
364
|
+
lines,
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
let descriptionEN = '';
|
|
368
|
+
let descriptionPTM = '';
|
|
369
|
+
let descriptionPTF = '';
|
|
370
|
+
let virtues: string[] = [];
|
|
371
|
+
let currentField = '';
|
|
372
|
+
|
|
373
|
+
for (const line of lines) {
|
|
374
|
+
if (line.startsWith('• Description EN:')) {
|
|
375
|
+
currentField = 'descriptionEN';
|
|
376
|
+
descriptionEN = line.split('• Description EN:')[1]?.trim() || '';
|
|
377
|
+
this.log(
|
|
378
|
+
'debug',
|
|
379
|
+
`Extracted descriptionEN for entry ${entryIndex + 1}`,
|
|
380
|
+
{ descriptionEN }
|
|
810
381
|
);
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
382
|
+
} else if (line.startsWith('• Description PT-M:')) {
|
|
383
|
+
currentField = 'descriptionPTM';
|
|
384
|
+
descriptionPTM = line.split('• Description PT-M:')[1]?.trim() || '';
|
|
385
|
+
this.log(
|
|
386
|
+
'debug',
|
|
387
|
+
`Extracted descriptionPTM for entry ${entryIndex + 1}`,
|
|
388
|
+
{ descriptionPTM }
|
|
389
|
+
);
|
|
390
|
+
} else if (line.startsWith('• Description PT-F:')) {
|
|
391
|
+
currentField = 'descriptionPTF';
|
|
392
|
+
descriptionPTF = line.split('• Description PT-F:')[1]?.trim() || '';
|
|
393
|
+
this.log(
|
|
394
|
+
'debug',
|
|
395
|
+
`Extracted descriptionPTF for entry ${entryIndex + 1}`,
|
|
396
|
+
{ descriptionPTF }
|
|
397
|
+
);
|
|
398
|
+
} else if (line.startsWith('• Virtues:')) {
|
|
399
|
+
currentField = 'virtues';
|
|
400
|
+
virtues =
|
|
401
|
+
line
|
|
402
|
+
.split('• Virtues:')[1]
|
|
403
|
+
?.split(',')
|
|
404
|
+
.map((v) => v.trim())
|
|
405
|
+
.filter((v) => v) || [];
|
|
406
|
+
this.log('debug', `Extracted virtues for entry ${entryIndex + 1}`, {
|
|
407
|
+
virtues,
|
|
817
408
|
});
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
if (!response) {
|
|
826
|
-
console.error(
|
|
827
|
-
`[RegenerateDuplicatesBatch] No response from model for ${combination} (archetypeIndex: ${archetypeIndex})`
|
|
409
|
+
} else if (currentField && currentField !== 'virtues') {
|
|
410
|
+
if (currentField === 'descriptionEN') {
|
|
411
|
+
descriptionEN += ' ' + line.trim();
|
|
412
|
+
this.log(
|
|
413
|
+
'debug',
|
|
414
|
+
`Appended to descriptionEN for entry ${entryIndex + 1}`,
|
|
415
|
+
{ descriptionEN }
|
|
828
416
|
);
|
|
829
|
-
continue;
|
|
830
417
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
newName,
|
|
838
|
-
null,
|
|
839
|
-
2
|
|
840
|
-
)}`
|
|
418
|
+
if (currentField === 'descriptionPTM') {
|
|
419
|
+
descriptionPTM += ' ' + line.trim();
|
|
420
|
+
this.log(
|
|
421
|
+
'debug',
|
|
422
|
+
`Appended to descriptionPTM for entry ${entryIndex + 1}`,
|
|
423
|
+
{ descriptionPTM }
|
|
841
424
|
);
|
|
842
|
-
|
|
843
|
-
// Validate the response format
|
|
844
|
-
if (
|
|
845
|
-
!newName ||
|
|
846
|
-
!newName.name_en ||
|
|
847
|
-
!newName.name_pt_male ||
|
|
848
|
-
!newName.name_pt_female
|
|
849
|
-
) {
|
|
850
|
-
console.error(
|
|
851
|
-
`[RegenerateDuplicatesBatch] Invalid response format for ${combination} (archetypeIndex: ${archetypeIndex}): ${response}`
|
|
852
|
-
);
|
|
853
|
-
newName = null;
|
|
854
|
-
continue;
|
|
855
|
-
}
|
|
856
|
-
} catch (error) {
|
|
857
|
-
console.error(
|
|
858
|
-
`[RegenerateDuplicatesBatch] Failed to parse AI response for ${combination} (archetypeIndex: ${archetypeIndex}): ${response}`,
|
|
859
|
-
error
|
|
860
|
-
);
|
|
861
|
-
continue;
|
|
862
425
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
and(
|
|
870
|
-
eq(schema.archetypesData.language, 'en-us'),
|
|
871
|
-
eq(schema.archetypesData.name, newName.name_en)
|
|
872
|
-
)
|
|
873
|
-
)
|
|
874
|
-
.limit(1)
|
|
875
|
-
.execute();
|
|
876
|
-
|
|
877
|
-
if (isDuplicate.length > 0) {
|
|
878
|
-
console.warn(
|
|
879
|
-
`[RegenerateDuplicatesBatch] Generated name "${newName.name_en}" is already used. Adding to existing names and retrying.`
|
|
426
|
+
if (currentField === 'descriptionPTF') {
|
|
427
|
+
descriptionPTF += ' ' + line.trim();
|
|
428
|
+
this.log(
|
|
429
|
+
'debug',
|
|
430
|
+
`Appended to descriptionPTF for entry ${entryIndex + 1}`,
|
|
431
|
+
{ descriptionPTF }
|
|
880
432
|
);
|
|
881
|
-
existingNames.push(newName.name_en);
|
|
882
|
-
newName = null;
|
|
883
|
-
continue;
|
|
884
433
|
}
|
|
885
|
-
|
|
886
|
-
// If we reach here, the name is unique
|
|
887
|
-
break;
|
|
888
434
|
}
|
|
435
|
+
}
|
|
889
436
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
.where(
|
|
910
|
-
and(
|
|
911
|
-
eq(schema.archetypesData.id, enId),
|
|
912
|
-
eq(schema.archetypesData.language, 'en-us')
|
|
913
|
-
)
|
|
914
|
-
)
|
|
915
|
-
.execute();
|
|
916
|
-
|
|
917
|
-
// Update Portuguese entry
|
|
918
|
-
const ptName =
|
|
919
|
-
gender === 'female' ? newName.name_pt_female : newName.name_pt_male;
|
|
920
|
-
await db
|
|
921
|
-
.update(schema.archetypesData)
|
|
922
|
-
.set({
|
|
923
|
-
name: ptName,
|
|
924
|
-
updatedAt: new Date().getTime(),
|
|
925
|
-
})
|
|
926
|
-
.where(
|
|
927
|
-
and(
|
|
928
|
-
eq(schema.archetypesData.id, ptId),
|
|
929
|
-
eq(schema.archetypesData.language, 'pt-br')
|
|
930
|
-
)
|
|
931
|
-
)
|
|
932
|
-
.execute();
|
|
933
|
-
|
|
934
|
-
console.log(
|
|
935
|
-
`[RegenerateDuplicatesBatch] Updated ${combination} (archetypeIndex: ${archetypeIndex}, gender: ${gender}) with new name - English: ${newName.name_en}, Portuguese: ${ptName}`
|
|
936
|
-
);
|
|
937
|
-
}
|
|
437
|
+
if (
|
|
438
|
+
!descriptionEN ||
|
|
439
|
+
!descriptionPTM ||
|
|
440
|
+
!descriptionPTF ||
|
|
441
|
+
virtues.length !== 3
|
|
442
|
+
) {
|
|
443
|
+
this.log(
|
|
444
|
+
'warn',
|
|
445
|
+
`Malformed description and virtues response for entry ${
|
|
446
|
+
entryIndex + 1
|
|
447
|
+
}`,
|
|
448
|
+
{
|
|
449
|
+
descriptionEN,
|
|
450
|
+
descriptionPTM,
|
|
451
|
+
descriptionPTF,
|
|
452
|
+
virtues,
|
|
453
|
+
response: entry,
|
|
454
|
+
}
|
|
455
|
+
);
|
|
938
456
|
}
|
|
939
|
-
}
|
|
940
457
|
|
|
941
|
-
|
|
458
|
+
return { descriptionEN, descriptionPTM, descriptionPTF, virtues };
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
this.log('info', 'Completed parseDescriptionAndVirtuesResponse', {
|
|
462
|
+
result,
|
|
463
|
+
});
|
|
464
|
+
return result;
|
|
942
465
|
}
|
|
943
466
|
|
|
944
|
-
async
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
467
|
+
async generateContent(
|
|
468
|
+
combinationString: string,
|
|
469
|
+
gender: Gender,
|
|
470
|
+
language: string,
|
|
471
|
+
descriptions: Array<{ descriptionEN: string }>
|
|
472
|
+
) {
|
|
473
|
+
await this.log('info', 'Starting generateContent', {
|
|
474
|
+
combinationString,
|
|
475
|
+
gender,
|
|
476
|
+
language,
|
|
477
|
+
});
|
|
948
478
|
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
479
|
+
const db = this.context.drizzle();
|
|
480
|
+
const archetypes = await db
|
|
481
|
+
.select()
|
|
482
|
+
.from(schema.archetypesData)
|
|
483
|
+
.where(
|
|
484
|
+
and(
|
|
485
|
+
eq(schema.archetypesData.combination, combinationString),
|
|
486
|
+
eq(schema.archetypesData.gender, gender),
|
|
487
|
+
eq(schema.archetypesData.language, 'en-us')
|
|
488
|
+
)
|
|
489
|
+
)
|
|
490
|
+
.execute();
|
|
953
491
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
492
|
+
await this.log('info', 'Fetched archetypes for content generation', {
|
|
493
|
+
archetypesCount: archetypes.length,
|
|
494
|
+
names: archetypes.map((a) => a.name),
|
|
495
|
+
});
|
|
957
496
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
497
|
+
const contentResults = [];
|
|
498
|
+
for (let i = 0; i < 3; i++) {
|
|
499
|
+
await this.log('debug', `Generating content for archetype ${i + 1}`, {
|
|
500
|
+
name: archetypes[i].name,
|
|
501
|
+
});
|
|
502
|
+
const contentMessages = this.context
|
|
503
|
+
.buildLLMMessages()
|
|
504
|
+
.generateCosmicMirrorArchetypeContent({
|
|
505
|
+
combination: combinationString,
|
|
506
|
+
name: archetypes[i].name,
|
|
507
|
+
description: descriptions[i].descriptionEN,
|
|
508
|
+
});
|
|
969
509
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
})
|
|
976
|
-
|
|
977
|
-
.
|
|
978
|
-
|
|
510
|
+
await this.log('debug', `Calling API for content of archetype ${i + 1}`, {
|
|
511
|
+
messages: contentMessages,
|
|
512
|
+
});
|
|
513
|
+
const contentResponse = await this.context
|
|
514
|
+
.api()
|
|
515
|
+
.callTogether.single(contentMessages, {});
|
|
516
|
+
if (!contentResponse) {
|
|
517
|
+
await this.log(
|
|
518
|
+
'error',
|
|
519
|
+
`No response for content of archetype ${i + 1}`,
|
|
520
|
+
{ combinationString, archetype: archetypes[i].name }
|
|
521
|
+
);
|
|
522
|
+
throw new Error(
|
|
523
|
+
`Failed to generate content for archetype ${
|
|
524
|
+
i + 1
|
|
525
|
+
} of: ${combinationString}`
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
await this.log(
|
|
529
|
+
'info',
|
|
530
|
+
`Received content response for archetype ${i + 1}`,
|
|
531
|
+
{ responseLength: contentResponse.length }
|
|
532
|
+
);
|
|
979
533
|
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
} dump entries for ${combination}: ${JSON.stringify(
|
|
984
|
-
dumpEntries,
|
|
985
|
-
null,
|
|
986
|
-
2
|
|
987
|
-
)}`
|
|
534
|
+
await this.log(
|
|
535
|
+
'debug',
|
|
536
|
+
`Parsing content response for archetype ${i + 1}`
|
|
988
537
|
);
|
|
538
|
+
const parsedContent = this.parseContentResponse(contentResponse);
|
|
539
|
+
contentResults.push(parsedContent);
|
|
540
|
+
await this.log('info', `Parsed content for archetype ${i + 1}`, {
|
|
541
|
+
parsedContent,
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
const index = (i + 1).toString();
|
|
545
|
+
for (const lang of ['en-us', 'pt-br']) {
|
|
546
|
+
const contentLangIndex =
|
|
547
|
+
lang === 'en-us' ? 0 : gender === 'male' ? 1 : 2;
|
|
548
|
+
const content = [
|
|
549
|
+
{
|
|
550
|
+
section: 'voiceOfTheSoul',
|
|
551
|
+
text: parsedContent[contentLangIndex].voiceOfTheSoul,
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
section: 'giftsYouBear',
|
|
555
|
+
text: parsedContent[contentLangIndex].giftsYouBear,
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
section: 'shadowsYouFace',
|
|
559
|
+
text: parsedContent[contentLangIndex].shadowsYouFace,
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
section: 'rhythmOfYourDays',
|
|
563
|
+
text: parsedContent[contentLangIndex].rhythmOfYourDays,
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
section: 'tiesThatBind',
|
|
567
|
+
text: parsedContent[contentLangIndex].tiesThatBind,
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
section: 'lightWithin',
|
|
571
|
+
text: parsedContent[contentLangIndex].lightWithin,
|
|
572
|
+
},
|
|
573
|
+
];
|
|
574
|
+
|
|
575
|
+
const id = `${combinationString}:${gender}:${index}${
|
|
576
|
+
lang === 'pt-br' ? ':pt' : ''
|
|
577
|
+
}`;
|
|
578
|
+
await this.log('debug', `Updating content for ${id}`, { content });
|
|
579
|
+
|
|
580
|
+
await db
|
|
581
|
+
.update(schema.archetypesData)
|
|
582
|
+
.set({
|
|
583
|
+
content: JSON.stringify(content),
|
|
584
|
+
updatedAt: new Date().getTime(),
|
|
585
|
+
})
|
|
586
|
+
.where(
|
|
587
|
+
and(
|
|
588
|
+
eq(schema.archetypesData.id, id),
|
|
589
|
+
eq(schema.archetypesData.language, lang)
|
|
590
|
+
)
|
|
591
|
+
)
|
|
592
|
+
.execute();
|
|
989
593
|
|
|
990
|
-
|
|
991
|
-
console.log(
|
|
992
|
-
`⚠️ [Recycle] No dump entries found for combination: ${combination}`
|
|
993
|
-
);
|
|
994
|
-
continue;
|
|
594
|
+
await this.log('info', `Updated content for ${id}`);
|
|
995
595
|
}
|
|
596
|
+
}
|
|
996
597
|
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
for (const entry of dumpEntries) {
|
|
1005
|
-
if (entry.rawText && entry.rawText.includes(`-EN ${index}.`)) {
|
|
1006
|
-
rawText = entry.rawText;
|
|
1007
|
-
selectedEntryId = entry.id;
|
|
1008
|
-
console.log(
|
|
1009
|
-
`[Recycle] Selected dump entry for index ${index} with id ${selectedEntryId}: ${rawText}`
|
|
1010
|
-
);
|
|
1011
|
-
break;
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
598
|
+
await this.log('info', 'Completed generateContent', {
|
|
599
|
+
combinationString,
|
|
600
|
+
gender,
|
|
601
|
+
language,
|
|
602
|
+
});
|
|
603
|
+
return contentResults;
|
|
604
|
+
}
|
|
1014
605
|
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
continue;
|
|
1020
|
-
}
|
|
606
|
+
private parseContentResponse(response: string) {
|
|
607
|
+
this.log('debug', 'Starting parseContentResponse', {
|
|
608
|
+
responseLength: response.length,
|
|
609
|
+
});
|
|
1021
610
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
console.log(
|
|
1030
|
-
`[Recycle] Cleaned text for index ${index}: ${cleanedText}`
|
|
1031
|
-
);
|
|
611
|
+
const sections = response
|
|
612
|
+
.split(/---/)
|
|
613
|
+
.map((block) => block.trim())
|
|
614
|
+
.filter((block) => block.length > 0);
|
|
615
|
+
this.log('debug', 'Split response into sections', {
|
|
616
|
+
sectionsCount: sections.length,
|
|
617
|
+
});
|
|
1032
618
|
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
new RegExp(`-EN ${index}\\.\\s*[^-]*(?=-EN|-PT|$)`, 's')
|
|
1036
|
-
);
|
|
1037
|
-
const ptSectionMatch = cleanedText.match(
|
|
1038
|
-
new RegExp(`-PT ${index}\\.\\s*[^-]*(?=-EN|-PT|$)`, 's')
|
|
1039
|
-
);
|
|
619
|
+
const result = sections.map((section, sectionIndex) => {
|
|
620
|
+
this.log('debug', `Processing section ${sectionIndex + 1}`, { section });
|
|
1040
621
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
622
|
+
const lines = section.split('\n').filter((line) => line.trim());
|
|
623
|
+
this.log('debug', `Split section ${sectionIndex + 1} into lines`, {
|
|
624
|
+
linesCount: lines.length,
|
|
625
|
+
lines,
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
const content: Record<string, string> = {};
|
|
629
|
+
let currentSection = '';
|
|
630
|
+
let currentText: string[] = [];
|
|
1046
631
|
|
|
632
|
+
for (const line of lines) {
|
|
1047
633
|
if (
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
!ptSectionMatch[0]
|
|
634
|
+
line.startsWith('EN:') ||
|
|
635
|
+
line.startsWith('PT-M:') ||
|
|
636
|
+
line.startsWith('PT-F:')
|
|
1052
637
|
) {
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
638
|
+
this.log(
|
|
639
|
+
'debug',
|
|
640
|
+
`Skipping language header in section ${sectionIndex + 1}`,
|
|
641
|
+
{ line }
|
|
1057
642
|
);
|
|
1058
643
|
continue;
|
|
1059
644
|
}
|
|
1060
645
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
)}, Portuguese: ${JSON.stringify(portuguese, null, 2)}`
|
|
1075
|
-
);
|
|
1076
|
-
|
|
1077
|
-
if (english.length === 0 || portuguese.length === 0) {
|
|
1078
|
-
console.error(
|
|
1079
|
-
`❌ [Recycle] Parsing failed for index ${index} of combination: ${combination}. Block: ${block}`
|
|
1080
|
-
);
|
|
1081
|
-
if (block) {
|
|
1082
|
-
await this.context
|
|
1083
|
-
.drizzle()
|
|
1084
|
-
.insert(schema.archetypeNameDumps)
|
|
1085
|
-
.values({
|
|
1086
|
-
id: `${combination}:${index}:${Date.now()}`,
|
|
1087
|
-
combination,
|
|
1088
|
-
rawText: block,
|
|
1089
|
-
parsedSuccessfully: 0,
|
|
1090
|
-
createdAt: Date.now(),
|
|
1091
|
-
});
|
|
1092
|
-
console.log(
|
|
1093
|
-
`[Recycle] Logged failed parse to archetype_name_dumps for index ${index} of ${combination}`
|
|
1094
|
-
);
|
|
1095
|
-
} else {
|
|
1096
|
-
console.error(
|
|
1097
|
-
`❌ [Recycle] Cannot log failed parse to archetype_name_dumps: block is undefined`
|
|
646
|
+
if (line.startsWith('#')) {
|
|
647
|
+
if (currentSection && currentText.length > 0) {
|
|
648
|
+
const key = currentSection
|
|
649
|
+
.toLowerCase()
|
|
650
|
+
.replace(/\s+/g, '')
|
|
651
|
+
.replace(/([A-Z])/g, (match) => match.toLowerCase());
|
|
652
|
+
content[key] = currentText.join(' ').trim();
|
|
653
|
+
this.log(
|
|
654
|
+
'debug',
|
|
655
|
+
`Extracted section ${currentSection} in section ${
|
|
656
|
+
sectionIndex + 1
|
|
657
|
+
}`,
|
|
658
|
+
{ key, text: content[key] }
|
|
1098
659
|
);
|
|
1099
660
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
`[Recycle] Extracted entries for index ${index} - englishEntry: ${JSON.stringify(
|
|
1108
|
-
englishEntry,
|
|
1109
|
-
null,
|
|
1110
|
-
2
|
|
1111
|
-
)}, ptEntry: ${JSON.stringify(ptEntry, null, 2)}`
|
|
1112
|
-
);
|
|
1113
|
-
|
|
1114
|
-
if (!englishEntry || !ptEntry) {
|
|
1115
|
-
console.warn(
|
|
1116
|
-
`⚠️ [Recycle] Skipping index ${index} for ${combination} due to missing parsed data. englishEntry: ${JSON.stringify(
|
|
1117
|
-
englishEntry
|
|
1118
|
-
)}, ptEntry: ${JSON.stringify(ptEntry)}`
|
|
661
|
+
currentSection = line.replace('# ', '');
|
|
662
|
+
currentText = [];
|
|
663
|
+
this.log(
|
|
664
|
+
'debug',
|
|
665
|
+
`Starting new section ${currentSection} in section ${
|
|
666
|
+
sectionIndex + 1
|
|
667
|
+
}`
|
|
1119
668
|
);
|
|
1120
|
-
|
|
669
|
+
} else {
|
|
670
|
+
currentText.push(line);
|
|
1121
671
|
}
|
|
672
|
+
}
|
|
1122
673
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
combination,
|
|
1138
|
-
gender,
|
|
1139
|
-
archetypeIndex: index.toString(),
|
|
1140
|
-
language: 'en-us',
|
|
1141
|
-
name: englishEntry.name,
|
|
1142
|
-
essenceLine: englishEntry.essenceLine,
|
|
1143
|
-
status: 'idle',
|
|
1144
|
-
})
|
|
1145
|
-
.onConflictDoUpdate({
|
|
1146
|
-
target: [schema.archetypesData.id],
|
|
1147
|
-
set: {
|
|
1148
|
-
name: englishEntry.name,
|
|
1149
|
-
essenceLine: englishEntry.essenceLine,
|
|
1150
|
-
updatedAt: new Date().getTime(),
|
|
1151
|
-
},
|
|
1152
|
-
});
|
|
1153
|
-
|
|
1154
|
-
console.log(
|
|
1155
|
-
`[Recycle] Inserting Portuguese entry for ${combination}, index ${index}, gender ${gender}: id=${ptId}, name=${ptName}, essenceLine=${ptEntry.essenceLine}`
|
|
1156
|
-
);
|
|
1157
|
-
await this.context
|
|
1158
|
-
.drizzle()
|
|
1159
|
-
.insert(schema.archetypesData)
|
|
1160
|
-
.values({
|
|
1161
|
-
id: ptId,
|
|
1162
|
-
combination,
|
|
1163
|
-
gender,
|
|
1164
|
-
archetypeIndex: index.toString(),
|
|
1165
|
-
language: 'pt-br',
|
|
1166
|
-
name: ptName,
|
|
1167
|
-
essenceLine: ptEntry.essenceLine,
|
|
1168
|
-
status: 'idle',
|
|
1169
|
-
})
|
|
1170
|
-
.onConflictDoUpdate({
|
|
1171
|
-
target: [schema.archetypesData.id],
|
|
1172
|
-
set: {
|
|
1173
|
-
name: ptName,
|
|
1174
|
-
essenceLine: ptEntry.essenceLine,
|
|
1175
|
-
updatedAt: new Date().getTime(),
|
|
1176
|
-
},
|
|
1177
|
-
});
|
|
674
|
+
if (currentSection && currentText.length > 0) {
|
|
675
|
+
const key = currentSection
|
|
676
|
+
.toLowerCase()
|
|
677
|
+
.replace(/\s+/g, '')
|
|
678
|
+
.replace(/([A-Z])/g, (match) => match.toLowerCase());
|
|
679
|
+
content[key] = currentText.join(' ').trim();
|
|
680
|
+
this.log(
|
|
681
|
+
'debug',
|
|
682
|
+
`Extracted final section ${currentSection} in section ${
|
|
683
|
+
sectionIndex + 1
|
|
684
|
+
}`,
|
|
685
|
+
{ key, text: content[key] }
|
|
686
|
+
);
|
|
687
|
+
}
|
|
1178
688
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
689
|
+
const parsedContent = {
|
|
690
|
+
voiceOfTheSoul:
|
|
691
|
+
content['thevoiceofthesoul'] || content['avozdaalma'] || '',
|
|
692
|
+
giftsYouBear:
|
|
693
|
+
content['thegiftsyoubear'] || content['osdonsquevocêcarrega'] || '',
|
|
694
|
+
shadowsYouFace:
|
|
695
|
+
content['theshadowsyouface'] || content['assombrasqueenfrenta'] || '',
|
|
696
|
+
rhythmOfYourDays:
|
|
697
|
+
content['therhythmofyourdays'] || content['oritmodosseusdias'] || '',
|
|
698
|
+
tiesThatBind:
|
|
699
|
+
content['thetiesthatbind'] || content['oslaçosqueteconectam'] || '',
|
|
700
|
+
lightWithin: content['thelightwithin'] || content['aluzinterior'] || '',
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
if (Object.values(parsedContent).some((value) => !value)) {
|
|
704
|
+
this.log(
|
|
705
|
+
'warn',
|
|
706
|
+
`Malformed content response for section ${sectionIndex + 1}`,
|
|
707
|
+
{
|
|
708
|
+
parsedContent,
|
|
709
|
+
response: section,
|
|
1194
710
|
}
|
|
1195
|
-
|
|
1196
|
-
console.log(
|
|
1197
|
-
`✅ [Recycle] Saved archetype ${index} (${gender}) for ${combination}`
|
|
1198
|
-
);
|
|
1199
|
-
}
|
|
711
|
+
);
|
|
1200
712
|
}
|
|
1201
713
|
|
|
1202
|
-
|
|
1203
|
-
}
|
|
714
|
+
return parsedContent;
|
|
715
|
+
});
|
|
1204
716
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
);
|
|
717
|
+
this.log('info', 'Completed parseContentResponse', { result });
|
|
718
|
+
return result;
|
|
1208
719
|
}
|
|
1209
720
|
|
|
1210
|
-
async
|
|
721
|
+
async generateLeonardoPrompts(
|
|
722
|
+
combinationString: string,
|
|
723
|
+
gender: Gender,
|
|
724
|
+
language: string,
|
|
725
|
+
descriptions: Array<{ descriptionEN: string }>
|
|
726
|
+
) {
|
|
727
|
+
await this.log('info', 'Starting generateLeonardoPrompts', {
|
|
728
|
+
combinationString,
|
|
729
|
+
gender,
|
|
730
|
+
language,
|
|
731
|
+
});
|
|
732
|
+
|
|
1211
733
|
const db = this.context.drizzle();
|
|
1212
|
-
const
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
const existing = await db
|
|
1225
|
-
.select({
|
|
1226
|
-
archetypeIndex: schema.archetypesData.archetypeIndex,
|
|
1227
|
-
language: schema.archetypesData.language,
|
|
1228
|
-
gender: schema.archetypesData.gender,
|
|
1229
|
-
})
|
|
1230
|
-
.from(schema.archetypesData)
|
|
1231
|
-
.where(eq(schema.archetypesData.combination, combination))
|
|
1232
|
-
.execute();
|
|
734
|
+
const archetypes = await db
|
|
735
|
+
.select()
|
|
736
|
+
.from(schema.archetypesData)
|
|
737
|
+
.where(
|
|
738
|
+
and(
|
|
739
|
+
eq(schema.archetypesData.combination, combinationString),
|
|
740
|
+
eq(schema.archetypesData.gender, gender),
|
|
741
|
+
eq(schema.archetypesData.language, 'en-us')
|
|
742
|
+
)
|
|
743
|
+
)
|
|
744
|
+
.execute();
|
|
1233
745
|
|
|
1234
|
-
|
|
746
|
+
await this.log('info', 'Fetched archetypes for Leonardo prompts', {
|
|
747
|
+
archetypesCount: archetypes.length,
|
|
748
|
+
names: archetypes.map((a) => a.name),
|
|
749
|
+
});
|
|
1235
750
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
751
|
+
const promptMessages = this.context
|
|
752
|
+
.buildLLMMessages()
|
|
753
|
+
.generateCosmicMirrorArchetypeLeonardoPrompts(
|
|
754
|
+
archetypes.map((arc, idx) => ({
|
|
755
|
+
name: arc.name,
|
|
756
|
+
description: descriptions[idx].descriptionEN,
|
|
757
|
+
}))
|
|
758
|
+
);
|
|
1242
759
|
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
760
|
+
await this.log('debug', 'Calling API for Leonardo prompts', {
|
|
761
|
+
messages: promptMessages,
|
|
762
|
+
});
|
|
763
|
+
const promptResponse = await this.context
|
|
764
|
+
.api()
|
|
765
|
+
.callTogether.single(promptMessages, {});
|
|
766
|
+
if (!promptResponse) {
|
|
767
|
+
await this.log('error', 'No response for Leonardo prompts', {
|
|
768
|
+
combinationString,
|
|
769
|
+
});
|
|
770
|
+
throw new Error(
|
|
771
|
+
`Failed to generate Leonardo prompts for: ${combinationString}`
|
|
772
|
+
);
|
|
1256
773
|
}
|
|
774
|
+
await this.log('info', 'Received Leonardo prompts response', {
|
|
775
|
+
responseLength: promptResponse.length,
|
|
776
|
+
});
|
|
1257
777
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
778
|
+
const parsedPrompts = this.parseLeonardoPromptResponse(promptResponse);
|
|
779
|
+
await this.log('info', 'Parsed Leonardo prompts', { parsedPrompts });
|
|
780
|
+
|
|
781
|
+
for (let i = 0; i < 3; i++) {
|
|
782
|
+
const index = (i + 1).toString();
|
|
783
|
+
const leonardoPrompt =
|
|
784
|
+
gender === 'male'
|
|
785
|
+
? parsedPrompts[i].malePrompt
|
|
786
|
+
: parsedPrompts[i].femalePrompt;
|
|
787
|
+
|
|
788
|
+
for (const lang of ['en-us', 'pt-br']) {
|
|
789
|
+
const id = `${combinationString}:${gender}:${index}${
|
|
790
|
+
lang === 'pt-br' ? ':pt' : ''
|
|
791
|
+
}`;
|
|
792
|
+
await this.log('debug', `Updating Leonardo prompt for ${id}`, {
|
|
793
|
+
leonardoPrompt,
|
|
794
|
+
});
|
|
1261
795
|
|
|
1262
|
-
|
|
1263
|
-
|
|
796
|
+
await db
|
|
797
|
+
.update(schema.archetypesData)
|
|
798
|
+
.set({
|
|
799
|
+
leonardoPrompt,
|
|
800
|
+
updatedAt: new Date().getTime(),
|
|
801
|
+
})
|
|
802
|
+
.where(
|
|
803
|
+
and(
|
|
804
|
+
eq(schema.archetypesData.id, id),
|
|
805
|
+
eq(schema.archetypesData.language, lang)
|
|
806
|
+
)
|
|
807
|
+
)
|
|
808
|
+
.execute();
|
|
1264
809
|
|
|
1265
|
-
|
|
1266
|
-
english: any[];
|
|
1267
|
-
portuguese: any[];
|
|
1268
|
-
} {
|
|
1269
|
-
console.log(`[Parse] Starting parsing of block: ${block}`);
|
|
1270
|
-
|
|
1271
|
-
// Split the block into sections based on -EN and -PT markers
|
|
1272
|
-
const sections = block.split(/^-EN|^-PT/m).filter((s) => s.trim());
|
|
1273
|
-
console.log(
|
|
1274
|
-
`[Parse] Split block into ${sections.length} sections: ${JSON.stringify(
|
|
1275
|
-
sections,
|
|
1276
|
-
null,
|
|
1277
|
-
2
|
|
1278
|
-
)}`
|
|
1279
|
-
);
|
|
1280
|
-
|
|
1281
|
-
const english: any[] = [];
|
|
1282
|
-
const portuguese: any[] = [];
|
|
1283
|
-
|
|
1284
|
-
// Track whether we're in an EN or PT section
|
|
1285
|
-
let currentLang: 'EN' | 'PT' | null = null;
|
|
1286
|
-
|
|
1287
|
-
for (let i = 0; i < block.length; i++) {
|
|
1288
|
-
if (block.startsWith('-EN', i)) {
|
|
1289
|
-
currentLang = 'EN';
|
|
1290
|
-
i += 3; // Skip past "-EN"
|
|
1291
|
-
} else if (block.startsWith('-PT', i)) {
|
|
1292
|
-
currentLang = 'PT';
|
|
1293
|
-
i += 3; // Skip past "-PT"
|
|
810
|
+
await this.log('info', `Updated Leonardo prompt for ${id}`);
|
|
1294
811
|
}
|
|
1295
812
|
}
|
|
1296
813
|
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
// Split the section into individual entries based on numbered markers (1., 2., 3.)
|
|
1306
|
-
const entries = section.split(/\n(?=\d+\.\s*)/).filter((e) => e.trim());
|
|
1307
|
-
console.log(
|
|
1308
|
-
`[Parse] Split section into ${entries.length} entries: ${JSON.stringify(
|
|
1309
|
-
entries,
|
|
1310
|
-
null,
|
|
1311
|
-
2
|
|
1312
|
-
)}`
|
|
1313
|
-
);
|
|
814
|
+
await this.log('info', 'Completed generateLeonardoPrompts', {
|
|
815
|
+
combinationString,
|
|
816
|
+
gender,
|
|
817
|
+
language,
|
|
818
|
+
});
|
|
819
|
+
return parsedPrompts;
|
|
820
|
+
}
|
|
1314
821
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
lines,
|
|
1320
|
-
null,
|
|
1321
|
-
2
|
|
1322
|
-
)}`
|
|
1323
|
-
);
|
|
822
|
+
private parseLeonardoPromptResponse(response: string) {
|
|
823
|
+
this.log('debug', 'Starting parseLeonardoPromptResponse', {
|
|
824
|
+
responseLength: response.length,
|
|
825
|
+
});
|
|
1324
826
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
const femMatch = lines
|
|
1361
|
-
.find((l) => l.includes('• Feminino:'))
|
|
1362
|
-
?.split('• Feminino:')[1]
|
|
1363
|
-
?.trim();
|
|
1364
|
-
console.log(`[Parse] Portuguese femMatch: ${femMatch}`);
|
|
1365
|
-
|
|
1366
|
-
const essenceMatch = lines
|
|
1367
|
-
.find((l) => l.includes('• Essência:'))
|
|
1368
|
-
?.split('• Essência:')[1]
|
|
1369
|
-
?.trim();
|
|
1370
|
-
console.log(`[Parse] Portuguese essenceMatch: ${essenceMatch}`);
|
|
1371
|
-
|
|
1372
|
-
if (mascMatch && femMatch && essenceMatch) {
|
|
1373
|
-
const ptEntry = {
|
|
1374
|
-
masc: mascMatch,
|
|
1375
|
-
fem: femMatch,
|
|
1376
|
-
essenceLine: essenceMatch,
|
|
1377
|
-
};
|
|
1378
|
-
portuguese.push(ptEntry);
|
|
1379
|
-
console.log(
|
|
1380
|
-
`[Parse] Added Portuguese entry: ${JSON.stringify(
|
|
1381
|
-
ptEntry,
|
|
1382
|
-
null,
|
|
1383
|
-
2
|
|
1384
|
-
)}`
|
|
1385
|
-
);
|
|
1386
|
-
} else {
|
|
1387
|
-
console.warn(
|
|
1388
|
-
`[Parse] Skipping Portuguese entry due to missing fields. mascMatch: ${mascMatch}, femMatch: ${femMatch}, essenceMatch: ${essenceMatch}`
|
|
1389
|
-
);
|
|
827
|
+
const lines = response.split('\n').filter((line) => line.trim());
|
|
828
|
+
this.log('debug', 'Split response into lines', {
|
|
829
|
+
linesCount: lines.length,
|
|
830
|
+
lines,
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
const prompts: Array<{ malePrompt: string; femalePrompt: string }> = [];
|
|
834
|
+
|
|
835
|
+
for (let i = 0; i < lines.length; i += 2) {
|
|
836
|
+
const maleLine = lines[i];
|
|
837
|
+
const femaleLine = lines[i + 1];
|
|
838
|
+
|
|
839
|
+
const malePrompt = maleLine.startsWith(`${Math.floor(i / 2) + 1}.m`)
|
|
840
|
+
? maleLine.split(`${Math.floor(i / 2) + 1}.m`)[1]?.trim()
|
|
841
|
+
: '';
|
|
842
|
+
const femalePrompt = femaleLine.startsWith(`${Math.floor(i / 2) + 1}.f`)
|
|
843
|
+
? femaleLine.split(`${Math.floor(i / 2) + 1}.f`)[1]?.trim()
|
|
844
|
+
: '';
|
|
845
|
+
|
|
846
|
+
prompts.push({ malePrompt, femalePrompt });
|
|
847
|
+
this.log(
|
|
848
|
+
'debug',
|
|
849
|
+
`Parsed prompts for archetype ${Math.floor(i / 2) + 1}`,
|
|
850
|
+
{ malePrompt, femalePrompt }
|
|
851
|
+
);
|
|
852
|
+
|
|
853
|
+
if (!malePrompt || !femalePrompt) {
|
|
854
|
+
this.log(
|
|
855
|
+
'warn',
|
|
856
|
+
`Malformed Leonardo prompt for archetype ${Math.floor(i / 2) + 1}`,
|
|
857
|
+
{
|
|
858
|
+
malePrompt,
|
|
859
|
+
femalePrompt,
|
|
860
|
+
maleLine,
|
|
861
|
+
femaleLine,
|
|
1390
862
|
}
|
|
1391
|
-
|
|
863
|
+
);
|
|
1392
864
|
}
|
|
1393
865
|
}
|
|
1394
866
|
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
null,
|
|
1399
|
-
2
|
|
1400
|
-
)}, Portuguese: ${JSON.stringify(portuguese, null, 2)}`
|
|
1401
|
-
);
|
|
867
|
+
this.log('info', 'Completed parseLeonardoPromptResponse', { prompts });
|
|
868
|
+
return prompts;
|
|
869
|
+
}
|
|
1402
870
|
|
|
1403
|
-
|
|
871
|
+
async fetchArchetypesFromDB(
|
|
872
|
+
combinationString: string,
|
|
873
|
+
gender: Gender,
|
|
874
|
+
language: string
|
|
875
|
+
) {
|
|
876
|
+
await this.log('debug', 'Executing fetchArchetypesFromDB', {
|
|
877
|
+
combinationString,
|
|
878
|
+
gender,
|
|
879
|
+
language,
|
|
880
|
+
});
|
|
881
|
+
const db = this.context.drizzle();
|
|
882
|
+
const result = await db
|
|
883
|
+
.select()
|
|
884
|
+
.from(schema.archetypesData)
|
|
885
|
+
.where(
|
|
886
|
+
and(
|
|
887
|
+
eq(schema.archetypesData.combination, combinationString),
|
|
888
|
+
eq(schema.archetypesData.gender, gender),
|
|
889
|
+
eq(schema.archetypesData.language, language)
|
|
890
|
+
)
|
|
891
|
+
)
|
|
892
|
+
.execute();
|
|
893
|
+
await this.log('debug', 'fetchArchetypesFromDB result', {
|
|
894
|
+
resultCount: result.length,
|
|
895
|
+
result,
|
|
896
|
+
});
|
|
897
|
+
return result;
|
|
1404
898
|
}
|
|
1405
899
|
}
|