@zodic/shared 0.0.248 → 0.0.250
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 +197 -161
- package/db/schema.ts +41 -16
- package/package.json +1 -1
- package/types/scopes/generic.ts +25 -0
- package/utils/astroPrompts/index.ts +26 -0
- package/utils/buildMessages.ts +84 -0
|
@@ -10,6 +10,7 @@ import { v4 as uuidv4 } from 'uuid';
|
|
|
10
10
|
import { cleanText, schema } from '../..';
|
|
11
11
|
import {
|
|
12
12
|
aspectReports,
|
|
13
|
+
astroDescriptionTemplates,
|
|
13
14
|
astroFeatureReports,
|
|
14
15
|
astroReports,
|
|
15
16
|
conceptsData,
|
|
@@ -18,8 +19,11 @@ import {
|
|
|
18
19
|
import {
|
|
19
20
|
AstrologicalReport,
|
|
20
21
|
AstroReportParams,
|
|
22
|
+
AstroReportRow,
|
|
23
|
+
ChatMessages,
|
|
21
24
|
Concept,
|
|
22
25
|
ControlNetConfig,
|
|
26
|
+
DescriptionTemplateRow,
|
|
23
27
|
Languages,
|
|
24
28
|
} from '../../types';
|
|
25
29
|
import {
|
|
@@ -27,6 +31,7 @@ import {
|
|
|
27
31
|
sizes,
|
|
28
32
|
StructuredConceptContent,
|
|
29
33
|
} from '../../types/scopes/legacy';
|
|
34
|
+
import { astroPrompts } from '../../utils/astroPrompts';
|
|
30
35
|
import { leonardoInitImages } from '../../utils/initImages';
|
|
31
36
|
import { buildConceptKVKey } from '../../utils/KVKeysBuilders';
|
|
32
37
|
import { AppContext } from '../base/AppContext';
|
|
@@ -2006,132 +2011,105 @@ export class ConceptService {
|
|
|
2006
2011
|
console.log(`✅ Stored duplicate keys for ${conceptSlug} in cache.`);
|
|
2007
2012
|
}
|
|
2008
2013
|
|
|
2009
|
-
// ? ASTRO PROCESSING
|
|
2010
2014
|
async generateAstroReportContent(
|
|
2011
|
-
params: AstroReportParams,
|
|
2015
|
+
params: AstroReportParams | { entityType: string; name: string; override?: boolean },
|
|
2012
2016
|
override: boolean = false
|
|
2013
2017
|
): Promise<void> {
|
|
2014
|
-
console.log(
|
|
2015
|
-
`🚀 Generating report content for ${JSON.stringify(
|
|
2016
|
-
params
|
|
2017
|
-
)}, override: ${override}`
|
|
2018
|
-
);
|
|
2018
|
+
console.log(`🚀 Generating content for ${JSON.stringify(params)}, override: ${override}`);
|
|
2019
2019
|
|
|
2020
2020
|
const db = drizzle(this.context.env.DB);
|
|
2021
2021
|
let table, whereClause;
|
|
2022
2022
|
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
`🗂️ Handling
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
nameValue = 'balanced';
|
|
2089
|
-
break;
|
|
2090
|
-
case 'pure':
|
|
2091
|
-
if (!('dominantElement' in params))
|
|
2092
|
-
throw new Error(
|
|
2093
|
-
'Missing dominantElement for pure element report'
|
|
2094
|
-
);
|
|
2095
|
-
nameValue = params.dominantElement;
|
|
2096
|
-
break;
|
|
2097
|
-
case 'preponderant_lacking':
|
|
2098
|
-
if (
|
|
2099
|
-
!('dominantElement' in params) ||
|
|
2100
|
-
!('lackingElement' in params)
|
|
2101
|
-
)
|
|
2102
|
-
throw new Error(
|
|
2103
|
-
'Missing dominantElement or lackingElement for preponderant_lacking element report'
|
|
2104
|
-
);
|
|
2105
|
-
nameValue = `${params.dominantElement}-${params.lackingElement}`;
|
|
2106
|
-
break;
|
|
2107
|
-
default:
|
|
2108
|
-
throw new Error(`Unknown element subtype: ${params}`);
|
|
2023
|
+
let report: AstroReportRow | DescriptionTemplateRow;
|
|
2024
|
+
const isReport = "reportType" in params;
|
|
2025
|
+
|
|
2026
|
+
if (isReport) {
|
|
2027
|
+
// Handle existing report types (sign, house, signInHouse, aspect, feature)
|
|
2028
|
+
switch (params.reportType) {
|
|
2029
|
+
case "sign":
|
|
2030
|
+
console.log(`🗂️ Handling sign report: type=${params.type}, pointName=${params.pointName}, sign=${params.sign}`);
|
|
2031
|
+
table = astroReports;
|
|
2032
|
+
whereClause = and(
|
|
2033
|
+
eq(astroReports.name, params.pointName),
|
|
2034
|
+
eq(astroReports.sign, params.sign),
|
|
2035
|
+
isNull(astroReports.house)
|
|
2036
|
+
);
|
|
2037
|
+
break;
|
|
2038
|
+
case "house":
|
|
2039
|
+
console.log(`🗂️ Handling house report: type=${params.type}, pointName=${params.pointName}, house=${params.house}`);
|
|
2040
|
+
table = astroReports;
|
|
2041
|
+
whereClause = and(
|
|
2042
|
+
eq(astroReports.name, params.pointName),
|
|
2043
|
+
eq(astroReports.house, params.house),
|
|
2044
|
+
isNull(astroReports.sign)
|
|
2045
|
+
);
|
|
2046
|
+
break;
|
|
2047
|
+
case "signInHouse":
|
|
2048
|
+
console.log(`🗂️ Handling signInHouse report: sign=${params.sign}, houseNumber=${params.houseNumber}`);
|
|
2049
|
+
table = houseReports;
|
|
2050
|
+
whereClause = and(
|
|
2051
|
+
eq(houseReports.sign, params.sign),
|
|
2052
|
+
eq(houseReports.house, params.houseNumber)
|
|
2053
|
+
);
|
|
2054
|
+
break;
|
|
2055
|
+
case "aspect":
|
|
2056
|
+
console.log(`🗂️ Handling aspect report: aspectingPlanet=${params.aspectingPlanet}, aspectedPlanet=${params.aspectedPlanet}, aspectingType=${params.aspectingType}`);
|
|
2057
|
+
table = aspectReports;
|
|
2058
|
+
whereClause = and(
|
|
2059
|
+
eq(aspectReports.aspectingPlanet, params.aspectingPlanet),
|
|
2060
|
+
eq(aspectReports.aspectedPlanet, params.aspectedPlanet),
|
|
2061
|
+
eq(aspectReports.aspect, params.aspectingType)
|
|
2062
|
+
);
|
|
2063
|
+
break;
|
|
2064
|
+
case "feature":
|
|
2065
|
+
console.log(`🗂️ Handling feature report: featureType=${params.featureType}, ${JSON.stringify(params)}`);
|
|
2066
|
+
table = astroFeatureReports;
|
|
2067
|
+
let nameValue: string;
|
|
2068
|
+
if (params.featureType === "element") {
|
|
2069
|
+
if (!("subtype" in params)) throw new Error("Missing subtype for element feature report");
|
|
2070
|
+
switch (params.subtype) {
|
|
2071
|
+
case "balanced":
|
|
2072
|
+
nameValue = "balanced";
|
|
2073
|
+
break;
|
|
2074
|
+
case "pure":
|
|
2075
|
+
if (!("dominantElement" in params)) throw new Error("Missing dominantElement for pure element report");
|
|
2076
|
+
nameValue = params.dominantElement;
|
|
2077
|
+
break;
|
|
2078
|
+
case "preponderant_lacking":
|
|
2079
|
+
if (!("dominantElement" in params) || !("lackingElement" in params)) throw new Error("Missing dominantElement or lackingElement for preponderant_lacking element report");
|
|
2080
|
+
nameValue = `${params.dominantElement}-${params.lackingElement}`;
|
|
2081
|
+
break;
|
|
2082
|
+
default:
|
|
2083
|
+
throw new Error(`Unknown element subtype: ${(params as any).subtype}`);
|
|
2084
|
+
}
|
|
2085
|
+
} else {
|
|
2086
|
+
if (!("name" in params)) throw new Error("Missing name for non-element feature report");
|
|
2087
|
+
nameValue = params.name;
|
|
2109
2088
|
}
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
if (!report) {
|
|
2129
|
-
console.error(`❌ No report found for ${JSON.stringify(params)}`);
|
|
2130
|
-
throw new Error(`❌ No report found for ${JSON.stringify(params)}`);
|
|
2089
|
+
whereClause = and(
|
|
2090
|
+
eq(astroFeatureReports.featureType, params.featureType),
|
|
2091
|
+
eq(astroFeatureReports.name, nameValue)
|
|
2092
|
+
);
|
|
2093
|
+
break;
|
|
2094
|
+
default:
|
|
2095
|
+
throw new Error(`Unknown report type: ${(params as any).reportType}`);
|
|
2096
|
+
}
|
|
2097
|
+
report = await db.select().from(table as any).where(whereClause).get() as AstroReportRow;
|
|
2098
|
+
} else {
|
|
2099
|
+
// Handle description templates
|
|
2100
|
+
console.log(`🗂️ Handling description template: entityType=${params.entityType}, name=${params.name}`);
|
|
2101
|
+
table = astroDescriptionTemplates;
|
|
2102
|
+
whereClause = and(
|
|
2103
|
+
eq(astroDescriptionTemplates.entityType, params.entityType),
|
|
2104
|
+
eq(astroDescriptionTemplates.name, params.name)
|
|
2105
|
+
);
|
|
2106
|
+
report = await db.select().from(table as any).where(whereClause).get() as DescriptionTemplateRow;
|
|
2131
2107
|
}
|
|
2132
2108
|
|
|
2133
2109
|
const id = report.id;
|
|
2134
|
-
const hasContent =
|
|
2110
|
+
const hasContent = isReport
|
|
2111
|
+
? (report as AstroReportRow).enReport && (report as AstroReportRow).ptReport
|
|
2112
|
+
: (report as DescriptionTemplateRow).enDescription && (report as DescriptionTemplateRow).ptDescription;
|
|
2135
2113
|
|
|
2136
2114
|
if (!override && hasContent) {
|
|
2137
2115
|
console.log(`⚡ Content already exists for ${id}, skipping`);
|
|
@@ -2142,57 +2120,57 @@ export class ConceptService {
|
|
|
2142
2120
|
const maxAttempts = 2;
|
|
2143
2121
|
|
|
2144
2122
|
while (attempts < maxAttempts) {
|
|
2145
|
-
let phase =
|
|
2123
|
+
let phase = "generation";
|
|
2146
2124
|
try {
|
|
2147
2125
|
attempts++;
|
|
2148
|
-
console.log(
|
|
2149
|
-
`🔄 Attempt ${attempts} to generate report content for ${id}...`
|
|
2150
|
-
);
|
|
2126
|
+
console.log(`🔄 Attempt ${attempts} to generate ${isReport ? "report" : "description"} content for ${id}...`);
|
|
2151
2127
|
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
.generateAstroReportContent({ params });
|
|
2155
|
-
|
|
2128
|
+
let messages: ChatMessages;
|
|
2129
|
+
if (isReport) {
|
|
2130
|
+
messages = this.context.buildLLMMessages().generateAstroReportContent({ params: params as AstroReportParams });
|
|
2131
|
+
} else {
|
|
2132
|
+
messages = this.generateDescriptionMessages(params);
|
|
2133
|
+
}
|
|
2156
2134
|
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
.callTogether.single(messages, {});
|
|
2135
|
+
console.log(`📨 Sending messages to AI: ${JSON.stringify(messages)}`);
|
|
2136
|
+
const response = await this.context.api().callTogether.single(messages, {});
|
|
2160
2137
|
if (!response) {
|
|
2161
2138
|
throw new Error(`❌ AI returned an empty response for ${id}`);
|
|
2162
2139
|
}
|
|
2163
2140
|
|
|
2164
|
-
phase =
|
|
2165
|
-
console.log(
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
}
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
.
|
|
2180
|
-
.
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2141
|
+
phase = "parsing";
|
|
2142
|
+
console.log(`📝 Received AI response for ${id}: ${response.slice(0, 200)}${response.length > 200 ? "..." : ""}`);
|
|
2143
|
+
|
|
2144
|
+
if (isReport) {
|
|
2145
|
+
const { enReport, ptReport } = await this.parseAstrologicalReportContent((params as AstroReportParams).reportType, response);
|
|
2146
|
+
console.log(`💾 Storing report content for ${id}`);
|
|
2147
|
+
await db
|
|
2148
|
+
.update(table as any)
|
|
2149
|
+
.set({
|
|
2150
|
+
enReport: JSON.stringify(enReport),
|
|
2151
|
+
ptReport: JSON.stringify(ptReport),
|
|
2152
|
+
})
|
|
2153
|
+
.where(eq((table as any).id, id))
|
|
2154
|
+
.run();
|
|
2155
|
+
} else {
|
|
2156
|
+
const { enDescription, ptDescription } = await this.parseDescriptionContent(response);
|
|
2157
|
+
console.log(`💾 Storing description content for ${id}`);
|
|
2158
|
+
await db
|
|
2159
|
+
.update(table as any)
|
|
2160
|
+
.set({
|
|
2161
|
+
enDescription,
|
|
2162
|
+
ptDescription,
|
|
2163
|
+
})
|
|
2164
|
+
.where(eq(astroDescriptionTemplates.id, id))
|
|
2165
|
+
.run();
|
|
2166
|
+
}
|
|
2186
2167
|
|
|
2187
|
-
console.log(`✅ Report content stored for ${id}`);
|
|
2168
|
+
console.log(`✅ ${isReport ? "Report" : "Description"} content stored for ${id}`);
|
|
2188
2169
|
return;
|
|
2189
2170
|
} catch (error) {
|
|
2190
|
-
console.error(
|
|
2191
|
-
`❌ Attempt ${attempts} failed at phase: ${phase} for ${id}`,
|
|
2192
|
-
(error as Error).message
|
|
2193
|
-
);
|
|
2171
|
+
console.error(`❌ Attempt ${attempts} failed at phase: ${phase} for ${id}`, (error as Error).message);
|
|
2194
2172
|
|
|
2195
|
-
const failureKey = `failures:astro:${params.reportType}:${id}`;
|
|
2173
|
+
const failureKey = `failures:astro:${isReport ? (params as AstroReportParams).reportType : "description"}:${id}`;
|
|
2196
2174
|
await this.context.kvConceptFailuresStore().put(
|
|
2197
2175
|
failureKey,
|
|
2198
2176
|
JSON.stringify({
|
|
@@ -2205,20 +2183,78 @@ export class ConceptService {
|
|
|
2205
2183
|
);
|
|
2206
2184
|
|
|
2207
2185
|
if (attempts >= maxAttempts) {
|
|
2208
|
-
console.error(
|
|
2209
|
-
|
|
2210
|
-
);
|
|
2211
|
-
throw new Error(
|
|
2212
|
-
`Failed to generate content for ${id} after ${maxAttempts} attempts`
|
|
2213
|
-
);
|
|
2186
|
+
console.error(`🚨 All ${maxAttempts} attempts failed at phase ${phase} for ${id}`);
|
|
2187
|
+
throw new Error(`Failed to generate ${isReport ? "report" : "description"} for ${id} after ${maxAttempts} attempts`);
|
|
2214
2188
|
}
|
|
2215
2189
|
|
|
2216
|
-
console.log(
|
|
2190
|
+
console.log("🔁 Retrying...");
|
|
2217
2191
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2218
2192
|
}
|
|
2219
2193
|
}
|
|
2220
2194
|
}
|
|
2221
2195
|
|
|
2196
|
+
private generateDescriptionMessages({
|
|
2197
|
+
entityType,
|
|
2198
|
+
name,
|
|
2199
|
+
isGeneric,
|
|
2200
|
+
}: {
|
|
2201
|
+
entityType: string;
|
|
2202
|
+
name: string;
|
|
2203
|
+
isGeneric?: boolean;
|
|
2204
|
+
}): ChatMessages {
|
|
2205
|
+
const prompt = astroPrompts.makeIntroDescription({
|
|
2206
|
+
entityType,
|
|
2207
|
+
name,
|
|
2208
|
+
isGeneric,
|
|
2209
|
+
});
|
|
2210
|
+
return [{ role: 'user', content: prompt }];
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
async parseDescriptionContent(
|
|
2214
|
+
response: string
|
|
2215
|
+
): Promise<{ enDescription: string; ptDescription: string }> {
|
|
2216
|
+
console.log(`📌 [START] Parsing description content`);
|
|
2217
|
+
|
|
2218
|
+
try {
|
|
2219
|
+
const enMatch = response.match(/-- EN\s*([\s\S]+?)-- PT/);
|
|
2220
|
+
const ptMatch = response.match(/-- PT\s*([\s\S]+)/);
|
|
2221
|
+
|
|
2222
|
+
if (!enMatch || !ptMatch) {
|
|
2223
|
+
throw new Error(`❌ Missing EN or PT description in response`);
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
const enDescription = enMatch[1].trim();
|
|
2227
|
+
const ptDescription = ptMatch[1].trim();
|
|
2228
|
+
|
|
2229
|
+
console.log(
|
|
2230
|
+
`✅ Parsed EN description: ${enDescription.slice(0, 100)}${
|
|
2231
|
+
enDescription.length > 100 ? '...' : ''
|
|
2232
|
+
}`
|
|
2233
|
+
);
|
|
2234
|
+
console.log(
|
|
2235
|
+
`✅ Parsed PT description: ${ptDescription.slice(0, 100)}${
|
|
2236
|
+
ptDescription.length > 100 ? '...' : ''
|
|
2237
|
+
}`
|
|
2238
|
+
);
|
|
2239
|
+
|
|
2240
|
+
return { enDescription, ptDescription };
|
|
2241
|
+
} catch (error) {
|
|
2242
|
+
console.error(`❌ Parsing failed for description`);
|
|
2243
|
+
|
|
2244
|
+
const failureKey = `failures:astro:parsing:description:${new Date().toISOString()}`;
|
|
2245
|
+
await this.context.kvConceptFailuresStore().put(
|
|
2246
|
+
failureKey,
|
|
2247
|
+
JSON.stringify({
|
|
2248
|
+
error: (error as Error).message,
|
|
2249
|
+
response,
|
|
2250
|
+
timestamp: new Date().toISOString(),
|
|
2251
|
+
})
|
|
2252
|
+
);
|
|
2253
|
+
|
|
2254
|
+
throw error;
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2222
2258
|
async parseAstrologicalReportContent(
|
|
2223
2259
|
reportType: AstroReportParams['reportType'],
|
|
2224
2260
|
response: string
|
package/db/schema.ts
CHANGED
|
@@ -189,8 +189,10 @@ export const astroReports = sqliteTable(
|
|
|
189
189
|
name: text('name').notNull(), // Slug (e.g., 'sun', 'pars_fortuna')
|
|
190
190
|
sign: text('sign'), // Slug (e.g., 'aries', 'taurus') - nullable for house-only reports
|
|
191
191
|
house: integer('house'), // Nullable, only for applicable placements
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
descriptionTemplateId: text('description_template_id').references(
|
|
193
|
+
() => astroDescriptionTemplates.id,
|
|
194
|
+
{ onDelete: 'set null' }
|
|
195
|
+
), // NEW: Foreign key to astro_description_templates.id
|
|
194
196
|
enReport: text('en_report'), // Full report content in English
|
|
195
197
|
ptReport: text('pt_report'), // Full report content in Portuguese
|
|
196
198
|
createdAt: integer('created_at').default(sql`CURRENT_TIMESTAMP`),
|
|
@@ -209,8 +211,10 @@ export const houseReports = sqliteTable(
|
|
|
209
211
|
id: text('id').primaryKey(), // Unique identifier
|
|
210
212
|
sign: text('sign').notNull(), // Lowercase slug (e.g., 'aries', 'taurus')
|
|
211
213
|
house: integer('house').notNull(), // House placement (1-12)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
+
descriptionTemplateId: text('description_template_id').references(
|
|
215
|
+
() => astroDescriptionTemplates.id,
|
|
216
|
+
{ onDelete: 'set null' }
|
|
217
|
+
), // NEW: Foreign key to astro_description_templates.id
|
|
214
218
|
enReport: text('en_report'), // Full report content in English
|
|
215
219
|
ptReport: text('pt_report'), // Full report content in Portuguese
|
|
216
220
|
createdAt: integer('created_at').default(sql`CURRENT_TIMESTAMP`),
|
|
@@ -218,6 +222,7 @@ export const houseReports = sqliteTable(
|
|
|
218
222
|
},
|
|
219
223
|
(t) => [
|
|
220
224
|
index('house_reports_sign_house_idx').on(t.sign, t.house), // Optimized index for sign + house lookups
|
|
225
|
+
index('house_reports_description_template_idx').on(t.descriptionTemplateId), // NEW: Index for foreign key performance
|
|
221
226
|
]
|
|
222
227
|
);
|
|
223
228
|
|
|
@@ -228,8 +233,10 @@ export const aspectReports = sqliteTable(
|
|
|
228
233
|
aspectingPlanet: text('aspecting_planet').notNull(), // Planet/point initiating the aspect (e.g., 'sun', 'mars')
|
|
229
234
|
aspectedPlanet: text('aspected_planet').notNull(), // Planet/point receiving the aspect (e.g., 'moon', 'venus')
|
|
230
235
|
aspect: text('aspect').notNull(), // Aspect type ('conjunction', 'trine', 'square', etc.)
|
|
231
|
-
|
|
232
|
-
|
|
236
|
+
descriptionTemplateId: text('description_template_id').references(
|
|
237
|
+
() => astroDescriptionTemplates.id,
|
|
238
|
+
{ onDelete: 'set null' }
|
|
239
|
+
), // NEW: Foreign key to astro_description_templates.id
|
|
233
240
|
enReport: text('en_report'), // Full report content in English
|
|
234
241
|
ptReport: text('pt_report'), // Full report content in Portuguese
|
|
235
242
|
createdAt: integer('created_at').default(sql`CURRENT_TIMESTAMP`),
|
|
@@ -239,11 +246,8 @@ export const aspectReports = sqliteTable(
|
|
|
239
246
|
index('aspect_reports_aspecting_idx').on(t.aspectingPlanet),
|
|
240
247
|
index('aspect_reports_aspected_idx').on(t.aspectedPlanet),
|
|
241
248
|
index('aspect_reports_aspect_idx').on(t.aspect),
|
|
242
|
-
index('aspect_reports_combined_idx').on(
|
|
243
|
-
|
|
244
|
-
t.aspectedPlanet,
|
|
245
|
-
t.aspect
|
|
246
|
-
), // Optimized for aspect lookups
|
|
249
|
+
index('aspect_reports_combined_idx').on(t.aspectingPlanet, t.aspectedPlanet, t.aspect),
|
|
250
|
+
index('aspect_reports_description_template_idx').on(t.descriptionTemplateId), // NEW: Index for foreign key performance
|
|
247
251
|
]
|
|
248
252
|
);
|
|
249
253
|
|
|
@@ -251,17 +255,38 @@ export const astroFeatureReports = sqliteTable(
|
|
|
251
255
|
'astro_feature_reports',
|
|
252
256
|
{
|
|
253
257
|
id: text('id').primaryKey(), // Unique identifier
|
|
254
|
-
featureType: text('feature_type').notNull(), // e.g., "moon_phase", "element", "mode", "hemisphere_east_west", "hemisphere_north_south"
|
|
255
|
-
name: text('name').notNull(), // e.g., "last-quarter-moon", "fire", "cardinal", "east
|
|
256
|
-
|
|
257
|
-
|
|
258
|
+
featureType: text('feature_type').notNull(), // e.g., "moon_phase", "element", "mode", "hemisphere_east_west", "hemisphere_north_south"
|
|
259
|
+
name: text('name').notNull(), // e.g., "last-quarter-moon", "fire", "cardinal", "east", "north"
|
|
260
|
+
descriptionTemplateId: text('description_template_id').references(
|
|
261
|
+
() => astroDescriptionTemplates.id,
|
|
262
|
+
{ onDelete: 'set null' }
|
|
263
|
+
), // NEW: Foreign key to astro_description_templates.id
|
|
258
264
|
enReport: text('en_report'), // Full report content in English (JSON string)
|
|
259
265
|
ptReport: text('pt_report'), // Full report content in Portuguese (JSON string)
|
|
260
266
|
createdAt: integer('created_at').default(sql`CURRENT_TIMESTAMP`),
|
|
261
267
|
updatedAt: integer('updated_at').default(sql`CURRENT_TIMESTAMP`),
|
|
262
268
|
},
|
|
263
269
|
(t) => [
|
|
264
|
-
index('astro_feature_reports_type_name_idx').on(t.featureType, t.name),
|
|
270
|
+
index('astro_feature_reports_type_name_idx').on(t.featureType, t.name),
|
|
271
|
+
index('astro_feature_reports_description_template_idx').on(t.descriptionTemplateId), // NEW: Index for foreign key performance
|
|
272
|
+
]
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
export const astroDescriptionTemplates = sqliteTable(
|
|
276
|
+
'astro_description_templates',
|
|
277
|
+
{
|
|
278
|
+
id: text('id').primaryKey(), // Unique identifier
|
|
279
|
+
entityType: text('entity_type').notNull(), // e.g., "planet", "key_point", "karmic_point", "arabic_part", "house", "aspect", "moon_phase", "element", "mode", "hemisphere_east_west", "hemisphere_north_south"
|
|
280
|
+
name: text('name').notNull(), // Slug (e.g., "last-quarter-moon", "fire", "cardinal", "east", "house_1")
|
|
281
|
+
enName: text('en_name'), // Optional human-readable name in English (e.g., "Last Quarter Moon", "Fire")
|
|
282
|
+
ptName: text('pt_name'), // Optional human-readable name in Portuguese (e.g., "Lua Minguante", "Fogo")
|
|
283
|
+
enDescription: text('en_description').notNull(), // Shared 150–180 word intro in English
|
|
284
|
+
ptDescription: text('pt_description').notNull(), // Shared 150–180 word intro in Portuguese
|
|
285
|
+
createdAt: integer('created_at').default(sql`CURRENT_TIMESTAMP`),
|
|
286
|
+
updatedAt: integer('updated_at').default(sql`CURRENT_TIMESTAMP`),
|
|
287
|
+
},
|
|
288
|
+
(t) => [
|
|
289
|
+
index('astro_description_templates_type_name_idx').on(t.entityType, t.name), // Optimized for lookups by type and slug
|
|
265
290
|
]
|
|
266
291
|
);
|
|
267
292
|
|
package/package.json
CHANGED
package/types/scopes/generic.ts
CHANGED
|
@@ -45,6 +45,31 @@ export interface AstrologicalReport {
|
|
|
45
45
|
sections: Record<string, ContentItem[]>;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
export interface AstroReportRow {
|
|
49
|
+
id: string;
|
|
50
|
+
type?: string; // For astroReports
|
|
51
|
+
name?: string;
|
|
52
|
+
sign?: string;
|
|
53
|
+
house?: number;
|
|
54
|
+
featureType?: string; // For astroFeatureReports
|
|
55
|
+
enReport: string | null;
|
|
56
|
+
ptReport: string | null;
|
|
57
|
+
enDescription?: string | null;
|
|
58
|
+
ptDescription?: string | null;
|
|
59
|
+
createdAt: number | null;
|
|
60
|
+
updatedAt: number | null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface DescriptionTemplateRow {
|
|
64
|
+
id: string;
|
|
65
|
+
entityType: string;
|
|
66
|
+
name: string;
|
|
67
|
+
enDescription: string | null;
|
|
68
|
+
ptDescription: string | null;
|
|
69
|
+
createdAt: number | null;
|
|
70
|
+
updatedAt: number | null;
|
|
71
|
+
}
|
|
72
|
+
|
|
48
73
|
export type AstroReportParams =
|
|
49
74
|
| {
|
|
50
75
|
reportType: 'sign';
|
|
@@ -49,6 +49,12 @@ interface AspectParams {
|
|
|
49
49
|
aspectingType: AspectType;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
interface DescriptionParams {
|
|
53
|
+
entityType: string;
|
|
54
|
+
name: string;
|
|
55
|
+
isGeneric?: boolean; // Flag for generic descriptions (aspects, moon phases, elements, modes, hemispheres)
|
|
56
|
+
}
|
|
57
|
+
|
|
52
58
|
export const astroPrompts = {
|
|
53
59
|
planetOrPoint: ({ type, sign, pointName }: PlanetOrPointParams): string => {
|
|
54
60
|
const validTypes = ['planet', 'key_point', 'karmic_point', 'arabic_part'];
|
|
@@ -224,4 +230,24 @@ export const astroPrompts = {
|
|
|
224
230
|
throw new Error(`Unknown feature type in params: ${params}`);
|
|
225
231
|
}
|
|
226
232
|
},
|
|
233
|
+
|
|
234
|
+
makeIntroDescription: ({ entityType, name, isGeneric }: DescriptionParams): string => {
|
|
235
|
+
const baseFormat = `
|
|
236
|
+
Make me an introductory, explanatory description of 150 to 180 words about the astrological ${entityType}.
|
|
237
|
+
The result must have an English version and a Portuguese version.
|
|
238
|
+
The result format must be exactly like the following:
|
|
239
|
+
-- EN
|
|
240
|
+
[Single block of plain text with English version]
|
|
241
|
+
-- PT
|
|
242
|
+
[Single block of plain text with Portuguese version]
|
|
243
|
+
`.trim();
|
|
244
|
+
|
|
245
|
+
if (isGeneric) {
|
|
246
|
+
return `
|
|
247
|
+
${baseFormat.replace("astrological", "astrological concept representing").replace("about the astrological", "about what the")}
|
|
248
|
+
`.trim();
|
|
249
|
+
} else {
|
|
250
|
+
return `${baseFormat} ${name ? `named ${name}.` : ""}`.trim();
|
|
251
|
+
}
|
|
252
|
+
},
|
|
227
253
|
};
|
package/utils/buildMessages.ts
CHANGED
|
@@ -327,7 +327,91 @@ export const buildLLMMessages = (env: BackendBindings) => ({
|
|
|
327
327
|
});
|
|
328
328
|
break;
|
|
329
329
|
|
|
330
|
+
case 'feature':
|
|
331
|
+
if (params.featureType === 'element') {
|
|
332
|
+
if (!('subtype' in params))
|
|
333
|
+
throw new Error('Missing subtype for element feature report');
|
|
334
|
+
switch (params.subtype) {
|
|
335
|
+
case 'balanced':
|
|
336
|
+
prompt = astroPrompts.feature({
|
|
337
|
+
featureType: 'element',
|
|
338
|
+
subtype: 'balanced',
|
|
339
|
+
});
|
|
340
|
+
break;
|
|
341
|
+
case 'pure':
|
|
342
|
+
if (!('dominantElement' in params))
|
|
343
|
+
throw new Error(
|
|
344
|
+
'Missing dominantElement for pure element report'
|
|
345
|
+
);
|
|
346
|
+
prompt = astroPrompts.feature({
|
|
347
|
+
featureType: 'element',
|
|
348
|
+
subtype: 'pure',
|
|
349
|
+
dominantElement: params.dominantElement,
|
|
350
|
+
});
|
|
351
|
+
break;
|
|
352
|
+
case 'preponderant_lacking':
|
|
353
|
+
if (
|
|
354
|
+
!('dominantElement' in params) ||
|
|
355
|
+
!('lackingElement' in params)
|
|
356
|
+
)
|
|
357
|
+
throw new Error(
|
|
358
|
+
'Missing dominantElement or lackingElement for preponderant_lacking element report'
|
|
359
|
+
);
|
|
360
|
+
prompt = astroPrompts.feature({
|
|
361
|
+
featureType: 'element',
|
|
362
|
+
subtype: 'preponderant_lacking',
|
|
363
|
+
dominantElement: params.dominantElement,
|
|
364
|
+
lackingElement: params.lackingElement,
|
|
365
|
+
});
|
|
366
|
+
break;
|
|
367
|
+
default:
|
|
368
|
+
throw new Error(
|
|
369
|
+
`Unknown element subtype: ${(params as any).subtype}`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
if (!('name' in params))
|
|
374
|
+
throw new Error('Missing name for non-element feature report');
|
|
375
|
+
// Narrow featureType to ensure it matches FeatureParams
|
|
376
|
+
switch (params.featureType) {
|
|
377
|
+
case 'moon_phase':
|
|
378
|
+
case 'mode':
|
|
379
|
+
prompt = astroPrompts.feature({
|
|
380
|
+
featureType: params.featureType,
|
|
381
|
+
name: params.name,
|
|
382
|
+
});
|
|
383
|
+
break;
|
|
384
|
+
case 'hemisphere_east_west':
|
|
385
|
+
case 'hemisphere_north_south':
|
|
386
|
+
if (
|
|
387
|
+
!(
|
|
388
|
+
['east', 'west'].includes(params.name) ||
|
|
389
|
+
['north', 'south'].includes(params.name)
|
|
390
|
+
)
|
|
391
|
+
) {
|
|
392
|
+
throw new Error(
|
|
393
|
+
`Invalid name "${params.name}" for ${params.featureType}`
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
prompt = astroPrompts.feature({
|
|
397
|
+
featureType: params.featureType,
|
|
398
|
+
name: params.name as 'east' | 'west' | 'north' | 'south',
|
|
399
|
+
});
|
|
400
|
+
break;
|
|
401
|
+
default:
|
|
402
|
+
throw new Error(
|
|
403
|
+
`Unknown feature type: ${(params as any).featureType}`
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
|
|
330
409
|
default:
|
|
410
|
+
console.error(
|
|
411
|
+
`❌ Unknown report type: ${
|
|
412
|
+
(params as any).reportType
|
|
413
|
+
}. Params: ${JSON.stringify(params)}`
|
|
414
|
+
);
|
|
331
415
|
throw new Error(`Unknown report type: ${(params as any).reportType}`);
|
|
332
416
|
}
|
|
333
417
|
|