@zodic/shared 0.0.226 → 0.0.228
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 +190 -42
- package/package.json +1 -1
- package/utils/buildMessages.ts +5 -6
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
KVNamespaceListKey,
|
|
3
3
|
KVNamespaceListResult,
|
|
4
4
|
} from '@cloudflare/workers-types';
|
|
5
|
-
import { and, eq, sql } from 'drizzle-orm';
|
|
5
|
+
import { and, eq, inArray, sql } from 'drizzle-orm';
|
|
6
6
|
import { drizzle } from 'drizzle-orm/d1';
|
|
7
7
|
import { inject, injectable } from 'inversify';
|
|
8
8
|
import 'reflect-metadata';
|
|
@@ -377,22 +377,52 @@ export class ConceptService {
|
|
|
377
377
|
async generateContent(
|
|
378
378
|
conceptSlug: Concept,
|
|
379
379
|
combinationString: string,
|
|
380
|
-
override: boolean = false // ✅
|
|
380
|
+
override: boolean = false // ✅ Optional override flag
|
|
381
381
|
): Promise<void> {
|
|
382
382
|
console.log(
|
|
383
383
|
`🚀 Generating content for concept: ${conceptSlug}, combination: ${combinationString}, override: ${override}`
|
|
384
384
|
);
|
|
385
385
|
|
|
386
|
-
const
|
|
387
|
-
const kvFailuresStore = this.context.kvConceptFailuresStore(); // 🔴 Replace with actual KV store
|
|
388
|
-
const kvKeyEN = buildConceptKVKey('en-us', conceptSlug, combinationString);
|
|
389
|
-
const kvKeyPT = buildConceptKVKey('pt-br', conceptSlug, combinationString);
|
|
390
|
-
const failureKey = `failures:content:${conceptSlug}:${combinationString}`;
|
|
386
|
+
const db = drizzle(this.context.env.DB); // ✅ Initialize Drizzle with Cloudflare D1
|
|
391
387
|
|
|
392
|
-
// ✅
|
|
393
|
-
|
|
394
|
-
|
|
388
|
+
// ✅ Fetch concept data from D1
|
|
389
|
+
console.log(
|
|
390
|
+
`📡 Fetching concept data for ${conceptSlug}:${combinationString}`
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
const conceptEntries = await db
|
|
394
|
+
.select({
|
|
395
|
+
id: conceptsData.id,
|
|
396
|
+
language: conceptsData.language,
|
|
397
|
+
name: conceptsData.name,
|
|
398
|
+
description: conceptsData.description,
|
|
399
|
+
poem: conceptsData.poem,
|
|
400
|
+
content: conceptsData.content,
|
|
401
|
+
})
|
|
402
|
+
.from(conceptsData)
|
|
403
|
+
.where(
|
|
404
|
+
and(
|
|
405
|
+
eq(conceptsData.conceptSlug, conceptSlug),
|
|
406
|
+
eq(conceptsData.combination, combinationString),
|
|
407
|
+
inArray(conceptsData.language, ['en-us', 'pt-br']) // ✅ Fetch both languages
|
|
408
|
+
)
|
|
409
|
+
)
|
|
410
|
+
.execute();
|
|
411
|
+
|
|
412
|
+
if (conceptEntries.length !== 2) {
|
|
413
|
+
throw new Error(
|
|
414
|
+
`❌ Missing concept data for ${conceptSlug}:${combinationString}. Ensure basic info is populated first.`
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const conceptEN = conceptEntries.find((c) => c.language === 'en-us');
|
|
419
|
+
const conceptPT = conceptEntries.find((c) => c.language === 'pt-br');
|
|
395
420
|
|
|
421
|
+
if (!conceptEN || !conceptPT) {
|
|
422
|
+
throw new Error(`❌ Could not find both EN and PT versions.`);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// ✅ Ensure basic info is available before generating content
|
|
396
426
|
if (
|
|
397
427
|
!conceptEN.name ||
|
|
398
428
|
!conceptEN.description ||
|
|
@@ -408,8 +438,8 @@ export class ConceptService {
|
|
|
408
438
|
|
|
409
439
|
if (
|
|
410
440
|
!override &&
|
|
411
|
-
conceptEN.content
|
|
412
|
-
conceptPT.content
|
|
441
|
+
Array.isArray(conceptEN?.content) && conceptEN.content.length > 0 &&
|
|
442
|
+
Array.isArray(conceptPT?.content) && conceptPT.content.length > 0
|
|
413
443
|
) {
|
|
414
444
|
console.log(`⚡ Content already exists for ${conceptSlug}, skipping.`);
|
|
415
445
|
return; // ✅ Skip regeneration
|
|
@@ -432,7 +462,6 @@ export class ConceptService {
|
|
|
432
462
|
combination: combinationString,
|
|
433
463
|
name: conceptEN.name, // Use English name since both languages match in meaning
|
|
434
464
|
description: conceptEN.description,
|
|
435
|
-
poem: conceptEN.poem,
|
|
436
465
|
});
|
|
437
466
|
|
|
438
467
|
// ✅ Call ChatGPT API
|
|
@@ -447,18 +476,39 @@ export class ConceptService {
|
|
|
447
476
|
|
|
448
477
|
// ✅ Parse structured content for both languages
|
|
449
478
|
const { structuredContentEN, structuredContentPT } =
|
|
450
|
-
this.parseStructuredContent(response);
|
|
479
|
+
this.parseStructuredContent(conceptSlug, response);
|
|
451
480
|
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
|
|
481
|
+
// ✅ Store content in D1
|
|
482
|
+
console.log(
|
|
483
|
+
`💾 Storing structured content for ${conceptSlug}:${combinationString} in D1...`
|
|
484
|
+
);
|
|
455
485
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
486
|
+
await db.transaction(async (tx) => {
|
|
487
|
+
await tx
|
|
488
|
+
.update(conceptsData)
|
|
489
|
+
.set({ content: JSON.stringify(structuredContentEN) })
|
|
490
|
+
.where(
|
|
491
|
+
and(
|
|
492
|
+
eq(conceptsData.id, conceptEN.id),
|
|
493
|
+
eq(conceptsData.language, 'en-us')
|
|
494
|
+
)
|
|
495
|
+
)
|
|
496
|
+
.execute();
|
|
497
|
+
|
|
498
|
+
await tx
|
|
499
|
+
.update(conceptsData)
|
|
500
|
+
.set({ content: JSON.stringify(structuredContentPT) })
|
|
501
|
+
.where(
|
|
502
|
+
and(
|
|
503
|
+
eq(conceptsData.id, conceptPT.id),
|
|
504
|
+
eq(conceptsData.language, 'pt-br')
|
|
505
|
+
)
|
|
506
|
+
)
|
|
507
|
+
.execute();
|
|
508
|
+
});
|
|
459
509
|
|
|
460
510
|
console.log(
|
|
461
|
-
`✅ Structured content stored for ${conceptSlug}
|
|
511
|
+
`✅ Structured content stored successfully for ${conceptSlug}:${combinationString}.`
|
|
462
512
|
);
|
|
463
513
|
return; // ✅ Exit loop if successful
|
|
464
514
|
} catch (error) {
|
|
@@ -468,7 +518,9 @@ export class ConceptService {
|
|
|
468
518
|
);
|
|
469
519
|
|
|
470
520
|
// ✅ Store failure details in KV for manual review
|
|
471
|
-
|
|
521
|
+
const failureKey = `failures:content:${conceptSlug}:${combinationString}`;
|
|
522
|
+
|
|
523
|
+
await this.context.kvConceptFailuresStore().put(
|
|
472
524
|
failureKey,
|
|
473
525
|
JSON.stringify({
|
|
474
526
|
error: (error as Error).message,
|
|
@@ -495,11 +547,16 @@ export class ConceptService {
|
|
|
495
547
|
}
|
|
496
548
|
}
|
|
497
549
|
|
|
498
|
-
private parseStructuredContent(
|
|
550
|
+
private parseStructuredContent(
|
|
551
|
+
conceptSlug: Concept,
|
|
552
|
+
response: string
|
|
553
|
+
): {
|
|
499
554
|
structuredContentEN: StructuredConceptContent;
|
|
500
555
|
structuredContentPT: StructuredConceptContent;
|
|
501
556
|
} {
|
|
502
|
-
console.log(
|
|
557
|
+
console.log(
|
|
558
|
+
`📌 [START] Parsing structured content for ${conceptSlug} from ChatGPT response.`
|
|
559
|
+
);
|
|
503
560
|
|
|
504
561
|
// ✅ Step 1: Clean artifacts before processing
|
|
505
562
|
let cleanedResponse = response
|
|
@@ -513,23 +570,114 @@ export class ConceptService {
|
|
|
513
570
|
cleanedResponse.slice(0, 500) + '...'
|
|
514
571
|
);
|
|
515
572
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
573
|
+
// ✅ Step 2: Define Section Titles Per Concept
|
|
574
|
+
const conceptSections: Record<string, { en: string[]; pt: string[] }> = {
|
|
575
|
+
crown: {
|
|
576
|
+
en: [
|
|
577
|
+
'Core Identity',
|
|
578
|
+
'Strengths and Challenges',
|
|
579
|
+
'Path to Fulfillment',
|
|
580
|
+
'Emotional Depth',
|
|
581
|
+
'Vision and Aspirations',
|
|
582
|
+
],
|
|
583
|
+
pt: [
|
|
584
|
+
'Identidade Essencial',
|
|
585
|
+
'Forças e Desafios',
|
|
586
|
+
'Caminho para a Plenitude',
|
|
587
|
+
'Profundidade Emocional',
|
|
588
|
+
'Visão e Aspirações',
|
|
589
|
+
],
|
|
590
|
+
},
|
|
591
|
+
scepter: {
|
|
592
|
+
en: [
|
|
593
|
+
'The Power of Expression',
|
|
594
|
+
'The Dance of Action and Reaction',
|
|
595
|
+
'Navigating Challenges',
|
|
596
|
+
'The Mirror of Relationships',
|
|
597
|
+
'Impact on the World',
|
|
598
|
+
],
|
|
599
|
+
pt: [
|
|
600
|
+
'O Poder da Expressão',
|
|
601
|
+
'A Dança da Ação e Reação',
|
|
602
|
+
'Navegando Desafios',
|
|
603
|
+
'O Espelho dos Relacionamentos',
|
|
604
|
+
'Impacto no Mundo',
|
|
605
|
+
],
|
|
606
|
+
},
|
|
607
|
+
amulet: {
|
|
608
|
+
en: [
|
|
609
|
+
'The Shield of Protection',
|
|
610
|
+
'The Strength Within',
|
|
611
|
+
'Nurturing Growth',
|
|
612
|
+
'The Cycle of Renewal',
|
|
613
|
+
'The Anchor of Wisdom',
|
|
614
|
+
],
|
|
615
|
+
pt: [
|
|
616
|
+
'O Escudo da Proteção',
|
|
617
|
+
'A Força Interior',
|
|
618
|
+
'Cultivando o Crescimento',
|
|
619
|
+
'O Ciclo da Renovação',
|
|
620
|
+
'A Âncora da Sabedoria',
|
|
621
|
+
],
|
|
622
|
+
},
|
|
623
|
+
ring: {
|
|
624
|
+
en: [
|
|
625
|
+
'Magnetic Pull',
|
|
626
|
+
'Hidden Desires',
|
|
627
|
+
'The Fated Dance',
|
|
628
|
+
'Unveiling the Shadows',
|
|
629
|
+
'Your Love Power',
|
|
630
|
+
],
|
|
631
|
+
pt: [
|
|
632
|
+
'Força Magnética',
|
|
633
|
+
'Desejos Ocultos',
|
|
634
|
+
'A Dança do Destino',
|
|
635
|
+
'Revelando as Sombras',
|
|
636
|
+
'Seu Poder no Amor',
|
|
637
|
+
],
|
|
638
|
+
},
|
|
639
|
+
lantern: {
|
|
640
|
+
en: [
|
|
641
|
+
'The Call to Awakening',
|
|
642
|
+
'Walking Through Shadows',
|
|
643
|
+
'The Inner Flame',
|
|
644
|
+
'Revelations and Truths',
|
|
645
|
+
'Becoming the Light',
|
|
646
|
+
],
|
|
647
|
+
pt: [
|
|
648
|
+
'O Chamado para o Despertar',
|
|
649
|
+
'Caminhando Pelas Sombras',
|
|
650
|
+
'A Chama Interior',
|
|
651
|
+
'Revelações e Verdades',
|
|
652
|
+
'Tornando-se a Luz',
|
|
653
|
+
],
|
|
654
|
+
},
|
|
655
|
+
orb: {
|
|
656
|
+
en: [
|
|
657
|
+
'The Path Unfolding',
|
|
658
|
+
'A Calling Beyond the Self',
|
|
659
|
+
'Turning Points of Fate',
|
|
660
|
+
'Mastering the Journey',
|
|
661
|
+
'Legacy and Impact',
|
|
662
|
+
],
|
|
663
|
+
pt: [
|
|
664
|
+
'O Caminho que se Revela',
|
|
665
|
+
'Um Chamado Além de Si Mesmo',
|
|
666
|
+
'Pontos de Virada do Destino',
|
|
667
|
+
'Dominando a Jornada',
|
|
668
|
+
'Legado e Impacto',
|
|
669
|
+
],
|
|
670
|
+
},
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
const sectionsEN = conceptSections[conceptSlug]?.en;
|
|
674
|
+
const sectionsPT = conceptSections[conceptSlug]?.pt;
|
|
675
|
+
|
|
676
|
+
if (!sectionsEN || !sectionsPT) {
|
|
677
|
+
throw new Error(`❌ Unknown concept: ${conceptSlug}`);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// ✅ Step 3: Extract English and Portuguese content separately
|
|
533
681
|
const enMatches = cleanedResponse.match(/EN:\s*([\s\S]+?)\s*PT:/);
|
|
534
682
|
const ptMatches = cleanedResponse.match(/PT:\s*([\s\S]+)/);
|
|
535
683
|
|
|
@@ -552,7 +700,7 @@ export class ConceptService {
|
|
|
552
700
|
ptContent.slice(0, 500) + '...'
|
|
553
701
|
);
|
|
554
702
|
|
|
555
|
-
// ✅ Step
|
|
703
|
+
// ✅ Step 4: Extract structured sections
|
|
556
704
|
function extractSections(text: string, sectionTitles: string[]) {
|
|
557
705
|
return sectionTitles.map((title) => {
|
|
558
706
|
console.log(`🔍 [PROCESSING] Extracting section: "${title}"`);
|
package/package.json
CHANGED
package/utils/buildMessages.ts
CHANGED
|
@@ -146,18 +146,15 @@ export const buildLLMMessages = (env: BackendBindings) => ({
|
|
|
146
146
|
generateConceptContent: ({
|
|
147
147
|
name,
|
|
148
148
|
description,
|
|
149
|
-
poem,
|
|
150
149
|
combination,
|
|
151
150
|
conceptSlug,
|
|
152
151
|
}: {
|
|
153
152
|
name: string;
|
|
154
153
|
description: string;
|
|
155
|
-
poem: string[];
|
|
156
154
|
conceptSlug: Concept;
|
|
157
155
|
combination: string;
|
|
158
156
|
}): ChatMessages => {
|
|
159
157
|
const zodiacCombination = mapConceptToPlanets(conceptSlug, combination);
|
|
160
|
-
const formattedPoem = poem.map((line) => `${line}`).join('\n');
|
|
161
158
|
|
|
162
159
|
return [
|
|
163
160
|
{
|
|
@@ -171,8 +168,6 @@ export const buildLLMMessages = (env: BackendBindings) => ({
|
|
|
171
168
|
- Combination: ${zodiacCombination}
|
|
172
169
|
- Name: ${name}
|
|
173
170
|
- Description: ${description}
|
|
174
|
-
- Poem:
|
|
175
|
-
${formattedPoem}
|
|
176
171
|
|
|
177
172
|
Generate additional content to elaborate on this concept for frontend display.
|
|
178
173
|
`,
|
|
@@ -246,7 +241,11 @@ export const buildLLMMessages = (env: BackendBindings) => ({
|
|
|
246
241
|
];
|
|
247
242
|
},
|
|
248
243
|
|
|
249
|
-
translateENConceptName: ({
|
|
244
|
+
translateENConceptName: ({
|
|
245
|
+
englishName,
|
|
246
|
+
}: {
|
|
247
|
+
englishName: string;
|
|
248
|
+
}): ChatMessages => [
|
|
250
249
|
{
|
|
251
250
|
role: 'system',
|
|
252
251
|
content: `
|