@zodic/shared 0.0.315 → 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 CHANGED
@@ -46,20 +46,20 @@ export const Api = (env: BackendBindings) => ({
46
46
  messages,
47
47
  ...options,
48
48
  });
49
-
49
+
50
50
  if (!response.choices[0].message.content) {
51
51
  throw new Error('Content is null');
52
52
  }
53
-
53
+
54
54
  // ✅ Extract API usage data
55
55
  const { usage } = response;
56
56
  if (usage) {
57
57
  console.log(`📊 API Usage: Tokens - ${usage.total_tokens}`);
58
-
58
+
59
59
  if (env.KV_API_USAGE) {
60
60
  // ✅ Check how many usage logs exist
61
61
  const { keys } = await env.KV_API_USAGE.list({ prefix: 'usage:' });
62
-
62
+
63
63
  if (keys.length < 10) {
64
64
  // ✅ Store new entry ONLY if there are less than 10
65
65
  const timestamp = new Date().toISOString();
@@ -70,22 +70,27 @@ export const Api = (env: BackendBindings) => ({
70
70
  completion_tokens: usage.completion_tokens,
71
71
  timestamp,
72
72
  };
73
-
74
- await env.KV_API_USAGE.put(`usage:${timestamp}`, JSON.stringify(logEntry), {
75
- expirationTtl: 60 * 60 * 24 * 7, // ✅ Store for 7 days
76
- });
77
-
73
+
74
+ await env.KV_API_USAGE.put(
75
+ `usage:${timestamp}`,
76
+ JSON.stringify(logEntry),
77
+ {
78
+ expirationTtl: 60 * 60 * 24 * 7, // ✅ Store for 7 days
79
+ }
80
+ );
81
+
78
82
  console.log(`✅ Logged API usage (Entry ${keys.length + 1}/10)`);
79
83
  } else {
80
- console.log(`⏳ API usage logging skipped: 10 records already exist.`);
84
+ console.log(
85
+ `⏳ API usage logging skipped: 10 records already exist.`
86
+ );
81
87
  }
82
88
  }
83
89
  } else {
84
90
  console.warn('⚠️ API Usage Data Missing');
85
91
  }
86
-
92
+
87
93
  return response.choices[0].message.content.trim();
88
-
89
94
  } catch (err: any) {
90
95
  console.error('❌ Error calling Together API:', err.message);
91
96
  throw err;
@@ -263,9 +268,9 @@ export const Api = (env: BackendBindings) => ({
263
268
  prompt,
264
269
  width,
265
270
  height,
266
- controlNets = [], // Optional
267
- initImageId = null, // Optional
268
- initStrength = 0.2, // Default strength for Image-to-Image
271
+ controlNets = [],
272
+ initImageId = null,
273
+ initStrength = 0.2,
269
274
  quantity = 3,
270
275
  negPrompt = null,
271
276
  }: LeonardoGenerateImageParams & {
@@ -297,7 +302,6 @@ export const Api = (env: BackendBindings) => ({
297
302
  num_images: quantity,
298
303
  };
299
304
 
300
- // ✅ Dynamically add `controlNets` based on available settings
301
305
  if (controlNets.length > 0) {
302
306
  // @ts-expect-error
303
307
  body['controlnets'] = controlNets.map((net) => {
@@ -342,6 +346,100 @@ export const Api = (env: BackendBindings) => ({
342
346
  throw err;
343
347
  }
344
348
  },
349
+
350
+ generatePresignedUrl: async (): Promise<{
351
+ id: string;
352
+ fields: string;
353
+ key: string;
354
+ url: string;
355
+ }> => {
356
+ const endpoint = 'https://cloud.leonardo.ai/api/rest/v1/init-image';
357
+ const headers = {
358
+ Authorization: `Bearer ${env.LEONARDO_API_KEY}`,
359
+ 'Content-Type': 'application/json',
360
+ };
361
+
362
+ try {
363
+ const response = await fetch(endpoint, {
364
+ method: 'POST',
365
+ headers,
366
+ body: JSON.stringify({}),
367
+ });
368
+
369
+ if (!response.ok) {
370
+ const error = await response.json();
371
+ console.error(
372
+ '❌ Error generating presigned URL from Leonardo API:',
373
+ error
374
+ );
375
+ throw new Error(`Leonardo API Error: ${response.status}`);
376
+ }
377
+
378
+ const data = await response.json() as any;
379
+ const result = data.uploadInitImage;
380
+
381
+ if (
382
+ !result ||
383
+ !result.id ||
384
+ !result.url ||
385
+ !result.fields ||
386
+ !result.key
387
+ ) {
388
+ console.error('❌ Invalid response from Leonardo API:', data);
389
+ throw new Error('Invalid response structure from Leonardo API');
390
+ }
391
+
392
+ return {
393
+ id: result.id,
394
+ fields: result.fields,
395
+ key: result.key,
396
+ url: result.url,
397
+ };
398
+ } catch (err: any) {
399
+ console.error('❌ Error generating presigned URL:', err.message);
400
+ throw err;
401
+ }
402
+ },
403
+
404
+ uploadImageToPresignedUrl: async (
405
+ file: File,
406
+ presignedResponse: { fields: string; url: string }
407
+ ): Promise<void> => {
408
+ const rawFields = presignedResponse.fields;
409
+ const fields = JSON.parse(rawFields);
410
+ const url = presignedResponse.url;
411
+
412
+ const formData = new FormData();
413
+
414
+ // Append all fields from the presigned response
415
+ Object.entries(fields).forEach(([key, value]) => {
416
+ formData.append(key, value as string);
417
+ });
418
+
419
+ // Append the file as the last field, as required by Leonardo.ai
420
+ formData.append('file', file);
421
+
422
+ try {
423
+ const response = await fetch(url, {
424
+ method: 'POST',
425
+ body: formData,
426
+ });
427
+
428
+ if (!response.ok) {
429
+ const errorText = await response.text();
430
+ console.error(
431
+ '❌ Error uploading image to presigned URL:',
432
+ errorText
433
+ );
434
+ throw new Error(`Failed to upload image: ${response.status}`);
435
+ }
436
+
437
+ console.log('✅ Successfully uploaded image to Leonardo.ai');
438
+ } catch (err: any) {
439
+ console.error('❌ Error uploading image:', err.message);
440
+ throw err;
441
+ }
442
+ },
345
443
  },
346
444
  callImageDescriber: async (imageUrl: string): Promise<string> => {
347
445
  const mimeType = 'image/png';