Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

sergio-eliot-rodriguez
Copy link
Collaborator

@sergio-eliot-rodriguez sergio-eliot-rodriguez commented Sep 13, 2025

Resolves #8992
Implemented Leonardo AI components expressed by user and later specified in #8992, namely actions
generate-motion
generate-image
unzoom-image
upload-image
upscale-image

Summary by CodeRabbit

  • New Features

    • Generate Image: configurable sizes, steps, guidance, seed, and dynamic model selection.
    • Generate Motion: create videos from images with selectable image IDs and paginated selection.
    • Unzoom Image: create unzoom variations from an image.
    • Upscale Image: generate higher-resolution outputs.
    • Upload Image: supports local file-path/stream uploads via two-step presigned flow; returns upload details.
  • Documentation

    • Added README with overview, API reference links, and support resources.
  • Refactor

    • Improved API client and unified request handling with new helpers.
  • Chores

    • Bumped component version to 0.1.0 and added dependencies.

Copy link

vercel bot commented Sep 13, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
pipedream-docs-redirect-do-not-edit Ignored Ignored Sep 23, 2025 5:36am

Copy link
Contributor

coderabbitai bot commented Sep 13, 2025

Walkthrough

Adds a Leonardo AI component: documentation, five new actions (generate-image, generate-motion, unzoom-image, upload-image, upscale-image), a refactored app client with generic request helpers and upload helpers, and package.json dependency and version updates; removes legacy authKeys. No triggers added.

Changes

Cohort / File(s) Summary of Changes
Documentation
components/leonardo_ai/README.md
New README introducing Leonardo AI component, linking to API reference and support resources.
Actions — image & motion
components/leonardo_ai/actions/generate-image/generate-image.mjs, components/leonardo_ai/actions/generate-motion/generate-motion.mjs
Added Generate Image and Generate Motion actions with props (dynamic model/image options), input validation, payload mapping, and run implementations that call the Leonardo app client's request surface and export summaries.
Actions — image ops
components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs, components/leonardo_ai/actions/upscale-image/upscale-image.mjs
Added Unzoom Image and Upscale Image actions; both accept imageId and optional flags, POST to respective variation endpoints via app._makeRequest, and export success summaries.
Action — upload
components/leonardo_ai/actions/upload-image/upload-image.mjs
Added Upload Image action using file-path/stream approach: obtains file stream/metadata, calls app.getUploadInitImage, constructs FormData (fields then file), uploads to presigned URL via app.uploadFileToPresignedUrl, returns init + upload results and summary.
App client refactor
components/leonardo_ai/leonardo_ai.app.mjs
Replaced legacy authKeys with _baseUrl, _getHeaders, and _makeRequest; added public wrappers: getPlatformModels, getUploadInitImage, uploadFileToPresignedUrl, getUserInfo, getGenerationsByUserId; standardized axios-based request handling and external URL handling.
Package metadata
components/leonardo_ai/package.json
Bumped version to 0.1.0 and added dependencies: @pipedream/platform and form-data.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Action as Generate Image Action
  participant App as Leonardo App Client
  participant API as Leonardo REST API

  User->>Action: Provide prompt, modelId, dimensions, options
  Action->>App: _makeRequest(method: POST, path: /generations, data)
  App->>API: POST /generations (with headers from _getHeaders)
  API-->>App: Generation response
  App-->>Action: Response
  Action-->>User: Export summary + API response
Loading
sequenceDiagram
  autonumber
  actor User
  participant Upload as Upload Image Action
  participant App as Leonardo App Client
  participant API as Leonardo REST API
  participant Storage as Presigned Storage

  User->>Upload: filePath, extension
  Upload->>App: getUploadInitImage({ extension })
  App->>API: POST /upload/init
  API-->>App: { uploadUrl, fields, uploadInitImage }
  App-->>Upload: upload init data
  Upload->>Storage: POST multipart (fields then file stream) to uploadUrl
  Storage-->>Upload: uploadResult (HTTP 200/201)
  Upload-->>User: Export summary + { uploadInitImage, uploadResult }
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

action

Suggested reviewers

  • GTFalcao
  • lcaresia

Poem

I hop on keys and sketch a stream,
New actions bloom like clover seam.
Images, motion, uploads too —
Unzoom, upscale, and dreams anew.
A rabbit cheers: hooray for glue! 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description references resolving #8992 and lists implemented actions but does not follow the repository's required template: the mandatory "WHY" section is missing and the description lacks details about motivation, testing, compatibility, and implementation notes required by the template. Please update the PR description to follow the repository template by adding the "WHY" section that explains the motivation, summarizes the implemented changes, documents testing performed and any compatibility or migration notes, and explicitly links to issue #8992.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Leonardo components" is concise and directly related to the main change set, which adds multiple Leonardo AI components and actions, so it accurately reflects the PR's primary purpose.
Linked Issues Check ✅ Passed The changes implement the coding objectives of issue [#8992] by adding the requested actions (generate-image, generate-motion, unzoom-image, upload-image, upscale-image) and supporting app helpers (model listing, presigned upload flow, user/generation retrieval), which satisfy the primary integration requirements; the PR does not add triggers or the email-sending workflow mentioned in the issue, which appear to be out of scope for this submission.
Out of Scope Changes Check ✅ Passed All modifications are directly related to the Leonardo AI integration: new action modules, app refactoring to centralize requests and upload helpers, README, and package.json dependency updates to support FormData and the platform SDK; there are no unrelated files or features changed in the provided summary.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pipedream-component-development
Copy link
Collaborator

Thank you so much for submitting this! We've added it to our backlog to review, and our team has been notified.

@pipedream-component-development
Copy link
Collaborator

Thanks for submitting this PR! When we review PRs, we follow the Pipedream component guidelines. If you're not familiar, here's a quick checklist:

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (18)
components/leonardo_ai/package.json (1)

4-4: Description overpromises 3D support not implemented in this PR

No 3D actions shipped here. Tighten scope to images and motion to avoid misleading users.

-  "description": "Pipedream Leonardo AI Components - Generate images, videos, and 3D models with AI",
+  "description": "Pipedream Leonardo AI Components - Generate images and motion with AI",
components/leonardo_ai/leonardo_ai.app.mjs (2)

59-65: Platform models response shape: add fallback

Docs often return platform_models (or a data wrapper). Add fallbacks to prevent empty results.

-      return data.custom_models || [];
+      return data.custom_models
+        || data.platform_models
+        || data.data
+        || [];

96-103: Large uploads: set maxBodyLength

S3-style presigned uploads can exceed default Axios limits.

       const response = await axios($, {
         url,
         method: "POST",
         data: formData,
         headers: {
           ...formData.getHeaders(),
         },
+        maxBodyLength: Infinity,
       });
components/leonardo_ai/actions/upscale-image/upscale-image.mjs (1)

9-16: Expose optional upscale params (mode/scale) behind props

Docs mention multiple upscaling modes and 2x/4x. Consider optional props to pass through without breaking current behavior.

   props: {
     app,
     imageId: {
       type: "string",
       label: "Image ID",
       description: "The ID of the image to upscale. This should be a previously generated or uploaded image ID.",
     },
+    scale: {
+      type: "integer",
+      label: "Scale",
+      description: "Upscale factor",
+      options: [2, 4],
+      optional: true,
+    },
+    mode: {
+      type: "string",
+      label: "Mode",
+      options: ["universal", "real-esrgan"],
+      optional: true,
+    },
   },
   async run({ $ }) {
     const { imageId } = this;
 
     const data = {
       id: imageId,
+      ...(this.scale ? { scale: this.scale } : {}),
+      ...(this.mode ? { mode: this.mode } : {}),
     };

Also applies to: 20-28

components/leonardo_ai/actions/upload-image/upload-image.mjs (6)

2-2: Remove unused import

axios is not used in this action.

-import { axios } from "@pipedream/platform";

34-34: Drop stray console.log

Avoid noisy logs in actions.

-    console.log(extension);

36-37: Robust base64 data URI handling

Support case-insensitive media subtypes and raw base64 strings.

-    const base64Data = file.replace(/^data:image\/[a-z]+;base64,/, '');
+    const base64Data = file.replace(/^data:(?:image\/[a-z0-9.+-]+);base64,/i, "");

40-44: Minor: simplify MIME mapping

Tighten jpg→jpeg mapping.

-    const fileObject = {
-      buffer: buffer,
-      name: `image.${extension}`,
-      type: `image/${extension === 'jpg' ? 'jpeg' : extension}`,
-    };
+    const mime = extension === "jpg" ? "jpeg" : extension;
+    const fileObject = { buffer, name: `image.${extension}`, type: `image/${mime}` };

52-55: Defensive parsing of presigned fields

Some APIs return fields as an object. Handle both cases.

-    const { uploadInitImage } = uploadResponse;
-    const fields = JSON.parse(uploadInitImage.fields);
-    const uploadUrl = uploadInitImage.url;
+    const { uploadInitImage } = uploadResponse;
+    const uploadUrl = uploadInitImage.url;
+    const fields = typeof uploadInitImage.fields === "string"
+      ? JSON.parse(uploadInitImage.fields)
+      : uploadInitImage.fields;

16-21: ESLint: object properties on new lines

Format option objects per lint rules.

-        { label: "PNG", value: "png" },
-        { label: "JPG", value: "jpg" },
-        { label: "JPEG", value: "jpeg" },
-        { label: "WebP", value: "webp" },
+        {
+          label: "PNG",
+          value: "png",
+        },
+        {
+          label: "JPG",
+          value: "jpg",
+        },
+        {
+          label: "JPEG",
+          value: "jpeg",
+        },
+        {
+          label: "WebP",
+          value: "webp",
+        },
components/leonardo_ai/README.md (3)

53-57: Upscale capabilities overstated

Current action doesn’t expose modes or scale factors. Adjust docs or add props.

 **Key Features:**
-- Multiple upscaling modes (Universal Upscaler, Real-ESRGAN)
-- 2x and 4x scale factors
-- High-quality image enhancement
+- High-quality image enhancement of an existing image ID

96-99: Link to specific endpoint docs

Point to endpoint pages for the actions added.

-For detailed information about Leonardo AI's API, visit the [official documentation](https://docs.leonardo.ai/reference).
+For detailed information about Leonardo AI's API, visit the [official documentation](https://docs.leonardo.ai/reference), including Generations, Variations, Motion, and Upload endpoints.

1-114: Confirmed endpoints in official docs — minor README example fixes recommended

Official docs (docs.leonardo.ai) document these endpoints and example request bodies: POST /generations, POST /generations-motion-svd, POST /variations/unzoom, POST /variations/upscale, POST /init-image (presigned upload init), and GET /platformModels. Action: update README examples in components/leonardo_ai/README.md (lines 1–114) to exactly match the official request-body shapes and types (e.g., use numeric types for numeric params like guidanceScale, motionStrength, numImages/numInferenceSteps/seed and ensure field names match the docs).

components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (1)

29-32: Send isVariation only when true to avoid unnecessary payload fields.

Slightly cleaner and avoids surprising APIs that treat falsey flags differently.

-    const data = {
-      id: imageId,
-      isVariation,
-    };
+    const data = { id: imageId };
+    if (isVariation) data.isVariation = true;
components/leonardo_ai/actions/generate-motion/generate-motion.mjs (2)

16-21: Add bounds for motionStrength.

Expose UI hints (min/max) to prevent invalid requests and guide users.

     motionStrength: {
       type: "integer",
       label: "Motion Strength",
-      description: "The motion strength for the video generation.",
+      description: "The motion strength for the video generation.",
+      min: 1,
+      max: 10,
       optional: true,
     },

22-27: Consider defaulting isPublic to false.

Prevents accidental public generations if the API defaults differ from expectations.

     isPublic: {
       type: "boolean",
       label: "Is Public",
       description: "Whether the generation is public or not.",
-      optional: true,
+      optional: true,
+      default: false,
     },
components/leonardo_ai/actions/generate-image/generate-image.mjs (2)

39-39: Fix lint: trailing space on the label line.

This is breaking the PR checks.

-      label: "Height", 
+      label: "Height",

97-99: Validate and guard guidanceScale to avoid sending NaN or out-of-range values.

Parses safely and enforces the documented 1–20 range.

-    if (guidanceScale) {
-      data.guidance_scale = parseFloat(guidanceScale);
-    }
+    if (guidanceScale !== undefined && guidanceScale !== "") {
+      const gs = parseFloat(guidanceScale);
+      if (Number.isNaN(gs)) {
+        throw new Error("Guidance Scale must be a number between 1 and 20.");
+      }
+      if (gs < 1 || gs > 20) {
+        throw new Error("Guidance Scale must be between 1 and 20.");
+      }
+      data.guidance_scale = gs;
+    }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 194d1ef and 1f0e5b4.

📒 Files selected for processing (9)
  • components/leonardo_ai/README.md (1 hunks)
  • components/leonardo_ai/actions/generate-image/generate-image.mjs (1 hunks)
  • components/leonardo_ai/actions/generate-motion/generate-motion.mjs (1 hunks)
  • components/leonardo_ai/actions/leonardo_ai_actions.mdc (1 hunks)
  • components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (1 hunks)
  • components/leonardo_ai/actions/upload-image/upload-image.mjs (1 hunks)
  • components/leonardo_ai/actions/upscale-image/upscale-image.mjs (1 hunks)
  • components/leonardo_ai/leonardo_ai.app.mjs (1 hunks)
  • components/leonardo_ai/package.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
components/leonardo_ai/actions/generate-motion/generate-motion.mjs (4)
components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (2)
  • data (29-32)
  • response (34-38)
components/leonardo_ai/actions/generate-image/generate-image.mjs (2)
  • data (87-92)
  • response (107-111)
components/leonardo_ai/actions/upscale-image/upscale-image.mjs (2)
  • data (20-22)
  • response (24-28)
components/leonardo_ai/leonardo_ai.app.mjs (3)
  • data (60-63)
  • data (67-73)
  • response (96-103)
components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (4)
components/leonardo_ai/actions/generate-image/generate-image.mjs (2)
  • data (87-92)
  • response (107-111)
components/leonardo_ai/actions/generate-motion/generate-motion.mjs (2)
  • data (50-52)
  • response (67-71)
components/leonardo_ai/actions/upscale-image/upscale-image.mjs (2)
  • data (20-22)
  • response (24-28)
components/leonardo_ai/leonardo_ai.app.mjs (3)
  • data (60-63)
  • data (67-73)
  • response (96-103)
components/leonardo_ai/actions/upscale-image/upscale-image.mjs (4)
components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (2)
  • data (29-32)
  • response (34-38)
components/leonardo_ai/actions/generate-image/generate-image.mjs (2)
  • data (87-92)
  • response (107-111)
components/leonardo_ai/actions/generate-motion/generate-motion.mjs (2)
  • data (50-52)
  • response (67-71)
components/leonardo_ai/leonardo_ai.app.mjs (3)
  • data (60-63)
  • data (67-73)
  • response (96-103)
components/leonardo_ai/actions/generate-image/generate-image.mjs (1)
components/leonardo_ai/leonardo_ai.app.mjs (3)
  • data (60-63)
  • data (67-73)
  • response (96-103)
components/leonardo_ai/leonardo_ai.app.mjs (5)
components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (2)
  • data (29-32)
  • response (34-38)
components/leonardo_ai/actions/generate-image/generate-image.mjs (2)
  • data (87-92)
  • response (107-111)
components/leonardo_ai/actions/generate-motion/generate-motion.mjs (2)
  • data (50-52)
  • response (67-71)
components/leonardo_ai/actions/upscale-image/upscale-image.mjs (2)
  • data (20-22)
  • response (24-28)
components/leonardo_ai/actions/upload-image/upload-image.mjs (1)
  • fields (53-53)
🪛 GitHub Actions: Pull Request Checks
components/leonardo_ai/README.md

[warning] File ignored because of a matching ignore pattern. Use '--no-ignore' to disable file ignore settings or use '--no-warn-ignored' to suppress this warning. Command: pnpm exec eslint components/leonardo_ai/README.md components/leonardo_ai/actions/generate-image/generate-image.mjs components/leonardo_ai/actions/generate-motion/generate-motion.mjs components/leonardo_ai/actions/leonardo_ai_actions.mdc components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs components/leonardo_ai/actions/upload-image/upload-image.mjs components/leonardo_ai/actions/upscale-image/upscale-image.mjs components/leonardo_ai/leonardo_ai.app.mjs components/leonardo_ai/package.json

components/leonardo_ai/actions/generate-image/generate-image.mjs

[error] 39-39: Trailing spaces not allowed (no-trailing-spaces). Command: pnpm exec eslint components/leonardo_ai/README.md components/leonardo_ai/actions/generate-image/generate-image.mjs components/leonardo_ai/actions/generate-motion/generate-motion.mjs components/leonardo_ai/actions/leonardo_ai_actions.mdc components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs components/leonardo_ai/actions/upload-image/upload-image.mjs components/leonardo_ai/actions/upscale-image/upscale-image.mjs components/leonardo_ai/leonardo_ai.app.mjs components/leonardo_ai/package.json

🪛 GitHub Check: Lint Code Base
components/leonardo_ai/actions/generate-image/generate-image.mjs

[failure] 39-39:
Trailing spaces not allowed

components/leonardo_ai/actions/upload-image/upload-image.mjs

[failure] 19-19:
Object properties must go on a new line


[failure] 19-19:
Expected a line break after this opening brace


[failure] 18-18:
Expected a line break before this closing brace


[failure] 18-18:
Object properties must go on a new line


[failure] 18-18:
Expected a line break after this opening brace


[failure] 17-17:
Expected a line break before this closing brace


[failure] 17-17:
Object properties must go on a new line


[failure] 17-17:
Expected a line break after this opening brace


[failure] 2-2:
'axios' is defined but never used

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
  • GitHub Check: pnpm publish
🔇 Additional comments (9)
components/leonardo_ai/package.json (2)

17-17: License alignment with repo

Confirm this package’s MIT license matches the monorepo’s overall license and publishing intent.

Would you like me to update this to match the repo’s root license if different?


18-20: Publishing config: verify intent to publish to npm

If this package isn't meant for npm, remove publishConfig to avoid accidental publishes.

-  "publishConfig": {
-    "access": "public"
-  },
+  // "publishConfig": { "access": "public" },
components/leonardo_ai/leonardo_ai.app.mjs (2)

66-75: Return shape vs. action expectation

getUploadInitImage returns data, while the action destructures { uploadInitImage }. Ensure shapes match.

Option A (return just the object expected by actions):

-      const data = await this.post({ $, path: "/init-image", data: { extension } });
-      return data;
+      const data = await this.post({ $, path: "/init-image", data: { extension } });
+      return data.uploadInitImage ?? data;

If API differs, update the action accordingly.


9-11: Endpoints naming/paths: confirmed against Leonardo API docs

All listed endpoints exist; docs/examples for request/response shapes:

File: components/leonardo_ai/leonardo_ai.app.mjs — lines 9–11, 59–70, 106.

components/leonardo_ai/actions/upscale-image/upscale-image.mjs (1)

24-31: LGTM for basic upscale call

Straightforward call with summary export looks good.

components/leonardo_ai/actions/upload-image/upload-image.mjs (1)

46-55: Return shape coupling with app method

This action assumes getUploadInitImage returns { uploadInitImage }. Confirm API response or harmonize app method to return that shape.

I can align both sides once we confirm the API payload.

components/leonardo_ai/README.md (1)

19-25: Generate Image features may be inaccurate

Docs list guidance scale, inference steps, seed, model selection, but the action snippet shows only prompt/size/count. Align docs with actual props.

I can update the README once the action’s final prop set is confirmed.

components/leonardo_ai/actions/leonardo_ai_actions.mdc (1)

1-26: Catalog entries look good and match the implemented actions.

No functional concerns from this static metadata.

components/leonardo_ai/actions/generate-motion/generate-motion.mjs (1)

67-71: Confirmed: endpoint and payload are correct.

POST https://cloud.leonardo.ai/api/rest/v1/generations-motion-svd accepts JSON fields imageId, motionStrength, isPublic, isInitImage, and isVariation — use the imageId from "Get Single Generation" (not the generationId).

Comment on lines +12 to +18
_getHeaders() {
return {
"Content-Type": "application/json",
"Authorization": `Bearer ${this.$auth.api_key}`,
"Accept": "application/json",
};
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid forcing Content-Type globally

Setting Content-Type: application/json for all verbs complicates non‑JSON requests. Let POST/PUT set it when needed.

-    _getHeaders() {
+    _getHeaders() {
       return {
-        "Content-Type": "application/json",
         "Authorization": `Bearer ${this.$auth.api_key}`,
         "Accept": "application/json",
       };
     },
+    _jsonHeaders() {
+      return { "Content-Type": "application/json" };
+    },

And in post(...):

-        method: "POST",
-        data,
+        method: "POST",
+        data,
+        headers: { ...this._jsonHeaders() },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_getHeaders() {
return {
"Content-Type": "application/json",
"Authorization": `Bearer ${this.$auth.api_key}`,
"Accept": "application/json",
};
},
_getHeaders() {
return {
"Authorization": `Bearer ${this.$auth.api_key}`,
"Accept": "application/json",
};
},
_jsonHeaders() {
return { "Content-Type": "application/json" };
},
🤖 Prompt for AI Agents
In components/leonardo_ai/leonardo_ai.app.mjs around lines 12 to 18, the helper
_getHeaders() force-sets "Content-Type: application/json" for all requests which
breaks non-JSON verbs; remove "Content-Type" from the global headers returned by
_getHeaders(), keep Authorization and Accept, and ensure request-specific
methods (e.g., post and put) add "Content-Type: application/json" only when
sending a JSON body; also make those methods merge any caller-supplied headers
so they do not get overwritten and only set the content-type if not already
provided.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (2)
components/leonardo_ai/leonardo_ai.app.mjs (2)

12-18: Don't set Content-Type globally; add JSON-specific helper instead

Forcing Content-Type: application/json on all requests breaks multipart/form-data and other non‑JSON calls. Define JSON headers separately.

     _getHeaders() {
-      return {
-        "Content-Type": "application/json",
-        "Authorization": `Bearer ${this.$auth.api_key}`,
-        "Accept": "application/json",
-      };
+      return {
+        "Authorization": `Bearer ${this.$auth.api_key}`,
+        "Accept": "application/json",
+      };
     },
+    _jsonHeaders() {
+      return { "Content-Type": "application/json" };
+    },

22-27: Caller headers are overwritten; merge them so callers can set per‑request headers

As written, opts.headers are dropped. Merge defaults with user headers, letting user values override.

-      return axios($, {
-        ...opts,
-        url: `${this._baseUrl()}${path}`,
-        headers: this._getHeaders(),
-      });
+      const { headers: userHeaders, ...rest } = opts || {};
+      return axios($, {
+        ...rest,
+        url: `${this._baseUrl()}${path}`,
+        headers: {
+          ...this._getHeaders(),
+          ...(userHeaders || {}),
+        },
+      });
🧹 Nitpick comments (4)
components/leonardo_ai/leonardo_ai.app.mjs (2)

28-37: Ensure JSON POSTs set Content-Type only when needed

With Content-Type removed from global headers, set it here and still allow caller override.

     async post({
       $ = this, path, data, ...opts
     }) {
       return this._makeRequest({
         $,
         path,
         method: "POST",
         data,
+        headers: {
+          ...this._jsonHeaders(),
+          ...(opts?.headers || {}),
+        },
         ...opts,
       });
     },

103-110: Large uploads: raise limits and set a sensible timeout

Prevent axios from choking on bigger files and avoid hanging requests.

       const response = await axios($, {
         url,
         method: "POST",
         data: formData,
         headers: {
           ...formData.getHeaders(),
         },
+        maxBodyLength: Infinity,
+        maxContentLength: Infinity,
+        timeout: 60000,
       });
components/leonardo_ai/actions/upload-image/upload-image.mjs (2)

46-46: Drop debug log

Avoid noisy console output in actions.

-    console.log(extension);

67-70: Handle both string and object forms of fields

Some APIs return fields as an object already. Be tolerant to both.

-    const fields = JSON.parse(uploadInitImage.fields);
+    const fields = typeof uploadInitImage.fields === "string"
+      ? JSON.parse(uploadInitImage.fields)
+      : uploadInitImage.fields;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f0e5b4 and 43c2645.

📒 Files selected for processing (4)
  • components/leonardo_ai/actions/generate-image/generate-image.mjs (1 hunks)
  • components/leonardo_ai/actions/upload-image/upload-image.mjs (1 hunks)
  • components/leonardo_ai/leonardo_ai.app.mjs (1 hunks)
  • components/leonardo_ai/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • components/leonardo_ai/package.json
  • components/leonardo_ai/actions/generate-image/generate-image.mjs
🧰 Additional context used
🧬 Code graph analysis (1)
components/leonardo_ai/leonardo_ai.app.mjs (5)
components/leonardo_ai/actions/generate-image/generate-image.mjs (2)
  • data (87-92)
  • response (107-111)
components/leonardo_ai/actions/generate-motion/generate-motion.mjs (2)
  • data (50-52)
  • response (67-71)
components/leonardo_ai/actions/upscale-image/upscale-image.mjs (2)
  • data (20-22)
  • response (24-28)
components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (2)
  • data (29-32)
  • response (34-38)
components/leonardo_ai/actions/upload-image/upload-image.mjs (1)
  • fields (68-68)
🪛 GitHub Check: Lint Code Base
components/leonardo_ai/actions/upload-image/upload-image.mjs

[failure] 57-57:
Expected indentation of 10 spaces but found 8


[failure] 56-56:
Trailing spaces not allowed


[failure] 49-49:
Strings must use doublequote


[failure] 48-48:
Strings must use doublequote


[failure] 31-31:
Missing trailing comma


[failure] 31-31:
Trailing spaces not allowed


[failure] 27-27:
Missing trailing comma


[failure] 23-23:
Missing trailing comma


[failure] 19-19:
Missing trailing comma


[failure] 2-2:
'axios' is defined but never used

🪛 GitHub Actions: Pull Request Checks
components/leonardo_ai/actions/upload-image/upload-image.mjs

[error] 2-2: ESLint: 'axios' is defined but never used. (no-unused-vars)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Verify TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
🔇 Additional comments (1)
components/leonardo_ai/leonardo_ai.app.mjs (1)

59-65: Verify response shape from /platformModels

Returning data.custom_models may be incorrect depending on API. Confirm actual field and provide resilient fallback.

-      return data.custom_models || [];
+      return data?.custom_models ?? data?.models ?? data?.platformModels ?? [];

If you want, I can cross-check the latest Leonardo API docs and update accordingly.

Copy link
Collaborator

@GTFalcao GTFalcao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @sergio-eliot-rodriguez , I've left some comments for you to check out. Some of them apply to multiple components, make sure they're all following the same standards.

Comment on lines 78 to 80
async uploadFileToPresignedUrl({
$, url, fields, file,
}) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can reuse the base request method just fine, as long as you don't overwrite the headers there. Have a look at similar components that use form-data requests. Normally you build the FormData object within the action itself instead of here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sergio-eliot-rodriguez can you refactor this one to use _makeRequest? Or is there something that prevents it in this specific case?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Needed some abstractions because of the formdata object and the preseigned url needed to be pass as is and is different from the usual api url. It is refactored now.

jocarino and others added 18 commits September 21, 2025 13:59
* fix: pagination prop and params struct

* fix: no need for paginate here

* chore: update version

* chore: cleanup

* chore: update package

* feat: allow raw response

* chore: bump package

* fix: buffer response instead

* Update components/google_drive/actions/download-file/download-file.mjs

Co-authored-by: Jorge Cortes <[email protected]>

* versions

* pnpm-lock.yaml

* pnpm-lock.yaml

* pnpm-lock.yaml

* feat: add content selector

* chore: bump package

* fix: comments

* chore: bump versions

* chore: fix versions

* fixes: QA fixes

* feat: add cursor to req

* package.json

---------

Co-authored-by: joao <[email protected]>
Co-authored-by: joaocoform <[email protected]>
Co-authored-by: Jorge Cortes <[email protected]>
Co-authored-by: Michelle Bergeron <[email protected]>
Co-authored-by: Luan Cazarine <[email protected]>
* update siteId prop

* pnpm-lock.yaml

* package.json version
* change prop order and refresh fields

* bump package.json
* use pipedriveApp instead of app

* bump package.json
* pipelineId - integer

* bump versions
* updates

* versions

* versions
* updates

* remove console.log

* versions
* new component

* pnpm-lock.yaml

* updates

* updates
* update search-crm

* limit results to one page

* update version

* package.json version
* widget props

* fix version
* Update Taiga component with new actions and sources

- Bump version to 0.1.0 in package.json and add dependency on @pipedream/platform.
- Introduce new actions for creating, updating, and deleting issues, tasks, and user stories.
- Add sources for tracking changes and deletions of issues and tasks.
- Implement utility functions for parsing and cleaning objects in common/utils.mjs.
- Enhance prop definitions for better integration with Taiga API.

* pnpm update

* Refactor Taiga actions to utilize parseObject utility

- Added parseObject utility for tags, watchers, and points in update-issue, update-task, and update-userstory actions.
- Removed the update-project action as it is no longer needed.
- Enhanced base source to include secret key validation for webhook security.
* add testSources prop

* pnpm-lock.yaml

* fix
* Added actions

* Added actions

* Added actions
* Added actions

* Update components/weaviate/actions/create-class/create-class.mjs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: Luan Cazarine <[email protected]>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
components/leonardo_ai/leonardo_ai.app.mjs (1)

62-69: Allow large multipart uploads.

For presigned S3 POSTs, axios needs relaxed limits for big files.

       const response = await axios($, {
         url,
         method: "POST",
         data: formData,
         headers: {
           ...formData.getHeaders(),
         },
+        maxContentLength: Infinity,
+        maxBodyLength: Infinity,
       });
components/leonardo_ai/actions/upload-image/upload-image.mjs (1)

65-73: Make fields parsing resilient (string or object).

Leonardo/S3 responses sometimes return fields as an object. Guard against double‑parse.

-    const { uploadInitImage } = uploadResponse;
-    const fields = JSON.parse(uploadInitImage.fields);
+    const { uploadInitImage } = uploadResponse;
+    const fields = typeof uploadInitImage.fields === "string"
+      ? JSON.parse(uploadInitImage.fields)
+      : uploadInitImage.fields;
components/leonardo_ai/actions/generate-motion/generate-motion.mjs (1)

22-39: Consider adding optional callback URL.

If the endpoint supports a callback, expose callbackUrl to help users chain workflows without polling.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 185b2cd and b5f7dce.

📒 Files selected for processing (8)
  • components/leonardo_ai/README.md (1 hunks)
  • components/leonardo_ai/actions/generate-image/generate-image.mjs (1 hunks)
  • components/leonardo_ai/actions/generate-motion/generate-motion.mjs (1 hunks)
  • components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (1 hunks)
  • components/leonardo_ai/actions/upload-image/upload-image.mjs (1 hunks)
  • components/leonardo_ai/actions/upscale-image/upscale-image.mjs (1 hunks)
  • components/leonardo_ai/leonardo_ai.app.mjs (1 hunks)
  • components/leonardo_ai/package.json (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • components/leonardo_ai/actions/upscale-image/upscale-image.mjs
  • components/leonardo_ai/package.json
  • components/leonardo_ai/README.md
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#18362
File: components/leonardo_ai/README.md:45-49
Timestamp: 2025-09-15T22:01:17.593Z
Learning: In Leonardo AI components (and likely other Pipedream components), prefer using info alert props on the component itself rather than detailed "Key Features" sections in README files for action documentation.
📚 Learning: 2025-09-15T22:01:11.472Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#18362
File: components/leonardo_ai/actions/generate-image/generate-image.mjs:103-105
Timestamp: 2025-09-15T22:01:11.472Z
Learning: In Pipedream components, pipedream/platform's axios implementation automatically excludes undefined values from HTTP requests, so there's no need to manually check for truthiness before including properties in request payloads.

Applied to files:

  • components/leonardo_ai/leonardo_ai.app.mjs
  • components/leonardo_ai/actions/upload-image/upload-image.mjs
  • components/leonardo_ai/actions/generate-image/generate-image.mjs
📚 Learning: 2025-01-29T22:59:38.825Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#15436
File: components/printful/printful.app.mjs:55-63
Timestamp: 2025-01-29T22:59:38.825Z
Learning: Console.log statements should be removed before merging PRs to maintain code quality and prevent potential security risks from exposing sensitive information in logs.

Applied to files:

  • components/leonardo_ai/actions/upload-image/upload-image.mjs
🧬 Code graph analysis (4)
components/leonardo_ai/leonardo_ai.app.mjs (5)
components/leonardo_ai/actions/generate-image/generate-image.mjs (2)
  • data (90-99)
  • response (101-106)
components/leonardo_ai/actions/generate-motion/generate-motion.mjs (2)
  • data (50-56)
  • response (58-63)
components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (2)
  • data (29-32)
  • response (34-39)
components/leonardo_ai/actions/upscale-image/upscale-image.mjs (2)
  • data (20-22)
  • response (24-29)
components/leonardo_ai/actions/upload-image/upload-image.mjs (1)
  • formData (67-67)
components/leonardo_ai/actions/generate-motion/generate-motion.mjs (4)
components/leonardo_ai/actions/generate-image/generate-image.mjs (2)
  • data (90-99)
  • response (101-106)
components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (2)
  • data (29-32)
  • response (34-39)
components/leonardo_ai/actions/upscale-image/upscale-image.mjs (2)
  • data (20-22)
  • response (24-29)
components/leonardo_ai/leonardo_ai.app.mjs (3)
  • data (40-43)
  • data (49-56)
  • response (62-69)
components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (4)
components/leonardo_ai/actions/generate-image/generate-image.mjs (2)
  • data (90-99)
  • response (101-106)
components/leonardo_ai/actions/generate-motion/generate-motion.mjs (2)
  • data (50-56)
  • response (58-63)
components/leonardo_ai/actions/upscale-image/upscale-image.mjs (2)
  • data (20-22)
  • response (24-29)
components/leonardo_ai/leonardo_ai.app.mjs (3)
  • data (40-43)
  • data (49-56)
  • response (62-69)
components/leonardo_ai/actions/generate-image/generate-image.mjs (1)
components/leonardo_ai/leonardo_ai.app.mjs (3)
  • data (40-43)
  • data (49-56)
  • response (62-69)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Lint Code Base
  • GitHub Check: Publish TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (7)
components/leonardo_ai/leonardo_ai.app.mjs (2)

12-18: Don’t set Content-Type globally; set JSON only when appropriate.

Global "Content-Type": "application/json" breaks/burdens non‑JSON requests. Add JSON content type only for JSON bodies; keep _getHeaders() lean. Also, add a sane timeout and avoid clobbering user-provided content-type.

@@
     _getHeaders() {
       return {
-        "Content-Type": "application/json",
         "Authorization": `Bearer ${this.$auth.api_key}`,
         "Accept": "application/json",
       };
     },
@@
     async _makeRequest({
       $ = this, 
       method = "GET", 
       path, 
       data, 
       ...opts
     }) {
       const { headers: userHeaders, ...rest } = opts;
-      const config = {
+      const baseHeaders = {
+        ...this._getHeaders(),
+        ...(userHeaders || {}),
+      };
+      // Add JSON content-type only when sending a plain JSON body and the caller didn't set one
+      const needsJson =
+        ["POST","PUT","PATCH"].includes(String(method).toUpperCase()) &&
+        data !== undefined &&
+        !(data && typeof data.getHeaders === "function") &&
+        !Object.keys(baseHeaders).some((h) => h.toLowerCase() === "content-type");
+      const headers = needsJson
+        ? { "Content-Type": "application/json", ...baseHeaders }
+        : baseHeaders;
+
+      const config = {
         method,
         ...rest,
         url: `${this._baseUrl()}${path}`,
-        headers: {
-          ...this._getHeaders(),
-          ...(userHeaders || {}),
-        },
+        headers,
         data,
+        timeout: 30000,
       };
       return await axios($, config);
     },

Also applies to: 19-38


39-45: Verify response shape from /platformModels.

You return data.custom_models || []. If the API returns a different key (e.g., platformModels), this will silently return an empty list.

Would you confirm the exact response shape and adjust accordingly?

components/leonardo_ai/actions/upload-image/upload-image.mjs (2)

80-86: LGTM overall.

Two‑step presigned flow, field ordering, and FormData construction look correct.


54-57: Don't pass this.syncDir — helper has a single-arg signature. getFileStreamAndMetadata is declared as getFileStreamAndMetadata(pathOrUrl: string) in platform/lib/file-stream.ts:43 and does not accept a second parameter; to forward this.syncDir you must change that helper and update all callers (e.g. components/leonardo_ai/actions/upload-image/upload-image.mjs:57).

Likely an incorrect or invalid review comment.

components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (1)

34-39: LGTM.

Clean payload mapping and summary. Defaults sensible.

components/leonardo_ai/actions/generate-image/generate-image.mjs (1)

101-106: LGTM otherwise.

Payload mapping and options source look consistent with the app client.

components/leonardo_ai/actions/generate-motion/generate-motion.mjs (1)

41-56: Validate mutually-exclusive flags; defer motionStrength range enforcement until API-confirmed.

Docs show motionStrength is an integer but no published min/max — enforce isInitImage XOR isVariation now; confirm numeric bounds with the API/support before enforcing a range.

File: components/leonardo_ai/actions/generate-motion/generate-motion.mjs Lines: 41-56

  async run({ $ }) {
    const {
      imageId,
      motionStrength,
      isPublic,
      isInitImage,
      isVariation,
    } = this;

+    if (isInitImage && isVariation) {
+      throw new Error("Choose either 'Is Init Image' or 'Is Variation', not both.");
+    }
+    // TODO: enforce allowed range once confirmed (e.g., 1–10)
+    // if (motionStrength !== undefined && (motionStrength < 1 || motionStrength > 10)) {
+    //   throw new Error("Motion Strength must be between 1 and 10.");
+    // }

Comment on lines 78 to 99
async run({ $ }) {
const {
prompt,
modelId,
width,
height,
numImages,
guidanceScale,
numInferenceSteps,
seed,
} = this;

const data = {
prompt,
width,
height,
num_images: numImages,
modelId,
guidance_scale: guidanceScale,
num_inference_steps: numInferenceSteps,
seed: seed
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Enforce image count vs. size constraint before calling the API.

Leonardo limits num_images to 1–4 when either dimension > 768. Validate to prevent API errors.

   async run({ $ }) {
     const {
       prompt,
       modelId,
       width,
       height,
       numImages,
       guidanceScale,
       numInferenceSteps,
       seed,
     } = this;

+    if ((width > 768 || height > 768) && numImages > 4) {
+      throw new Error("When width or height exceeds 768, Number of Images must be 1–4.");
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async run({ $ }) {
const {
prompt,
modelId,
width,
height,
numImages,
guidanceScale,
numInferenceSteps,
seed,
} = this;
const data = {
prompt,
width,
height,
num_images: numImages,
modelId,
guidance_scale: guidanceScale,
num_inference_steps: numInferenceSteps,
seed: seed
};
async run({ $ }) {
const {
prompt,
modelId,
width,
height,
numImages,
guidanceScale,
numInferenceSteps,
seed,
} = this;
if ((width > 768 || height > 768) && numImages > 4) {
throw new Error("When width or height exceeds 768, Number of Images must be 1–4.");
}
const data = {
prompt,
width,
height,
num_images: numImages,
modelId,
guidance_scale: guidanceScale,
num_inference_steps: numInferenceSteps,
seed: seed
};
🤖 Prompt for AI Agents
In components/leonardo_ai/actions/generate-image/generate-image.mjs around lines
78 to 99, add validation before constructing the data payload to enforce
Leonardo's constraint: if either width or height is > 768 then ensure numImages
is between 1 and 4; if not, throw a clear error (or return a rejected Promise)
indicating "numImages must be between 1 and 4 when width or height exceeds 768".
Perform this check early (before building the data object) so the API call is
never attempted with invalid parameters.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
components/leonardo_ai/leonardo_ai.app.mjs (1)

29-40: Add a sane default timeout to outgoing requests

Avoid hanging executions on network stalls.

       const config = {
         method,
         ...rest,
         url: `${this._baseUrl()}${path}`,
+        timeout: 60_000,
         headers: {
           ...this._getHeaders(),
           ...(userHeaders || {}),
         },
         data,
       };
components/leonardo_ai/actions/generate-image/generate-image.mjs (1)

20-26: Ensure models list populates after app fix

Once getPlatformModels() returns platformModels, this options() should work. If the endpoint is unavailable, consider try/catch and return [] to keep the UI usable.

       async options() {
-        const models = await this.app.getPlatformModels();
+        let models = [];
+        try {
+          models = await this.app.getPlatformModels();
+        } catch {
+          return [];
+        }
         return models.map((model) => ({
           label: model.name || model.id,
           value: model.id,
         }));
       },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b5f7dce and 1d35b09.

📒 Files selected for processing (4)
  • components/leonardo_ai/actions/generate-image/generate-image.mjs (1 hunks)
  • components/leonardo_ai/actions/generate-motion/generate-motion.mjs (1 hunks)
  • components/leonardo_ai/actions/upload-image/upload-image.mjs (1 hunks)
  • components/leonardo_ai/leonardo_ai.app.mjs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • components/leonardo_ai/actions/generate-motion/generate-motion.mjs
  • components/leonardo_ai/actions/upload-image/upload-image.mjs
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#18362
File: components/leonardo_ai/README.md:45-49
Timestamp: 2025-09-15T22:01:17.593Z
Learning: In Leonardo AI components (and likely other Pipedream components), prefer using info alert props on the component itself rather than detailed "Key Features" sections in README files for action documentation.
📚 Learning: 2025-09-15T22:01:11.472Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#18362
File: components/leonardo_ai/actions/generate-image/generate-image.mjs:103-105
Timestamp: 2025-09-15T22:01:11.472Z
Learning: In Pipedream components, pipedream/platform's axios implementation automatically excludes undefined values from HTTP requests, so there's no need to manually check for truthiness before including properties in request payloads.

Applied to files:

  • components/leonardo_ai/actions/generate-image/generate-image.mjs
  • components/leonardo_ai/leonardo_ai.app.mjs
🧬 Code graph analysis (2)
components/leonardo_ai/actions/generate-image/generate-image.mjs (1)
components/leonardo_ai/leonardo_ai.app.mjs (3)
  • data (42-45)
  • data (51-58)
  • response (64-71)
components/leonardo_ai/leonardo_ai.app.mjs (5)
components/leonardo_ai/actions/generate-image/generate-image.mjs (2)
  • data (90-99)
  • response (101-106)
components/leonardo_ai/actions/generate-motion/generate-motion.mjs (2)
  • data (50-56)
  • response (58-63)
components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (2)
  • data (29-32)
  • response (34-39)
components/leonardo_ai/actions/upscale-image/upscale-image.mjs (2)
  • data (20-22)
  • response (24-29)
components/leonardo_ai/actions/upload-image/upload-image.mjs (1)
  • formData (67-67)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Lint Code Base
🔇 Additional comments (3)
components/leonardo_ai/leonardo_ai.app.mjs (2)

11-17: Don’t force Content-Type globally; set JSON CT conditionally in _makeRequest

Keep Authorization/Accept in defaults and only add Content-Type: application/json for non-FormData POST/PUT/PATCH when callers haven’t provided a CT header. This avoids breaking non‑JSON requests and keeps callers in control.

@@
     _getHeaders() {
       return {
-        "Content-Type": "application/json",
         "Authorization": `Bearer ${this.$auth.api_key}`,
         "Accept": "application/json",
       };
     },
@@
       const config = {
         method,
         ...rest,
         url: `${this._baseUrl()}${path}`,
-        headers: {
-          ...this._getHeaders(),
-          ...(userHeaders || {}),
-        },
+        headers: {
+          ...this._getHeaders(),
+          // Add JSON CT only when appropriate and not overridden by caller
+          ...((["POST","PUT","PATCH"].includes(String(method).toUpperCase())
+              && data
+              && typeof data?.getHeaders !== "function"
+              && !(userHeaders?.["Content-Type"] || userHeaders?.["content-type"]))
+            ? { "Content-Type": "application/json" }
+            : {}),
+          ...(userHeaders || {}),
+        },
         data,
       };

Also applies to: 29-38


61-71: No change required — Node form-data is used

components/leonardo_ai/actions/upload-image/upload-image.mjs imports FormData from "form-data" (import at line 2) and constructs new FormData() (line 67), so formData.getHeaders() is valid in components/leonardo_ai/leonardo_ai.app.mjs.

Likely an incorrect or invalid review comment.

components/leonardo_ai/actions/generate-image/generate-image.mjs (1)

29-44: Enforce multiples-of-8 and correct generation limits

  • File: components/leonardo_ai/actions/generate-image/generate-image.mjs (lines 29–44): validate width%8===0 and height%8===0 and update field descriptions to state "must be multiple of 8".
  • If this action calls the Create Generation endpoint: change max to 1024 and enforce range 32–1024 (divisible by 8). If it also supports Canvas/Inpainting, document that canvas may allow up to 1536×1536 and gate that path separately.
  • Enforce num_images rules: default 4; allow 1–8 normally; restrict to 1–4 when width>768 or height>768.

Comment on lines +45 to +52
numImages: {
type: "integer",
label: "Number of Images",
description: "Number of images to generate (1-8). If either width or height is over 768, must be between 1 and 4.",
default: 1,
min: 1,
max: 8,
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validate size vs. image count before calling the API

Enforce “if width or height > 768, numImages must be 1–4” to prevent API errors.

   async run({ $ }) {
     const {
       prompt,
       modelId,
       width,
       height,
       numImages,
       guidanceScale,
       numInferenceSteps,
       seed,
     } = this;

+    if ((width > 768 || height > 768) && numImages > 4) {
+      throw new Error("When Width or Height exceeds 768, Number of Images must be between 1 and 4.");
+    }

Also applies to: 78-88

🤖 Prompt for AI Agents
In components/leonardo_ai/actions/generate-image/generate-image.mjs around lines
45-52 (and also apply to 78-88), the current schema allows numImages up to 8
regardless of width/height; add validation before calling the API that checks if
either width or height is greater than 768, and if so enforce numImages is
between 1 and 4 (otherwise allow 1–8). Implement this by reading the width and
height inputs, and if either >768 and numImages is outside 1–4, throw/return a
validation error (or clamp/adjust per project convention) with a clear message;
apply the same validation logic in the other block at lines 78-88.

Comment on lines +41 to +47
async getPlatformModels() {
const data = await this._makeRequest({
method: "GET",
path: "/platformModels",
});
return data.custom_models || [];
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: getPlatformModels returns wrong field; options() will be empty

/platformModels responds with the platform models list, not custom_models. Returning the wrong property empties the Model dropdown.

-      return data.custom_models || [];
+      return data.platformModels || data.platform_models || [];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async getPlatformModels() {
const data = await this._makeRequest({
method: "GET",
path: "/platformModels",
});
return data.custom_models || [];
},
async getPlatformModels() {
const data = await this._makeRequest({
method: "GET",
path: "/platformModels",
});
return data.platformModels || data.platform_models || [];
},
🤖 Prompt for AI Agents
In components/leonardo_ai/leonardo_ai.app.mjs around lines 41 to 47, the
getPlatformModels function returns data.custom_models which is the wrong
property and causes the Model dropdown to be empty; update the return to use the
actual response field (e.g., return data.platform_models || [] or the correct
camelCase variant returned by the API) so the method returns the platform models
list, and add a defensive fallback to an empty array.

@sergio-eliot-rodriguez
Copy link
Collaborator Author

hey @GTFalcao would you mind giving another shot to reviewing the PR and advise on whether to implement async option to imageID field that woulld cover only a subset of the entire image IDs that could be used for components? the reason being is because I think we can only list generated images id, but not variation nor init images ids. Thanks!

@GTFalcao
Copy link
Collaborator

Hey @sergio-eliot-rodriguez amazing work! This is almost ready to be shipped. I've marked almost all the comments as resolved, please check the ones that aren't resolved yet.

Also personally I would chip down the AI-generated readme to just the key info and links, no specifics on each component's features and all that. It's not a bad thing, we just don't usually add this because it's a piece of information that can get outdated quite easily.

If we want to give example values for props, we should do that in their description. And if we need to give additional key information about a component, we can add an alert prop of the "info" type which serves this exact purpose.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d35b09 and 6765ddc.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • components/leonardo_ai/README.md (1 hunks)
  • components/leonardo_ai/actions/generate-motion/generate-motion.mjs (1 hunks)
  • components/leonardo_ai/leonardo_ai.app.mjs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • components/leonardo_ai/README.md
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#18362
File: components/leonardo_ai/README.md:45-49
Timestamp: 2025-09-15T22:01:17.593Z
Learning: In Leonardo AI components (and likely other Pipedream components), prefer using info alert props on the component itself rather than detailed "Key Features" sections in README files for action documentation.
📚 Learning: 2025-09-15T22:01:11.472Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#18362
File: components/leonardo_ai/actions/generate-image/generate-image.mjs:103-105
Timestamp: 2025-09-15T22:01:11.472Z
Learning: In Pipedream components, pipedream/platform's axios implementation automatically excludes undefined values from HTTP requests, so there's no need to manually check for truthiness before including properties in request payloads.

Applied to files:

  • components/leonardo_ai/leonardo_ai.app.mjs
🧬 Code graph analysis (2)
components/leonardo_ai/leonardo_ai.app.mjs (5)
components/leonardo_ai/actions/generate-motion/generate-motion.mjs (3)
  • data (99-105)
  • response (107-112)
  • userId (21-21)
components/leonardo_ai/actions/unzoom-image/unzoom-image.mjs (2)
  • data (29-32)
  • response (34-39)
components/leonardo_ai/actions/generate-image/generate-image.mjs (2)
  • data (90-99)
  • response (101-106)
components/leonardo_ai/actions/upscale-image/upscale-image.mjs (2)
  • data (20-22)
  • response (24-29)
components/leonardo_ai/actions/upload-image/upload-image.mjs (1)
  • formData (67-67)
components/leonardo_ai/actions/generate-motion/generate-motion.mjs (1)
components/leonardo_ai/leonardo_ai.app.mjs (4)
  • data (53-56)
  • data (62-69)
  • data (87-91)
  • data (97-105)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Lint Code Base
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (7)
components/leonardo_ai/leonardo_ai.app.mjs (5)

52-58: Duplicate comment: Bug already identified - getPlatformModels returns wrong field

Based on the past review comments, this issue has already been identified. The API documentation shows the response structure includes custom_models in a nested data object, but the platform models endpoint returns a list of public Platform Models. The current implementation returns data.custom_models, which appears to be incorrect for platform models.


11-17: Duplicate comment: Content-Type header forcing issue already identified

This exact issue was already flagged in past reviews - forcing Content-Type: application/json globally breaks non-JSON requests like file uploads.


18-51: Excellent header merging implementation!

The refactored _makeRequest method properly addresses the previous header merging issues. It correctly:

  • Merges caller-provided headers with defaults
  • Handles external URLs (presigned URLs) by skipping default headers
  • Preserves the original opts object integrity
  • Provides clean separation between internal API calls and external requests

72-85: File upload implementation looks good!

The uploadFileToPresignedUrl method correctly:

  • Uses external URL mode (bypasses default headers)
  • Leverages formData.getHeaders() for proper multipart headers
  • Routes through the improved _makeRequest with proper header merging

This addresses the previous concerns about file upload handling.


94-107: Verify the params parameter handling in _makeRequest

The getGenerationsByUserId method passes params for query parameters, but the _makeRequest method doesn't explicitly handle the params field - it only handles data for request body.

components/leonardo_ai/actions/generate-motion/generate-motion.mjs (2)

15-63: Excellent async options implementation!

The imageId prop implements a sophisticated async options pattern that:

  • Properly handles pagination with prevContext and offset tracking
  • Gracefully extracts userId from the API response structure
  • Creates meaningful option labels combining image ID and generation ID
  • Implements proper pagination control with hasMore logic
  • Handles empty/missing data scenarios defensively

This addresses the previous feedback about providing async options for image selection.


90-116: LGTM - Clean implementation!

The run method follows good practices:

  • Proper destructuring of props
  • Clean data object construction (leveraging Pipedream's automatic undefined exclusion)
  • Correct API endpoint usage (/generations-motion-svd)
  • Meaningful success message
  • Returns the full API response

$: this,
});
// Extract userId from the response structure
const userId = userInfo.user_details?.[0]?.user?.id || userInfo.id;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Potential issue with userId extraction fallback

The userId extraction uses userInfo.user_details?.[0]?.user?.id || userInfo.id as fallback. This might work, but without knowing the exact API response structure, the fallback to userInfo.id could be incorrect if the user info response doesn't have a top-level id field.

Let me verify the actual response structure from the /me endpoint:


🌐 Web query:

Leonardo AI API /me endpoint response structure getUserInfo

💡 Result:

Example response (GET /api/rest/v1/me — getUserInfo) — sample JSON structure:

{
"user_details": [
{
"user": {
"id": "string",
"username": "string"
},
"tokenRenewalDate": "string",
"paidTokens": 0,
"subscriptionTokens": 0,
"subscriptionGptTokens": 0,
"subscriptionModelTokens": 0,
"apiConcurrencySlots": 0,
"apiPaidTokens": 0,
"apiSubscriptionTokens": 0,
"apiPlanTokenRenewalDate": "string"
}
]
}

Notes: this matches the documented sample for the /me (Get user information) endpoint. For full, authoritative docs and any updates see the Leonardo API documentation. [1][2]

Sources:
[1] API listing / Get user information (example response).
[2] Leonardo.ai API documentation overview.


Fix userId extraction — don't fallback to a non-existent top-level id

The /me response places the id at user_details[0].user.id (no top-level id), so userInfo.id will be undefined; update the code to rely on userInfo.user_details?.[0]?.user?.id and add explicit handling/error if missing — components/leonardo_ai/actions/generate-motion/generate-motion.mjs:21

🤖 Prompt for AI Agents
In components/leonardo_ai/actions/generate-motion/generate-motion.mjs around
line 21, the code falls back to a non-existent top-level userInfo.id; change the
extraction to rely solely on userInfo.user_details?.[0]?.user?.id and add
explicit handling when that value is missing (throw or return a clear error/log
and avoid proceeding), so the function either uses the nested id or fails fast
with a descriptive message when user id is not present.

@sergio-eliot-rodriguez
Copy link
Collaborator Author

@GTFalcao i think i addressed the pending conversations to be resolved. looking fwd to your review! thanks

Copy link
Collaborator

@GTFalcao GTFalcao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update @sergio-eliot-rodriguez , looks good to me! Moving it forward

@vunguyenhung vunguyenhung merged commit f5742b2 into PipedreamHQ:master Sep 24, 2025
10 checks passed
@sergio-eliot-rodriguez sergio-eliot-rodriguez deleted the leonardo_components branch September 24, 2025 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Leonard Image generation