@zodic/shared 0.0.76 → 0.0.77

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.
@@ -0,0 +1,197 @@
1
+ import { inject, injectable } from 'inversify';
2
+ import 'reflect-metadata';
3
+ import { schema } from '../..';
4
+ import { Gender, VALID_GENDERS_ARRAY } from '../../types';
5
+ import {
6
+ KVArchetype,
7
+ LeonardoGenerateImageResponse,
8
+ sizes,
9
+ } from '../../types/scopes/legacy';
10
+ import { buildCosmicMirrorArchetypeKVKey } from '../../utils/KVKeysBuilders';
11
+ import { AppContext } from '../base/AppContext';
12
+
13
+ @injectable()
14
+ export class LeonardoService {
15
+ constructor(@inject(AppContext) private context: AppContext) {}
16
+
17
+ /**
18
+ * Processes and generates missing Leonardo prompts for archetypes.
19
+ */
20
+ async processArchetypesPrompts(crown: string): Promise<void> {
21
+ console.log(`Processing Leonardo prompts for crown: ${crown}`);
22
+
23
+ const promptsToGenerate: Array<{
24
+ kvKey: string;
25
+ name: string;
26
+ visualDescription: string;
27
+ }> = [];
28
+
29
+ // Fetch archetypes that need prompts
30
+ for (const gender of VALID_GENDERS_ARRAY) {
31
+ for (let index = 1; index <= 3; index++) {
32
+ const kvKey = buildCosmicMirrorArchetypeKVKey(
33
+ 'en-us',
34
+ crown,
35
+ gender,
36
+ index
37
+ );
38
+ console.log(`Fetching archetype from KV: ${kvKey}`);
39
+
40
+ const kvData = await this.context
41
+ .kvCosmicMirrorArchetypesStore()
42
+ .get<KVArchetype>(kvKey, 'json');
43
+ if (kvData && !kvData.leonardoPrompt) {
44
+ promptsToGenerate.push({
45
+ kvKey,
46
+ name: kvData.name,
47
+ visualDescription: kvData.visualDescription,
48
+ });
49
+ }
50
+ }
51
+ }
52
+
53
+ if (promptsToGenerate.length === 0) {
54
+ console.log('No archetype prompts need to be generated.');
55
+ return;
56
+ }
57
+
58
+ console.log('Archetypes requiring prompt generation:', promptsToGenerate);
59
+
60
+ // Generate prompts
61
+ const archetypes = promptsToGenerate.map(({ name, visualDescription }) => ({
62
+ name,
63
+ visualDescription,
64
+ }));
65
+ const leonardoPrompts =
66
+ await this.generateCosmicMirrorArchetypesLeonardoPrompts(archetypes);
67
+
68
+ if (leonardoPrompts.length !== archetypes.length) {
69
+ throw new Error('Mismatch between archetypes and generated prompts.');
70
+ }
71
+
72
+ // Update KV with generated prompts
73
+ for (const [index, promptSet] of leonardoPrompts.entries()) {
74
+ const { kvKey } = promptsToGenerate[index];
75
+ console.log(`Updating KV with prompts for archetype: ${kvKey}`);
76
+
77
+ const kvData = await this.context
78
+ .kvCosmicMirrorArchetypesStore()
79
+ .get<KVArchetype>(kvKey, 'json');
80
+ if (!kvData) {
81
+ console.warn(`KV data not found for key: ${kvKey}. Skipping update.`);
82
+ continue;
83
+ }
84
+
85
+ kvData.leonardoPrompt = promptSet;
86
+ await this.context
87
+ .kvCosmicMirrorArchetypesStore()
88
+ .put(kvKey, JSON.stringify(kvData));
89
+ }
90
+
91
+ console.log(`Completed prompt processing for crown: ${crown}`);
92
+ }
93
+
94
+ /**
95
+ * Queues image generation for archetypes if no images exist.
96
+ */
97
+ async queueImagesForArchetypes(crown: string, gender: Gender): Promise<void> {
98
+ console.log(
99
+ `Queuing image generation for crown: ${crown}, gender: ${gender}`
100
+ );
101
+
102
+ for (let index = 1; index <= 3; index++) {
103
+ const kvKey = buildCosmicMirrorArchetypeKVKey(
104
+ 'en-us',
105
+ crown,
106
+ gender,
107
+ index
108
+ );
109
+ console.info(`Fetching KV for archetype: ${kvKey}`);
110
+
111
+ const kvData = await this.context
112
+ .kvCosmicMirrorArchetypesStore()
113
+ .get<KVArchetype>(kvKey, 'json');
114
+ if (!kvData || kvData.images.length >= 3) {
115
+ console.info(
116
+ `Skipping image generation for index ${index}, images already exist.`
117
+ );
118
+ continue;
119
+ }
120
+
121
+ console.info(`No images found. Queuing generation for index ${index}...`);
122
+
123
+ const generationResponse = await this.queueImageGeneration(
124
+ kvData.leonardoPrompt
125
+ );
126
+ const generationId = generationResponse.sdGenerationJob.generationId;
127
+
128
+ if (!generationId) {
129
+ throw new Error('Leonardo generation failed to return a valid ID.');
130
+ }
131
+
132
+ await this.context.drizzle().insert(schema.generations).values({
133
+ id: generationId,
134
+ archetypeIndex: index,
135
+ conceptCombinationId: null,
136
+ type: 'archetype_image',
137
+ status: 'pending',
138
+ createdAt: new Date(),
139
+ });
140
+
141
+ console.info(
142
+ `Queued generation ${generationId} for archetype index ${index}`
143
+ );
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Generates Leonardo prompts for archetypes.
149
+ */
150
+ private async generateCosmicMirrorArchetypesLeonardoPrompts(
151
+ archetypes: Array<{ name: string; visualDescription: string }>
152
+ ): Promise<string[]> {
153
+ console.log(`Generating Leonardo prompts for archetypes`);
154
+
155
+ const messages = this.context
156
+ .buildLLMMessages()
157
+ .generateCosmicMirrorArchetypesLeonardoPrompts(archetypes);
158
+ const response = await this.context.api().callDeepSeek.single(messages, {});
159
+
160
+ if (!response) {
161
+ throw new Error('Leonardo prompts not generated');
162
+ }
163
+
164
+ console.log('Leonardo Prompts from API -> ', response);
165
+
166
+ return response
167
+ .split('\n')
168
+ .map((prompt) => prompt.trim())
169
+ .filter(Boolean);
170
+ }
171
+
172
+ /**
173
+ * Queues an image generation request to Leonardo.
174
+ */
175
+ private async queueImageGeneration(
176
+ prompt: string
177
+ ): Promise<LeonardoGenerateImageResponse> {
178
+ console.log(`Queuing Leonardo image generation...`);
179
+
180
+ const { width, height } = sizes['alchemy']['post4:5'];
181
+
182
+ try {
183
+ const response = await this.context
184
+ .api()
185
+ .callLeonardo.generateImage({ width, height, prompt });
186
+ if (!response) {
187
+ throw new Error('Leonardo image generation failed');
188
+ }
189
+
190
+ console.log('Leonardo Image Generation Response:', response);
191
+ return response;
192
+ } catch (error) {
193
+ console.error('Error queuing Leonardo image generation:', error);
194
+ throw error;
195
+ }
196
+ }
197
+ }
@@ -0,0 +1,156 @@
1
+ import { inject, injectable } from 'inversify';
2
+ import { Gender } from '../../types';
3
+ import { KVArchetype } from '../../types/scopes/legacy';
4
+ import { AppContext } from '../base';
5
+ import { ArchetypeService } from '../services/ArchetypeService';
6
+ import { LeonardoService } from '../services/LeonardoService';
7
+
8
+ @injectable()
9
+ export class ArchetypeWorkflow {
10
+ constructor(
11
+ @inject(AppContext) private context: AppContext,
12
+ @inject(ArchetypeService) private archetypeService: ArchetypeService,
13
+ @inject(LeonardoService) private leonardoService: LeonardoService
14
+ ) {}
15
+
16
+ async execute(combinationString: string, gender: Gender) {
17
+ const kvKeyBase = `cosmic-mirror:archetypes:${combinationString}:${gender}`;
18
+
19
+ console.log('Starting ArchetypeWorkflow for:', {
20
+ combinationString,
21
+ gender,
22
+ });
23
+
24
+ const archetypes = await Promise.all(
25
+ Array.from({ length: 3 }, (_, i) => i + 1).map(async (index) => {
26
+ const kvKey = `${kvKeyBase}:${index}`;
27
+ try {
28
+ console.log(`Fetching archetype from KV: ${kvKey}`);
29
+ return await this.context
30
+ .kvCosmicMirrorArchetypesStore()
31
+ .get<KVArchetype>(kvKey, 'json');
32
+ } catch (error) {
33
+ console.error(
34
+ `Error fetching archetype KV for index ${index}:`,
35
+ error
36
+ );
37
+ return null;
38
+ }
39
+ })
40
+ ).then((results) => results.filter((result) => result !== null)); // Filter out nulls
41
+
42
+ const isGenerating = archetypes.some(
43
+ (archetype) => archetype?.status === 'generating'
44
+ );
45
+
46
+ if (isGenerating) {
47
+ console.log(
48
+ `Archetype images are already being generated for: ${combinationString}, gender: ${gender}`
49
+ );
50
+ return { message: 'Images are being processed, please try again later.' };
51
+ }
52
+
53
+ const missingOrIncomplete =
54
+ archetypes.length < 3 ||
55
+ archetypes.some((archetype) => archetype?.images.length < 3);
56
+
57
+ if (missingOrIncomplete) {
58
+ console.log(
59
+ `Archetypes missing or incomplete for combination: ${combinationString}, gender: ${gender}. Generating...`
60
+ );
61
+
62
+ await this.archetypeService.generateBasicInfo(combinationString);
63
+
64
+ const updatedArchetypes = await Promise.all(
65
+ Array.from({ length: 3 }, (_, i) => i + 1).map(async (index) => {
66
+ const kvKey = `${kvKeyBase}:${index}`;
67
+ try {
68
+ console.log(`Re-fetching archetype from KV: ${kvKey}`);
69
+ return await this.context
70
+ .kvCosmicMirrorArchetypesStore()
71
+ .get<KVArchetype>(kvKey, 'json');
72
+ } catch (error) {
73
+ console.error(
74
+ `Error fetching archetype KV after generation for index ${index}:`,
75
+ error
76
+ );
77
+ return null;
78
+ }
79
+ })
80
+ ).then((results) => results.filter((results) => results != null));
81
+
82
+ archetypes.length = 0;
83
+ archetypes.push(...updatedArchetypes);
84
+ }
85
+
86
+ const missingPrompts = archetypes.some(
87
+ (archetype) => !archetype?.leonardoPrompt
88
+ );
89
+
90
+ if (missingPrompts) {
91
+ console.log('Generating missing prompts...');
92
+ await this.leonardoService.processArchetypesPrompts(combinationString);
93
+ }
94
+
95
+ const archetypesNeedingImages = archetypes.filter(
96
+ (archetype) => archetype?.images.length < 3
97
+ );
98
+
99
+ if (archetypesNeedingImages.length > 0) {
100
+ console.log('Queuing image generation for missing archetypes...');
101
+ try {
102
+ await Promise.all(
103
+ archetypesNeedingImages.map(async (archetype, index) => {
104
+ archetype.status = 'generating';
105
+ const kvKey = `${kvKeyBase}:${index + 1}`;
106
+ await this.context
107
+ .kvCosmicMirrorArchetypesStore()
108
+ .put(kvKey, JSON.stringify(archetype));
109
+ })
110
+ );
111
+
112
+ await this.leonardoService.queueImagesForArchetypes(
113
+ combinationString,
114
+ gender
115
+ );
116
+ } catch (error) {
117
+ console.error('Error queuing image generation:', error);
118
+
119
+ await Promise.all(
120
+ archetypesNeedingImages.map(async (archetype, index) => {
121
+ archetype.status = 'idle';
122
+ const kvKey = `${kvKeyBase}:${index + 1}`;
123
+ await this.context
124
+ .kvCosmicMirrorArchetypesStore()
125
+ .put(kvKey, JSON.stringify(archetype));
126
+ })
127
+ );
128
+
129
+ throw error;
130
+ }
131
+
132
+ return { message: 'Images are being processed, please try again later.' };
133
+ }
134
+
135
+ await Promise.all(
136
+ archetypes.map(async (archetype, index) => {
137
+ archetype.status = 'idle';
138
+ const kvKey = `${kvKeyBase}:${index + 1}`;
139
+ await this.context
140
+ .kvCosmicMirrorArchetypesStore()
141
+ .put(kvKey, JSON.stringify(archetype));
142
+ })
143
+ );
144
+
145
+ console.log('Archetypes fully processed and ready:', archetypes);
146
+ return {
147
+ archetypes: archetypes.map((arc) => ({
148
+ name: arc?.name,
149
+ content: arc?.content,
150
+ description: arc?.description,
151
+ virtues: arc?.virtues,
152
+ images: arc?.images,
153
+ })),
154
+ };
155
+ }
156
+ }
@@ -18,24 +18,28 @@ export class ConceptWorkflow {
18
18
  switch (phase) {
19
19
  case 'basic-info':
20
20
  await this.conceptService.generateBasicInfo(
21
+ 'en-us',
21
22
  conceptSlug,
22
23
  combinationString
23
24
  );
24
25
  break;
25
26
  case 'content':
26
27
  await this.conceptService.generateContent(
28
+ 'en-us',
27
29
  conceptSlug,
28
30
  combinationString
29
31
  );
30
32
  break;
31
33
  case 'leonardo-prompt':
32
34
  await this.conceptService.generatePrompt(
35
+ 'en-us',
33
36
  conceptSlug,
34
37
  combinationString
35
38
  );
36
39
  break;
37
40
  case 'images':
38
41
  await this.conceptService.generateImages(
42
+ 'en-us',
39
43
  conceptSlug,
40
44
  combinationString
41
45
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zodic/shared",
3
- "version": "0.0.76",
3
+ "version": "0.0.77",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "publishConfig": {
package/utils/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jwtVerify } from 'jose';
2
- import { AuthCtx, Provider, ProviderInfo } from '../types';
2
+ import { AuthCtx, Gender, Provider, ProviderInfo, VALID_GENDERS_ARRAY } from '../types';
3
3
 
4
4
  export const verifyToken = async (
5
5
  c: AuthCtx,
@@ -48,3 +48,18 @@ export const providers: (c: AuthCtx) => Record<Provider, ProviderInfo> = (
48
48
  resourceUrl: '',
49
49
  },
50
50
  });
51
+
52
+ export const GENDER_CODE_MAP: Record<string, Gender> =
53
+ VALID_GENDERS_ARRAY.reduce((map, gender) => {
54
+ const key = gender[0];
55
+ map[key] = gender;
56
+ return map;
57
+ }, {} as Record<string, Gender>);
58
+
59
+ export function createEmptyGenderPrompts(): Record<Gender, string> {
60
+ const emptyPrompts: Record<Gender, string> = {} as Record<Gender, string>;
61
+ Object.values(GENDER_CODE_MAP).forEach((gender) => {
62
+ emptyPrompts[gender] = '';
63
+ });
64
+ return emptyPrompts;
65
+ }