@zodic/shared 0.0.37 → 0.0.38
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/api/astrology/index.ts +37 -0
- package/api/index.ts +702 -0
- package/base/AppContext.ts +93 -0
- package/base/index.ts +0 -0
- package/db/schema.ts +2 -2
- package/package.json +3 -2
- package/services/ConceptService.ts +207 -0
- package/tsconfig.json +2 -0
- package/types/scopes/generic.ts +14 -0
- package/types/scopes/legacy.ts +255 -0
- package/utils/buildMessages.ts +134 -0
- package/utils/conceptPrompts.ts +49 -0
- package/workflow/ConceptWorkflow.ts +58 -0
package/api/index.ts
ADDED
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BackendBindings,
|
|
3
|
+
ChatMessages,
|
|
4
|
+
NatalChartInterpretation,
|
|
5
|
+
} from '../types';
|
|
6
|
+
import {
|
|
7
|
+
AstrologyApiResponse,
|
|
8
|
+
ChatGPTOptions,
|
|
9
|
+
HouseCuspsReport,
|
|
10
|
+
LeonardoGenerateImageParams,
|
|
11
|
+
LeonardoGenerateImageResponse,
|
|
12
|
+
LeonardoGeneratePresignedUrlParams,
|
|
13
|
+
LeonardoRequestBody,
|
|
14
|
+
LeonardoUploadImageParams,
|
|
15
|
+
NatalHouseCuspReport,
|
|
16
|
+
TimezoneResponse,
|
|
17
|
+
} from '../types/scopes/legacy';
|
|
18
|
+
import { makeAstrologyApiCall } from './astrology';
|
|
19
|
+
|
|
20
|
+
export const Api = (env: BackendBindings) => ({
|
|
21
|
+
callChatGPT: async (
|
|
22
|
+
messages: ChatMessages,
|
|
23
|
+
{ model = 'gpt-4', options = {} }: ChatGPTOptions
|
|
24
|
+
): Promise<string> => {
|
|
25
|
+
const endpoint = 'https://api.openai.com/v1/chat/completions';
|
|
26
|
+
const headers = {
|
|
27
|
+
'Content-Type': 'application/json',
|
|
28
|
+
Authorization: `Bearer ${env.OPENAI_API_KEY}`,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const body = JSON.stringify({
|
|
32
|
+
model,
|
|
33
|
+
messages,
|
|
34
|
+
...options,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetch(endpoint, { method: 'POST', headers, body });
|
|
39
|
+
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
const error = await response.json();
|
|
42
|
+
console.error('Error from OpenAI API:', error);
|
|
43
|
+
throw new Error(`OpenAI API Error: ${response.status}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const data = (await response.json()) as any;
|
|
47
|
+
return data.choices[0].message.content.trim() as string;
|
|
48
|
+
} catch (err: any) {
|
|
49
|
+
console.error('Error calling ChatGPT API:', err.message);
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
callLeonardo: {
|
|
55
|
+
generateImage: async ({
|
|
56
|
+
prompt,
|
|
57
|
+
width,
|
|
58
|
+
height,
|
|
59
|
+
controlNets = [], // Optional
|
|
60
|
+
initImageId = null, // Optional
|
|
61
|
+
initStrength = 0.2, // Default strength for Image-to-Image
|
|
62
|
+
quantity = 3,
|
|
63
|
+
}: LeonardoGenerateImageParams & {
|
|
64
|
+
initImageId?: string | null;
|
|
65
|
+
initStrength?: number;
|
|
66
|
+
}): Promise<LeonardoGenerateImageResponse> => {
|
|
67
|
+
const fixedData = {
|
|
68
|
+
modelId: '2067ae52-33fd-4a82-bb92-c2c55e7d2786',
|
|
69
|
+
presetStyle: 'DYNAMIC',
|
|
70
|
+
alchemy: true,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const negativePrompt =
|
|
74
|
+
'blurry, boring, dark, low quality, lowres, grainy, distorted, overexposed, oversaturated, flat, rough, noisy, abstract, monochrome, dull, cracked, chipped, uneven, asymmetrical, deformed, pixelated, cartoonish, out of focus, messy, dull lighting, rough texture, unbalanced, low contrast, poorly drawn, faded colors, chaotic, unnatural metals, overly sharp colors, text, writing, letters, symbols, logos, watermark, calligraphy, caption, bad anatomy, malformed hands, extra fingers, missing fingers, poorly drawn face, distorted eyes, crossed eyes, out-of-proportion eyes, deformed limbs, floating limbs, disconnected limbs, mutated hands, elongated neck, unrealistic body proportions, disfigured, extra limbs, unnatural skin texture, melted features, unnatural fingers, poorly rendered hands, blurry face';
|
|
75
|
+
|
|
76
|
+
const endpoint = 'https://cloud.leonardo.ai/api/rest/v1/generations';
|
|
77
|
+
const headers = {
|
|
78
|
+
Authorization: `Bearer ${env.LEONARDO_API_KEY}`,
|
|
79
|
+
'Content-Type': 'application/json',
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const body: LeonardoRequestBody = {
|
|
83
|
+
...fixedData,
|
|
84
|
+
prompt,
|
|
85
|
+
width,
|
|
86
|
+
height,
|
|
87
|
+
negative_prompt: negativePrompt,
|
|
88
|
+
num_images: quantity,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (controlNets.length > 0) {
|
|
92
|
+
body['controlnets'] = controlNets.map((net) => ({
|
|
93
|
+
initImageId: net.initImageId,
|
|
94
|
+
initImageType: net.initImageType,
|
|
95
|
+
preprocessorId: net.preprocessorId,
|
|
96
|
+
strengthType: net.strengthType,
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (initImageId) {
|
|
101
|
+
body['init_generation_image_id'] = initImageId;
|
|
102
|
+
body['init_strength'] = initStrength;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const response = await fetch(endpoint, {
|
|
107
|
+
method: 'POST',
|
|
108
|
+
headers,
|
|
109
|
+
body: JSON.stringify(body),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (!response.ok) {
|
|
113
|
+
const error = await response.json();
|
|
114
|
+
console.error('Error from Leonardo API:', error);
|
|
115
|
+
throw new Error(`Leonardo API Error: ${response.status}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const data = (await response.json()) as LeonardoGenerateImageResponse;
|
|
119
|
+
return data;
|
|
120
|
+
} catch (err: any) {
|
|
121
|
+
console.error('Error calling Leonardo API:', err.message);
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
generatePresignedUrl: async ({
|
|
126
|
+
extension,
|
|
127
|
+
}: LeonardoGeneratePresignedUrlParams): Promise<{
|
|
128
|
+
url: string;
|
|
129
|
+
fields: string;
|
|
130
|
+
id: string;
|
|
131
|
+
key: string;
|
|
132
|
+
}> => {
|
|
133
|
+
console.log('Generating presigned URL with extension:', extension);
|
|
134
|
+
|
|
135
|
+
const endpoint = 'https://cloud.leonardo.ai/api/rest/v1/init-image';
|
|
136
|
+
const headers = {
|
|
137
|
+
Authorization: `Bearer ${env.LEONARDO_API_KEY}`,
|
|
138
|
+
'Content-Type': 'application/json',
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
if (!extension) {
|
|
142
|
+
throw new Error('Invalid MIME type');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const body = JSON.stringify({
|
|
146
|
+
extension,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
const response = await fetch(endpoint, {
|
|
151
|
+
method: 'POST',
|
|
152
|
+
headers,
|
|
153
|
+
body,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
const error = await response.text();
|
|
158
|
+
console.error('Leonardo API error:', error);
|
|
159
|
+
throw new Error(
|
|
160
|
+
`Presigned URL generation failed: ${response.status}`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const rawResponse = (await response.json()) as any;
|
|
165
|
+
console.log('Presigned URL response:', rawResponse);
|
|
166
|
+
|
|
167
|
+
if (
|
|
168
|
+
!rawResponse.uploadInitImage ||
|
|
169
|
+
!rawResponse.uploadInitImage.url ||
|
|
170
|
+
!rawResponse.uploadInitImage.fields
|
|
171
|
+
) {
|
|
172
|
+
throw new Error('Invalid presigned URL response');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return rawResponse.uploadInitImage;
|
|
176
|
+
} catch (error: any) {
|
|
177
|
+
console.error('Error generating presigned URL:', error.message);
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
uploadImage: async ({
|
|
182
|
+
presignedUrl,
|
|
183
|
+
fields,
|
|
184
|
+
file,
|
|
185
|
+
}: LeonardoUploadImageParams) => {
|
|
186
|
+
console.log('Starting image upload to:', presignedUrl);
|
|
187
|
+
|
|
188
|
+
if (!(file instanceof Blob)) {
|
|
189
|
+
throw new Error('Invalid file type for upload');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
console.log('Parsing fields...');
|
|
194
|
+
const parsedFields = JSON.parse(fields);
|
|
195
|
+
|
|
196
|
+
if (!parsedFields || typeof parsedFields !== 'object') {
|
|
197
|
+
throw new Error('Invalid fields format');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log('Parsed fields:', parsedFields);
|
|
201
|
+
|
|
202
|
+
const formData = new FormData();
|
|
203
|
+
|
|
204
|
+
Object.entries(parsedFields).forEach(([key, value]) => {
|
|
205
|
+
formData.append(key, value as string);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
formData.append('file', file);
|
|
209
|
+
|
|
210
|
+
console.log('Uploading with FormData:', Array.from(formData.entries()));
|
|
211
|
+
|
|
212
|
+
const response = await fetch(presignedUrl, {
|
|
213
|
+
method: 'POST',
|
|
214
|
+
body: formData,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (!response.ok) {
|
|
218
|
+
const errorDetails = await response.text();
|
|
219
|
+
console.error('Upload failed:', errorDetails);
|
|
220
|
+
throw new Error(`Image upload failed: ${response.status}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log('Image uploaded successfully');
|
|
224
|
+
} catch (error: any) {
|
|
225
|
+
console.error('Error during uploadImage:', error.message);
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
callImageDescriber: async (imageUrl: string): Promise<string> => {
|
|
232
|
+
const mimeType = 'image/png';
|
|
233
|
+
const endpoint =
|
|
234
|
+
'https://us-central1-describepicture.cloudfunctions.net/describe_picture_api';
|
|
235
|
+
const hexKey = env.DESCRIBER_API_KEY;
|
|
236
|
+
if (!hexKey) throw new Error('Describer API Key not found');
|
|
237
|
+
|
|
238
|
+
const appId = env.DESCRIBER_APP_ID;
|
|
239
|
+
if (!appId) throw new Error('Describer APP ID not found');
|
|
240
|
+
|
|
241
|
+
const data = JSON.stringify({
|
|
242
|
+
imageUrl,
|
|
243
|
+
prompt: env.PROMPT_IMAGE_DESCRIBER,
|
|
244
|
+
mimeType,
|
|
245
|
+
appId,
|
|
246
|
+
imageBase64: '',
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const encryptData = async (
|
|
250
|
+
data: string,
|
|
251
|
+
hexKey: string
|
|
252
|
+
): Promise<{ iv: string; encryptedData: string }> => {
|
|
253
|
+
const keyBytes = Uint8Array.from(
|
|
254
|
+
hexKey.match(/.{2}/g)?.map((byte) => parseInt(byte, 16)) || []
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
const key = await crypto.subtle.importKey(
|
|
258
|
+
'raw',
|
|
259
|
+
keyBytes,
|
|
260
|
+
{ name: 'AES-GCM' },
|
|
261
|
+
false,
|
|
262
|
+
['encrypt']
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
266
|
+
const encodedData = new TextEncoder().encode(data);
|
|
267
|
+
|
|
268
|
+
const encrypted = await crypto.subtle.encrypt(
|
|
269
|
+
{ name: 'AES-GCM', iv },
|
|
270
|
+
key,
|
|
271
|
+
encodedData
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
iv: btoa(String.fromCharCode(...iv)),
|
|
276
|
+
encryptedData: btoa(String.fromCharCode(...new Uint8Array(encrypted))),
|
|
277
|
+
};
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const { iv, encryptedData } = await encryptData(data, hexKey);
|
|
281
|
+
|
|
282
|
+
const payload = { iv, encryptedData };
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
const response = await fetch(endpoint, {
|
|
286
|
+
method: 'POST',
|
|
287
|
+
headers: { 'Content-Type': 'application/json' },
|
|
288
|
+
body: JSON.stringify(payload),
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
if (!response.ok) {
|
|
292
|
+
const error = await response.json();
|
|
293
|
+
console.error('Error from Image Describer API:', error);
|
|
294
|
+
throw new Error(`Image Describer API Error: ${response.status}`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const result = (await response.json()) as string;
|
|
298
|
+
return result;
|
|
299
|
+
} catch (err: any) {
|
|
300
|
+
console.error('Error calling Image Describer API:', err.message);
|
|
301
|
+
throw err;
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
callUForm: async (imageUrl: string): Promise<string> => {
|
|
306
|
+
try {
|
|
307
|
+
const res = await fetch(imageUrl);
|
|
308
|
+
console.log('IMAGE URL -> ', imageUrl);
|
|
309
|
+
const blob = await res.arrayBuffer();
|
|
310
|
+
|
|
311
|
+
const input = {
|
|
312
|
+
image: [...new Uint8Array(blob)],
|
|
313
|
+
prompt: env.PROMPT_IMAGE_DESCRIBER,
|
|
314
|
+
max_tokens: 512,
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const response = await env.AI.run('@cf/llava-hf/llava-1.5-7b-hf', input);
|
|
318
|
+
|
|
319
|
+
if (!response) {
|
|
320
|
+
throw new Error('UForm model did not return a response.');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
console.log('UForm Model Response ->', response.description);
|
|
324
|
+
return response.description;
|
|
325
|
+
} catch (err: any) {
|
|
326
|
+
console.error('Error using UForm model:', err.message);
|
|
327
|
+
throw err;
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
callAstrology: {
|
|
332
|
+
getNatalChartInterpretation: async ({
|
|
333
|
+
day,
|
|
334
|
+
month,
|
|
335
|
+
year,
|
|
336
|
+
hour,
|
|
337
|
+
min,
|
|
338
|
+
lat,
|
|
339
|
+
lon,
|
|
340
|
+
tzone,
|
|
341
|
+
}: {
|
|
342
|
+
day: number;
|
|
343
|
+
month: number;
|
|
344
|
+
year: number;
|
|
345
|
+
hour: number;
|
|
346
|
+
min: number;
|
|
347
|
+
lat: number;
|
|
348
|
+
lon: number;
|
|
349
|
+
tzone: number;
|
|
350
|
+
}): Promise<NatalChartInterpretation> => {
|
|
351
|
+
const path = `natal_chart_interpretation`;
|
|
352
|
+
const payload = { day, month, year, hour, min, lat, lon, tzone };
|
|
353
|
+
|
|
354
|
+
return makeAstrologyApiCall(path, payload, env);
|
|
355
|
+
},
|
|
356
|
+
|
|
357
|
+
getHouseCuspsReport: async ({
|
|
358
|
+
day,
|
|
359
|
+
month,
|
|
360
|
+
year,
|
|
361
|
+
hour,
|
|
362
|
+
min,
|
|
363
|
+
lat,
|
|
364
|
+
lon,
|
|
365
|
+
tzone,
|
|
366
|
+
}: {
|
|
367
|
+
day: number;
|
|
368
|
+
month: number;
|
|
369
|
+
year: number;
|
|
370
|
+
hour: number;
|
|
371
|
+
min: number;
|
|
372
|
+
lat: number;
|
|
373
|
+
lon: number;
|
|
374
|
+
tzone: number;
|
|
375
|
+
}): Promise<HouseCuspsReport[]> => {
|
|
376
|
+
const path = `house_cusps_report/tropical`;
|
|
377
|
+
const payload = { day, month, year, hour, min, lat, lon, tzone };
|
|
378
|
+
|
|
379
|
+
return makeAstrologyApiCall(path, payload, env);
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
getNatalHouseCuspReports: async ({
|
|
383
|
+
day,
|
|
384
|
+
month,
|
|
385
|
+
year,
|
|
386
|
+
hour,
|
|
387
|
+
min,
|
|
388
|
+
lat,
|
|
389
|
+
lon,
|
|
390
|
+
tzone,
|
|
391
|
+
}: {
|
|
392
|
+
day: number;
|
|
393
|
+
month: number;
|
|
394
|
+
year: number;
|
|
395
|
+
hour: number;
|
|
396
|
+
min: number;
|
|
397
|
+
lat: number;
|
|
398
|
+
lon: number;
|
|
399
|
+
tzone: number;
|
|
400
|
+
}): Promise<NatalHouseCuspReport[]> => {
|
|
401
|
+
const path = `natal_house_cusp_report`;
|
|
402
|
+
const payload = { day, month, year, hour, min, lat, lon, tzone };
|
|
403
|
+
|
|
404
|
+
return makeAstrologyApiCall(path, payload, env);
|
|
405
|
+
},
|
|
406
|
+
|
|
407
|
+
getPlanetSignReport: async ({
|
|
408
|
+
planetName,
|
|
409
|
+
day,
|
|
410
|
+
month,
|
|
411
|
+
year,
|
|
412
|
+
hour,
|
|
413
|
+
min,
|
|
414
|
+
lat,
|
|
415
|
+
lon,
|
|
416
|
+
tzone,
|
|
417
|
+
}: {
|
|
418
|
+
planetName: string;
|
|
419
|
+
day: number;
|
|
420
|
+
month: number;
|
|
421
|
+
year: number;
|
|
422
|
+
hour: number;
|
|
423
|
+
min: number;
|
|
424
|
+
lat: number;
|
|
425
|
+
lon: number;
|
|
426
|
+
tzone: number;
|
|
427
|
+
}): Promise<{ planet_name: string; sign: string; report: string }> => {
|
|
428
|
+
const path = `general_sign_report/tropical/${planetName}`;
|
|
429
|
+
const payload = { day, month, year, hour, min, lat, lon, tzone };
|
|
430
|
+
|
|
431
|
+
return makeAstrologyApiCall(path, payload, env);
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
getAscendantReport: async ({
|
|
435
|
+
day,
|
|
436
|
+
month,
|
|
437
|
+
year,
|
|
438
|
+
hour,
|
|
439
|
+
min,
|
|
440
|
+
lat,
|
|
441
|
+
lon,
|
|
442
|
+
tzone,
|
|
443
|
+
}: {
|
|
444
|
+
day: number;
|
|
445
|
+
month: number;
|
|
446
|
+
year: number;
|
|
447
|
+
hour: number;
|
|
448
|
+
min: number;
|
|
449
|
+
lat: number;
|
|
450
|
+
lon: number;
|
|
451
|
+
tzone: number;
|
|
452
|
+
}): Promise<{ ascendant: string; report: string }> => {
|
|
453
|
+
const path = `general_ascendant_report/tropical`;
|
|
454
|
+
const payload = { day, month, year, hour, min, lat, lon, tzone };
|
|
455
|
+
|
|
456
|
+
return makeAstrologyApiCall(path, payload, env);
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
|
|
460
|
+
callTimezone: async ({
|
|
461
|
+
lat,
|
|
462
|
+
lon,
|
|
463
|
+
}: {
|
|
464
|
+
lat: number;
|
|
465
|
+
lon: number;
|
|
466
|
+
}): Promise<number> => {
|
|
467
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
468
|
+
const url = `https://maps.googleapis.com/maps/api/timezone/json?location=${lat},${lon}×tamp=${timestamp}&key=${env.GOOGLE_TIMEZONE_API_KEY}`;
|
|
469
|
+
|
|
470
|
+
try {
|
|
471
|
+
const response = await fetch(url);
|
|
472
|
+
|
|
473
|
+
if (!response.ok) {
|
|
474
|
+
const errorText = await response.text();
|
|
475
|
+
throw new Error(
|
|
476
|
+
`Timezone API call failed: ${response.statusText} - ${errorText}`
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const data = (await response.json()) as TimezoneResponse;
|
|
481
|
+
|
|
482
|
+
if (data.status !== 'OK') {
|
|
483
|
+
throw new Error(`Timezone API error: ${data.status}`);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const tzone = (data.rawOffset + data.dstOffset) / 3600;
|
|
487
|
+
|
|
488
|
+
return tzone;
|
|
489
|
+
} catch (error) {
|
|
490
|
+
console.error('Error calling Timezone API:', error);
|
|
491
|
+
throw new Error('Failed to fetch timezone data');
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
|
|
495
|
+
callPiApi: {
|
|
496
|
+
faceSwap: async ({
|
|
497
|
+
sourceImageUrl,
|
|
498
|
+
targetImageUrl,
|
|
499
|
+
}: {
|
|
500
|
+
sourceImageUrl: string;
|
|
501
|
+
targetImageUrl: string;
|
|
502
|
+
}): Promise<{
|
|
503
|
+
taskId: string;
|
|
504
|
+
message: string;
|
|
505
|
+
}> => {
|
|
506
|
+
const endpoint = 'https://api.piapi.ai/api/v1/task';
|
|
507
|
+
const headers = {
|
|
508
|
+
Authorization: `Bearer ${env.PIAPI_API_KEY}`,
|
|
509
|
+
'Content-Type': 'application/json',
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
const body = JSON.stringify({
|
|
513
|
+
model: 'Qubico/image-toolkit',
|
|
514
|
+
type: 'faceswap',
|
|
515
|
+
input: {
|
|
516
|
+
source_image_url: sourceImageUrl,
|
|
517
|
+
target_image_url: targetImageUrl,
|
|
518
|
+
},
|
|
519
|
+
config: {
|
|
520
|
+
webhook_config: {
|
|
521
|
+
endpoint:
|
|
522
|
+
'https://zodic-backend.lucdelbel.workers.dev/api/webhook/faceswap',
|
|
523
|
+
// secret: '',
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
try {
|
|
529
|
+
const response = await fetch(endpoint, {
|
|
530
|
+
method: 'POST',
|
|
531
|
+
headers,
|
|
532
|
+
body,
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
if (!response.ok) {
|
|
536
|
+
const error = await response.json();
|
|
537
|
+
console.error('Error from PiAPI FaceSwap:', error);
|
|
538
|
+
throw new Error(`PiAPI FaceSwap Error: ${response.status}`);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const data = (await response.json()) as {
|
|
542
|
+
task_id: string;
|
|
543
|
+
message: string;
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
console.log('FaceSwap task created successfully:', data);
|
|
547
|
+
|
|
548
|
+
return {
|
|
549
|
+
taskId: data.task_id,
|
|
550
|
+
message: data.message,
|
|
551
|
+
};
|
|
552
|
+
} catch (err: any) {
|
|
553
|
+
console.error('Error calling PiAPI FaceSwap:', err.message);
|
|
554
|
+
throw err;
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
},
|
|
558
|
+
|
|
559
|
+
mock: {
|
|
560
|
+
callTimezone: async ({
|
|
561
|
+
lat,
|
|
562
|
+
lon,
|
|
563
|
+
}: {
|
|
564
|
+
lat: number;
|
|
565
|
+
lon: number;
|
|
566
|
+
}): Promise<TimezoneResponse> => {
|
|
567
|
+
console.log('Mocked callTimezone invoked');
|
|
568
|
+
await new Promise((resolve) => setTimeout(resolve, 3000)); // Simulate 3 seconds delay
|
|
569
|
+
return {
|
|
570
|
+
dstOffset: 3600, // Daylight saving time offset in seconds
|
|
571
|
+
rawOffset: -10800, // Standard time offset in seconds (UTC-3)
|
|
572
|
+
status: 'OK',
|
|
573
|
+
timeZoneId: 'America/Sao_Paulo', // Timezone ID for São Paulo
|
|
574
|
+
timeZoneName: 'Brasília Standard Time', // Official name for the timezone
|
|
575
|
+
};
|
|
576
|
+
},
|
|
577
|
+
|
|
578
|
+
callAstrology: async ({
|
|
579
|
+
day,
|
|
580
|
+
month,
|
|
581
|
+
year,
|
|
582
|
+
hour,
|
|
583
|
+
min,
|
|
584
|
+
lat,
|
|
585
|
+
lon,
|
|
586
|
+
tzone,
|
|
587
|
+
}: {
|
|
588
|
+
day: number;
|
|
589
|
+
month: number;
|
|
590
|
+
year: number;
|
|
591
|
+
hour: number;
|
|
592
|
+
min: number;
|
|
593
|
+
lat: number;
|
|
594
|
+
lon: number;
|
|
595
|
+
tzone: number;
|
|
596
|
+
}): Promise<AstrologyApiResponse> => {
|
|
597
|
+
console.log('Mocked callAstrology invoked');
|
|
598
|
+
await new Promise((resolve) => setTimeout(resolve, 3000)); // Simulate 3 seconds delay
|
|
599
|
+
return [
|
|
600
|
+
{
|
|
601
|
+
name: 'Sun',
|
|
602
|
+
fullDegree: 259.3692854715937,
|
|
603
|
+
normDegree: 19.369285471593685,
|
|
604
|
+
speed: 1.0163786145803477,
|
|
605
|
+
isRetro: 'false',
|
|
606
|
+
sign: 'Sagittarius',
|
|
607
|
+
house: 11,
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
name: 'Moon',
|
|
611
|
+
fullDegree: 113.01532109301493,
|
|
612
|
+
normDegree: 23.015321093014933,
|
|
613
|
+
speed: 13.422889565180862,
|
|
614
|
+
isRetro: 'false',
|
|
615
|
+
sign: 'Libra',
|
|
616
|
+
house: 7,
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
name: 'Mars',
|
|
620
|
+
fullDegree: 319.2803341423472,
|
|
621
|
+
normDegree: 19.280334142347215,
|
|
622
|
+
speed: 0.7621766783897992,
|
|
623
|
+
isRetro: 'false',
|
|
624
|
+
sign: 'Aquarius',
|
|
625
|
+
house: 1,
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
name: 'Mercury',
|
|
629
|
+
fullDegree: 267.157142291648,
|
|
630
|
+
normDegree: 27.157142291647972,
|
|
631
|
+
speed: -1.129353838866472,
|
|
632
|
+
isRetro: 'true',
|
|
633
|
+
sign: 'Sagittarius',
|
|
634
|
+
house: 12,
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
name: 'Jupiter',
|
|
638
|
+
fullDegree: 286.74844079893995,
|
|
639
|
+
normDegree: 16.748440798939953,
|
|
640
|
+
speed: 0.21989257845513263,
|
|
641
|
+
isRetro: 'false',
|
|
642
|
+
sign: 'Capricorn',
|
|
643
|
+
house: 12,
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
name: 'Venus',
|
|
647
|
+
fullDegree: 302.46109957231073,
|
|
648
|
+
normDegree: 2.4610995723107294,
|
|
649
|
+
speed: 1.1733155507134867,
|
|
650
|
+
isRetro: 'false',
|
|
651
|
+
sign: 'Aquarius',
|
|
652
|
+
house: 1,
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
name: 'Saturn',
|
|
656
|
+
fullDegree: 232.5728863112664,
|
|
657
|
+
normDegree: 22.572886311266387,
|
|
658
|
+
speed: 0.1112696563244877,
|
|
659
|
+
isRetro: 'false',
|
|
660
|
+
sign: 'Scorpio',
|
|
661
|
+
house: 10,
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
name: 'Uranus',
|
|
665
|
+
fullDegree: 254.1300701628793,
|
|
666
|
+
normDegree: 14.130070162879292,
|
|
667
|
+
speed: 0.06114507772452163,
|
|
668
|
+
isRetro: 'false',
|
|
669
|
+
sign: 'Sagittarius',
|
|
670
|
+
house: 11,
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
name: 'Neptune',
|
|
674
|
+
fullDegree: 270.70233881243786,
|
|
675
|
+
normDegree: 0.7023388124378585,
|
|
676
|
+
speed: 0.037269287688777034,
|
|
677
|
+
isRetro: 'false',
|
|
678
|
+
sign: 'Capricorn',
|
|
679
|
+
house: 12,
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
name: 'Pluto',
|
|
683
|
+
fullDegree: 213.81656661729093,
|
|
684
|
+
normDegree: 3.816566617290931,
|
|
685
|
+
speed: 0.030884363476755057,
|
|
686
|
+
isRetro: 'false',
|
|
687
|
+
sign: 'Scorpio',
|
|
688
|
+
house: 9,
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
name: 'Ascendant',
|
|
692
|
+
fullDegree: 288.3761917494198,
|
|
693
|
+
normDegree: 18.376191749419775,
|
|
694
|
+
speed: 0,
|
|
695
|
+
isRetro: 'false',
|
|
696
|
+
sign: 'Aquarius',
|
|
697
|
+
house: 1,
|
|
698
|
+
},
|
|
699
|
+
];
|
|
700
|
+
},
|
|
701
|
+
},
|
|
702
|
+
});
|