@zodic/shared 0.0.236 β 0.0.237
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 +259 -24
- package/package.json +1 -1
|
@@ -550,22 +550,20 @@ export class ConceptService {
|
|
|
550
550
|
structuredContentEN: StructuredConceptContent;
|
|
551
551
|
structuredContentPT: StructuredConceptContent;
|
|
552
552
|
}> {
|
|
553
|
-
console.log(
|
|
554
|
-
`π [START] Parsing structured content for ${conceptSlug} from ChatGPT response.`
|
|
555
|
-
);
|
|
553
|
+
console.log(`π [START] Parsing structured content for ${conceptSlug}`);
|
|
556
554
|
|
|
557
555
|
// β
Step 1: Clean artifacts before processing
|
|
558
556
|
let cleanedResponse = response
|
|
559
557
|
.replace(/[*#β’]/g, '') // Remove bullet points, bold markers, headings
|
|
560
558
|
.replace(/---+/g, '') // Remove dividers (like "---")
|
|
561
559
|
.replace(/\n\s*\n/g, '\n\n') // Normalize multiple line breaks
|
|
562
|
-
.replace(/\s+EN:\s*/g, '\nEN: ') //
|
|
563
|
-
.replace(/\s+PT:\s*/g, '\nPT: ') //
|
|
560
|
+
.replace(/\s+EN:\s*/g, '\nEN: ') // Normalize "EN:"
|
|
561
|
+
.replace(/\s+PT:\s*/g, '\nPT: ') // Normalize "PT:"
|
|
564
562
|
.trim();
|
|
565
563
|
|
|
566
564
|
console.log(
|
|
567
565
|
'πΉ [CLEANED RESPONSE]:',
|
|
568
|
-
cleanedResponse.slice(0,
|
|
566
|
+
cleanedResponse.slice(0, 3000) + '...'
|
|
569
567
|
);
|
|
570
568
|
|
|
571
569
|
try {
|
|
@@ -677,12 +675,13 @@ export class ConceptService {
|
|
|
677
675
|
}
|
|
678
676
|
|
|
679
677
|
// β
Step 3: Detect if AI interleaved EN/PT sections
|
|
680
|
-
const interleavedMatches = cleanedResponse.match(
|
|
681
|
-
/(EN:\s*[\s\S]+?PT:\s*[\s\S]+?)(?=EN:|$)/
|
|
682
|
-
);
|
|
683
678
|
let extractedEN = '';
|
|
684
679
|
let extractedPT = '';
|
|
685
680
|
|
|
681
|
+
const interleavedMatches = cleanedResponse.match(
|
|
682
|
+
/(EN:\s*[\s\S]+?PT:\s*[\s\S]+?)(?=EN:|$)/g
|
|
683
|
+
);
|
|
684
|
+
|
|
686
685
|
if (interleavedMatches) {
|
|
687
686
|
console.warn(
|
|
688
687
|
'β οΈ Detected interleaved EN/PT sections. Parsing accordingly.'
|
|
@@ -706,21 +705,21 @@ export class ConceptService {
|
|
|
706
705
|
}
|
|
707
706
|
|
|
708
707
|
console.log(
|
|
709
|
-
'β
|
|
710
|
-
extractedEN.slice(0,
|
|
708
|
+
'β
Extracted English Content:',
|
|
709
|
+
extractedEN.slice(0, 1000) + '...'
|
|
711
710
|
);
|
|
712
711
|
console.log(
|
|
713
|
-
'β
|
|
714
|
-
extractedPT.slice(0,
|
|
712
|
+
'β
Extracted Portuguese Content:',
|
|
713
|
+
extractedPT.slice(0, 1000) + '...'
|
|
715
714
|
);
|
|
716
715
|
|
|
717
716
|
// β
Step 4: Extract structured sections dynamically
|
|
718
717
|
function extractSections(text: string, sectionTitles: string[]) {
|
|
719
718
|
return sectionTitles.map((title) => {
|
|
720
|
-
console.log(`π
|
|
719
|
+
console.log(`π Extracting section: "${title}"`);
|
|
721
720
|
|
|
722
721
|
const regex = new RegExp(
|
|
723
|
-
|
|
722
|
+
`(?:\\d+\\.\\s*)?${title}\\s*:?\\s*([\\s\\S]+?)(?=\\n(?:\\d+\\.\\s*)?[A-Z]|$)`
|
|
724
723
|
);
|
|
725
724
|
const match = text.match(regex);
|
|
726
725
|
|
|
@@ -734,10 +733,7 @@ export class ConceptService {
|
|
|
734
733
|
.map((p) => p.trim())
|
|
735
734
|
.filter((p) => p.length > 0);
|
|
736
735
|
|
|
737
|
-
console.log(
|
|
738
|
-
`β
[EXTRACTED] "${title}" - Parsed Content:`,
|
|
739
|
-
paragraphs
|
|
740
|
-
);
|
|
736
|
+
console.log(`β
Extracted "${title}" - Parsed Content:`, paragraphs);
|
|
741
737
|
return { type: 'section', title, content: paragraphs };
|
|
742
738
|
});
|
|
743
739
|
}
|
|
@@ -746,17 +742,15 @@ export class ConceptService {
|
|
|
746
742
|
const structuredContentPT = extractSections(extractedPT, sectionsPT);
|
|
747
743
|
|
|
748
744
|
console.log(
|
|
749
|
-
'π―
|
|
745
|
+
'π― Parsed English Content:',
|
|
750
746
|
JSON.stringify(structuredContentEN, null, 2)
|
|
751
747
|
);
|
|
752
748
|
console.log(
|
|
753
|
-
'π―
|
|
749
|
+
'π― Parsed Portuguese Content:',
|
|
754
750
|
JSON.stringify(structuredContentPT, null, 2)
|
|
755
751
|
);
|
|
756
752
|
|
|
757
|
-
console.log(
|
|
758
|
-
'β
[COMPLETE] Structured content parsing finished successfully.'
|
|
759
|
-
);
|
|
753
|
+
console.log('β
Structured content parsing finished successfully.');
|
|
760
754
|
|
|
761
755
|
return {
|
|
762
756
|
structuredContentEN,
|
|
@@ -784,6 +778,247 @@ export class ConceptService {
|
|
|
784
778
|
}
|
|
785
779
|
}
|
|
786
780
|
|
|
781
|
+
// async parseStructuredContent(
|
|
782
|
+
// conceptSlug: Concept,
|
|
783
|
+
// response: string
|
|
784
|
+
// ): Promise<{
|
|
785
|
+
// structuredContentEN: StructuredConceptContent;
|
|
786
|
+
// structuredContentPT: StructuredConceptContent;
|
|
787
|
+
// }> {
|
|
788
|
+
// console.log(
|
|
789
|
+
// `π [START] Parsing structured content for ${conceptSlug} from ChatGPT response.`
|
|
790
|
+
// );
|
|
791
|
+
|
|
792
|
+
// // β
Step 1: Clean artifacts before processing
|
|
793
|
+
// let cleanedResponse = response
|
|
794
|
+
// .replace(/[*#β’]/g, '') // Remove bullet points, bold markers, headings
|
|
795
|
+
// .replace(/---+/g, '') // Remove dividers (like "---")
|
|
796
|
+
// .replace(/\n\s*\n/g, '\n\n') // Normalize multiple line breaks
|
|
797
|
+
// .replace(/\s+EN:\s*/g, '\nEN: ') // Ensure consistent "EN:" formatting
|
|
798
|
+
// .replace(/\s+PT:\s*/g, '\nPT: ') // Ensure consistent "PT:" formatting
|
|
799
|
+
// .trim();
|
|
800
|
+
|
|
801
|
+
// console.log(
|
|
802
|
+
// 'πΉ [CLEANED RESPONSE]:',
|
|
803
|
+
// cleanedResponse.slice(0, 500) + '...'
|
|
804
|
+
// );
|
|
805
|
+
|
|
806
|
+
// try {
|
|
807
|
+
// // β
Step 2: Define Section Titles Per Concept
|
|
808
|
+
// const conceptSections: Record<string, { en: string[]; pt: string[] }> = {
|
|
809
|
+
// crown: {
|
|
810
|
+
// en: [
|
|
811
|
+
// 'Core Identity',
|
|
812
|
+
// 'Strengths and Challenges',
|
|
813
|
+
// 'Path to Fulfillment',
|
|
814
|
+
// 'Emotional Depth',
|
|
815
|
+
// 'Vision and Aspirations',
|
|
816
|
+
// ],
|
|
817
|
+
// pt: [
|
|
818
|
+
// 'Identidade Essencial',
|
|
819
|
+
// 'ForΓ§as e Desafios',
|
|
820
|
+
// 'Caminho para a Plenitude',
|
|
821
|
+
// 'Profundidade Emocional',
|
|
822
|
+
// 'VisΓ£o e AspiraΓ§Γ΅es',
|
|
823
|
+
// ],
|
|
824
|
+
// },
|
|
825
|
+
// scepter: {
|
|
826
|
+
// en: [
|
|
827
|
+
// 'The Power of Expression',
|
|
828
|
+
// 'The Dance of Action and Reaction',
|
|
829
|
+
// 'Navigating Challenges',
|
|
830
|
+
// 'The Mirror of Relations',
|
|
831
|
+
// 'Impact on the World',
|
|
832
|
+
// ],
|
|
833
|
+
// pt: [
|
|
834
|
+
// 'O Poder da ExpressΓ£o',
|
|
835
|
+
// 'A DanΓ§a da AΓ§Γ£o e ReaΓ§Γ£o',
|
|
836
|
+
// 'Navegando Desafios',
|
|
837
|
+
// 'O Espelho das RelaΓ§Γ΅es',
|
|
838
|
+
// 'Impacto no Mundo',
|
|
839
|
+
// ],
|
|
840
|
+
// },
|
|
841
|
+
// amulet: {
|
|
842
|
+
// en: [
|
|
843
|
+
// 'The Shield of Protection',
|
|
844
|
+
// 'The Strength Within',
|
|
845
|
+
// 'Nurturing Growth',
|
|
846
|
+
// 'The Cycle of Renewal',
|
|
847
|
+
// 'The Anchor of Wisdom',
|
|
848
|
+
// ],
|
|
849
|
+
// pt: [
|
|
850
|
+
// 'O Escudo da ProteΓ§Γ£o',
|
|
851
|
+
// 'A ForΓ§a Interior',
|
|
852
|
+
// 'Cultivando o Crescimento',
|
|
853
|
+
// 'O Ciclo da RenovaΓ§Γ£o',
|
|
854
|
+
// 'A Γncora da Sabedoria',
|
|
855
|
+
// ],
|
|
856
|
+
// },
|
|
857
|
+
// ring: {
|
|
858
|
+
// en: [
|
|
859
|
+
// 'Magnetic Pull',
|
|
860
|
+
// 'Hidden Desires',
|
|
861
|
+
// 'The Fated Dance',
|
|
862
|
+
// 'Unveiling the Shadows',
|
|
863
|
+
// 'Your Love Power',
|
|
864
|
+
// ],
|
|
865
|
+
// pt: [
|
|
866
|
+
// 'ForΓ§a MagnΓ©tica',
|
|
867
|
+
// 'Desejos Ocultos',
|
|
868
|
+
// 'A DanΓ§a do Destino',
|
|
869
|
+
// 'Revelando as Sombras',
|
|
870
|
+
// 'Seu Poder no Amor',
|
|
871
|
+
// ],
|
|
872
|
+
// },
|
|
873
|
+
// lantern: {
|
|
874
|
+
// en: [
|
|
875
|
+
// 'The Call to Awakening',
|
|
876
|
+
// 'Walking Through Shadows',
|
|
877
|
+
// 'The Inner Flame',
|
|
878
|
+
// 'Revelations and Truths',
|
|
879
|
+
// 'Becoming the Light',
|
|
880
|
+
// ],
|
|
881
|
+
// pt: [
|
|
882
|
+
// 'O Chamado para o Despertar',
|
|
883
|
+
// 'Caminhando Pelas Sombras',
|
|
884
|
+
// 'A Chama Interior',
|
|
885
|
+
// 'RevelaΓ§Γ΅es e Verdades',
|
|
886
|
+
// 'Tornando-se a Luz',
|
|
887
|
+
// ],
|
|
888
|
+
// },
|
|
889
|
+
// orb: {
|
|
890
|
+
// en: [
|
|
891
|
+
// 'The Path Unfolding',
|
|
892
|
+
// 'A Calling Beyond the Self',
|
|
893
|
+
// 'Turning Points of Fate',
|
|
894
|
+
// 'Mastering the Journey',
|
|
895
|
+
// 'Legacy and Impact',
|
|
896
|
+
// ],
|
|
897
|
+
// pt: [
|
|
898
|
+
// 'O Caminho que se Revela',
|
|
899
|
+
// 'Um Chamado AlΓ©m de Si Mesmo',
|
|
900
|
+
// 'Pontos de Virada do Destino',
|
|
901
|
+
// 'Dominando a Jornada',
|
|
902
|
+
// 'Legado e Impacto',
|
|
903
|
+
// ],
|
|
904
|
+
// },
|
|
905
|
+
// };
|
|
906
|
+
|
|
907
|
+
// const sectionsEN = conceptSections[conceptSlug]?.en;
|
|
908
|
+
// const sectionsPT = conceptSections[conceptSlug]?.pt;
|
|
909
|
+
|
|
910
|
+
// if (!sectionsEN || !sectionsPT) {
|
|
911
|
+
// throw new Error(`Unknown concept: ${conceptSlug}`);
|
|
912
|
+
// }
|
|
913
|
+
|
|
914
|
+
// // β
Step 3: Detect if AI interleaved EN/PT sections
|
|
915
|
+
// const interleavedMatches = cleanedResponse.match(
|
|
916
|
+
// /(EN:\s*[\s\S]+?PT:\s*[\s\S]+?)(?=EN:|$)/
|
|
917
|
+
// );
|
|
918
|
+
// let extractedEN = '';
|
|
919
|
+
// let extractedPT = '';
|
|
920
|
+
|
|
921
|
+
// if (interleavedMatches) {
|
|
922
|
+
// console.warn(
|
|
923
|
+
// 'β οΈ Detected interleaved EN/PT sections. Parsing accordingly.'
|
|
924
|
+
// );
|
|
925
|
+
// extractedEN = interleavedMatches
|
|
926
|
+
// .map(
|
|
927
|
+
// (pair) => pair.match(/EN:\s*([\s\S]+?)\s*PT:/)?.[1]?.trim() || ''
|
|
928
|
+
// )
|
|
929
|
+
// .join('\n\n');
|
|
930
|
+
// extractedPT = interleavedMatches
|
|
931
|
+
// .map((pair) => pair.match(/PT:\s*([\s\S]+)/)?.[1]?.trim() || '')
|
|
932
|
+
// .join('\n\n');
|
|
933
|
+
// } else {
|
|
934
|
+
// console.log(
|
|
935
|
+
// 'β
Standard format detected (EN block followed by PT block).'
|
|
936
|
+
// );
|
|
937
|
+
// extractedEN =
|
|
938
|
+
// cleanedResponse.match(/EN:\s*([\s\S]+?)\s*PT:/)?.[1]?.trim() || '';
|
|
939
|
+
// extractedPT =
|
|
940
|
+
// cleanedResponse.match(/PT:\s*([\s\S]+)/)?.[1]?.trim() || '';
|
|
941
|
+
// }
|
|
942
|
+
|
|
943
|
+
// console.log(
|
|
944
|
+
// 'β
[MATCH SUCCESS] Extracted English Content:',
|
|
945
|
+
// extractedEN.slice(0, 500) + '...'
|
|
946
|
+
// );
|
|
947
|
+
// console.log(
|
|
948
|
+
// 'β
[MATCH SUCCESS] Extracted Portuguese Content:',
|
|
949
|
+
// extractedPT.slice(0, 500) + '...'
|
|
950
|
+
// );
|
|
951
|
+
|
|
952
|
+
// // β
Step 4: Extract structured sections dynamically
|
|
953
|
+
// function extractSections(text: string, sectionTitles: string[]) {
|
|
954
|
+
// return sectionTitles.map((title) => {
|
|
955
|
+
// console.log(`π [PROCESSING] Extracting section: "${title}"`);
|
|
956
|
+
|
|
957
|
+
// const regex = new RegExp(
|
|
958
|
+
// `\\d+\\.\\s*${title}:\\s*([\\s\\S]+?)(?=\\n\\d+\\.\\s*[A-Z]|$)`
|
|
959
|
+
// );
|
|
960
|
+
// const match = text.match(regex);
|
|
961
|
+
|
|
962
|
+
// if (!match) {
|
|
963
|
+
// throw new Error(`β Missing section: "${title}"`);
|
|
964
|
+
// }
|
|
965
|
+
|
|
966
|
+
// const paragraphs = match[1]
|
|
967
|
+
// .trim()
|
|
968
|
+
// .split(/\n{2,}/)
|
|
969
|
+
// .map((p) => p.trim())
|
|
970
|
+
// .filter((p) => p.length > 0);
|
|
971
|
+
|
|
972
|
+
// console.log(
|
|
973
|
+
// `β
[EXTRACTED] "${title}" - Parsed Content:`,
|
|
974
|
+
// paragraphs
|
|
975
|
+
// );
|
|
976
|
+
// return { type: 'section', title, content: paragraphs };
|
|
977
|
+
// });
|
|
978
|
+
// }
|
|
979
|
+
|
|
980
|
+
// const structuredContentEN = extractSections(extractedEN, sectionsEN);
|
|
981
|
+
// const structuredContentPT = extractSections(extractedPT, sectionsPT);
|
|
982
|
+
|
|
983
|
+
// console.log(
|
|
984
|
+
// 'π― [FINAL RESULT] Parsed English Content:',
|
|
985
|
+
// JSON.stringify(structuredContentEN, null, 2)
|
|
986
|
+
// );
|
|
987
|
+
// console.log(
|
|
988
|
+
// 'π― [FINAL RESULT] Parsed Portuguese Content:',
|
|
989
|
+
// JSON.stringify(structuredContentPT, null, 2)
|
|
990
|
+
// );
|
|
991
|
+
|
|
992
|
+
// console.log(
|
|
993
|
+
// 'β
[COMPLETE] Structured content parsing finished successfully.'
|
|
994
|
+
// );
|
|
995
|
+
|
|
996
|
+
// return {
|
|
997
|
+
// structuredContentEN,
|
|
998
|
+
// structuredContentPT,
|
|
999
|
+
// };
|
|
1000
|
+
// } catch (error) {
|
|
1001
|
+
// console.error(`β Parsing failed for ${conceptSlug}. Logging failure.`);
|
|
1002
|
+
|
|
1003
|
+
// // β
Store failure in KV for debugging
|
|
1004
|
+
// const kvFailuresStore = this.context.kvConceptFailuresStore();
|
|
1005
|
+
// const failureKey = `failures:content:parsing:${conceptSlug}:${new Date().toISOString()}`;
|
|
1006
|
+
|
|
1007
|
+
// await kvFailuresStore.put(
|
|
1008
|
+
// failureKey,
|
|
1009
|
+
// JSON.stringify({
|
|
1010
|
+
// error: (error as Error).message,
|
|
1011
|
+
// conceptSlug,
|
|
1012
|
+
// cleanedResponse,
|
|
1013
|
+
// timestamp: new Date().toISOString(),
|
|
1014
|
+
// })
|
|
1015
|
+
// );
|
|
1016
|
+
|
|
1017
|
+
// console.error(`π¨ Failure logged in KV: ${failureKey}`);
|
|
1018
|
+
// throw error;
|
|
1019
|
+
// }
|
|
1020
|
+
// }
|
|
1021
|
+
|
|
787
1022
|
// private parseStructuredContent(
|
|
788
1023
|
// conceptSlug: Concept,
|
|
789
1024
|
// response: string
|