@zodic/shared 0.0.153 โ 0.0.155
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/api/index.ts +1 -1
- package/app/base/AppContext.ts +12 -7
- package/app/services/ConceptService.ts +87 -27
- package/app/workflow/ConceptWorkflow.ts +65 -4
- package/data/zodiacSignCombinations.ts +1730 -0
- package/package.json +2 -1
- package/tsconfig.json +2 -0
- package/types/scopes/cloudflare.ts +2 -0
- package/utils/buildMessages.ts +6 -1
- package/utils/conceptPrompts.ts +10 -1
package/app/api/index.ts
CHANGED
|
@@ -38,7 +38,7 @@ export const Api = (env: BackendBindings) => ({
|
|
|
38
38
|
callTogether: {
|
|
39
39
|
single: async (
|
|
40
40
|
messages: ChatMessages,
|
|
41
|
-
{ model = 'deepseek-ai/DeepSeek-V3', options = {
|
|
41
|
+
{ model = 'deepseek-ai/DeepSeek-V3', options = {} }: DeepSeekOptions
|
|
42
42
|
): Promise<string> => {
|
|
43
43
|
try {
|
|
44
44
|
const response = await together(env).chat.completions.create({
|
package/app/base/AppContext.ts
CHANGED
|
@@ -24,25 +24,30 @@ export class AppContext {
|
|
|
24
24
|
|
|
25
25
|
kvConceptsStore() {
|
|
26
26
|
if (!this.env.KV_CONCEPTS) {
|
|
27
|
-
throw new Error(
|
|
28
|
-
'KV_CONCEPTS is not defined in the environment.'
|
|
29
|
-
);
|
|
27
|
+
throw new Error('KV_CONCEPTS is not defined in the environment.');
|
|
30
28
|
}
|
|
31
29
|
return this.env.KV_CONCEPTS;
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
kvConceptFailuresStore() {
|
|
35
33
|
if (!this.env.KV_CONCEPT_FAILURES) {
|
|
36
|
-
throw new Error(
|
|
37
|
-
'KV_CONCEPT_FAILURES is not defined in the environment.'
|
|
38
|
-
);
|
|
34
|
+
throw new Error('KV_CONCEPT_FAILURES is not defined in the environment.');
|
|
39
35
|
}
|
|
40
36
|
return this.env.KV_CONCEPT_FAILURES;
|
|
41
37
|
}
|
|
42
38
|
|
|
39
|
+
kvConceptNamesStore() {
|
|
40
|
+
if (!this.env.KV_CONCEPT_NAMES) {
|
|
41
|
+
throw new Error('KV_CONCEPT_NAMES is not defined in the environment.');
|
|
42
|
+
}
|
|
43
|
+
return this.env.KV_CONCEPT_NAMES;
|
|
44
|
+
}
|
|
45
|
+
|
|
43
46
|
kvConceptsManagementStore() {
|
|
44
47
|
if (!this.env.KV_CONCEPTS_MANAGEMENT) {
|
|
45
|
-
throw new Error(
|
|
48
|
+
throw new Error(
|
|
49
|
+
'KV_CONCEPTS_MANAGEMENT is not defined in the environment.'
|
|
50
|
+
);
|
|
46
51
|
}
|
|
47
52
|
return this.env.KV_CONCEPTS_MANAGEMENT;
|
|
48
53
|
}
|
|
@@ -23,23 +23,27 @@ export class ConceptService {
|
|
|
23
23
|
async generateBasicInfo(
|
|
24
24
|
conceptSlug: Concept,
|
|
25
25
|
combinationString: string,
|
|
26
|
-
override: boolean = false
|
|
26
|
+
override: boolean = false
|
|
27
27
|
): Promise<void> {
|
|
28
28
|
console.log(
|
|
29
29
|
`๐ Generating basic info for concept: ${conceptSlug}, combination: ${combinationString}, override: ${override}`
|
|
30
30
|
);
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
const kvStore = this.context.kvConceptsStore();
|
|
33
|
-
const kvFailuresStore = this.context.kvConceptFailuresStore();
|
|
33
|
+
const kvFailuresStore = this.context.kvConceptFailuresStore();
|
|
34
|
+
const kvNamesStore = this.context.kvConceptNamesStore(); // โ
New KV for storing names
|
|
34
35
|
const kvKeyEN = buildConceptKVKey('en-us', conceptSlug, combinationString);
|
|
35
36
|
const kvKeyPT = buildConceptKVKey('pt-br', conceptSlug, combinationString);
|
|
36
37
|
const failureKey = `failures:basic-info:${conceptSlug}:${combinationString}`;
|
|
37
|
-
|
|
38
|
+
|
|
39
|
+
const namesKeyEN = `${conceptSlug}:en-us`; // โ
Key for English names
|
|
40
|
+
const namesKeyPT = `${conceptSlug}:pt-br`; // โ
Key for Portuguese names
|
|
41
|
+
|
|
38
42
|
// โ
Check if data already exists
|
|
39
43
|
if (!override) {
|
|
40
44
|
const existingEN = await this.getKVConcept(kvKeyEN);
|
|
41
45
|
const existingPT = await this.getKVConcept(kvKeyPT);
|
|
42
|
-
|
|
46
|
+
|
|
43
47
|
if (
|
|
44
48
|
existingEN.name &&
|
|
45
49
|
existingEN.description &&
|
|
@@ -53,41 +57,87 @@ export class ConceptService {
|
|
|
53
57
|
console.log(
|
|
54
58
|
`โก Basic info already exists for ${conceptSlug}, combination: ${combinationString}. Skipping.`
|
|
55
59
|
);
|
|
56
|
-
return;
|
|
60
|
+
return;
|
|
57
61
|
}
|
|
58
62
|
}
|
|
59
|
-
|
|
63
|
+
|
|
60
64
|
let attempts = 0;
|
|
61
65
|
const maxAttempts = 3;
|
|
62
|
-
|
|
66
|
+
|
|
63
67
|
while (attempts < maxAttempts) {
|
|
64
|
-
let phase = 'generation';
|
|
68
|
+
let phase = 'generation';
|
|
65
69
|
try {
|
|
66
70
|
attempts++;
|
|
67
71
|
console.log(`๐ Attempt ${attempts} to generate basic info...`);
|
|
68
|
-
|
|
69
|
-
// โ
|
|
72
|
+
|
|
73
|
+
// โ
Fetch the full name lists
|
|
74
|
+
let allNamesEN = JSON.parse(
|
|
75
|
+
(await kvNamesStore.get(namesKeyEN)) || '[]'
|
|
76
|
+
) as string[];
|
|
77
|
+
let allNamesPT = JSON.parse(
|
|
78
|
+
(await kvNamesStore.get(namesKeyPT)) || '[]'
|
|
79
|
+
) as string[];
|
|
80
|
+
|
|
81
|
+
// โ
Get only the last 50 names for AI context
|
|
82
|
+
const recentNamesEN = allNamesEN.slice(-50);
|
|
83
|
+
const recentNamesPT = allNamesPT.slice(-50);
|
|
84
|
+
|
|
85
|
+
// โ
Build the messages to request content, ensuring uniqueness
|
|
70
86
|
const messages = this.context
|
|
71
87
|
.buildLLMMessages()
|
|
72
88
|
.generateConceptBasicInfo({
|
|
73
89
|
combination: combinationString,
|
|
74
90
|
conceptSlug,
|
|
91
|
+
existingNames: recentNamesEN, // ๐ฅ Pass only recent names
|
|
75
92
|
});
|
|
76
|
-
|
|
93
|
+
|
|
77
94
|
// โ
Call ChatGPT API
|
|
78
|
-
|
|
79
|
-
.api()
|
|
80
|
-
.callTogether.single(messages, {});
|
|
95
|
+
let response = await this.context.api().callTogether.single(messages, {});
|
|
81
96
|
if (!response) {
|
|
82
97
|
throw new Error(`โ AI returned an empty response`);
|
|
83
98
|
}
|
|
84
|
-
|
|
85
|
-
phase = '
|
|
86
|
-
|
|
99
|
+
|
|
100
|
+
phase = 'cleaning';
|
|
101
|
+
response = this.cleanAIResponse(response); // โ
Early Cleaning Step
|
|
102
|
+
|
|
103
|
+
phase = 'parsing';
|
|
104
|
+
|
|
87
105
|
// โ
Parse response for both languages
|
|
88
|
-
|
|
106
|
+
let { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT } =
|
|
89
107
|
this.parseBasicInfoResponse(response);
|
|
90
|
-
|
|
108
|
+
|
|
109
|
+
// ๐ฅ Check if generated English name is unique
|
|
110
|
+
if (allNamesEN.includes(nameEN)) {
|
|
111
|
+
console.warn(`โ ๏ธ Duplicate Crown Name Detected: "${nameEN}"`);
|
|
112
|
+
if (attempts >= maxAttempts) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`๐จ Could not generate a unique English name after ${maxAttempts} attempts.`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
console.log('๐ Retrying due to duplicate English name...');
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ๐ฅ Check if generated Portuguese name is unique
|
|
122
|
+
if (allNamesPT.includes(namePT)) {
|
|
123
|
+
console.warn(`โ ๏ธ Duplicate Portuguese Name Detected: "${namePT}"`);
|
|
124
|
+
if (attempts >= maxAttempts) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
`๐จ Could not generate a unique Portuguese name after ${maxAttempts} attempts.`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
console.log('๐ Retrying due to duplicate Portuguese name...');
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// โ
Append new names to the full list
|
|
134
|
+
allNamesEN.push(nameEN);
|
|
135
|
+
allNamesPT.push(namePT);
|
|
136
|
+
|
|
137
|
+
// โ
Store updated name lists in KV
|
|
138
|
+
await kvNamesStore.put(namesKeyEN, JSON.stringify(allNamesEN));
|
|
139
|
+
await kvNamesStore.put(namesKeyPT, JSON.stringify(allNamesPT));
|
|
140
|
+
|
|
91
141
|
// ๐ English version
|
|
92
142
|
const conceptEN = await this.getKVConcept(kvKeyEN);
|
|
93
143
|
Object.assign(conceptEN, {
|
|
@@ -97,7 +147,7 @@ export class ConceptService {
|
|
|
97
147
|
status: 'idle',
|
|
98
148
|
});
|
|
99
149
|
await kvStore.put(kvKeyEN, JSON.stringify(conceptEN));
|
|
100
|
-
|
|
150
|
+
|
|
101
151
|
// ๐ง๐ท Portuguese version
|
|
102
152
|
const conceptPT = await this.getKVConcept(kvKeyPT);
|
|
103
153
|
Object.assign(conceptPT, {
|
|
@@ -107,7 +157,7 @@ export class ConceptService {
|
|
|
107
157
|
status: 'idle',
|
|
108
158
|
});
|
|
109
159
|
await kvStore.put(kvKeyPT, JSON.stringify(conceptPT));
|
|
110
|
-
|
|
160
|
+
|
|
111
161
|
console.log(
|
|
112
162
|
`โ
Basic info stored for ${conceptSlug}, combination: ${combinationString}, in both languages.`
|
|
113
163
|
);
|
|
@@ -117,20 +167,20 @@ export class ConceptService {
|
|
|
117
167
|
`โ Attempt ${attempts} failed at phase: ${phase}`,
|
|
118
168
|
(error as Error).message
|
|
119
169
|
);
|
|
120
|
-
|
|
170
|
+
|
|
121
171
|
// โ
Store failure details in KV for manual review
|
|
122
172
|
await kvFailuresStore.put(
|
|
123
173
|
failureKey,
|
|
124
174
|
JSON.stringify({
|
|
125
175
|
error: (error as Error).message,
|
|
126
176
|
attempt: attempts,
|
|
127
|
-
phase, // โ
Identify if failure occurred in "generation" or "parsing"
|
|
177
|
+
phase, // โ
Identify if failure occurred in "generation", "cleaning", or "parsing"
|
|
128
178
|
conceptSlug,
|
|
129
179
|
combinationString,
|
|
130
180
|
timestamp: new Date().toISOString(),
|
|
131
181
|
})
|
|
132
182
|
);
|
|
133
|
-
|
|
183
|
+
|
|
134
184
|
if (attempts >= maxAttempts) {
|
|
135
185
|
console.error(
|
|
136
186
|
`๐จ All ${maxAttempts} attempts failed during ${phase}. Logged failure.`
|
|
@@ -139,13 +189,23 @@ export class ConceptService {
|
|
|
139
189
|
`Failed to generate basic info after ${maxAttempts} attempts`
|
|
140
190
|
);
|
|
141
191
|
}
|
|
142
|
-
|
|
192
|
+
|
|
143
193
|
console.log('๐ Retrying...');
|
|
144
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
194
|
+
await new Promise((resolve) => setTimeout(resolve, 500)); // โณ Small delay before retrying
|
|
145
195
|
}
|
|
146
196
|
}
|
|
147
197
|
}
|
|
148
198
|
|
|
199
|
+
private cleanAIResponse(response: string): string {
|
|
200
|
+
console.log('๐งผ Cleaning AI Response...');
|
|
201
|
+
|
|
202
|
+
return response
|
|
203
|
+
.replace(/\*/g, '') // โ
Remove asterisks
|
|
204
|
+
.replace(/#/g, '') // โ
Remove hashtags
|
|
205
|
+
.replace(/---+/g, '') // โ
Remove unnecessary dividers
|
|
206
|
+
.trim();
|
|
207
|
+
}
|
|
208
|
+
|
|
149
209
|
private parseBasicInfoResponse(response: string): {
|
|
150
210
|
nameEN: string;
|
|
151
211
|
descriptionEN: string;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { inject, injectable } from 'inversify';
|
|
2
|
+
import pMap from 'p-map';
|
|
3
|
+
import { zodiacSignCombinations } from '../../data/zodiacSignCombinations';
|
|
2
4
|
import { Concept, ConceptPhase } from '../../types';
|
|
3
5
|
import { ConceptService } from '../services/ConceptService';
|
|
4
6
|
|
|
@@ -13,7 +15,7 @@ export class ConceptWorkflow {
|
|
|
13
15
|
override?: boolean
|
|
14
16
|
) {
|
|
15
17
|
console.log(
|
|
16
|
-
|
|
18
|
+
`๐ Processing single concept: ${conceptSlug} | ${combinationString} | Phase: ${phase}`
|
|
17
19
|
);
|
|
18
20
|
|
|
19
21
|
try {
|
|
@@ -50,14 +52,73 @@ export class ConceptWorkflow {
|
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
console.log(
|
|
53
|
-
|
|
55
|
+
`โ
Concept ${conceptSlug}:${combinationString} successfully processed for phase: ${phase}`
|
|
54
56
|
);
|
|
55
57
|
} catch (error) {
|
|
56
58
|
console.error(
|
|
57
|
-
|
|
59
|
+
`โ Error processing concept ${conceptSlug}:${combinationString} | Phase: ${phase}:`,
|
|
58
60
|
error
|
|
59
61
|
);
|
|
60
|
-
throw error;
|
|
61
62
|
}
|
|
62
63
|
}
|
|
64
|
+
|
|
65
|
+
async processBatch(
|
|
66
|
+
conceptSlug: Concept,
|
|
67
|
+
combinations: string[],
|
|
68
|
+
phase: ConceptPhase,
|
|
69
|
+
override?: boolean
|
|
70
|
+
) {
|
|
71
|
+
console.log(
|
|
72
|
+
`๐ Processing batch for concept: ${conceptSlug}, Phase: ${phase}, Total Combinations: ${combinations.length}`
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const concurrency = 20; // Adjust based on response speed
|
|
76
|
+
const failedCombinations: string[] = [];
|
|
77
|
+
|
|
78
|
+
await pMap(
|
|
79
|
+
combinations,
|
|
80
|
+
async (combination) => {
|
|
81
|
+
try {
|
|
82
|
+
await this.processSingle(conceptSlug, combination, phase, override);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error(`โ Error processing ${combination}:`, error);
|
|
85
|
+
failedCombinations.push(combination); // Store failed ones
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{ concurrency }
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (failedCombinations.length > 0) {
|
|
92
|
+
console.warn(`โ ๏ธ Retrying failed combinations:`, failedCombinations);
|
|
93
|
+
await this.processBatch(conceptSlug, failedCombinations, phase, override); // Retry
|
|
94
|
+
}
|
|
95
|
+
console.log(
|
|
96
|
+
`โ
Batch processing completed for concept: ${conceptSlug}, Phase: ${phase}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async processAllCombinations(
|
|
101
|
+
conceptSlug: Concept,
|
|
102
|
+
phase: ConceptPhase,
|
|
103
|
+
override?: boolean
|
|
104
|
+
) {
|
|
105
|
+
console.log(`๐ Fetching all possible combinations for: ${conceptSlug}`);
|
|
106
|
+
|
|
107
|
+
// ๐ฅ Replace with the actual function that retrieves all pre-defined combinations from KV or DB
|
|
108
|
+
const combinations = zodiacSignCombinations;
|
|
109
|
+
if (!combinations.length) {
|
|
110
|
+
console.warn(`โ ๏ธ No combinations found for ${conceptSlug}, skipping.`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.log(
|
|
115
|
+
`๐ Processing ALL combinations for ${conceptSlug}, Phase: ${phase}, Total: ${combinations.length}`
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
await this.processBatch(conceptSlug, combinations, phase, override);
|
|
119
|
+
|
|
120
|
+
console.log(
|
|
121
|
+
`โ
All combinations processed for concept: ${conceptSlug}, Phase: ${phase}`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
63
124
|
}
|