orangecat / module-mago-ai-image
Mago Assistant add-on: AI product images from a gallery image and a prompt, with pluggable providers (Google Gemini, OpenAI).
Package info
github.com/olivertar/m2_mago_ai_image
Type:magento2-module
pkg:composer/orangecat/module-mago-ai-image
Requires
- php: ~8.2.0||~8.3.0||~8.4.0||~8.5.0
- ext-gd: *
- mage-os/framework: *
- mage-os/module-ai-base: *
- mage-os/module-authorization: *
- mage-os/module-backend: *
- mage-os/module-catalog: *
- mage-os/module-config: *
- mage-os/module-media-storage: *
- mage-os/module-store: *
- mage-os/module-user: *
- mago-assistant/magento2-mago: ^1.1
- symfony/ai-gemini-platform: ^0.14
- symfony/ai-open-ai-platform: ^0.14
- symfony/ai-platform: ^0.14
- symfony/http-client: ^7.3||^8.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Mago Assistant add-on that creates product images with AI from one of the product's gallery images and a prompt ("a person walking through the city wearing this jacket"). Images are created as previews and only reach the product gallery when someone attaches them.
One module holds everything: the generation core, the Google Gemini and OpenAI providers, the
dummy provider (developer mode only) and the Mago tools. The chat model (e.g. Kimi) only
orchestrates; the image is made by the selected image provider.
Install
composer require orangecat/module-mago-ai-image bin/magento module:enable Orangecat_MagoAiImage bin/magento setup:upgrade
Requires MagoAssistant_Mago (^1.1) and MageOS_AiBase.
Configuration
Stores > Configuration > Mago Assistant > AI Product Images (mago_ai_image/*).
General (mago_ai_image/general/*):
| Field | Path | Default |
|---|---|---|
| Enabled | enabled |
No |
| Image Provider | provider |
— |
| Maximum Images per Request | max_images |
4 |
| Provider Timeout (seconds) | timeout |
90 |
| Maximum Reference Image Size (MB) | max_reference_mb |
10 |
| Keep Unattached Previews (days) | preview_retention_days |
7 |
Google Gemini (mago_ai_image/gemini/*) and OpenAI (mago_ai_image/openai/*):
| Field | Path | Notes |
|---|---|---|
| Credentials | credential_source |
own (key below) or ai_base (a MageOS_AiBase row: google for Gemini, openai for OpenAI; "OpenAI compatible" rows such as Kimi have no image API) |
| API Key | api_key |
obscure field, Encrypted backend, sensitive in TypePool |
| AI Service | ai_service |
MageOS_AiBase row id |
| Model | model |
Gemini: gemini-nano-banana-2.1; OpenAI: gpt-image-2.5-sunburst |
| Image Size / Quality | image_size (Gemini: 1K/2K/4K), quality (OpenAI: low/medium/high) |
Empty = model default |
| — | base_url |
Gemini only, config.xml only, without the API version |
The reference image and the prompt are sent to the selected provider.
Providers
Gemini and OpenAI go through the Symfony AI bridges (symfony/ai-gemini-platform,
symfony/ai-open-ai-platform, ^0.14 like MageOS_AiBase), built per request by
Model\Platform\PlatformFactory with the configured timeout and no redirects. The model is built
from the configured name instead of the bridge catalog, so newer models work without a bridge
release. Model\Platform\ResponseReader reads the HTTP status and body before the bridge
converts the result: the bridges drop the provider's error details (moderation_blocked,
promptFeedback), and the usage is read from the body.
- Gemini —
POST {base_url}/v1beta/models/{model}:generateContentwith the key inx-goog-api-key; one call per image; aspect ratios 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9. - OpenAI —
POST https://api.openai.com/v1/images/edits(multipart, oneimage) with the key inAuthorization: Bearer; the key must start withsk-; one call returnsnimages; aspect ratios map to sizes near 1.5 MP (multiples of 16);gpt-image-1*models only accept 1:1, 3:2 and 2:3. GPT Image models may require API Organization Verification (a 403 is reported as such). - Dummy — tints the reference image with GD, no network; developer mode only.
Errors are mapped to ProviderUnavailableException (key, access, unknown model),
ContentRejectedException (safety/moderation), ProviderTimeoutException and
ProviderException (quota, other).
Mago tools
| Tool | Access | Magento ACL | Does |
|---|---|---|---|
mago_product_image_list |
read | Magento_Catalog::products |
Gallery images of a SKU, pending previews, supported aspect ratios and image count |
mago_product_image_generate |
write | Orangecat_MagoAiImage::generate |
Generates previews from a gallery image and a prompt; confirmed by the admin, since every image is billed |
mago_product_image_attach |
write | Orangecat_MagoAiImage::attach |
Adds a preview to the product gallery, with roles and label |
The tools are hidden while generation is disabled or the default provider has no credentials. A call that cannot succeed (no product permission, unknown SKU, empty prompt, preview already attached, unknown role) is refused before the confirmation card. Previews are served from this store's media URL, so the chat panel shows them inline (it only loads images from its own origin).
Example questions:
- "Generate an image for WJ12 of a person walking through the city wearing this jacket, 3:4 format"
- "Use the back photo of WJ12 as reference and make two images in a studio with a white background"
- "Add the first one as the base image with the label 'City walk'"
Every returned field is PiiClass::PUBLIC (no personal data); admin_url is tokenised by Mago.
bin/magento mago:tool:verify mago_product_image_list '{"sku":"WJ12"}'
CLI and API
bin/magento mago:ai-image:providers bin/magento mago:ai-image:generate --sku=WJ12 --prompt="A person walking through the city wearing this jacket" \ [--provider=gemini] [--media-entry=<gallery id>] [--count=2] [--aspect-ratio=3:4] \ [--attach --roles=image,thumbnail --label="City"]
From code: Orangecat\MagoAiImage\Api\ImageGeneratorInterface::generateForProduct() returns
previews (GenerationInterface), GenerationManagementInterface::attach() adds one to the gallery
and discard() drops it. Previews are stored in pub/media/mago_ai_image/preview/. The
mago_ai_image_cleanup_previews cron job removes them after the retention period: unattached
ones are discarded, attached ones only lose the preview file (the gallery keeps its own copy, and
file is set to empty). Until then the Mago chat history keeps showing them. Generations are
kept in mago_ai_image_generation, with the admin who requested them.
ACL: Orangecat_MagoAiImage::generate, Orangecat_MagoAiImage::attach (under Catalog > Inventory >
Products) and Orangecat_MagoAiImage::config. The generate and attach tools also require
Magento_Catalog::products at run time, so a role cannot change galleries of products it cannot edit.
Adding a provider from another module
-
Implement
Orangecat\MagoAiImage\Api\ImageProviderInterfaceand add it to theprovidersargument ofOrangecat\MagoAiImage\Model\ProviderPoolindi.xml. -
Add a group with the provider code to the
mago_ai_imagesection inetc/adminhtml/system.xml, like thegeminigroup:credential_source,api_key(obscure+Magento\Config\Model\Config\Backend\Encrypted),ai_service(a virtualType ofOrangecat\MagoAiImage\Model\Config\Source\AiBaseServicewith the accepted MageOS_AiBase codes),model. Markmago_ai_image/<code>/api_keyas sensitive inMagento\Config\Model\Config\TypePool.Do not declare
backend_model="...Encrypted"on the key inconfig.xml: Magento would decrypt the value on read, andProviderConfigInterfacewould decrypt it a second time. -
Read settings only through
ProviderConfigInterface(getApiKey(),getModel(),getValue()). Never log or return the key. -
Use
Model\Config::getTimeout()as the HTTP timeout and throw the exceptions above with messages fit for an administrator. For a provider with a Symfony AI bridge, followModel\Provider\Gemini: a platform from the bridge factory, the model built from its name, andModel\Platform\ResponseReaderto check the response before converting it.
Privacy
The reference image and the prompt are sent to the configured provider (Google, OpenAI). The admin
who generated each preview is stored in mago_ai_image_generation.admin_user_id.
License
MIT, see LICENSE.