@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 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 = { } }: DeepSeekOptions
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({
@@ -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('KV_CONCEPTS_MANAGEMENT is not defined in the environment.');
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 // โœ… New optional override param
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(); // ๐Ÿ”ด Replace with actual KV store
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; // โœ… Skip regeneration
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'; // ๐Ÿ“Œ Track the phase
68
+ let phase = 'generation';
65
69
  try {
66
70
  attempts++;
67
71
  console.log(`๐Ÿ”„ Attempt ${attempts} to generate basic info...`);
68
-
69
- // โœ… Build the messages to request content
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
- const response = await this.context
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 = 'parsing'; // โœ… Switch to parsing 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
- const { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT } =
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, 2000)); // โณ Small delay before retrying
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
- `Processing single concept for slug: ${conceptSlug}, combination: ${combinationString}, phase: ${phase}`
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
- `Concept ${conceptSlug}:${combinationString} successfully processed for phase: ${phase}`
55
+ `โœ… Concept ${conceptSlug}:${combinationString} successfully processed for phase: ${phase}`
54
56
  );
55
57
  } catch (error) {
56
58
  console.error(
57
- `Error processing concept ${conceptSlug}:${combinationString} for phase ${phase}:`,
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
  }