-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Leonardo components #18362
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Leonardo components #18362
Changes from all commits
61d5961
1f0e5b4
43c2645
185b2cd
b52e6af
768d13e
cab79a5
ab578e6
47cc79a
39af3b6
53176bc
48338b7
cdbc17b
4ea8c2e
0c0e183
8268c0e
d5c554d
07aa960
cef1e1b
3cb8df5
6f23e27
e35feaf
874a7cf
f381c67
d11624e
dc3964a
bbde963
fc90f7b
961dd55
e838ebe
85bd98a
2dfb941
ffa140c
1884cf0
4e0aa1d
5a236a9
52c9a94
5e65398
7073397
61539b7
2863b95
d15d706
1721bbf
0185460
72c48a3
8b4f66c
3260986
7a6774b
d2d0b88
c04c193
b5f7dce
1d35b09
18e7669
6765ddc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Leonardo AI | ||
|
||
[Leonardo AI](https://leonardo.ai) is an AI-powered image generation platform that allows you to create stunning images, videos, and 3D models using advanced machine learning models. | ||
|
||
## API Reference | ||
|
||
For detailed information about Leonardo AI's API, visit the [official documentation](https://docs.leonardo.ai/reference). | ||
|
||
## Support | ||
|
||
For support with this component or Leonardo AI's API, please refer to: | ||
- [Leonardo AI Documentation](https://docs.leonardo.ai) | ||
- [Leonardo AI Community](https://community.leonardo.ai) | ||
- [Pipedream Support](https://pipedream.com/support) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import app from "../../leonardo_ai.app.mjs"; | ||
|
||
export default { | ||
key: "leonardo_ai-generate-image", | ||
name: "Generate Image", | ||
description: "Generates new images using Leonardo AI's image generation API. [See the documentation](https://docs.leonardo.ai/reference/creategeneration)", | ||
version: "0.0.1", | ||
type: "action", | ||
props: { | ||
app, | ||
prompt: { | ||
type: "string", | ||
label: "Prompt", | ||
description: "The text prompt describing the image you want to generate.", | ||
}, | ||
modelId: { | ||
type: "string", | ||
label: "Model", | ||
description: "The model to use for generation. Leave empty to use the default model.", | ||
async options() { | ||
const models = await this.app.getPlatformModels(); | ||
return models.map((model) => ({ | ||
label: model.name || model.id, | ||
value: model.id, | ||
})); | ||
}, | ||
optional: true, | ||
}, | ||
width: { | ||
type: "integer", | ||
label: "Width", | ||
description: "Width of the generated image in pixels.", | ||
default: 1024, | ||
min: 32, | ||
max: 1536, | ||
}, | ||
height: { | ||
type: "integer", | ||
label: "Height", | ||
description: "Height of the generated image in pixels.", | ||
default: 768, | ||
min: 32, | ||
max: 1536, | ||
}, | ||
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, | ||
}, | ||
guidanceScale: { | ||
type: "integer", | ||
label: "Guidance Scale", | ||
description: "How closely the model should follow the prompt. Must be between 1 and 20. Higher values = more adherence to prompt.", | ||
default: 7, | ||
min: 1, | ||
max: 20, | ||
optional: true, | ||
}, | ||
numInferenceSteps: { | ||
type: "integer", | ||
label: "Inference Steps", | ||
description: "Number of denoising steps. More steps = higher quality but slower generation.", | ||
default: 15, | ||
min: 10, | ||
max: 60, | ||
optional: true, | ||
}, | ||
seed: { | ||
type: "integer", | ||
label: "Seed", | ||
description: "Random seed for reproducible generation. Leave empty for random generation.", | ||
optional: true, | ||
}, | ||
}, | ||
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, | ||
}; | ||
|
||
const response = await this.app._makeRequest({ | ||
$, | ||
method: "POST", | ||
path: "/generations", | ||
data, | ||
}); | ||
|
||
$.export("$summary", `Successfully generated ${numImages} image(s)`); | ||
return response; | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import app from "../../leonardo_ai.app.mjs"; | ||
|
||
export default { | ||
key: "leonardo_ai-generate-motion", | ||
name: "Generate Motion", | ||
description: "Generates a motion (video) from the provided image using Leonardo AI's SVD Motion Generation API. [See the documentation](https://docs.leonardo.ai/reference/createsvdmotiongeneration)", | ||
version: "0.0.1", | ||
type: "action", | ||
props: { | ||
app, | ||
imageId: { | ||
type: "string", | ||
label: "Image ID", | ||
description: "The ID of the image to generate motion from. You can either select from previously generated images or manually enter the ID of an uploaded image.", | ||
async options({ prevContext }) { | ||
// Get user info to retrieve userId | ||
const userInfo = await this.app.getUserInfo({ | ||
$: this, | ||
}); | ||
// Extract userId from the response structure | ||
const userId = userInfo.user_details?.[0]?.user?.id || userInfo.id; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainPotential issue with userId extraction fallback The userId extraction uses Let me verify the actual response structure from the 🌐 Web query:
💡 Result: Example response (GET /api/rest/v1/me — getUserInfo) — sample JSON structure: { 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: Fix userId extraction — don't fallback to a non-existent top-level The /me response places the id at 🤖 Prompt for AI Agents
|
||
|
||
// Get generations with pagination | ||
const offset = prevContext?.offset || 0; | ||
const limit = 20; | ||
|
||
const generations = await this.app.getGenerationsByUserId({ | ||
$: this, | ||
userId, | ||
offset, | ||
limit, | ||
}); | ||
|
||
// Extract image IDs from generated_images array | ||
const options = []; | ||
if (generations.generations) { | ||
for (const generation of generations.generations) { | ||
if (generation.generated_images) { | ||
for (const image of generation.generated_images) { | ||
options.push({ | ||
label: `Image ${image.id} (Generation ${generation.id})`, | ||
value: image.id, | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Check if there are more pages | ||
const hasMore = generations.generations && generations.generations.length === limit; | ||
const nextOffset = hasMore | ||
? offset + limit | ||
: null; | ||
|
||
return { | ||
options, | ||
context: nextOffset | ||
? { | ||
offset: nextOffset, | ||
} | ||
: {}, | ||
}; | ||
}, | ||
}, | ||
motionStrength: { | ||
type: "integer", | ||
label: "Motion Strength", | ||
description: "The motion strength for the video generation.", | ||
optional: true, | ||
}, | ||
isPublic: { | ||
type: "boolean", | ||
label: "Is Public", | ||
description: "Whether the generation is public or not.", | ||
optional: true, | ||
}, | ||
isInitImage: { | ||
type: "boolean", | ||
label: "Is Init Image", | ||
description: "Whether the image being used is an init image uploaded by the user.", | ||
optional: true, | ||
}, | ||
isVariation: { | ||
type: "boolean", | ||
label: "Is Variation", | ||
description: "Whether the image being used is a variation image.", | ||
optional: true, | ||
}, | ||
}, | ||
async run({ $ }) { | ||
const { | ||
imageId, | ||
motionStrength, | ||
isPublic, | ||
isInitImage, | ||
isVariation, | ||
} = this; | ||
|
||
const data = { | ||
imageId, | ||
motionStrength, | ||
isPublic, | ||
isInitImage, | ||
isVariation, | ||
}; | ||
|
||
const response = await this.app._makeRequest({ | ||
$, | ||
method: "POST", | ||
path: "/generations-motion-svd", | ||
data, | ||
}); | ||
|
||
$.export("$summary", `Successfully generated motion from image ID: ${imageId}`); | ||
return response; | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import app from "../../leonardo_ai.app.mjs"; | ||
|
||
export default { | ||
key: "leonardo_ai-unzoom-image", | ||
name: "Unzoom Image", | ||
description: "Creates an unzoom variation for a generated or variation image using Leonardo AI's unzoom API. [See the documentation](https://docs.leonardo.ai/reference/createvariationunzoom)", | ||
version: "0.0.1", | ||
type: "action", | ||
props: { | ||
app, | ||
imageId: { | ||
type: "string", | ||
label: "Image ID", | ||
description: "The ID of the image to create an unzoom variation for. This should be a previously generated or variation image ID.", | ||
}, | ||
isVariation: { | ||
type: "boolean", | ||
label: "Is Variation", | ||
description: "Whether the image is a variation image.", | ||
default: false, | ||
}, | ||
}, | ||
async run({ $ }) { | ||
const { | ||
imageId, | ||
isVariation, | ||
} = this; | ||
|
||
const data = { | ||
id: imageId, | ||
isVariation, | ||
}; | ||
|
||
const response = await this.app._makeRequest({ | ||
$, | ||
method: "POST", | ||
path: "/variations/unzoom", | ||
data, | ||
}); | ||
|
||
$.export("$summary", `Successfully created unzoom variation for image ID: ${imageId}`); | ||
return response; | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import app from "../../leonardo_ai.app.mjs"; | ||
import FormData from "form-data"; | ||
import { getFileStreamAndMetadata } from "@pipedream/platform"; | ||
|
||
export default { | ||
key: "leonardo_ai-upload-image", | ||
name: "Upload Image", | ||
description: "Uploads a new image to Leonardo AI for use in generations and variations. [See the documentation](https://docs.leonardo.ai/docs/how-to-upload-an-image-using-a-presigned-url)", | ||
version: "0.0.1", | ||
type: "action", | ||
props: { | ||
app, | ||
extension: { | ||
type: "string", | ||
label: "File Extension", | ||
description: "The file extension of the image to upload.", | ||
options: [ | ||
{ | ||
label: "PNG", | ||
value: "png", | ||
}, | ||
{ | ||
label: "JPG", | ||
value: "jpg", | ||
}, | ||
{ | ||
label: "JPEG", | ||
value: "jpeg", | ||
GTFalcao marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
{ | ||
label: "WebP", | ||
value: "webp", | ||
}, | ||
], | ||
}, | ||
filePath: { | ||
type: "string", | ||
label: "File Path or URL", | ||
description: "The file to upload. Provide either a file URL or a path to a file in the `/tmp` directory (for example, `/tmp/myImage.png`)", | ||
}, | ||
syncDir: { | ||
type: "dir", | ||
accessMode: "read", | ||
sync: true, | ||
optional: true, | ||
}, | ||
}, | ||
async run({ $ }) { | ||
const { | ||
extension, | ||
filePath, | ||
} = this; | ||
// Get file stream from URL or /tmp based path | ||
const { | ||
stream, | ||
metadata, | ||
} = await getFileStreamAndMetadata(filePath); | ||
|
||
// Step 1: Get the presigned URL, upload fields appended to formData | ||
const uploadResponse = await this.app.getUploadInitImage({ | ||
$, | ||
extension, | ||
}); | ||
|
||
const { uploadInitImage } = uploadResponse; | ||
const fields = JSON.parse(uploadInitImage.fields); | ||
const formData = new FormData(); | ||
|
||
//Important: Order of fields is sanctioned by Leonardo AI API. Fields go first, then the file | ||
for (const [ | ||
label, | ||
value, | ||
] of Object.entries(fields)) { | ||
formData.append(label, value.toString()); | ||
} | ||
formData.append("file", stream, { | ||
contentType: metadata.contentType, | ||
knownLength: metadata.size, | ||
filename: metadata.name, | ||
}); | ||
const uploadUrl = uploadInitImage.url; | ||
|
||
// Step 2: Upload the file to the presigned URL | ||
const uploadResult = await this.app.uploadFileToPresignedUrl({ | ||
$, | ||
url: uploadUrl, | ||
formData, | ||
}); | ||
|
||
$.export("$summary", `Successfully uploaded image: ${metadata.name || filePath}`); | ||
return { | ||
uploadInitImage, | ||
uploadResult, | ||
}; | ||
}, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate size vs. image count before calling the API
Enforce “if width or height > 768, numImages must be 1–4” to prevent API errors.
Also applies to: 78-88
🤖 Prompt for AI Agents