@zodic/shared 0.0.314 → 0.0.316
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 +114 -16
- package/app/services/ArchetypeService.ts +738 -1244
- package/app/services/LeonardoService.ts +187 -288
- package/app/workflow/ArchetypeWorkflow.ts +175 -141
- package/db/migrations/0014_green_marvel_apes.sql +10 -0
- package/db/migrations/meta/0014_snapshot.json +2723 -0
- package/db/migrations/meta/_journal.json +7 -0
- package/db/schema.ts +17 -0
- package/package.json +1 -1
- package/utils/buildMessages.ts +273 -59
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { and, eq } from 'drizzle-orm';
|
|
1
2
|
import { inject, injectable } from 'inversify';
|
|
3
|
+
import { schema } from '../..';
|
|
2
4
|
import { Gender } from '../../types';
|
|
3
|
-
import {
|
|
4
|
-
import { buildCosmicMirrorArchetypeKVKey } from '../../utils/KVKeysBuilders';
|
|
5
|
-
import { AppContext } from '../base/AppContext';
|
|
5
|
+
import { AppContext } from '../base';
|
|
6
6
|
import { ArchetypeService } from '../services/ArchetypeService';
|
|
7
7
|
import { LeonardoService } from '../services/LeonardoService';
|
|
8
8
|
|
|
@@ -14,176 +14,210 @@ export class ArchetypeWorkflow {
|
|
|
14
14
|
@inject(LeonardoService) private leonardoService: LeonardoService
|
|
15
15
|
) {}
|
|
16
16
|
|
|
17
|
-
async
|
|
18
|
-
|
|
17
|
+
private async log(
|
|
18
|
+
level: 'info' | 'debug' | 'warn' | 'error',
|
|
19
|
+
message: string,
|
|
20
|
+
context: Record<string, any> = {}
|
|
21
|
+
) {
|
|
22
|
+
const logId = `archetype-workflow:${Date.now()}`;
|
|
23
|
+
const logMessage = `[${level.toUpperCase()}] ${message}`;
|
|
24
|
+
|
|
25
|
+
console[level](logMessage, context);
|
|
26
|
+
const db = this.context.drizzle();
|
|
27
|
+
try {
|
|
28
|
+
await db
|
|
29
|
+
.insert(schema.logs)
|
|
30
|
+
.values({
|
|
31
|
+
id: logId,
|
|
32
|
+
level,
|
|
33
|
+
message,
|
|
34
|
+
context: JSON.stringify(context),
|
|
35
|
+
createdAt: new Date().getTime(),
|
|
36
|
+
})
|
|
37
|
+
.execute();
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error('[ERROR] Failed to persist log to database:', {
|
|
40
|
+
error,
|
|
41
|
+
logId,
|
|
42
|
+
message,
|
|
43
|
+
context,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async execute(
|
|
49
|
+
combinationString: string,
|
|
50
|
+
gender: Gender,
|
|
51
|
+
language: string = 'en-us'
|
|
52
|
+
) {
|
|
53
|
+
await this.log('info', 'Starting ArchetypeWorkflow', {
|
|
19
54
|
combinationString,
|
|
20
55
|
gender,
|
|
56
|
+
language,
|
|
21
57
|
});
|
|
22
58
|
|
|
23
|
-
|
|
59
|
+
// Step 1: Check if archetypes exist, if not, generate names
|
|
60
|
+
let archetypes = await this.archetypeService.fetchArchetypesFromDB(
|
|
24
61
|
combinationString,
|
|
25
|
-
gender
|
|
62
|
+
gender,
|
|
63
|
+
language
|
|
26
64
|
);
|
|
65
|
+
if (archetypes.length === 0) {
|
|
66
|
+
await this.log('info', 'No archetypes found, generating names', {
|
|
67
|
+
combinationString,
|
|
68
|
+
gender,
|
|
69
|
+
language,
|
|
70
|
+
});
|
|
71
|
+
await this.archetypeService.generateArchetypeNames(combinationString);
|
|
72
|
+
archetypes = await this.archetypeService.fetchArchetypesFromDB(
|
|
73
|
+
combinationString,
|
|
74
|
+
gender,
|
|
75
|
+
language
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
await this.log('info', 'Fetched archetypes', {
|
|
79
|
+
archetypesCount: archetypes.length,
|
|
80
|
+
archetypeIds: archetypes.map((a) => a.id),
|
|
81
|
+
});
|
|
27
82
|
|
|
28
|
-
if
|
|
29
|
-
|
|
30
|
-
|
|
83
|
+
// Step 2: Check if any archetypes are already being processed (images are generating)
|
|
84
|
+
const db = this.context.drizzle();
|
|
85
|
+
await this.log('debug', 'Checking for in-progress image generations', {
|
|
86
|
+
combinationString,
|
|
87
|
+
gender,
|
|
88
|
+
});
|
|
89
|
+
const inProgressGenerations = await db
|
|
90
|
+
.select()
|
|
91
|
+
.from(schema.generations)
|
|
92
|
+
.where(
|
|
93
|
+
and(
|
|
94
|
+
eq(schema.generations.status, 'pending'),
|
|
95
|
+
eq(schema.generations.gender, gender),
|
|
96
|
+
eq(schema.generations.archetypeDataId, combinationString)
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
.execute();
|
|
100
|
+
|
|
101
|
+
if (inProgressGenerations.length > 0) {
|
|
102
|
+
await this.log(
|
|
103
|
+
'warn',
|
|
104
|
+
`Images are already being processed for ${combinationString}, gender: ${gender}`,
|
|
105
|
+
{
|
|
106
|
+
inProgressGenerationsCount: inProgressGenerations.length,
|
|
107
|
+
generationIds: inProgressGenerations.map((g) => g.id),
|
|
108
|
+
}
|
|
31
109
|
);
|
|
32
110
|
return { message: 'Images are being processed, please try again later.' };
|
|
33
111
|
}
|
|
112
|
+
await this.log('info', 'No in-progress image generations found', {
|
|
113
|
+
combinationString,
|
|
114
|
+
gender,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Step 3: Generate missing textual content (description, virtues, content, leonardoPrompt)
|
|
118
|
+
const archetypesNeedingText = archetypes.filter(
|
|
119
|
+
(arc) =>
|
|
120
|
+
!arc.description ||
|
|
121
|
+
arc.virtues === '[]' ||
|
|
122
|
+
arc.content === '[]' ||
|
|
123
|
+
!arc.leonardoPrompt
|
|
124
|
+
);
|
|
34
125
|
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
|
|
126
|
+
if (archetypesNeedingText.length > 0) {
|
|
127
|
+
await this.log('info', 'Generating missing textual content', {
|
|
128
|
+
combinationString,
|
|
129
|
+
gender,
|
|
130
|
+
language,
|
|
131
|
+
archetypesNeedingTextCount: archetypesNeedingText.length,
|
|
132
|
+
archetypeIds: archetypesNeedingText.map((a) => a.id),
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const descVirtues =
|
|
136
|
+
await this.archetypeService.generateDescriptionsAndVirtues(
|
|
137
|
+
combinationString,
|
|
138
|
+
gender,
|
|
139
|
+
language
|
|
140
|
+
);
|
|
141
|
+
await this.archetypeService.generateContent(
|
|
142
|
+
combinationString,
|
|
143
|
+
gender,
|
|
144
|
+
language,
|
|
145
|
+
descVirtues
|
|
38
146
|
);
|
|
39
|
-
await this.archetypeService.
|
|
147
|
+
await this.archetypeService.generateLeonardoPrompts(
|
|
148
|
+
combinationString,
|
|
149
|
+
gender,
|
|
150
|
+
language,
|
|
151
|
+
descVirtues
|
|
152
|
+
);
|
|
153
|
+
} else {
|
|
154
|
+
await this.log('info', 'No textual content generation needed', {
|
|
155
|
+
combinationString,
|
|
156
|
+
gender,
|
|
157
|
+
language,
|
|
158
|
+
});
|
|
40
159
|
}
|
|
41
160
|
|
|
42
|
-
|
|
161
|
+
// Refetch archetypes after generating textual content
|
|
162
|
+
const updatedArchetypes = await this.archetypeService.fetchArchetypesFromDB(
|
|
43
163
|
combinationString,
|
|
44
|
-
gender
|
|
164
|
+
gender,
|
|
165
|
+
language
|
|
45
166
|
);
|
|
167
|
+
await this.log('info', 'Refetched archetypes', {
|
|
168
|
+
updatedArchetypesCount: updatedArchetypes.length,
|
|
169
|
+
archetypeIds: updatedArchetypes.map((a) => a.id),
|
|
170
|
+
});
|
|
46
171
|
|
|
47
|
-
|
|
48
|
-
console.log('Generating missing Leonardo prompts...');
|
|
49
|
-
await this.leonardoService.processArchetypesPrompts(combinationString);
|
|
50
|
-
}
|
|
51
|
-
|
|
172
|
+
// Step 4: Queue image generation if needed
|
|
52
173
|
const archetypesNeedingImages = updatedArchetypes.filter(
|
|
53
|
-
(arc) => arc.images.length < 3
|
|
174
|
+
(arc) => JSON.parse(arc.images).length < 3
|
|
54
175
|
);
|
|
55
176
|
|
|
56
177
|
if (archetypesNeedingImages.length > 0) {
|
|
57
|
-
|
|
58
|
-
|
|
178
|
+
await this.log(
|
|
179
|
+
'info',
|
|
180
|
+
'Queuing image generation for missing archetypes',
|
|
181
|
+
{
|
|
182
|
+
archetypesNeedingImagesCount: archetypesNeedingImages.length,
|
|
183
|
+
archetypeIds: archetypesNeedingImages.map((a) => a.id),
|
|
184
|
+
}
|
|
185
|
+
);
|
|
59
186
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
187
|
+
const batch = archetypesNeedingImages.map((arc) => ({
|
|
188
|
+
body: {
|
|
189
|
+
archetypeDataId: arc.id,
|
|
190
|
+
combination: combinationString,
|
|
191
|
+
gender,
|
|
192
|
+
language,
|
|
193
|
+
archetypeIndex: arc.archetypeIndex,
|
|
194
|
+
},
|
|
195
|
+
}));
|
|
196
|
+
await this.log('debug', 'Sending batch to ARCHETYPE_POPULATION_QUEUE', {
|
|
197
|
+
batchCount: batch.length,
|
|
198
|
+
batch,
|
|
199
|
+
});
|
|
200
|
+
await this.context.env.ARCHETYPE_POPULATION_QUEUE.sendBatch(batch);
|
|
201
|
+
await this.log('info', 'Successfully queued image generation', {
|
|
202
|
+
batchCount: batch.length,
|
|
203
|
+
});
|
|
70
204
|
|
|
71
205
|
return { message: 'Images are being processed, please try again later.' };
|
|
72
206
|
}
|
|
73
207
|
|
|
74
|
-
await this.
|
|
75
|
-
|
|
76
|
-
|
|
208
|
+
await this.log('info', 'Archetypes fully processed and ready', {
|
|
209
|
+
updatedArchetypes,
|
|
210
|
+
});
|
|
77
211
|
return {
|
|
78
212
|
archetypes: updatedArchetypes.map(
|
|
79
|
-
({ name,
|
|
213
|
+
({ name, essenceLine, description, virtues, images }) => ({
|
|
80
214
|
name,
|
|
81
|
-
|
|
215
|
+
essenceLine,
|
|
82
216
|
description,
|
|
83
|
-
virtues,
|
|
84
|
-
images,
|
|
217
|
+
virtues: JSON.parse(virtues),
|
|
218
|
+
images: JSON.parse(images),
|
|
85
219
|
})
|
|
86
220
|
),
|
|
87
221
|
};
|
|
88
222
|
}
|
|
89
|
-
|
|
90
|
-
async generateNames({
|
|
91
|
-
combinations,
|
|
92
|
-
overrideExisting,
|
|
93
|
-
}: {
|
|
94
|
-
combinations: string[];
|
|
95
|
-
overrideExisting?: boolean;
|
|
96
|
-
}): Promise<void> {
|
|
97
|
-
if (combinations.length === 0) return;
|
|
98
|
-
|
|
99
|
-
if (combinations.length === 1) {
|
|
100
|
-
await this.archetypeService.generateArchetypeNames(
|
|
101
|
-
combinations[0],
|
|
102
|
-
overrideExisting
|
|
103
|
-
);
|
|
104
|
-
} else {
|
|
105
|
-
const entries = combinations.map((combination) => ({
|
|
106
|
-
combination,
|
|
107
|
-
}));
|
|
108
|
-
|
|
109
|
-
await this.archetypeService.generateArchetypeNamesBatch(
|
|
110
|
-
entries,
|
|
111
|
-
overrideExisting
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Fetch all three archetypes from KV for a given combination and gender.
|
|
118
|
-
*/
|
|
119
|
-
private async fetchArchetypesFromKV(
|
|
120
|
-
combinationString: string,
|
|
121
|
-
gender: Gender
|
|
122
|
-
): Promise<KVArchetype[]> {
|
|
123
|
-
return Promise.all(
|
|
124
|
-
Array.from({ length: 3 }, (_, i) => i + 1).map(async (index) => {
|
|
125
|
-
const kvKey = buildCosmicMirrorArchetypeKVKey(
|
|
126
|
-
'en-us',
|
|
127
|
-
combinationString,
|
|
128
|
-
gender,
|
|
129
|
-
index
|
|
130
|
-
);
|
|
131
|
-
console.log(`Fetching archetype from KV: ${kvKey}`);
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
return (
|
|
135
|
-
(await this.context
|
|
136
|
-
.kvCosmicMirrorArchetypesStore()
|
|
137
|
-
.get<KVArchetype>(kvKey, 'json')) || this.createEmptyArchetype()
|
|
138
|
-
);
|
|
139
|
-
} catch (error) {
|
|
140
|
-
console.error(
|
|
141
|
-
`Error fetching archetype KV for index ${index}:`,
|
|
142
|
-
error
|
|
143
|
-
);
|
|
144
|
-
return this.createEmptyArchetype();
|
|
145
|
-
}
|
|
146
|
-
})
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Update the status of archetypes in KV.
|
|
152
|
-
*/
|
|
153
|
-
private async updateArchetypeStatus(
|
|
154
|
-
archetypes: KVArchetype[],
|
|
155
|
-
status: 'idle' | 'generating',
|
|
156
|
-
gender: Gender
|
|
157
|
-
): Promise<void> {
|
|
158
|
-
await Promise.all(
|
|
159
|
-
archetypes.map(async (archetype, index) => {
|
|
160
|
-
const kvKey = buildCosmicMirrorArchetypeKVKey(
|
|
161
|
-
'en-us',
|
|
162
|
-
archetype.name,
|
|
163
|
-
gender,
|
|
164
|
-
index + 1
|
|
165
|
-
);
|
|
166
|
-
archetype.status = status;
|
|
167
|
-
await this.context
|
|
168
|
-
.kvCosmicMirrorArchetypesStore()
|
|
169
|
-
.put(kvKey, JSON.stringify(archetype));
|
|
170
|
-
})
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Create an empty KVArchetype object when KV data is missing.
|
|
176
|
-
*/
|
|
177
|
-
private createEmptyArchetype(): KVArchetype {
|
|
178
|
-
return {
|
|
179
|
-
name: '',
|
|
180
|
-
description: '',
|
|
181
|
-
content: '',
|
|
182
|
-
leonardoPrompt: '',
|
|
183
|
-
visualDescription: '',
|
|
184
|
-
virtues: [],
|
|
185
|
-
images: [],
|
|
186
|
-
status: 'idle',
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
223
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
CREATE TABLE `logs` (
|
|
2
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
3
|
+
`level` text NOT NULL,
|
|
4
|
+
`message` text NOT NULL,
|
|
5
|
+
`context` text DEFAULT '{}' NOT NULL,
|
|
6
|
+
`created_at` integer DEFAULT CURRENT_TIMESTAMP
|
|
7
|
+
);
|
|
8
|
+
--> statement-breakpoint
|
|
9
|
+
ALTER TABLE `generations` ADD `archetype_data_id` text REFERENCES archetypes_data(id);--> statement-breakpoint
|
|
10
|
+
CREATE INDEX `generations_archetype_data_id_idx` ON `generations` (`archetype_data_id`);
|