@zodic/shared 0.0.365 → 0.0.367

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.
@@ -1529,30 +1529,22 @@ export class ConceptService {
1529
1529
  }
1530
1530
 
1531
1531
  async generateAstroReportContent(
1532
- params:
1533
- | AstroReportParams
1534
- | { entityType: string; name: string; override?: boolean },
1532
+ params: AstroReportParams | { entityType: string; name: string; override?: boolean },
1535
1533
  override: boolean = false
1536
1534
  ): Promise<void> {
1537
1535
  console.log(
1538
- `🚀 Generating content for ${JSON.stringify(
1539
- params
1540
- )}, override: ${override}`
1536
+ `🚀 Generating content for ${JSON.stringify(params)}, override: ${override}`
1541
1537
  );
1542
-
1538
+
1543
1539
  const db = drizzle(this.context.env.DB);
1544
1540
  let table, whereClause;
1545
-
1541
+
1546
1542
  let report: AstroReportRow | DescriptionTemplateRow;
1547
1543
  const isReport = 'reportType' in params;
1548
-
1544
+
1549
1545
  if (isReport) {
1550
- // Handle existing report types (sign, house, signInHouse, aspect, feature)
1551
1546
  switch (params.reportType) {
1552
1547
  case 'sign':
1553
- console.log(
1554
- `🗂️ Handling sign report: type=${params.type}, pointName=${params.pointName}, sign=${params.sign}`
1555
- );
1556
1548
  table = astroReports;
1557
1549
  whereClause = and(
1558
1550
  eq(astroReports.name, params.pointName),
@@ -1561,9 +1553,6 @@ export class ConceptService {
1561
1553
  );
1562
1554
  break;
1563
1555
  case 'house':
1564
- console.log(
1565
- `🗂️ Handling house report: type=${params.type}, pointName=${params.pointName}, house=${params.house}`
1566
- );
1567
1556
  table = astroReports;
1568
1557
  whereClause = and(
1569
1558
  eq(astroReports.name, params.pointName),
@@ -1572,9 +1561,6 @@ export class ConceptService {
1572
1561
  );
1573
1562
  break;
1574
1563
  case 'signInHouse':
1575
- console.log(
1576
- `🗂️ Handling signInHouse report: sign=${params.sign}, houseNumber=${params.houseNumber}`
1577
- );
1578
1564
  table = houseReports;
1579
1565
  whereClause = and(
1580
1566
  eq(houseReports.sign, params.sign),
@@ -1582,9 +1568,6 @@ export class ConceptService {
1582
1568
  );
1583
1569
  break;
1584
1570
  case 'aspect':
1585
- console.log(
1586
- `🗂️ Handling aspect report: aspectingPlanet=${params.aspectingPlanet}, aspectedPlanet=${params.aspectedPlanet}, aspectingType=${params.aspectingType}`
1587
- );
1588
1571
  table = aspectReports;
1589
1572
  whereClause = and(
1590
1573
  eq(aspectReports.aspectingPlanet, params.aspectingPlanet),
@@ -1593,11 +1576,6 @@ export class ConceptService {
1593
1576
  );
1594
1577
  break;
1595
1578
  case 'feature':
1596
- console.log(
1597
- `🗂️ Handling feature report: featureType=${
1598
- params.featureType
1599
- }, ${JSON.stringify(params)}`
1600
- );
1601
1579
  table = astroFeatureReports;
1602
1580
  let nameValue: string;
1603
1581
  if (params.featureType === 'element') {
@@ -1609,25 +1587,18 @@ export class ConceptService {
1609
1587
  break;
1610
1588
  case 'pure':
1611
1589
  if (!('dominantElement' in params))
1612
- throw new Error(
1613
- 'Missing dominantElement for pure element report'
1614
- );
1590
+ throw new Error('Missing dominantElement for pure element report');
1615
1591
  nameValue = params.dominantElement;
1616
1592
  break;
1617
1593
  case 'preponderant_lacking':
1618
- if (
1619
- !('dominantElement' in params) ||
1620
- !('lackingElement' in params)
1621
- )
1594
+ if (!('dominantElement' in params) || !('lackingElement' in params))
1622
1595
  throw new Error(
1623
1596
  'Missing dominantElement or lackingElement for preponderant_lacking element report'
1624
1597
  );
1625
1598
  nameValue = `${params.dominantElement}-${params.lackingElement}`;
1626
1599
  break;
1627
1600
  default:
1628
- throw new Error(
1629
- `Unknown element subtype: ${(params as any).subtype}`
1630
- );
1601
+ throw new Error(`Unknown element subtype: ${(params as any).subtype}`);
1631
1602
  }
1632
1603
  } else {
1633
1604
  if (!('name' in params))
@@ -1648,10 +1619,6 @@ export class ConceptService {
1648
1619
  .where(whereClause)
1649
1620
  .get()) as AstroReportRow;
1650
1621
  } else {
1651
- // Handle description templates
1652
- console.log(
1653
- `🗂️ Handling description template: entityType=${params.entityType}, name=${params.name}`
1654
- );
1655
1622
  table = astroDescriptionTemplates;
1656
1623
  whereClause = and(
1657
1624
  eq(astroDescriptionTemplates.entityType, params.entityType),
@@ -1663,24 +1630,21 @@ export class ConceptService {
1663
1630
  .where(whereClause)
1664
1631
  .get()) as DescriptionTemplateRow;
1665
1632
  }
1666
-
1633
+
1667
1634
  const id = report.id;
1668
1635
  const hasContent = isReport
1669
- ? (report as AstroReportRow).enReport &&
1670
- (report as AstroReportRow).ptReport
1671
- : (report as DescriptionTemplateRow).enDescription !==
1672
- 'Pending generation' &&
1673
- (report as DescriptionTemplateRow).ptDescription !==
1674
- 'Pendente de geração';
1675
-
1636
+ ? (report as AstroReportRow).enReport && (report as AstroReportRow).ptReport
1637
+ : (report as DescriptionTemplateRow).enDescription !== 'Pending generation' &&
1638
+ (report as DescriptionTemplateRow).ptDescription !== 'Pendente de geração';
1639
+
1676
1640
  if (!override && hasContent) {
1677
1641
  console.log(`⚡ Content already exists for ${id}, skipping`);
1678
1642
  return;
1679
1643
  }
1680
-
1644
+
1681
1645
  let attempts = 0;
1682
1646
  const maxAttempts = 2;
1683
-
1647
+
1684
1648
  while (attempts < maxAttempts) {
1685
1649
  let phase = 'generation';
1686
1650
  try {
@@ -1690,7 +1654,7 @@ export class ConceptService {
1690
1654
  isReport ? 'report' : 'description'
1691
1655
  } content for ${id}...`
1692
1656
  );
1693
-
1657
+
1694
1658
  let messages: ChatMessages;
1695
1659
  if (isReport) {
1696
1660
  messages = this.context
@@ -1701,7 +1665,7 @@ export class ConceptService {
1701
1665
  } else {
1702
1666
  messages = this.generateDescriptionMessages(params);
1703
1667
  }
1704
-
1668
+
1705
1669
  console.log(`📨 Sending messages to AI: ${JSON.stringify(messages)}`);
1706
1670
  const response = await this.context
1707
1671
  .api()
@@ -1709,20 +1673,19 @@ export class ConceptService {
1709
1673
  if (!response) {
1710
1674
  throw new Error(`❌ AI returned an empty response for ${id}`);
1711
1675
  }
1712
-
1676
+
1713
1677
  phase = 'parsing';
1714
1678
  console.log(
1715
1679
  `📝 Received AI response for ${id}: ${response.slice(0, 200)}${
1716
1680
  response.length > 200 ? '...' : ''
1717
1681
  }`
1718
1682
  );
1719
-
1683
+
1720
1684
  if (isReport) {
1721
- const { enReport, ptReport } =
1722
- await this.parseAstrologicalReportContent(
1723
- (params as AstroReportParams).reportType,
1724
- response
1725
- );
1685
+ const { enReport, ptReport } = await this.parseAstrologicalReportContent(
1686
+ (params as AstroReportParams).reportType,
1687
+ response
1688
+ );
1726
1689
  console.log(`💾 Storing report content for ${id}`);
1727
1690
  await db
1728
1691
  .update(table as any)
@@ -1733,8 +1696,7 @@ export class ConceptService {
1733
1696
  .where(eq((table as any).id, id))
1734
1697
  .run();
1735
1698
  } else {
1736
- const { enDescription, ptDescription } =
1737
- await this.parseDescriptionContent(response);
1699
+ const { enDescription, ptDescription } = await this.parseDescriptionContent(response);
1738
1700
  console.log(`💾 Storing description content for ${id}`);
1739
1701
  await db
1740
1702
  .update(table as any)
@@ -1745,17 +1707,16 @@ export class ConceptService {
1745
1707
  .where(eq(astroDescriptionTemplates.id, id))
1746
1708
  .run();
1747
1709
  }
1748
-
1710
+
1749
1711
  console.log(
1750
1712
  `✅ ${isReport ? 'Report' : 'Description'} content stored for ${id}`
1751
1713
  );
1752
1714
  return;
1753
1715
  } catch (error) {
1754
1716
  console.error(
1755
- `❌ Attempt ${attempts} failed at phase: ${phase} for ${id}`,
1756
- (error as Error).message
1717
+ `❌ Attempt ${attempts} failed at phase: ${phase} for ${id}: ${(error as Error).message}`
1757
1718
  );
1758
-
1719
+
1759
1720
  const failureKey = `failures:astro:${
1760
1721
  isReport ? (params as AstroReportParams).reportType : 'description'
1761
1722
  }:${id}`;
@@ -1769,7 +1730,7 @@ export class ConceptService {
1769
1730
  timestamp: new Date().toISOString(),
1770
1731
  })
1771
1732
  );
1772
-
1733
+
1773
1734
  if (attempts >= maxAttempts) {
1774
1735
  console.error(
1775
1736
  `🚨 All ${maxAttempts} attempts failed at phase ${phase} for ${id}`
@@ -1780,7 +1741,7 @@ export class ConceptService {
1780
1741
  } for ${id} after ${maxAttempts} attempts`
1781
1742
  );
1782
1743
  }
1783
-
1744
+
1784
1745
  console.log('🔁 Retrying...');
1785
1746
  await new Promise((resolve) => setTimeout(resolve, 2000));
1786
1747
  }
@@ -1901,47 +1862,58 @@ export class ConceptService {
1901
1862
 
1902
1863
  private parseAstrologicalReport(response: string): AstrologicalReport[] {
1903
1864
  console.log('📌 Starting parseAstrologicalReport');
1904
-
1905
- const reports: AstrologicalReport[] = [];
1865
+
1866
+ // Step 1: Strip any unexpected introductory text
1867
+ const prefixRegex = /^[\s\S]*?(?=--\s*(EN|PT)\s*\n)/;
1868
+ response = response.replace(prefixRegex, '').trim();
1869
+ console.log(`📝 Cleaned response:\n${response.slice(0, 200)}${response.length > 200 ? '...' : ''}`);
1870
+
1871
+ // Step 2: Split into language sections
1906
1872
  const languageSections = response
1907
1873
  .split(/--\s*(EN|PT)\s*\n/)
1908
1874
  .filter(Boolean);
1909
1875
  console.log(`🔍 Split response into ${languageSections.length} sections`);
1910
-
1876
+
1877
+ // Validate: Expect exactly 4 parts (EN, content, PT, content)
1878
+ if (languageSections.length !== 4) {
1879
+ throw new Error(
1880
+ `Invalid response format: Expected exactly 2 sections (EN and PT), got ${languageSections.length / 2}`
1881
+ );
1882
+ }
1883
+
1884
+ const reports: AstrologicalReport[] = [];
1885
+
1911
1886
  for (let i = 0; i < languageSections.length; i += 2) {
1912
1887
  const language = languageSections[i].trim();
1913
1888
  const content = languageSections[i + 1]?.trim();
1914
1889
  console.log(`🚀 Processing section ${i / 2 + 1}: Language = ${language}`);
1915
-
1890
+
1916
1891
  if (!content || !language.match(/^(EN|PT)$/)) {
1917
- console.warn(
1918
- `⚠️ Skipping section ${
1919
- i / 2 + 1
1920
- }: Invalid content or language (${language})`
1892
+ throw new Error(
1893
+ `Invalid section ${i / 2 + 1}: Language must be "EN" or "PT", got "${language}"`
1921
1894
  );
1922
- continue;
1923
1895
  }
1924
-
1896
+
1925
1897
  console.log(
1926
1898
  `📜 Raw content for ${language}:\n${content.slice(0, 500)}${
1927
1899
  content.length > 500 ? '...' : ''
1928
1900
  }`
1929
1901
  );
1930
-
1902
+
1931
1903
  const report: AstrologicalReport = {
1932
1904
  language,
1933
1905
  title: [],
1934
1906
  sections: {},
1935
1907
  };
1936
-
1908
+
1937
1909
  const lines = content.split('\n').map((line) => line.trim());
1938
1910
  console.log(`🔢 Split into ${lines.length} lines`);
1939
-
1911
+
1940
1912
  let currentSection = '';
1941
1913
  for (let j = 0; j < lines.length; j++) {
1942
1914
  const line = lines[j];
1943
1915
  console.log(`📏 Processing line ${j + 1}: "${line}"`);
1944
-
1916
+
1945
1917
  if (line.startsWith('### ') && report.title.length === 0) {
1946
1918
  const titleContent = cleanText(line.replace('### ', ''));
1947
1919
  console.log(`🏷️ Found title: "${titleContent}"`);
@@ -1951,32 +1923,43 @@ export class ConceptService {
1951
1923
  });
1952
1924
  continue;
1953
1925
  }
1954
-
1926
+
1955
1927
  if (line.startsWith('#### ')) {
1956
1928
  currentSection = cleanText(line.replace('#### ', '').trim());
1957
1929
  console.log(`🗂️ New section: "${currentSection}"`);
1958
- report.sections[currentSection] =
1959
- report.sections[currentSection] || [];
1930
+ report.sections[currentSection] = report.sections[currentSection] || [];
1960
1931
  continue;
1961
1932
  }
1962
-
1933
+
1963
1934
  if (!line || !currentSection) {
1964
1935
  console.log(
1965
1936
  `⏩ Skipping line: ${!line ? 'Empty' : 'No current section'}`
1966
1937
  );
1967
1938
  continue;
1968
1939
  }
1969
-
1940
+
1941
+ // Validate language consistency
1942
+ const hasPortuguese = /[ãáâéêíóôõúç]/i.test(line);
1943
+ const hasEnglish = /[a-zA-Z]/.test(line); // Fixed regex to check for English letters
1944
+ if (hasPortuguese && language === 'EN') {
1945
+ throw new Error(
1946
+ `Invalid content in EN section: Portuguese characters detected in "${line}"`
1947
+ );
1948
+ } else if (!hasPortuguese && language === 'PT' && !hasEnglish) {
1949
+ console.warn(
1950
+ `⚠️ Possible invalid content in PT section: No recognizable characters in "${line}"`
1951
+ );
1952
+ }
1953
+
1970
1954
  if (line.match(/^\d+\.\s/) || line.startsWith('- ')) {
1971
1955
  const listItem = line.replace(/^\d+\.\s|- /, '').trim();
1972
1956
  console.log(`🔹 Bullet point detected: "${listItem}"`);
1973
- const boldMatch = listItem.match(/^\*\*(.+?)\*\*[:\s]*(.+)?$/); // Updated to allow standalone bold
1974
-
1957
+ const boldMatch = listItem.match(/^\*\*(.+?)\*\*[:\s]*(.+)?$/);
1958
+
1975
1959
  if (boldMatch) {
1976
1960
  const title = cleanText(boldMatch[1]);
1977
1961
  let content = boldMatch[2] ? cleanText(boldMatch[2]) : '';
1978
-
1979
- // Look ahead for content if none on the same line
1962
+
1980
1963
  if (!content && j + 1 < lines.length) {
1981
1964
  const nextLine = lines[j + 1];
1982
1965
  console.log(`🔍 Checking next line for content: "${nextLine}"`);
@@ -1988,10 +1971,10 @@ export class ConceptService {
1988
1971
  ) {
1989
1972
  content = cleanText(nextLine);
1990
1973
  console.log(`✅ Found content in next line: "${content}"`);
1991
- j++; // Skip the next line since we’ve consumed it
1974
+ j++;
1992
1975
  }
1993
1976
  }
1994
-
1977
+
1995
1978
  console.log(
1996
1979
  `✅ Bold match found - Title: "${title}", Content: "${
1997
1980
  content || 'None'
@@ -2000,7 +1983,7 @@ export class ConceptService {
2000
1983
  report.sections[currentSection].push({
2001
1984
  type: 'bullet-point',
2002
1985
  title,
2003
- content: content || '', // Empty string if no content found
1986
+ content: content || '',
2004
1987
  });
2005
1988
  } else {
2006
1989
  console.log(`❌ No bold match - Full content: "${listItem}"`);
@@ -2011,14 +1994,14 @@ export class ConceptService {
2011
1994
  }
2012
1995
  continue;
2013
1996
  }
2014
-
1997
+
2015
1998
  console.log(`📝 Paragraph: "${line}"`);
2016
1999
  report.sections[currentSection].push({
2017
2000
  type: 'p',
2018
2001
  content: cleanText(line),
2019
2002
  });
2020
2003
  }
2021
-
2004
+
2022
2005
  console.log(
2023
2006
  `🏁 Finished parsing ${language} section. Result:\n${JSON.stringify(
2024
2007
  report,
@@ -2028,7 +2011,7 @@ export class ConceptService {
2028
2011
  );
2029
2012
  reports.push(report);
2030
2013
  }
2031
-
2014
+
2032
2015
  console.log(`🎉 Parsing complete. Total reports: ${reports.length}`);
2033
2016
  return reports;
2034
2017
  }
package/db/schema.ts CHANGED
@@ -168,11 +168,11 @@ export const astroAspects = sqliteTable(
168
168
  userId: text('user_id')
169
169
  .notNull()
170
170
  .references(() => users.id, { onDelete: 'cascade' }), // Links to the user
171
- aspectingPlanet: text('aspecting_planet').notNull(), // E.g., "Sun"
172
- aspectedPlanet: text('aspected_planet').notNull(), // E.g., "Mercury"
173
- aspectingPlanetId: integer('aspecting_planet_id').notNull(), // Index of the aspecting planet
174
- aspectedPlanetId: integer('aspected_planet_id').notNull(), // Index of the aspected planet
175
- type: text('type').notNull(), // E.g., "Conjunction", "Square"
171
+ aspectingPlanet: text('aspecting_planet').notNull(), // E.g., "sun" - planets + ascendant and midheaven at first
172
+ aspectedPlanet: text('aspected_planet').notNull(), // E.g., "mercury"
173
+ aspectingPlanetId: integer('aspecting_planet_id').notNull(), // Index of the aspecting planet in the api's list (0 for sun, 1 for moon)
174
+ aspectedPlanetId: integer('aspected_planet_id').notNull(), // Index of the aspected planet, same as aspecting id
175
+ type: text('type').notNull(), // E.g., "conjunction", "square"
176
176
  orb: real('orb').notNull(), // Orb value
177
177
  diff: real('diff').notNull(), // Difference value
178
178
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zodic/shared",
3
- "version": "0.0.365",
3
+ "version": "0.0.367",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -137,20 +137,35 @@ export const astroPrompts = {
137
137
  aspectingType,
138
138
  }: AspectParams): string => {
139
139
  return `
140
- Make me a report for the astrological aspect: ${
140
+ Generate a report for the astrological aspect: ${
141
141
  pointNameMap[aspectingPlanet] || aspectingPlanet
142
142
  } ${aspectingType} ${pointNameMap[aspectedPlanet] || aspectedPlanet}.
143
- The response should include an English version and a Portuguese version.
144
- Use "###" for titles and "####" for subtitles.
145
- The response format should be exactly like this:
146
-
143
+ The response must include an English version (EN) and a Portuguese version (PT).
144
+ Use "###" for titles and "####" for subtitles. Do not use the "—" character.
145
+ Do not include any introductory text like "Here’s your report" or similar phrases.
146
+ The response must start with "-- EN" and follow this exact format:
147
+
147
148
  -- EN
148
-
149
- [English Report - structured with headings, sections, and detailed explanations]
150
-
149
+
150
+ ### Title of the Report
151
+
152
+ #### Section 1
153
+ - **Bold Item 1**: Content for item 1.
154
+ - **Bold Item 2**: Content for item 2.
155
+
156
+ #### Section 2
157
+ Paragraph content here.
158
+
151
159
  -- PT
152
-
153
- [Portuguese Report - structured exactly the same as the English version]
160
+
161
+ ### Título do Relatório
162
+
163
+ #### Seção 1
164
+ - **Item em Negrito 1**: Conteúdo do item 1.
165
+ - **Item em Negrito 2**: Conteúdo do item 2.
166
+
167
+ #### Seção 2
168
+ Conteúdo do parágrafo aqui.
154
169
  `.trim();
155
170
  },
156
171