@zodic/shared 0.0.315 → 0.0.317
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 +825 -1259
- package/app/services/LeonardoService.ts +187 -288
- package/app/workflow/ArchetypeWorkflow.ts +40 -558
- package/package.json +1 -1
- package/utils/buildMessages.ts +73 -58
|
@@ -1,431 +1,103 @@
|
|
|
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
|
|
|
9
|
+
interface ParsedDescriptionAndVirtues {
|
|
10
|
+
descriptionEN: string;
|
|
11
|
+
descriptionPTM: string;
|
|
12
|
+
descriptionPTF: string;
|
|
13
|
+
virtuesEN: string[];
|
|
14
|
+
virtuesPT: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface LeonardoPrompt {
|
|
18
|
+
malePrompt: string;
|
|
19
|
+
femalePrompt: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
10
22
|
@injectable()
|
|
11
23
|
export class ArchetypeService {
|
|
12
24
|
constructor(@inject(AppContext) private context: AppContext) {}
|
|
13
25
|
|
|
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();
|
|
26
|
+
private async log(
|
|
27
|
+
level: 'info' | 'debug' | 'warn' | 'error',
|
|
28
|
+
message: string,
|
|
29
|
+
context: Record<string, any> = {}
|
|
30
|
+
) {
|
|
31
|
+
const logId = `archetype-service:${Date.now()}`;
|
|
32
|
+
const logMessage = `[${level.toUpperCase()}] ${message}`;
|
|
258
33
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
34
|
+
console[level](logMessage, context);
|
|
35
|
+
const db = this.context.drizzle();
|
|
36
|
+
try {
|
|
37
|
+
await db
|
|
38
|
+
.insert(schema.logs)
|
|
39
|
+
.values({
|
|
40
|
+
id: logId,
|
|
41
|
+
level,
|
|
42
|
+
message,
|
|
43
|
+
context: JSON.stringify(context),
|
|
44
|
+
createdAt: new Date().getTime(),
|
|
45
|
+
})
|
|
46
|
+
.execute();
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error('[ERROR] Failed to persist log to database:', {
|
|
49
|
+
error,
|
|
50
|
+
logId,
|
|
51
|
+
message,
|
|
52
|
+
context,
|
|
53
|
+
});
|
|
262
54
|
}
|
|
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
55
|
}
|
|
288
56
|
|
|
57
|
+
// Replace generateArchetypeNames and generateArchetypeNamesBatch
|
|
289
58
|
async generateArchetypeNames(
|
|
290
|
-
|
|
59
|
+
combinationString: string,
|
|
291
60
|
overrideExisting: boolean = false,
|
|
292
|
-
indexesToGenerate
|
|
61
|
+
indexesToGenerate: number[] = [1, 2, 3]
|
|
293
62
|
) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
63
|
+
await this.log('info', 'Starting generateArchetypeNames', {
|
|
64
|
+
combinationString,
|
|
65
|
+
overrideExisting,
|
|
66
|
+
indexesToGenerate,
|
|
67
|
+
});
|
|
298
68
|
|
|
299
|
-
const [sun, ascendant, moon] =
|
|
69
|
+
const [sun, ascendant, moon] = combinationString.split(
|
|
70
|
+
'-'
|
|
71
|
+
) as ZodiacSignSlug[];
|
|
300
72
|
const prompt = generateArchetypePrompt([
|
|
301
|
-
{
|
|
302
|
-
sun,
|
|
303
|
-
ascendant,
|
|
304
|
-
moon,
|
|
305
|
-
indexesToGenerate: indexesToGenerate ?? [1, 2, 3],
|
|
306
|
-
},
|
|
73
|
+
{ sun, ascendant, moon, indexesToGenerate },
|
|
307
74
|
]);
|
|
308
75
|
const messages: ChatMessages = [{ role: 'user', content: prompt }];
|
|
309
|
-
const response = await this.context.api().callTogether.single(messages, {});
|
|
310
76
|
|
|
77
|
+
await this.log('debug', 'Calling API to generate archetype names', {
|
|
78
|
+
messages,
|
|
79
|
+
});
|
|
80
|
+
const response = await this.context.api().callTogether.single(messages, {});
|
|
311
81
|
if (!response) {
|
|
312
|
-
|
|
313
|
-
|
|
82
|
+
await this.log('error', 'No response for archetype names generation', {
|
|
83
|
+
combinationString,
|
|
84
|
+
});
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Failed to generate archetype names for: ${combinationString}`
|
|
87
|
+
);
|
|
314
88
|
}
|
|
89
|
+
await this.log('info', 'Received archetype names response', {
|
|
90
|
+
responseLength: response.length,
|
|
91
|
+
});
|
|
315
92
|
|
|
316
93
|
const { english: englishNames, portuguese: portugueseVariants } =
|
|
317
94
|
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 };
|
|
95
|
+
await this.log('debug', 'Parsed archetype names', {
|
|
96
|
+
englishNames,
|
|
97
|
+
portugueseVariants,
|
|
414
98
|
});
|
|
415
99
|
|
|
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());
|
|
100
|
+
const db = this.context.drizzle();
|
|
429
101
|
|
|
430
102
|
async function isEnglishNameDuplicate(name: string): Promise<boolean> {
|
|
431
103
|
const result = await db
|
|
@@ -439,967 +111,861 @@ export class ArchetypeService {
|
|
|
439
111
|
)
|
|
440
112
|
.limit(1)
|
|
441
113
|
.execute();
|
|
442
|
-
|
|
443
114
|
return result.length > 0;
|
|
444
115
|
}
|
|
445
116
|
|
|
446
|
-
for (let i = 0; i <
|
|
447
|
-
const
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const { english, portuguese } = this.parseArchetypeNameBlocks(block);
|
|
117
|
+
for (let i = 0; i < englishNames.length; i++) {
|
|
118
|
+
const index = (i + 1).toString();
|
|
119
|
+
const entry = englishNames[i];
|
|
120
|
+
const ptVariant = portugueseVariants[i];
|
|
452
121
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
`
|
|
122
|
+
if (!indexesToGenerate.includes(parseInt(index))) {
|
|
123
|
+
await this.log(
|
|
124
|
+
'debug',
|
|
125
|
+
`Skipping index ${index} as it is not in indexesToGenerate`,
|
|
126
|
+
{ index, indexesToGenerate }
|
|
457
127
|
);
|
|
458
|
-
continue;
|
|
128
|
+
continue;
|
|
459
129
|
}
|
|
460
130
|
|
|
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;
|
|
131
|
+
if (await isEnglishNameDuplicate(entry.name)) {
|
|
132
|
+
await this.log('warn', `Duplicate name skipped: ${entry.name}`, {
|
|
133
|
+
combinationString,
|
|
134
|
+
index,
|
|
135
|
+
});
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
487
138
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
.
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
},
|
|
507
|
-
});
|
|
139
|
+
for (const gender of ['male', 'female']) {
|
|
140
|
+
for (const lang of ['en-us', 'pt-br']) {
|
|
141
|
+
const id = `${combinationString}:${gender}:${index}${
|
|
142
|
+
lang === 'pt-br' ? ':pt' : ''
|
|
143
|
+
}`;
|
|
144
|
+
const name =
|
|
145
|
+
lang === 'en-us'
|
|
146
|
+
? entry.name
|
|
147
|
+
: gender === 'female'
|
|
148
|
+
? ptVariant.fem
|
|
149
|
+
: ptVariant.masc;
|
|
150
|
+
const essenceLine =
|
|
151
|
+
lang === 'en-us' ? entry.essenceLine : ptVariant.essenceLine;
|
|
152
|
+
|
|
153
|
+
await this.log('debug', `Saving archetype name for ${id}`, {
|
|
154
|
+
name,
|
|
155
|
+
essenceLine,
|
|
156
|
+
});
|
|
508
157
|
|
|
509
158
|
await db
|
|
510
159
|
.insert(schema.archetypesData)
|
|
511
160
|
.values({
|
|
512
|
-
id
|
|
513
|
-
combination,
|
|
161
|
+
id,
|
|
162
|
+
combination: combinationString,
|
|
514
163
|
gender,
|
|
515
164
|
archetypeIndex: index,
|
|
516
|
-
language:
|
|
517
|
-
name
|
|
518
|
-
essenceLine
|
|
165
|
+
language: lang,
|
|
166
|
+
name,
|
|
167
|
+
essenceLine,
|
|
519
168
|
status: 'idle',
|
|
520
169
|
})
|
|
521
170
|
.onConflictDoUpdate({
|
|
522
171
|
target: [schema.archetypesData.id],
|
|
523
172
|
set: {
|
|
524
|
-
name
|
|
525
|
-
essenceLine
|
|
173
|
+
name,
|
|
174
|
+
essenceLine,
|
|
526
175
|
updatedAt: new Date().getTime(),
|
|
527
176
|
},
|
|
528
177
|
});
|
|
529
178
|
|
|
530
|
-
|
|
531
|
-
|
|
179
|
+
await this.log(
|
|
180
|
+
'info',
|
|
181
|
+
`Saved archetype ${index} (${gender}, ${lang}) for ${combinationString}`
|
|
532
182
|
);
|
|
533
183
|
}
|
|
534
184
|
}
|
|
535
|
-
|
|
536
|
-
console.log(`🏁 [Batch] Finished combination: ${combination}`);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
console.log(
|
|
540
|
-
`🎉 [Batch] Completed processing for all ${entries.length} combinations`
|
|
541
|
-
);
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
async findIncompleteCombinations(): Promise<
|
|
545
|
-
Array<{ combination: string; count: number }>
|
|
546
|
-
> {
|
|
547
|
-
const db = this.context.drizzle();
|
|
548
|
-
|
|
549
|
-
const result = await db
|
|
550
|
-
.select({
|
|
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
|
-
}));
|
|
564
|
-
|
|
565
|
-
console.log(`🔍 Found ${typedResult.length} incomplete combinations`);
|
|
566
|
-
return typedResult;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
async regenerateMissingArchetypeNames(overrideExisting: boolean = false) {
|
|
570
|
-
const incomplete = await this.findIncompleteCombinations();
|
|
571
|
-
console.log(
|
|
572
|
-
`🔁 Starting regeneration for ${incomplete.length} combinations`
|
|
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();
|
|
588
|
-
|
|
589
|
-
for (let i = 1; i <= 3; i++) {
|
|
590
|
-
const hasAll =
|
|
591
|
-
existing.filter((e) => e.archetypeIndex === i.toString()).length ===
|
|
592
|
-
4;
|
|
593
|
-
if (!hasAll) indexesToGenerate.push(i);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
if (indexesToGenerate.length > 0) {
|
|
597
|
-
console.log(
|
|
598
|
-
`🧩 Regenerating indexes ${indexesToGenerate.join(
|
|
599
|
-
','
|
|
600
|
-
)} for ${combination}`
|
|
601
|
-
);
|
|
602
|
-
await this.generateArchetypeNames(
|
|
603
|
-
combination,
|
|
604
|
-
overrideExisting,
|
|
605
|
-
indexesToGenerate
|
|
606
|
-
);
|
|
607
|
-
}
|
|
608
185
|
}
|
|
609
186
|
|
|
610
|
-
|
|
187
|
+
await this.log('info', 'Completed generateArchetypeNames', {
|
|
188
|
+
combinationString,
|
|
189
|
+
});
|
|
611
190
|
}
|
|
612
191
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
`🔁 [Batch] Starting regeneration for ${compositions.length} combinations`
|
|
621
|
-
);
|
|
622
|
-
|
|
623
|
-
const prompt = generateArchetypePrompt(compositions);
|
|
624
|
-
const messages: ChatMessages = [{ role: 'user', content: prompt }];
|
|
625
|
-
const response = await this.context.api().callTogether.single(messages, {});
|
|
626
|
-
|
|
627
|
-
if (!response) {
|
|
628
|
-
console.error(`❌ [Batch] No response from model during regeneration`);
|
|
629
|
-
return;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// Clean up the response by removing unexpected suffixes
|
|
633
|
-
const cleanedResponse = response
|
|
634
|
-
.replace(/###|---\s*###|-\s*###/g, '')
|
|
635
|
-
.trim();
|
|
636
|
-
|
|
637
|
-
const blocks = cleanedResponse
|
|
638
|
-
.split(/Composition \d+/)
|
|
639
|
-
.slice(1)
|
|
640
|
-
.map((b) => b.trim());
|
|
192
|
+
private parseArchetypeNameBlocks(response: string): {
|
|
193
|
+
english: Array<{ name: string; essenceLine: string }>;
|
|
194
|
+
portuguese: Array<{ masc: string; fem: string; essenceLine: string }>;
|
|
195
|
+
} {
|
|
196
|
+
this.log('debug', 'Starting parseArchetypeNameBlocks', {
|
|
197
|
+
responseLength: response.length,
|
|
198
|
+
});
|
|
641
199
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
const indexes = comp.indexesToGenerate ?? [1, 2, 3];
|
|
200
|
+
const blocks = response.split(/\n\d+\.\n/).filter(Boolean);
|
|
201
|
+
this.log('debug', 'Split response into blocks', {
|
|
202
|
+
blocksCount: blocks.length,
|
|
203
|
+
});
|
|
647
204
|
|
|
648
|
-
|
|
205
|
+
const english: Array<{ name: string; essenceLine: string }> = [];
|
|
206
|
+
const portuguese: Array<{
|
|
207
|
+
masc: string;
|
|
208
|
+
fem: string;
|
|
209
|
+
essenceLine: string;
|
|
210
|
+
}> = [];
|
|
649
211
|
|
|
650
|
-
|
|
212
|
+
for (const block of blocks) {
|
|
213
|
+
const nameMatch = block.match(/- Name: (.+)/);
|
|
214
|
+
const essenceLineMatch = block.match(/- Essence Line: (.+)/);
|
|
215
|
+
const ptMascMatch = block.match(/- Portuguese \(Masc\): (.+)/);
|
|
216
|
+
const ptFemMatch = block.match(/- Portuguese \(Fem\): (.+)/);
|
|
217
|
+
const ptEssenceLineMatch = block.match(/- Portuguese Essence Line: (.+)/);
|
|
651
218
|
|
|
652
219
|
if (
|
|
653
|
-
|
|
654
|
-
|
|
220
|
+
nameMatch &&
|
|
221
|
+
essenceLineMatch &&
|
|
222
|
+
ptMascMatch &&
|
|
223
|
+
ptFemMatch &&
|
|
224
|
+
ptEssenceLineMatch
|
|
655
225
|
) {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
.
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
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;
|
|
696
|
-
|
|
697
|
-
await this.context
|
|
698
|
-
.drizzle()
|
|
699
|
-
.insert(schema.archetypesData)
|
|
700
|
-
.values({
|
|
701
|
-
id: enId,
|
|
702
|
-
combination,
|
|
703
|
-
gender,
|
|
704
|
-
archetypeIndex: index.toString(),
|
|
705
|
-
language: 'en-us',
|
|
706
|
-
name: englishEntry.name,
|
|
707
|
-
essenceLine: englishEntry.essenceLine,
|
|
708
|
-
status: 'idle',
|
|
709
|
-
})
|
|
710
|
-
.onConflictDoUpdate({
|
|
711
|
-
target: [schema.archetypesData.id],
|
|
712
|
-
set: {
|
|
713
|
-
name: englishEntry.name,
|
|
714
|
-
essenceLine: englishEntry.essenceLine,
|
|
715
|
-
updatedAt: new Date().getTime(),
|
|
716
|
-
},
|
|
717
|
-
});
|
|
718
|
-
|
|
719
|
-
await this.context
|
|
720
|
-
.drizzle()
|
|
721
|
-
.insert(schema.archetypesData)
|
|
722
|
-
.values({
|
|
723
|
-
id: ptId,
|
|
724
|
-
combination,
|
|
725
|
-
gender,
|
|
726
|
-
archetypeIndex: index.toString(),
|
|
727
|
-
language: 'pt-br',
|
|
728
|
-
name: ptName,
|
|
729
|
-
essenceLine: ptEntry.essenceLine,
|
|
730
|
-
status: 'idle',
|
|
731
|
-
})
|
|
732
|
-
.onConflictDoUpdate({
|
|
733
|
-
target: [schema.archetypesData.id],
|
|
734
|
-
set: {
|
|
735
|
-
name: ptName,
|
|
736
|
-
essenceLine: ptEntry.essenceLine,
|
|
737
|
-
updatedAt: new Date().getTime(),
|
|
738
|
-
},
|
|
739
|
-
});
|
|
740
|
-
|
|
741
|
-
console.log(
|
|
742
|
-
`✅ [Batch] Saved archetype ${index} (${gender}) for ${combination}`
|
|
743
|
-
);
|
|
744
|
-
}
|
|
226
|
+
english.push({
|
|
227
|
+
name: nameMatch[1].trim(),
|
|
228
|
+
essenceLine: essenceLineMatch[1].trim(),
|
|
229
|
+
});
|
|
230
|
+
portuguese.push({
|
|
231
|
+
masc: ptMascMatch[1].trim(),
|
|
232
|
+
fem: ptFemMatch[1].trim(),
|
|
233
|
+
essenceLine: ptEssenceLineMatch[1].trim(),
|
|
234
|
+
});
|
|
235
|
+
} else {
|
|
236
|
+
this.log('warn', 'Malformed archetype name block', { block });
|
|
745
237
|
}
|
|
746
|
-
|
|
747
|
-
console.log(`🏁 [Batch] Finished combination: ${combination}`);
|
|
748
238
|
}
|
|
749
239
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
240
|
+
this.log('info', 'Completed parseArchetypeNameBlocks', {
|
|
241
|
+
english,
|
|
242
|
+
portuguese,
|
|
243
|
+
});
|
|
244
|
+
return { english, portuguese };
|
|
753
245
|
}
|
|
754
246
|
|
|
755
|
-
async
|
|
756
|
-
|
|
247
|
+
async generateDescriptionsAndVirtues(
|
|
248
|
+
combinationString: string,
|
|
249
|
+
gender: Gender,
|
|
250
|
+
language: string
|
|
757
251
|
) {
|
|
252
|
+
await this.log('info', 'Starting generateDescriptionsAndVirtues', {
|
|
253
|
+
combinationString,
|
|
254
|
+
gender,
|
|
255
|
+
language,
|
|
256
|
+
});
|
|
257
|
+
|
|
758
258
|
const db = this.context.drizzle();
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
259
|
+
const archetypes = await db
|
|
260
|
+
.select()
|
|
261
|
+
.from(schema.archetypesData)
|
|
262
|
+
.where(
|
|
263
|
+
and(
|
|
264
|
+
eq(schema.archetypesData.combination, combinationString),
|
|
265
|
+
eq(schema.archetypesData.gender, gender),
|
|
266
|
+
eq(schema.archetypesData.language, 'en-us')
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
.execute();
|
|
270
|
+
|
|
271
|
+
await this.log('info', 'Fetched archetypes for description generation', {
|
|
272
|
+
archetypesCount: archetypes.length,
|
|
273
|
+
names: archetypes.map((a) => a.name),
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
const descVirtueMessages = this.context
|
|
277
|
+
.buildLLMMessages()
|
|
278
|
+
.generateCosmicMirrorDescriptionAndVirtues({
|
|
279
|
+
combination: combinationString,
|
|
280
|
+
names: [archetypes[0].name, archetypes[1].name, archetypes[2].name],
|
|
281
|
+
essenceLines: [
|
|
282
|
+
archetypes[0].essenceLine,
|
|
283
|
+
archetypes[1].essenceLine,
|
|
284
|
+
archetypes[2].essenceLine,
|
|
285
|
+
],
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
await this.log('debug', 'Calling API for descriptions and virtues', {
|
|
289
|
+
messages: descVirtueMessages,
|
|
290
|
+
});
|
|
291
|
+
const descVirtueResponse = await this.context
|
|
292
|
+
.api()
|
|
293
|
+
.callTogether.single(descVirtueMessages, {});
|
|
294
|
+
if (!descVirtueResponse) {
|
|
295
|
+
await this.log('error', 'No response for descriptions and virtues', {
|
|
296
|
+
combinationString,
|
|
297
|
+
});
|
|
298
|
+
throw new Error(
|
|
299
|
+
`Failed to generate descriptions and virtues for: ${combinationString}`
|
|
769
300
|
);
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
301
|
+
}
|
|
302
|
+
await this.log('info', 'Received descriptions and virtues response', {
|
|
303
|
+
responseLength: descVirtueResponse.length,
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const parsedDescVirtues = this.parseDescriptionAndVirtuesResponse(descVirtueResponse);
|
|
307
|
+
await this.log('info', 'Parsed descriptions and virtues', {
|
|
308
|
+
parsedDescVirtues,
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
for (let i = 0; i < 3; i++) {
|
|
312
|
+
const index = (i + 1).toString();
|
|
313
|
+
|
|
314
|
+
// Update English entry (en-us)
|
|
315
|
+
const enId = `${combinationString}:${gender}:${index}`;
|
|
316
|
+
await this.log('debug', `Updating description and virtues for ${enId}`, {
|
|
317
|
+
description: parsedDescVirtues[i].descriptionEN,
|
|
318
|
+
virtues: parsedDescVirtues[i].virtuesEN,
|
|
319
|
+
});
|
|
320
|
+
await db
|
|
321
|
+
.update(schema.archetypesData)
|
|
322
|
+
.set({
|
|
323
|
+
description: parsedDescVirtues[i].descriptionEN,
|
|
324
|
+
virtues: JSON.stringify(parsedDescVirtues[i].virtuesEN),
|
|
325
|
+
updatedAt: new Date().getTime(),
|
|
326
|
+
})
|
|
775
327
|
.where(
|
|
776
328
|
and(
|
|
777
|
-
eq(schema.archetypesData.
|
|
329
|
+
eq(schema.archetypesData.id, enId),
|
|
778
330
|
eq(schema.archetypesData.language, 'en-us')
|
|
779
331
|
)
|
|
780
332
|
)
|
|
781
333
|
.execute();
|
|
334
|
+
await this.log('info', `Updated description and virtues for ${enId}`);
|
|
335
|
+
|
|
336
|
+
// Update Portuguese entries (pt-br) for both male and female
|
|
337
|
+
const ptIdMale = `${combinationString}:${gender}:${index}:pt`;
|
|
338
|
+
await this.log('debug', `Updating description and virtues for ${ptIdMale} (male)`, {
|
|
339
|
+
description: parsedDescVirtues[i].descriptionPTM,
|
|
340
|
+
virtues: parsedDescVirtues[i].virtuesPT,
|
|
341
|
+
});
|
|
342
|
+
await db
|
|
343
|
+
.update(schema.archetypesData)
|
|
344
|
+
.set({
|
|
345
|
+
description: parsedDescVirtues[i].descriptionPTM,
|
|
346
|
+
virtues: JSON.stringify(parsedDescVirtues[i].virtuesPT),
|
|
347
|
+
updatedAt: new Date().getTime(),
|
|
348
|
+
})
|
|
349
|
+
.where(
|
|
350
|
+
and(
|
|
351
|
+
eq(schema.archetypesData.id, ptIdMale),
|
|
352
|
+
eq(schema.archetypesData.language, 'pt-br'),
|
|
353
|
+
eq(schema.archetypesData.gender, 'male')
|
|
354
|
+
)
|
|
355
|
+
)
|
|
356
|
+
.execute();
|
|
357
|
+
await this.log('info', `Updated description and virtues for ${ptIdMale} (male)`);
|
|
358
|
+
|
|
359
|
+
const ptIdFemale = `${combinationString}:${gender}:${index}:pt`;
|
|
360
|
+
await this.log('debug', `Updating description and virtues for ${ptIdFemale} (female)`, {
|
|
361
|
+
description: parsedDescVirtues[i].descriptionPTF,
|
|
362
|
+
virtues: parsedDescVirtues[i].virtuesPT,
|
|
363
|
+
});
|
|
364
|
+
await db
|
|
365
|
+
.update(schema.archetypesData)
|
|
366
|
+
.set({
|
|
367
|
+
description: parsedDescVirtues[i].descriptionPTF,
|
|
368
|
+
virtues: JSON.stringify(parsedDescVirtues[i].virtuesPT),
|
|
369
|
+
updatedAt: new Date().getTime(),
|
|
370
|
+
})
|
|
371
|
+
.where(
|
|
372
|
+
and(
|
|
373
|
+
eq(schema.archetypesData.id, ptIdFemale),
|
|
374
|
+
eq(schema.archetypesData.language, 'pt-br'),
|
|
375
|
+
eq(schema.archetypesData.gender, 'female')
|
|
376
|
+
)
|
|
377
|
+
)
|
|
378
|
+
.execute();
|
|
379
|
+
await this.log('info', `Updated description and virtues for ${ptIdFemale} (female)`);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
await this.log('info', 'Completed generateDescriptionsAndVirtues', {
|
|
383
|
+
combinationString,
|
|
384
|
+
gender,
|
|
385
|
+
language,
|
|
386
|
+
});
|
|
387
|
+
return parsedDescVirtues;
|
|
388
|
+
}
|
|
782
389
|
|
|
783
|
-
let existingNames = existingNamesResult.map((r) => r.name);
|
|
784
|
-
console.log(
|
|
785
|
-
`[RegenerateDuplicatesBatch] Existing names for ${combination}: ${existingNames.join(
|
|
786
|
-
', '
|
|
787
|
-
)}`
|
|
788
|
-
);
|
|
789
|
-
|
|
790
|
-
// Process each entry with the duplicate name in this combination
|
|
791
|
-
for (const entry of entries) {
|
|
792
|
-
const { archetypeIndex, essenceLine } = entry;
|
|
793
|
-
console.log(
|
|
794
|
-
`[RegenerateDuplicatesBatch] Regenerating name for ${combination} (archetypeIndex: ${archetypeIndex})`
|
|
795
|
-
);
|
|
796
|
-
|
|
797
|
-
let newName: {
|
|
798
|
-
name_en: string;
|
|
799
|
-
name_pt_male: string;
|
|
800
|
-
name_pt_female: string;
|
|
801
|
-
} | null = null;
|
|
802
|
-
let attempts = 0;
|
|
803
|
-
const maxAttempts = 5;
|
|
804
|
-
|
|
805
|
-
// Generate a new name until a unique one is found
|
|
806
|
-
while (attempts < maxAttempts) {
|
|
807
|
-
attempts++;
|
|
808
|
-
console.log(
|
|
809
|
-
`[RegenerateDuplicatesBatch] Attempt ${attempts} to generate a new name for ${combination} (archetypeIndex: ${archetypeIndex})`
|
|
810
|
-
);
|
|
811
|
-
|
|
812
|
-
// Build the prompt
|
|
813
|
-
const prompt = duplicatedArchetypeNamePrompt({
|
|
814
|
-
combination,
|
|
815
|
-
essenceLine: essenceLine || 'A unique and meaningful archetype', // Fallback for null essenceLine
|
|
816
|
-
existingNames,
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
// Send the request to the AI
|
|
820
|
-
const messages: ChatMessages = [{ role: 'user', content: prompt }];
|
|
821
|
-
const response = await this.context
|
|
822
|
-
.api()
|
|
823
|
-
.callTogether.single(messages, {});
|
|
824
|
-
|
|
825
|
-
if (!response) {
|
|
826
|
-
console.error(
|
|
827
|
-
`[RegenerateDuplicatesBatch] No response from model for ${combination} (archetypeIndex: ${archetypeIndex})`
|
|
828
|
-
);
|
|
829
|
-
continue;
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
// Parse the response
|
|
833
|
-
try {
|
|
834
|
-
newName = JSON.parse(response);
|
|
835
|
-
console.log(
|
|
836
|
-
`[RegenerateDuplicatesBatch] Generated new name: ${JSON.stringify(
|
|
837
|
-
newName,
|
|
838
|
-
null,
|
|
839
|
-
2
|
|
840
|
-
)}`
|
|
841
|
-
);
|
|
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
|
-
}
|
|
863
|
-
|
|
864
|
-
// Check if the new name is unique (globally across all combinations)
|
|
865
|
-
const isDuplicate = await db
|
|
866
|
-
.select({ name: schema.archetypesData.name })
|
|
867
|
-
.from(schema.archetypesData)
|
|
868
|
-
.where(
|
|
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.`
|
|
880
|
-
);
|
|
881
|
-
existingNames.push(newName.name_en);
|
|
882
|
-
newName = null;
|
|
883
|
-
continue;
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
// If we reach here, the name is unique
|
|
887
|
-
break;
|
|
888
|
-
}
|
|
889
390
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
391
|
+
private parseDescriptionAndVirtuesResponse(response: string): ParsedDescriptionAndVirtues[] {
|
|
392
|
+
this.log('debug', 'Starting parseDescriptionAndVirtuesResponse', {
|
|
393
|
+
responseLength: response.length,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
const entries = response
|
|
397
|
+
.split(/---/)
|
|
398
|
+
.map((block) => block.trim())
|
|
399
|
+
.filter((block) => block.length > 0);
|
|
400
|
+
|
|
401
|
+
this.log('debug', 'Split response into entries', {
|
|
402
|
+
entriesCount: entries.length,
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
if (entries.length !== 3) {
|
|
406
|
+
this.log('error', 'Expected exactly 3 archetype entries', {
|
|
407
|
+
entriesCount: entries.length,
|
|
408
|
+
response,
|
|
409
|
+
});
|
|
410
|
+
throw new Error(`Expected exactly 3 archetype entries, but got ${entries.length}`);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const result = entries.map((entry, entryIndex) => {
|
|
414
|
+
this.log('debug', `Processing entry ${entryIndex + 1}`, { entry });
|
|
415
|
+
|
|
416
|
+
const lines = entry.split('\n').map((line) => line.trim()).filter((line) => line);
|
|
417
|
+
this.log('debug', `Split entry ${entryIndex + 1} into lines`, {
|
|
418
|
+
linesCount: lines.length,
|
|
419
|
+
lines,
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
let descriptionEN = '';
|
|
423
|
+
let descriptionPTM = '';
|
|
424
|
+
let descriptionPTF = '';
|
|
425
|
+
let virtuesEN: string[] = [];
|
|
426
|
+
let virtuesPT: string[] = [];
|
|
427
|
+
let currentField = '';
|
|
428
|
+
|
|
429
|
+
for (let i = 0; i < lines.length; i++) {
|
|
430
|
+
let line = lines[i];
|
|
431
|
+
|
|
432
|
+
// Remove markdown bold (**...**) from field names
|
|
433
|
+
line = line.replace(/\*\*(.*?)\*\*/g, '$1');
|
|
434
|
+
|
|
435
|
+
if (line.startsWith('1.') || line.startsWith('2.') || line.startsWith('3.')) {
|
|
436
|
+
continue; // Skip entry number lines
|
|
895
437
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
)
|
|
915
|
-
.
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
.
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
)
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
);
|
|
438
|
+
|
|
439
|
+
if (line.startsWith('• Description EN:')) {
|
|
440
|
+
currentField = 'descriptionEN';
|
|
441
|
+
descriptionEN = line.split('• Description EN:')[1]?.trim() || '';
|
|
442
|
+
this.log('debug', `Extracted descriptionEN for entry ${entryIndex + 1}`, { descriptionEN });
|
|
443
|
+
} else if (line.startsWith('• Description PT-M:')) {
|
|
444
|
+
currentField = 'descriptionPTM';
|
|
445
|
+
descriptionPTM = line.split('• Description PT-M:')[1]?.trim() || '';
|
|
446
|
+
this.log('debug', `Extracted descriptionPTM for entry ${entryIndex + 1}`, { descriptionPTM });
|
|
447
|
+
} else if (line.startsWith('• Description PT-F:')) {
|
|
448
|
+
currentField = 'descriptionPTF';
|
|
449
|
+
descriptionPTF = line.split('• Description PT-F:')[1]?.trim() || '';
|
|
450
|
+
this.log('debug', `Extracted descriptionPTF for entry ${entryIndex + 1}`, { descriptionPTF });
|
|
451
|
+
} else if (line.startsWith('• Virtues EN:')) {
|
|
452
|
+
currentField = 'virtuesEN';
|
|
453
|
+
virtuesEN = line
|
|
454
|
+
.split('• Virtues EN:')[1]
|
|
455
|
+
?.split(',')
|
|
456
|
+
.map((v) => v.trim())
|
|
457
|
+
.filter((v) => v)
|
|
458
|
+
.slice(0, 3);
|
|
459
|
+
this.log('debug', `Extracted virtuesEN for entry ${entryIndex + 1}`, { virtuesEN });
|
|
460
|
+
} else if (line.startsWith('• Virtues PT:')) {
|
|
461
|
+
currentField = 'virtuesPT';
|
|
462
|
+
virtuesPT = line
|
|
463
|
+
.split('• Virtues PT:')[1]
|
|
464
|
+
?.split(',')
|
|
465
|
+
.map((v) => v.trim())
|
|
466
|
+
.filter((v) => v)
|
|
467
|
+
.slice(0, 3);
|
|
468
|
+
this.log('debug', `Extracted virtuesPT for entry ${entryIndex + 1}`, { virtuesPT });
|
|
469
|
+
} else if (line.startsWith('• Virtues:')) {
|
|
470
|
+
// Fallback for older response format: use English virtues and attempt to translate
|
|
471
|
+
currentField = 'virtuesEN';
|
|
472
|
+
virtuesEN = line
|
|
473
|
+
.split('• Virtues:')[1]
|
|
474
|
+
?.split(',')
|
|
475
|
+
.map((v) => v.trim())
|
|
476
|
+
.filter((v) => v)
|
|
477
|
+
.slice(0, 3);
|
|
478
|
+
this.log('debug', `Extracted virtuesEN (fallback) for entry ${entryIndex + 1}`, { virtuesEN });
|
|
479
|
+
|
|
480
|
+
// Simple translation mapping for common virtues (as a fallback)
|
|
481
|
+
const virtueTranslations: { [key: string]: string } = {
|
|
482
|
+
Harmony: 'Harmonia',
|
|
483
|
+
Intuition: 'Intuição',
|
|
484
|
+
Grace: 'Graça',
|
|
485
|
+
// Add more translations as needed
|
|
486
|
+
};
|
|
487
|
+
virtuesPT = virtuesEN.map((v) => virtueTranslations[v] || v); // Fallback to English if no translation
|
|
488
|
+
this.log('debug', `Generated virtuesPT (fallback) for entry ${entryIndex + 1}`, { virtuesPT });
|
|
489
|
+
} else if (currentField && !currentField.startsWith('virtues')) {
|
|
490
|
+
if (currentField === 'descriptionEN') {
|
|
491
|
+
descriptionEN += ' ' + line;
|
|
492
|
+
this.log('debug', `Appended to descriptionEN for entry ${entryIndex + 1}`, { descriptionEN });
|
|
493
|
+
} else if (currentField === 'descriptionPTM') {
|
|
494
|
+
descriptionPTM += ' ' + line;
|
|
495
|
+
this.log('debug', `Appended to descriptionPTM for entry ${entryIndex + 1}`, { descriptionPTM });
|
|
496
|
+
} else if (currentField === 'descriptionPTF') {
|
|
497
|
+
descriptionPTF += ' ' + line;
|
|
498
|
+
this.log('debug', `Appended to descriptionPTF for entry ${entryIndex + 1}`, { descriptionPTF });
|
|
499
|
+
}
|
|
937
500
|
}
|
|
938
501
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
502
|
+
|
|
503
|
+
// Validate the parsed data
|
|
504
|
+
if (!descriptionEN || !descriptionPTM || !descriptionPTF || virtuesEN.length !== 3 || virtuesPT.length !== 3) {
|
|
505
|
+
this.log('warn', `Malformed description and virtues response for entry ${entryIndex + 1}`, {
|
|
506
|
+
descriptionEN,
|
|
507
|
+
descriptionPTM,
|
|
508
|
+
descriptionPTF,
|
|
509
|
+
virtuesEN,
|
|
510
|
+
virtuesPT,
|
|
511
|
+
response: entry,
|
|
512
|
+
});
|
|
513
|
+
throw new Error(`Malformed response for entry ${entryIndex + 1}: Missing required fields`);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return { descriptionEN, descriptionPTM, descriptionPTF, virtuesEN, virtuesPT };
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
this.log('info', 'Completed parseDescriptionAndVirtuesResponse', { result });
|
|
520
|
+
return result;
|
|
942
521
|
}
|
|
943
522
|
|
|
944
|
-
async
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
523
|
+
async generateContent(
|
|
524
|
+
combinationString: string,
|
|
525
|
+
gender: Gender,
|
|
526
|
+
language: string,
|
|
527
|
+
descriptions: Array<{ descriptionEN: string }>
|
|
528
|
+
) {
|
|
529
|
+
await this.log('info', 'Starting generateContent', {
|
|
530
|
+
combinationString,
|
|
531
|
+
gender,
|
|
532
|
+
language,
|
|
533
|
+
});
|
|
948
534
|
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
535
|
+
const db = this.context.drizzle();
|
|
536
|
+
const archetypes = await db
|
|
537
|
+
.select()
|
|
538
|
+
.from(schema.archetypesData)
|
|
539
|
+
.where(
|
|
540
|
+
and(
|
|
541
|
+
eq(schema.archetypesData.combination, combinationString),
|
|
542
|
+
eq(schema.archetypesData.gender, gender),
|
|
543
|
+
eq(schema.archetypesData.language, 'en-us')
|
|
544
|
+
)
|
|
545
|
+
)
|
|
546
|
+
.execute();
|
|
547
|
+
|
|
548
|
+
await this.log('info', 'Fetched archetypes for content generation', {
|
|
549
|
+
archetypesCount: archetypes.length,
|
|
550
|
+
names: archetypes.map((a) => a.name),
|
|
551
|
+
});
|
|
953
552
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
553
|
+
const contentResults = [];
|
|
554
|
+
for (let i = 0; i < 3; i++) {
|
|
555
|
+
await this.log('debug', `Generating content for archetype ${i + 1}`, {
|
|
556
|
+
name: archetypes[i].name,
|
|
557
|
+
});
|
|
558
|
+
const contentMessages = this.context
|
|
559
|
+
.buildLLMMessages()
|
|
560
|
+
.generateCosmicMirrorArchetypeContent({
|
|
561
|
+
combination: combinationString,
|
|
562
|
+
name: archetypes[i].name,
|
|
563
|
+
description: descriptions[i].descriptionEN,
|
|
564
|
+
});
|
|
957
565
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
const
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
',
|
|
967
|
-
|
|
566
|
+
await this.log('debug', `Calling API for content of archetype ${i + 1}`, {
|
|
567
|
+
messages: contentMessages,
|
|
568
|
+
});
|
|
569
|
+
const contentResponse = await this.context
|
|
570
|
+
.api()
|
|
571
|
+
.callTogether.single(contentMessages, {});
|
|
572
|
+
if (!contentResponse) {
|
|
573
|
+
await this.log(
|
|
574
|
+
'error',
|
|
575
|
+
`No response for content of archetype ${i + 1}`,
|
|
576
|
+
{ combinationString, archetype: archetypes[i].name }
|
|
577
|
+
);
|
|
578
|
+
throw new Error(
|
|
579
|
+
`Failed to generate content for archetype ${
|
|
580
|
+
i + 1
|
|
581
|
+
} of: ${combinationString}`
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
await this.log(
|
|
585
|
+
'info',
|
|
586
|
+
`Received content response for archetype ${i + 1}`,
|
|
587
|
+
{ responseLength: contentResponse.length }
|
|
968
588
|
);
|
|
969
589
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
id: schema.archetypeNameDumps.id,
|
|
974
|
-
rawText: schema.archetypeNameDumps.rawText,
|
|
975
|
-
})
|
|
976
|
-
.from(schema.archetypeNameDumps)
|
|
977
|
-
.where(eq(schema.archetypeNameDumps.combination, combination))
|
|
978
|
-
.execute();
|
|
979
|
-
|
|
980
|
-
console.log(
|
|
981
|
-
`[Recycle] Retrieved ${
|
|
982
|
-
dumpEntries.length
|
|
983
|
-
} dump entries for ${combination}: ${JSON.stringify(
|
|
984
|
-
dumpEntries,
|
|
985
|
-
null,
|
|
986
|
-
2
|
|
987
|
-
)}`
|
|
590
|
+
await this.log(
|
|
591
|
+
'debug',
|
|
592
|
+
`Parsing content response for archetype ${i + 1}`
|
|
988
593
|
);
|
|
594
|
+
const parsedContent = this.parseContentResponse(contentResponse);
|
|
595
|
+
contentResults.push(parsedContent);
|
|
596
|
+
await this.log('info', `Parsed content for archetype ${i + 1}`, {
|
|
597
|
+
parsedContent,
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
const index = (i + 1).toString();
|
|
601
|
+
for (const lang of ['en-us', 'pt-br']) {
|
|
602
|
+
const contentLangIndex =
|
|
603
|
+
lang === 'en-us' ? 0 : gender === 'male' ? 1 : 2;
|
|
604
|
+
const content = [
|
|
605
|
+
{
|
|
606
|
+
section: 'voiceOfTheSoul',
|
|
607
|
+
text: parsedContent[contentLangIndex].voiceOfTheSoul,
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
section: 'giftsYouBear',
|
|
611
|
+
text: parsedContent[contentLangIndex].giftsYouBear,
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
section: 'shadowsYouFace',
|
|
615
|
+
text: parsedContent[contentLangIndex].shadowsYouFace,
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
section: 'rhythmOfYourDays',
|
|
619
|
+
text: parsedContent[contentLangIndex].rhythmOfYourDays,
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
section: 'tiesThatBind',
|
|
623
|
+
text: parsedContent[contentLangIndex].tiesThatBind,
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
section: 'lightWithin',
|
|
627
|
+
text: parsedContent[contentLangIndex].lightWithin,
|
|
628
|
+
},
|
|
629
|
+
];
|
|
630
|
+
|
|
631
|
+
const id = `${combinationString}:${gender}:${index}${
|
|
632
|
+
lang === 'pt-br' ? ':pt' : ''
|
|
633
|
+
}`;
|
|
634
|
+
await this.log('debug', `Updating content for ${id}`, { content });
|
|
635
|
+
|
|
636
|
+
await db
|
|
637
|
+
.update(schema.archetypesData)
|
|
638
|
+
.set({
|
|
639
|
+
content: JSON.stringify(content),
|
|
640
|
+
updatedAt: new Date().getTime(),
|
|
641
|
+
})
|
|
642
|
+
.where(
|
|
643
|
+
and(
|
|
644
|
+
eq(schema.archetypesData.id, id),
|
|
645
|
+
eq(schema.archetypesData.language, lang)
|
|
646
|
+
)
|
|
647
|
+
)
|
|
648
|
+
.execute();
|
|
989
649
|
|
|
990
|
-
|
|
991
|
-
console.log(
|
|
992
|
-
`⚠️ [Recycle] No dump entries found for combination: ${combination}`
|
|
993
|
-
);
|
|
994
|
-
continue;
|
|
650
|
+
await this.log('info', `Updated content for ${id}`);
|
|
995
651
|
}
|
|
652
|
+
}
|
|
996
653
|
|
|
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
|
-
}
|
|
654
|
+
await this.log('info', 'Completed generateContent', {
|
|
655
|
+
combinationString,
|
|
656
|
+
gender,
|
|
657
|
+
language,
|
|
658
|
+
});
|
|
659
|
+
return contentResults;
|
|
660
|
+
}
|
|
1014
661
|
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
continue;
|
|
1020
|
-
}
|
|
662
|
+
private parseContentResponse(response: string) {
|
|
663
|
+
this.log('debug', 'Starting parseContentResponse', {
|
|
664
|
+
responseLength: response.length,
|
|
665
|
+
});
|
|
1021
666
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
console.log(
|
|
1030
|
-
`[Recycle] Cleaned text for index ${index}: ${cleanedText}`
|
|
1031
|
-
);
|
|
667
|
+
const sections = response
|
|
668
|
+
.split(/---/)
|
|
669
|
+
.map((block) => block.trim())
|
|
670
|
+
.filter((block) => block.length > 0);
|
|
671
|
+
this.log('debug', 'Split response into sections', {
|
|
672
|
+
sectionsCount: sections.length,
|
|
673
|
+
});
|
|
1032
674
|
|
|
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
|
-
);
|
|
675
|
+
const result = sections.map((section, sectionIndex) => {
|
|
676
|
+
this.log('debug', `Processing section ${sectionIndex + 1}`, { section });
|
|
1040
677
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
678
|
+
const lines = section.split('\n').filter((line) => line.trim());
|
|
679
|
+
this.log('debug', `Split section ${sectionIndex + 1} into lines`, {
|
|
680
|
+
linesCount: lines.length,
|
|
681
|
+
lines,
|
|
682
|
+
});
|
|
1046
683
|
|
|
684
|
+
const content: Record<string, string> = {};
|
|
685
|
+
let currentSection = '';
|
|
686
|
+
let currentText: string[] = [];
|
|
687
|
+
|
|
688
|
+
for (const line of lines) {
|
|
1047
689
|
if (
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
!ptSectionMatch[0]
|
|
690
|
+
line.startsWith('EN:') ||
|
|
691
|
+
line.startsWith('PT-M:') ||
|
|
692
|
+
line.startsWith('PT-F:')
|
|
1052
693
|
) {
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
694
|
+
this.log(
|
|
695
|
+
'debug',
|
|
696
|
+
`Skipping language header in section ${sectionIndex + 1}`,
|
|
697
|
+
{ line }
|
|
1057
698
|
);
|
|
1058
699
|
continue;
|
|
1059
700
|
}
|
|
1060
701
|
|
|
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`
|
|
702
|
+
if (line.startsWith('#')) {
|
|
703
|
+
if (currentSection && currentText.length > 0) {
|
|
704
|
+
const key = currentSection
|
|
705
|
+
.toLowerCase()
|
|
706
|
+
.replace(/\s+/g, '')
|
|
707
|
+
.replace(/([A-Z])/g, (match) => match.toLowerCase());
|
|
708
|
+
content[key] = currentText.join(' ').trim();
|
|
709
|
+
this.log(
|
|
710
|
+
'debug',
|
|
711
|
+
`Extracted section ${currentSection} in section ${
|
|
712
|
+
sectionIndex + 1
|
|
713
|
+
}`,
|
|
714
|
+
{ key, text: content[key] }
|
|
1098
715
|
);
|
|
1099
716
|
}
|
|
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)}`
|
|
717
|
+
currentSection = line.replace('# ', '');
|
|
718
|
+
currentText = [];
|
|
719
|
+
this.log(
|
|
720
|
+
'debug',
|
|
721
|
+
`Starting new section ${currentSection} in section ${
|
|
722
|
+
sectionIndex + 1
|
|
723
|
+
}`
|
|
1119
724
|
);
|
|
1120
|
-
|
|
725
|
+
} else {
|
|
726
|
+
currentText.push(line);
|
|
1121
727
|
}
|
|
728
|
+
}
|
|
1122
729
|
|
|
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
|
-
});
|
|
730
|
+
if (currentSection && currentText.length > 0) {
|
|
731
|
+
const key = currentSection
|
|
732
|
+
.toLowerCase()
|
|
733
|
+
.replace(/\s+/g, '')
|
|
734
|
+
.replace(/([A-Z])/g, (match) => match.toLowerCase());
|
|
735
|
+
content[key] = currentText.join(' ').trim();
|
|
736
|
+
this.log(
|
|
737
|
+
'debug',
|
|
738
|
+
`Extracted final section ${currentSection} in section ${
|
|
739
|
+
sectionIndex + 1
|
|
740
|
+
}`,
|
|
741
|
+
{ key, text: content[key] }
|
|
742
|
+
);
|
|
743
|
+
}
|
|
1178
744
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
745
|
+
const parsedContent = {
|
|
746
|
+
voiceOfTheSoul:
|
|
747
|
+
content['thevoiceofthesoul'] || content['avozdaalma'] || '',
|
|
748
|
+
giftsYouBear:
|
|
749
|
+
content['thegiftsyoubear'] || content['osdonsquevocêcarrega'] || '',
|
|
750
|
+
shadowsYouFace:
|
|
751
|
+
content['theshadowsyouface'] || content['assombrasqueenfrenta'] || '',
|
|
752
|
+
rhythmOfYourDays:
|
|
753
|
+
content['therhythmofyourdays'] || content['oritmodosseusdias'] || '',
|
|
754
|
+
tiesThatBind:
|
|
755
|
+
content['thetiesthatbind'] || content['oslaçosqueteconectam'] || '',
|
|
756
|
+
lightWithin: content['thelightwithin'] || content['aluzinterior'] || '',
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
if (Object.values(parsedContent).some((value) => !value)) {
|
|
760
|
+
this.log(
|
|
761
|
+
'warn',
|
|
762
|
+
`Malformed content response for section ${sectionIndex + 1}`,
|
|
763
|
+
{
|
|
764
|
+
parsedContent,
|
|
765
|
+
response: section,
|
|
1194
766
|
}
|
|
1195
|
-
|
|
1196
|
-
console.log(
|
|
1197
|
-
`✅ [Recycle] Saved archetype ${index} (${gender}) for ${combination}`
|
|
1198
|
-
);
|
|
1199
|
-
}
|
|
767
|
+
);
|
|
1200
768
|
}
|
|
1201
769
|
|
|
1202
|
-
|
|
1203
|
-
}
|
|
770
|
+
return parsedContent;
|
|
771
|
+
});
|
|
1204
772
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
);
|
|
773
|
+
this.log('info', 'Completed parseContentResponse', { result });
|
|
774
|
+
return result;
|
|
1208
775
|
}
|
|
1209
776
|
|
|
1210
|
-
async
|
|
777
|
+
async generateLeonardoPrompts(
|
|
778
|
+
combinationString: string,
|
|
779
|
+
gender: Gender,
|
|
780
|
+
language: string,
|
|
781
|
+
descriptions: Array<{ descriptionEN: string }>
|
|
782
|
+
) {
|
|
783
|
+
await this.log('info', 'Starting generateLeonardoPrompts', {
|
|
784
|
+
combinationString,
|
|
785
|
+
gender,
|
|
786
|
+
language,
|
|
787
|
+
});
|
|
788
|
+
|
|
1211
789
|
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();
|
|
790
|
+
const archetypes = await db
|
|
791
|
+
.select()
|
|
792
|
+
.from(schema.archetypesData)
|
|
793
|
+
.where(
|
|
794
|
+
and(
|
|
795
|
+
eq(schema.archetypesData.combination, combinationString),
|
|
796
|
+
eq(schema.archetypesData.gender, gender),
|
|
797
|
+
eq(schema.archetypesData.language, 'en-us')
|
|
798
|
+
)
|
|
799
|
+
)
|
|
800
|
+
.execute();
|
|
1233
801
|
|
|
1234
|
-
|
|
802
|
+
await this.log('info', 'Fetched archetypes for Leonardo prompts', {
|
|
803
|
+
archetypesCount: archetypes.length,
|
|
804
|
+
names: archetypes.map((a) => a.name),
|
|
805
|
+
});
|
|
1235
806
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
807
|
+
const promptMessages = this.context
|
|
808
|
+
.buildLLMMessages()
|
|
809
|
+
.generateCosmicMirrorArchetypeLeonardoPrompts(
|
|
810
|
+
archetypes.map((arc, idx) => ({
|
|
811
|
+
name: arc.name,
|
|
812
|
+
description: descriptions[idx].descriptionEN,
|
|
813
|
+
}))
|
|
814
|
+
);
|
|
1242
815
|
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
816
|
+
await this.log('debug', 'Calling API for Leonardo prompts', {
|
|
817
|
+
messages: promptMessages,
|
|
818
|
+
});
|
|
819
|
+
const promptResponse = await this.context
|
|
820
|
+
.api()
|
|
821
|
+
.callTogether.single(promptMessages, {});
|
|
822
|
+
if (!promptResponse) {
|
|
823
|
+
await this.log('error', 'No response for Leonardo prompts', {
|
|
824
|
+
combinationString,
|
|
825
|
+
});
|
|
826
|
+
throw new Error(
|
|
827
|
+
`Failed to generate Leonardo prompts for: ${combinationString}`
|
|
828
|
+
);
|
|
1256
829
|
}
|
|
830
|
+
await this.log('info', 'Received Leonardo prompts response', {
|
|
831
|
+
responseLength: promptResponse.length,
|
|
832
|
+
});
|
|
1257
833
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
834
|
+
const parsedPrompts = this.parseLeonardoPromptResponse(promptResponse);
|
|
835
|
+
await this.log('info', 'Parsed Leonardo prompts', { parsedPrompts });
|
|
836
|
+
|
|
837
|
+
for (let i = 0; i < 3; i++) {
|
|
838
|
+
const index = (i + 1).toString();
|
|
839
|
+
const leonardoPrompt =
|
|
840
|
+
gender === 'male'
|
|
841
|
+
? parsedPrompts[i].malePrompt
|
|
842
|
+
: parsedPrompts[i].femalePrompt;
|
|
843
|
+
|
|
844
|
+
for (const lang of ['en-us', 'pt-br']) {
|
|
845
|
+
const id = `${combinationString}:${gender}:${index}${
|
|
846
|
+
lang === 'pt-br' ? ':pt' : ''
|
|
847
|
+
}`;
|
|
848
|
+
await this.log('debug', `Updating Leonardo prompt for ${id}`, {
|
|
849
|
+
leonardoPrompt,
|
|
850
|
+
});
|
|
1261
851
|
|
|
1262
|
-
|
|
1263
|
-
|
|
852
|
+
await db
|
|
853
|
+
.update(schema.archetypesData)
|
|
854
|
+
.set({
|
|
855
|
+
leonardoPrompt,
|
|
856
|
+
updatedAt: new Date().getTime(),
|
|
857
|
+
})
|
|
858
|
+
.where(
|
|
859
|
+
and(
|
|
860
|
+
eq(schema.archetypesData.id, id),
|
|
861
|
+
eq(schema.archetypesData.language, lang)
|
|
862
|
+
)
|
|
863
|
+
)
|
|
864
|
+
.execute();
|
|
1264
865
|
|
|
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"
|
|
866
|
+
await this.log('info', `Updated Leonardo prompt for ${id}`);
|
|
1294
867
|
}
|
|
1295
868
|
}
|
|
1296
869
|
|
|
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
|
-
);
|
|
1314
|
-
|
|
1315
|
-
for (const entry of entries) {
|
|
1316
|
-
const lines = entry.split('\n').filter((l) => l.trim());
|
|
1317
|
-
console.log(
|
|
1318
|
-
`[Parse] Extracted ${lines.length} lines from entry: ${JSON.stringify(
|
|
1319
|
-
lines,
|
|
1320
|
-
null,
|
|
1321
|
-
2
|
|
1322
|
-
)}`
|
|
1323
|
-
);
|
|
870
|
+
await this.log('info', 'Completed generateLeonardoPrompts', {
|
|
871
|
+
combinationString,
|
|
872
|
+
gender,
|
|
873
|
+
language,
|
|
874
|
+
});
|
|
875
|
+
return parsedPrompts;
|
|
876
|
+
}
|
|
1324
877
|
|
|
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
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
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
|
-
);
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
878
|
+
private parseLeonardoPromptResponse(response: string): LeonardoPrompt[] {
|
|
879
|
+
this.log('debug', 'Starting parseLeonardoPromptResponse', {
|
|
880
|
+
responseLength: response.length,
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
const lines = response.split('\n').filter((line) => line.trim());
|
|
884
|
+
this.log('debug', 'Split response into lines', {
|
|
885
|
+
linesCount: lines.length,
|
|
886
|
+
lines,
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
const prompts: LeonardoPrompt[] = [];
|
|
890
|
+
let currentArchetype = 0;
|
|
891
|
+
|
|
892
|
+
for (let i = 0; i < lines.length; i += 4) {
|
|
893
|
+
// Expect pairs of label and prompt: "1.m", male prompt, "1.f", female prompt
|
|
894
|
+
const maleLabel = lines[i]; // "1.m"
|
|
895
|
+
const malePromptLine = lines[i + 1]; // Male prompt text
|
|
896
|
+
const femaleLabel = lines[i + 2]; // "1.f"
|
|
897
|
+
const femalePromptLine = lines[i + 3]; // Female prompt text
|
|
898
|
+
|
|
899
|
+
currentArchetype++;
|
|
900
|
+
const expectedMaleLabel = `${currentArchetype}.m`;
|
|
901
|
+
const expectedFemaleLabel = `${currentArchetype}.f`;
|
|
902
|
+
|
|
903
|
+
if (!maleLabel?.startsWith(expectedMaleLabel) || !femaleLabel?.startsWith(expectedFemaleLabel)) {
|
|
904
|
+
this.log('warn', `Malformed Leonardo prompt format for archetype ${currentArchetype}`, {
|
|
905
|
+
maleLabel,
|
|
906
|
+
malePromptLine,
|
|
907
|
+
femaleLabel,
|
|
908
|
+
femalePromptLine,
|
|
909
|
+
lines,
|
|
910
|
+
});
|
|
911
|
+
throw new Error(`Expected ${expectedMaleLabel} and ${expectedFemaleLabel} at lines ${i} and ${i + 2}`);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
const malePrompt = malePromptLine?.trim() || '';
|
|
915
|
+
const femalePrompt = femalePromptLine?.trim() || '';
|
|
916
|
+
|
|
917
|
+
if (!malePrompt || !femalePrompt) {
|
|
918
|
+
this.log('warn', `Empty Leonardo prompt for archetype ${currentArchetype}`, {
|
|
919
|
+
malePrompt,
|
|
920
|
+
femalePrompt,
|
|
921
|
+
malePromptLine,
|
|
922
|
+
femalePromptLine,
|
|
923
|
+
});
|
|
924
|
+
throw new Error(`Empty prompt for archetype ${currentArchetype}`);
|
|
1392
925
|
}
|
|
926
|
+
|
|
927
|
+
prompts.push({ malePrompt, femalePrompt });
|
|
928
|
+
this.log('debug', `Parsed prompts for archetype ${currentArchetype}`, { malePrompt, femalePrompt });
|
|
1393
929
|
}
|
|
930
|
+
|
|
931
|
+
if (prompts.length !== 3) {
|
|
932
|
+
this.log('error', 'Expected exactly 3 archetype prompts', {
|
|
933
|
+
promptsCount: prompts.length,
|
|
934
|
+
response,
|
|
935
|
+
});
|
|
936
|
+
throw new Error(`Expected exactly 3 archetype prompts, but got ${prompts.length}`);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
this.log('info', 'Completed parseLeonardoPromptResponse', { prompts });
|
|
940
|
+
return prompts;
|
|
941
|
+
}
|
|
1394
942
|
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
943
|
+
async fetchArchetypesFromDB(
|
|
944
|
+
combinationString: string,
|
|
945
|
+
gender: Gender,
|
|
946
|
+
language: string
|
|
947
|
+
) {
|
|
948
|
+
await this.log('debug', 'Executing fetchArchetypesFromDB', {
|
|
949
|
+
combinationString,
|
|
950
|
+
gender,
|
|
951
|
+
language,
|
|
952
|
+
});
|
|
953
|
+
const db = this.context.drizzle();
|
|
954
|
+
const result = await db
|
|
955
|
+
.select()
|
|
956
|
+
.from(schema.archetypesData)
|
|
957
|
+
.where(
|
|
958
|
+
and(
|
|
959
|
+
eq(schema.archetypesData.combination, combinationString),
|
|
960
|
+
eq(schema.archetypesData.gender, gender),
|
|
961
|
+
eq(schema.archetypesData.language, language)
|
|
962
|
+
)
|
|
963
|
+
)
|
|
964
|
+
.execute();
|
|
965
|
+
await this.log('debug', 'fetchArchetypesFromDB result', {
|
|
966
|
+
resultCount: result.length,
|
|
967
|
+
result,
|
|
968
|
+
});
|
|
969
|
+
return result;
|
|
1404
970
|
}
|
|
1405
971
|
}
|