@zodic/shared 0.0.145 β 0.0.147
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/services/ConceptService.ts +218 -135
- package/package.json +1 -1
- package/utils/buildMessages.ts +15 -11
|
@@ -4,7 +4,11 @@ import 'reflect-metadata';
|
|
|
4
4
|
import { v4 as uuidv4 } from 'uuid';
|
|
5
5
|
import { schema } from '../..';
|
|
6
6
|
import { Concept, ControlNetConfig, Languages } from '../../types';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
KVConcept,
|
|
9
|
+
sizes,
|
|
10
|
+
StructuredConceptContent,
|
|
11
|
+
} from '../../types/scopes/legacy';
|
|
8
12
|
import { leonardoInitImages } from '../../utils/initImages';
|
|
9
13
|
import { buildConceptKVKey } from '../../utils/KVKeysBuilders';
|
|
10
14
|
import { AppContext } from '../base/AppContext';
|
|
@@ -24,98 +28,119 @@ export class ConceptService {
|
|
|
24
28
|
console.log(
|
|
25
29
|
`π Generating basic info for concept: ${conceptSlug}, combination: ${combinationString}, override: ${override}`
|
|
26
30
|
);
|
|
27
|
-
|
|
31
|
+
|
|
28
32
|
const kvStore = this.context.kvConceptsStore();
|
|
29
33
|
const kvFailuresStore = this.context.kvConceptFailuresStore(); // π΄ Replace with actual KV store
|
|
30
|
-
const kvKeyEN = buildConceptKVKey(
|
|
31
|
-
const kvKeyPT = buildConceptKVKey(
|
|
34
|
+
const kvKeyEN = buildConceptKVKey('en-us', conceptSlug, combinationString);
|
|
35
|
+
const kvKeyPT = buildConceptKVKey('pt-br', conceptSlug, combinationString);
|
|
32
36
|
const failureKey = `failures:basic-info:${conceptSlug}:${combinationString}`;
|
|
33
|
-
|
|
37
|
+
|
|
34
38
|
// β
Check if data already exists
|
|
35
39
|
if (!override) {
|
|
36
40
|
const existingEN = await this.getKVConcept(kvKeyEN);
|
|
37
41
|
const existingPT = await this.getKVConcept(kvKeyPT);
|
|
38
|
-
|
|
42
|
+
|
|
39
43
|
if (
|
|
40
|
-
existingEN.name &&
|
|
41
|
-
|
|
44
|
+
existingEN.name &&
|
|
45
|
+
existingEN.description &&
|
|
46
|
+
Array.isArray(existingEN.poem) &&
|
|
47
|
+
existingEN.poem.length > 0 &&
|
|
48
|
+
existingPT.name &&
|
|
49
|
+
existingPT.description &&
|
|
50
|
+
Array.isArray(existingPT.poem) &&
|
|
51
|
+
existingPT.poem.length > 0
|
|
42
52
|
) {
|
|
43
|
-
console.log(
|
|
53
|
+
console.log(
|
|
54
|
+
`β‘ Basic info already exists for ${conceptSlug}, combination: ${combinationString}. Skipping.`
|
|
55
|
+
);
|
|
44
56
|
return; // β
Skip regeneration
|
|
45
57
|
}
|
|
46
58
|
}
|
|
47
|
-
|
|
59
|
+
|
|
48
60
|
let attempts = 0;
|
|
49
61
|
const maxAttempts = 3;
|
|
50
|
-
|
|
62
|
+
|
|
51
63
|
while (attempts < maxAttempts) {
|
|
52
|
-
let phase =
|
|
64
|
+
let phase = 'generation'; // π Track the phase
|
|
53
65
|
try {
|
|
54
66
|
attempts++;
|
|
55
67
|
console.log(`π Attempt ${attempts} to generate basic info...`);
|
|
56
|
-
|
|
68
|
+
|
|
57
69
|
// β
Build the messages to request content
|
|
58
|
-
const messages = this.context
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
const messages = this.context
|
|
71
|
+
.buildLLMMessages()
|
|
72
|
+
.generateConceptBasicInfo({
|
|
73
|
+
combination: combinationString,
|
|
74
|
+
conceptSlug,
|
|
75
|
+
});
|
|
76
|
+
|
|
63
77
|
// β
Call ChatGPT API
|
|
64
|
-
const response = await this.context
|
|
78
|
+
const response = await this.context
|
|
79
|
+
.api()
|
|
80
|
+
.callChatGPT.single(messages, {});
|
|
65
81
|
if (!response) {
|
|
66
82
|
throw new Error(`β AI returned an empty response`);
|
|
67
83
|
}
|
|
68
|
-
|
|
69
|
-
phase =
|
|
70
|
-
|
|
84
|
+
|
|
85
|
+
phase = 'parsing'; // β
Switch to parsing phase
|
|
86
|
+
|
|
71
87
|
// β
Parse response for both languages
|
|
72
88
|
const { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT } =
|
|
73
89
|
this.parseBasicInfoResponse(response);
|
|
74
|
-
|
|
90
|
+
|
|
75
91
|
// π English version
|
|
76
92
|
const conceptEN = await this.getKVConcept(kvKeyEN);
|
|
77
93
|
Object.assign(conceptEN, {
|
|
78
94
|
name: nameEN,
|
|
79
95
|
description: descriptionEN,
|
|
80
96
|
poem: poemEN,
|
|
81
|
-
status:
|
|
97
|
+
status: 'idle',
|
|
82
98
|
});
|
|
83
99
|
await kvStore.put(kvKeyEN, JSON.stringify(conceptEN));
|
|
84
|
-
|
|
100
|
+
|
|
85
101
|
// π§π· Portuguese version
|
|
86
102
|
const conceptPT = await this.getKVConcept(kvKeyPT);
|
|
87
103
|
Object.assign(conceptPT, {
|
|
88
104
|
name: namePT,
|
|
89
105
|
description: descriptionPT,
|
|
90
106
|
poem: poemPT,
|
|
91
|
-
status:
|
|
107
|
+
status: 'idle',
|
|
92
108
|
});
|
|
93
109
|
await kvStore.put(kvKeyPT, JSON.stringify(conceptPT));
|
|
94
|
-
|
|
110
|
+
|
|
95
111
|
console.log(
|
|
96
112
|
`β
Basic info stored for ${conceptSlug}, combination: ${combinationString}, in both languages.`
|
|
97
113
|
);
|
|
98
114
|
return; // β
Exit loop if successful
|
|
99
|
-
|
|
100
115
|
} catch (error) {
|
|
101
|
-
console.error(
|
|
102
|
-
|
|
116
|
+
console.error(
|
|
117
|
+
`β Attempt ${attempts} failed at phase: ${phase}`,
|
|
118
|
+
(error as Error).message
|
|
119
|
+
);
|
|
120
|
+
|
|
103
121
|
// β
Store failure details in KV for manual review
|
|
104
|
-
await kvFailuresStore.put(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
122
|
+
await kvFailuresStore.put(
|
|
123
|
+
failureKey,
|
|
124
|
+
JSON.stringify({
|
|
125
|
+
error: (error as Error).message,
|
|
126
|
+
attempt: attempts,
|
|
127
|
+
phase, // β
Identify if failure occurred in "generation" or "parsing"
|
|
128
|
+
conceptSlug,
|
|
129
|
+
combinationString,
|
|
130
|
+
timestamp: new Date().toISOString(),
|
|
131
|
+
})
|
|
132
|
+
);
|
|
133
|
+
|
|
113
134
|
if (attempts >= maxAttempts) {
|
|
114
|
-
console.error(
|
|
115
|
-
|
|
135
|
+
console.error(
|
|
136
|
+
`π¨ All ${maxAttempts} attempts failed during ${phase}. Logged failure.`
|
|
137
|
+
);
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Failed to generate basic info after ${maxAttempts} attempts`
|
|
140
|
+
);
|
|
116
141
|
}
|
|
117
|
-
|
|
118
|
-
console.log(
|
|
142
|
+
|
|
143
|
+
console.log('π Retrying...');
|
|
119
144
|
await new Promise((resolve) => setTimeout(resolve, 2000)); // β³ Small delay before retrying
|
|
120
145
|
}
|
|
121
146
|
}
|
|
@@ -130,27 +155,33 @@ export class ConceptService {
|
|
|
130
155
|
poemPT: string[];
|
|
131
156
|
} {
|
|
132
157
|
console.log('π Parsing basic info response from ChatGPT:', response);
|
|
133
|
-
|
|
158
|
+
|
|
134
159
|
const enMatch = response.match(
|
|
135
160
|
/EN:\s*β’\s*Name:\s*(.+?)\s*β’\s*Description:\s*([\s\S]+?)\s*β’\s*Poetic Passage:\s*([\s\S]+?)\s*(?=PT:|$)/
|
|
136
161
|
);
|
|
137
162
|
const ptMatch = response.match(
|
|
138
163
|
/PT:\s*β’\s*Nome:\s*(.+?)\s*β’\s*DescriΓ§Γ£o:\s*([\s\S]+?)\s*β’\s*Passagem PoΓ©tica:\s*([\s\S]+)/
|
|
139
164
|
);
|
|
140
|
-
|
|
165
|
+
|
|
141
166
|
if (!enMatch || !ptMatch) {
|
|
142
167
|
console.error('β Invalid basic info response format:', response);
|
|
143
168
|
throw new Error('Invalid basic info response format');
|
|
144
169
|
}
|
|
145
|
-
|
|
170
|
+
|
|
146
171
|
const nameEN = enMatch[1].trim();
|
|
147
172
|
const descriptionEN = enMatch[2].trim();
|
|
148
|
-
const poemEN = enMatch[3]
|
|
149
|
-
|
|
173
|
+
const poemEN = enMatch[3]
|
|
174
|
+
.trim()
|
|
175
|
+
.split(/\n+/)
|
|
176
|
+
.map((line) => line.trim()); // β
Split into array
|
|
177
|
+
|
|
150
178
|
const namePT = ptMatch[1].trim();
|
|
151
179
|
const descriptionPT = ptMatch[2].trim();
|
|
152
|
-
const poemPT = ptMatch[3]
|
|
153
|
-
|
|
180
|
+
const poemPT = ptMatch[3]
|
|
181
|
+
.trim()
|
|
182
|
+
.split(/\n+/)
|
|
183
|
+
.map((line) => line.trim()); // β
Split into array
|
|
184
|
+
|
|
154
185
|
console.log('β
Successfully parsed basic info:', {
|
|
155
186
|
nameEN,
|
|
156
187
|
descriptionEN,
|
|
@@ -159,7 +190,7 @@ export class ConceptService {
|
|
|
159
190
|
descriptionPT,
|
|
160
191
|
poemPT,
|
|
161
192
|
});
|
|
162
|
-
|
|
193
|
+
|
|
163
194
|
return { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT };
|
|
164
195
|
}
|
|
165
196
|
|
|
@@ -167,40 +198,65 @@ export class ConceptService {
|
|
|
167
198
|
* Generate the Leonardo prompt for a concept.
|
|
168
199
|
*/
|
|
169
200
|
async generatePrompt(
|
|
170
|
-
language: Languages,
|
|
171
201
|
conceptSlug: Concept,
|
|
172
|
-
combinationString: string
|
|
202
|
+
combinationString: string,
|
|
203
|
+
override: boolean = false
|
|
173
204
|
): Promise<void> {
|
|
174
|
-
const kvKey = buildConceptKVKey(language, conceptSlug, combinationString);
|
|
175
205
|
console.log(
|
|
176
|
-
|
|
206
|
+
`π Generating Leonardo prompt for concept: ${conceptSlug}, combination: ${combinationString}`
|
|
177
207
|
);
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
208
|
+
|
|
209
|
+
// β
Build KV keys for both languages
|
|
210
|
+
const kvKeyEN = buildConceptKVKey('en-us', conceptSlug, combinationString);
|
|
211
|
+
const kvKeyPT = buildConceptKVKey('pt-br', conceptSlug, combinationString);
|
|
212
|
+
|
|
213
|
+
// β
Retrieve existing KV data
|
|
214
|
+
const conceptEN = await this.getKVConcept(kvKeyEN);
|
|
215
|
+
const conceptPT = await this.getKVConcept(kvKeyPT);
|
|
216
|
+
|
|
217
|
+
// β
Check if prompt already exists
|
|
218
|
+
if (!override && conceptEN.leonardoPrompt && conceptPT.leonardoPrompt) {
|
|
219
|
+
console.log(`β‘ Leonardo prompt already exists for ${conceptSlug}, skipping.`);
|
|
220
|
+
return; // β
Skip regeneration
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// β
Ensure basic info is present
|
|
224
|
+
if (
|
|
225
|
+
!conceptEN.name || !conceptEN.description || !conceptEN.poem ||
|
|
226
|
+
!conceptPT.name || !conceptPT.description || !conceptPT.poem
|
|
227
|
+
) {
|
|
181
228
|
throw new Error(
|
|
182
|
-
|
|
229
|
+
`β Basic info must be populated before generating Leonardo prompt for concept: ${conceptSlug}`
|
|
183
230
|
);
|
|
184
231
|
}
|
|
185
|
-
|
|
232
|
+
|
|
233
|
+
// β
Generate prompt request
|
|
186
234
|
const messages = this.context
|
|
187
235
|
.buildLLMMessages()
|
|
188
236
|
.generateConceptLeonardoPrompt({
|
|
189
237
|
conceptSlug,
|
|
190
238
|
combination: combinationString,
|
|
191
239
|
});
|
|
192
|
-
|
|
240
|
+
|
|
241
|
+
// β
Call ChatGPT API
|
|
193
242
|
const response = await this.context.api().callChatGPT.single(messages, {});
|
|
194
243
|
if (!response) {
|
|
195
244
|
throw new Error(
|
|
196
|
-
|
|
245
|
+
`β Failed to generate Leonardo prompt for concept: ${conceptSlug}`
|
|
197
246
|
);
|
|
198
247
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
248
|
+
|
|
249
|
+
// β
Store the generated prompt in both languages
|
|
250
|
+
conceptEN.leonardoPrompt = response.trim();
|
|
251
|
+
conceptPT.leonardoPrompt = response.trim();
|
|
252
|
+
|
|
253
|
+
await Promise.all([
|
|
254
|
+
this.context.kvConceptsStore().put(kvKeyEN, JSON.stringify(conceptEN)),
|
|
255
|
+
this.context.kvConceptsStore().put(kvKeyPT, JSON.stringify(conceptPT)),
|
|
256
|
+
]);
|
|
257
|
+
|
|
202
258
|
console.log(
|
|
203
|
-
|
|
259
|
+
`β
Leonardo prompt stored for concept: ${conceptSlug}, combination: ${combinationString}, in both languages.`
|
|
204
260
|
);
|
|
205
261
|
}
|
|
206
262
|
|
|
@@ -215,93 +271,114 @@ export class ConceptService {
|
|
|
215
271
|
console.log(
|
|
216
272
|
`π Generating content for concept: ${conceptSlug}, combination: ${combinationString}, override: ${override}`
|
|
217
273
|
);
|
|
218
|
-
|
|
274
|
+
|
|
219
275
|
const kvStore = this.context.kvConceptsStore();
|
|
220
276
|
const kvFailuresStore = this.context.kvConceptFailuresStore(); // π΄ Replace with actual KV store
|
|
221
|
-
const kvKeyEN = buildConceptKVKey(
|
|
222
|
-
const kvKeyPT = buildConceptKVKey(
|
|
277
|
+
const kvKeyEN = buildConceptKVKey('en-us', conceptSlug, combinationString);
|
|
278
|
+
const kvKeyPT = buildConceptKVKey('pt-br', conceptSlug, combinationString);
|
|
223
279
|
const failureKey = `failures:content:${conceptSlug}:${combinationString}`;
|
|
224
|
-
|
|
280
|
+
|
|
225
281
|
// β
Ensure basic info is available before generating content
|
|
226
282
|
const conceptEN = await this.getKVConcept(kvKeyEN);
|
|
227
283
|
const conceptPT = await this.getKVConcept(kvKeyPT);
|
|
228
|
-
|
|
284
|
+
|
|
229
285
|
if (
|
|
230
|
-
!conceptEN.name ||
|
|
231
|
-
!
|
|
286
|
+
!conceptEN.name ||
|
|
287
|
+
!conceptEN.description ||
|
|
288
|
+
!conceptEN.poem ||
|
|
289
|
+
!conceptPT.name ||
|
|
290
|
+
!conceptPT.description ||
|
|
291
|
+
!conceptPT.poem
|
|
232
292
|
) {
|
|
233
293
|
throw new Error(
|
|
234
294
|
`β Basic info must be populated before generating content for ${conceptSlug}`
|
|
235
295
|
);
|
|
236
296
|
}
|
|
237
|
-
|
|
238
|
-
if (
|
|
297
|
+
|
|
298
|
+
if (
|
|
299
|
+
!override &&
|
|
300
|
+
conceptEN.content?.length > 0 &&
|
|
301
|
+
conceptPT.content?.length > 0
|
|
302
|
+
) {
|
|
239
303
|
console.log(`β‘ Content already exists for ${conceptSlug}, skipping.`);
|
|
240
304
|
return; // β
Skip regeneration
|
|
241
305
|
}
|
|
242
|
-
|
|
306
|
+
|
|
243
307
|
let attempts = 0;
|
|
244
308
|
const maxAttempts = 3;
|
|
245
|
-
|
|
309
|
+
|
|
246
310
|
while (attempts < maxAttempts) {
|
|
247
|
-
let phase =
|
|
311
|
+
let phase = 'generation'; // π Track phase
|
|
248
312
|
try {
|
|
249
313
|
attempts++;
|
|
250
314
|
console.log(`π Attempt ${attempts} to generate content...`);
|
|
251
|
-
|
|
315
|
+
|
|
252
316
|
// β
Build messages for LLM
|
|
253
|
-
const messages = this.context
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
317
|
+
const messages = this.context
|
|
318
|
+
.buildLLMMessages()
|
|
319
|
+
.generateConceptContent({
|
|
320
|
+
conceptSlug,
|
|
321
|
+
combination: combinationString,
|
|
322
|
+
name: conceptEN.name, // Use English name since both languages match in meaning
|
|
323
|
+
description: conceptEN.description,
|
|
324
|
+
poem: conceptEN.poem,
|
|
325
|
+
});
|
|
326
|
+
|
|
261
327
|
// β
Call ChatGPT API
|
|
262
|
-
const response = await this.context
|
|
328
|
+
const response = await this.context
|
|
329
|
+
.api()
|
|
330
|
+
.callChatGPT.single(messages, {});
|
|
263
331
|
if (!response) {
|
|
264
332
|
throw new Error(`β AI returned an empty response`);
|
|
265
333
|
}
|
|
266
|
-
|
|
267
|
-
phase =
|
|
268
|
-
|
|
334
|
+
|
|
335
|
+
phase = 'parsing'; // β
Switch to parsing phase
|
|
336
|
+
|
|
269
337
|
// β
Parse structured content for both languages
|
|
270
338
|
const { structuredContentEN, structuredContentPT } =
|
|
271
339
|
this.parseStructuredContent(response);
|
|
272
|
-
|
|
340
|
+
|
|
273
341
|
// π Store English content
|
|
274
342
|
Object.assign(conceptEN, { content: structuredContentEN });
|
|
275
343
|
await kvStore.put(kvKeyEN, JSON.stringify(conceptEN));
|
|
276
|
-
|
|
344
|
+
|
|
277
345
|
// π§π· Store Portuguese content
|
|
278
346
|
Object.assign(conceptPT, { content: structuredContentPT });
|
|
279
347
|
await kvStore.put(kvKeyPT, JSON.stringify(conceptPT));
|
|
280
|
-
|
|
348
|
+
|
|
281
349
|
console.log(
|
|
282
350
|
`β
Structured content stored for ${conceptSlug}, combination: ${combinationString}, in both languages.`
|
|
283
351
|
);
|
|
284
352
|
return; // β
Exit loop if successful
|
|
285
|
-
|
|
286
353
|
} catch (error) {
|
|
287
|
-
console.error(
|
|
288
|
-
|
|
354
|
+
console.error(
|
|
355
|
+
`β Attempt ${attempts} failed at phase: ${phase}`,
|
|
356
|
+
(error as Error).message
|
|
357
|
+
);
|
|
358
|
+
|
|
289
359
|
// β
Store failure details in KV for manual review
|
|
290
|
-
await kvFailuresStore.put(
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
360
|
+
await kvFailuresStore.put(
|
|
361
|
+
failureKey,
|
|
362
|
+
JSON.stringify({
|
|
363
|
+
error: (error as Error).message,
|
|
364
|
+
attempt: attempts,
|
|
365
|
+
phase, // β
Identify failure phase
|
|
366
|
+
conceptSlug,
|
|
367
|
+
combinationString,
|
|
368
|
+
timestamp: new Date().toISOString(),
|
|
369
|
+
})
|
|
370
|
+
);
|
|
371
|
+
|
|
299
372
|
if (attempts >= maxAttempts) {
|
|
300
|
-
console.error(
|
|
301
|
-
|
|
373
|
+
console.error(
|
|
374
|
+
`π¨ All ${maxAttempts} attempts failed at phase ${phase}. Logged failure.`
|
|
375
|
+
);
|
|
376
|
+
throw new Error(
|
|
377
|
+
`Failed to generate content after ${maxAttempts} attempts`
|
|
378
|
+
);
|
|
302
379
|
}
|
|
303
|
-
|
|
304
|
-
console.log(
|
|
380
|
+
|
|
381
|
+
console.log('π Retrying...');
|
|
305
382
|
await new Promise((resolve) => setTimeout(resolve, 2000)); // β³ Small delay before retrying
|
|
306
383
|
}
|
|
307
384
|
}
|
|
@@ -311,51 +388,57 @@ export class ConceptService {
|
|
|
311
388
|
structuredContentEN: StructuredConceptContent;
|
|
312
389
|
structuredContentPT: StructuredConceptContent;
|
|
313
390
|
} {
|
|
314
|
-
console.log(
|
|
315
|
-
|
|
391
|
+
console.log(
|
|
392
|
+
'π Parsing structured content from ChatGPT response:',
|
|
393
|
+
response
|
|
394
|
+
);
|
|
395
|
+
|
|
316
396
|
const sections = [
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
397
|
+
'Core Identity',
|
|
398
|
+
'Strengths and Challenges',
|
|
399
|
+
'Path to Fulfillment',
|
|
400
|
+
'Emotional Depth',
|
|
401
|
+
'Vision and Aspirations',
|
|
322
402
|
];
|
|
323
|
-
|
|
403
|
+
|
|
324
404
|
const sectionsPT = [
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
405
|
+
'Identidade Essencial',
|
|
406
|
+
'ForΓ§as e Desafios',
|
|
407
|
+
'Caminho para a Plenitude',
|
|
408
|
+
'Profundidade Emocional',
|
|
409
|
+
'VisΓ£o e AspiraΓ§Γ΅es',
|
|
330
410
|
];
|
|
331
|
-
|
|
411
|
+
|
|
332
412
|
// β
Match English and Portuguese content separately
|
|
333
413
|
const enMatches = response.match(/EN:\s*([\s\S]+?)\s*PT:/);
|
|
334
414
|
const ptMatches = response.match(/PT:\s*([\s\S]+)/);
|
|
335
|
-
|
|
415
|
+
|
|
336
416
|
if (!enMatches || !ptMatches) {
|
|
337
|
-
throw new Error(
|
|
417
|
+
throw new Error('β Missing English or Portuguese content in response.');
|
|
338
418
|
}
|
|
339
|
-
|
|
419
|
+
|
|
340
420
|
const enContent = enMatches[1].trim();
|
|
341
421
|
const ptContent = ptMatches[1].trim();
|
|
342
|
-
|
|
422
|
+
|
|
343
423
|
function extractSections(text: string, sectionTitles: string[]) {
|
|
344
424
|
return sectionTitles.map((title) => {
|
|
345
425
|
const regex = new RegExp(`${title}:\\s*([\\s\\S]+?)(?=\\n\\d|$)`);
|
|
346
426
|
const match = text.match(regex);
|
|
347
|
-
|
|
427
|
+
|
|
348
428
|
if (!match) {
|
|
349
|
-
return { type:
|
|
429
|
+
return { type: 'section', title, content: ['β Section Missing'] };
|
|
350
430
|
}
|
|
351
|
-
|
|
431
|
+
|
|
352
432
|
// β
Split content into paragraphs
|
|
353
|
-
const paragraphs = match[1]
|
|
354
|
-
|
|
355
|
-
|
|
433
|
+
const paragraphs = match[1]
|
|
434
|
+
.trim()
|
|
435
|
+
.split(/\n\n+/)
|
|
436
|
+
.map((p) => p.trim());
|
|
437
|
+
|
|
438
|
+
return { type: 'section', title, content: paragraphs };
|
|
356
439
|
});
|
|
357
440
|
}
|
|
358
|
-
|
|
441
|
+
|
|
359
442
|
return {
|
|
360
443
|
structuredContentEN: extractSections(enContent, sections),
|
|
361
444
|
structuredContentPT: extractSections(ptContent, sectionsPT),
|
package/package.json
CHANGED
package/utils/buildMessages.ts
CHANGED
|
@@ -122,18 +122,22 @@ export const buildLLMMessages = (env: BackendBindings) => ({
|
|
|
122
122
|
}: {
|
|
123
123
|
combination: string;
|
|
124
124
|
conceptSlug: Concept;
|
|
125
|
-
}): ChatMessages =>
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
125
|
+
}): ChatMessages => {
|
|
126
|
+
const zodiacCombination = mapConceptToPlanets(conceptSlug, combination);
|
|
127
|
+
|
|
128
|
+
return [
|
|
129
|
+
{
|
|
130
|
+
role: 'system',
|
|
131
|
+
content: conceptPrompts(env)['leonardoPrompt'][conceptSlug],
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
role: 'user',
|
|
135
|
+
content: `
|
|
136
|
+
Combination: ${zodiacCombination}
|
|
134
137
|
`,
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
},
|
|
137
141
|
generateConceptContent: ({
|
|
138
142
|
name,
|
|
139
143
|
description,
|