CoolFace
Apppublic

Vishnu-bot/silver-ornaments-agent

sourceHugging Faceupdated 3mo agoView on Hugging Face
0likes
App README

๐Ÿช™ Silver Ornament Shop โ€” Festival Marketing Bot

Automatically generates premium, catalogue-quality Instagram campaigns for an Indian silver ornament shop. Detects upcoming festivals and generates one independent campaign per festival, each with:

  • โ€”โœ… Fully AI-generated creative, no real product photo used โ€” img2img was tried extensively but is conclusively unavailable on HF's free Inference Providers for every model tested (see content_generator.py's HF_IMG2IMG_MODELS comment for the investigation), and no GPU is available for a local diffusers pipeline. The AI now generates the jewellery itself as part of one coherent text-to-image scene.
  • โ€”โœ… Festival-aware jewelry type selection: the LLM picks which TYPE of silver jewellery suits each festival's tradition (jhumka earrings, necklace, bangles, pendant, ring, anklet, bracelet, a gemstone-inlaid set, a deity idol, or a rakhi-thread piece) from a curated vocabulary โ€” see content_generator.JEWELRY_TYPE_OPTIONS โ€” varying across festivals rather than repeating the same piece every time.
  • โ€”โœ… A bounding-region layout engine with a 3-role type system (script greeting / serif headline / sans supporting text + CTA), a limited luxury colour palette, contrast-aware text colour, and an automatic overlap/fit validation pass โ€” nothing is ever cropped, overlapping, or illegible against the scene
  • โ€”โœ… Left-aligned premium marketing layout: logo, greeting, headline, subheadline, icon-supported feature badges, and a prominent filled gold CTA button, all stacked in a left column with guaranteed contrast โ€” the AI scene prompt positions the jewellery in the right portion of frame to match. Logo is fully legible (not a subtle watermark).
  • โ€”โœ… Final creative delivered as a 1080ร—1350 px (4:5) Instagram portrait
  • โ€”โœ… Instagram caption with storytelling, product highlights, and 8โ€“12 hashtags
  • โ€”โœ… Trends collected from Google News RSS, Bing News RSS, DuckDuckGo, Reddit, and pytrends
  • โ€”โœ… Real scraped hashtags: trend_analyzer.fetch_scraped_hashtags() pulls a live pool of currently-active hashtags for the festival name; ContentGenerator.select_best_hashtags() then has the LLM pick the subset most relevant to that specific caption/headline, rather than relying solely on the LLM's own static knowledge
  • โ€”โœ… Final QA agent: before a campaign is accepted, ContentGenerator.review_creative() sends the actual rendered image + all its text to a vision-capable model (Groq first, HF Inference Providers as fallback) for a holistic review โ€” visual quality, composition/branding coherence, grammar/persuasiveness, typography best practice, no hallucinations/distortions. If it finds real problems, the creative is regenerated from scratch (QA_MAX_RETRIES, default 2) before being flagged and exported anyway rather than blocking the campaign. Toggle with FINAL_QA_ENABLED (default true)
Note on the retired real-photo pipeline: an earlier revision of this project used uploaded ornament photos from images/, either regenerated via image-to-image or blended into an AI scene via a hybrid compositor. That code (image_composer.build_hybrid_creative, prepare_product_reference, and product_enhancer.py's blending helpers) is left in the codebase, unused, as a safety net in case real-photo mode becomes viable again (e.g. a working img2img provider is found, or a GPU becomes available). main.py no longer calls any of it.

How it works

Check festivals.json
        โ†“
Detect festivals 4โ€“15 days away
        โ†“
For each festival (independently):
    โ”œโ”€โ”€ Collect live trends (5 sources, each independently fallible)        โ† trend_analyzer.py
    โ”œโ”€โ”€ Generate: jewelry_type / greeting / headline / subheadline /
    โ”‚       badges(3) / design_choices / caption / hashtags / CTA           โ† Groq LLM
    โ”œโ”€โ”€ Generate a full AI scene prompt โ€” jewellery (of the chosen type)
    โ”‚       and its festive luxury set, composed together for the strict
    โ”‚       3-zone layout below                                            โ† content_generator.py
    โ”œโ”€โ”€ Text-to-image generates the complete scene                         โ† content_generator.py
    โ”œโ”€โ”€ Lay out logo + left-column text hierarchy + icon badges + CTA button via a bounding-region
    โ”‚       layout engine: 3-role typography, contrast-aware colour,
    โ”‚       limited luxury palette, auto-fitting type, and an automatic
    โ”‚       overlap/fit validation pass                                    โ† image_composer.py
    โ””โ”€โ”€ Save: caption.txt ยท creative.png (1080ร—1350) ยท meta.json ยท trends.json
        โ†“
If no festival detected โ†’ fallback to user-supplied product keyword

On design fidelity, honestly: since no real product photo is used any more, there is no "fidelity" to preserve โ€” the AI invents the jewellery freely within the chosen jewelry_type. This is a deliberate product decision, not a fallback: img2img was tried exhaustively (5+ models, 2 different provider-routing strategies) and conclusively rejected by every available HF Inference Provider on this account, and no GPU is available to run a local diffusers pipeline instead. See content_generator.py's HF_IMG2IMG_MODELS comment for the full investigation trail if that ever changes and real-photo mode is worth reviving (the retired code is still there, unused โ€” see the note above).

Jewelry type selection: ContentGenerator.generate_content() has the LLM pick one jewelry_type per festival from a curated vocabulary (JEWELRY_TYPE_OPTIONS in content_generator.py) โ€” reasoning about which piece actually suits that festival's tradition (e.g. a bracelet or rakhi-adjacent piece for a sibling-gifting festival, a deity idol for a festival centred on worship of a specific god), and instructed to vary its choice across festivals rather than defaulting to the same type every time.

No-real-photo, always-on: every campaign now goes through the same single AI-generation path โ€” there's no per-campaign fallback branching to document, since there's only one path. If the AI scene generation fails entirely (all HF text-to-image models down), the campaign's creative is simply absent for that run rather than shipping a broken placeholder โ€” logged clearly, and the caption/hashtags/meta.json are still saved.


Setup (local)

bash
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
# Fill in GROQ_API_KEY and HF_TOKEN in .env

Add your assets:

images/
โ”œโ”€โ”€ ornament_01.jpg    โ† used for festival 1, then deleted
โ”œโ”€โ”€ ornament_02.jpg    โ† used for festival 2, then deleted
โ””โ”€โ”€ ...

logo/
โ””โ”€โ”€ logo.png           โ† composited on every creative, never deleted

Then run:

bash
python main.py                          # auto-detect festivals
python main.py --input "Silver Bangle"  # fallback product campaign
python main.py --dry-run                # show what would fire, no generation

Deploying on Hugging Face Spaces (Free CPU)

  1. 1.Create a new Space โ€” SDK = Gradio.
  2. 2.Upload all project files. The YAML block above configures the Space automatically.
  3. 3.In Settings โ†’ Variables and secrets, add:
  4. 4.GROQ_API_KEY โ€” console.groq.com/keys (free)
  5. 5.HF_TOKEN โ€” huggingface.co/settings/tokens (Read scope)
  6. 6.HF_IMG2IMG_ENABLED (optional, default true) โ€” set to false to skip the image-to-image chain entirely and go straight to the hybrid fallback. Useful if your HF account's providers don't currently serve img2img for any of the models in HF_IMG2IMG_MODELS (contentgenerator.py) โ€” check the campaign log for `"rejected the image-to-image task"` messages to confirm before disabling. See that file's comment above `HFIMG2IMG_MODELS` for how to find a working model/provider combination.
  7. 7.Upload ornament images to images/ and your logo to logo/ via the Files tab.
  8. 8.Open the Space and click Generate Campaigns.
Tip: packages.txt in this repo installs fonts-dejavu-core via apt, which provides the fonts used for text overlays. No manual setup needed on Spaces. First run only: the background-removal model (isnet-general-use, ~170 MB) downloads on the first campaign and is cached afterwards. Expect the first campaign to take noticeably longer (~30-60s extra) than subsequent ones. If your Space has no network egress on first boot, segmentation degrades gracefully โ€” see "Image enhancement" below.

Festival database

Festivals are stored in festivals.json โ€” not hardcoded in Python. Each entry has:

FieldPurpose
nameDisplay name
dateISO date (YYYY-MM-DD) โ€” update annually
country / regionUsed for regional trend queries
significancePassed to the LLM for cultural storytelling
gifting_themeGuides caption tone
colorsTwo-colour pair for the luxury background fallback gradient
visual_themeLuxury background descriptors (props/light/mood) for the AI background prompt
keywordsFestival-specific search terms for trend collection

Update dates at the start of each year (Hindu festival dates shift annually).


Image generation & compositing

StageToolWhat it does
Jewelry type + copyGroq LLM (ContentGenerator.generate_content())Picks a festival-appropriate jewelry_type from a curated vocabulary, plus greeting/headline/subheadline/badges/caption/hashtags/cta
Scene promptContentGenerator.generate_full_scene_prompt()Writes a text-to-image prompt for the complete scene (jewellery of the chosen type + festive set), composed for the strict 3-zone layout below
Scene generationContentGenerator.create_ai_image() (HF text-to-image)Generates the full AI scene โ€” jewellery and background together, one coherent photograph
Final compositingimage_composer.compose_studio_creative() (Pillow + NumPy)Left-aligned bounding-region layout: logo + 3-role typography (script/serif/sans) + icon-supported feature badges + a filled CTA button + limited luxury palette + contrast-aware colour, with an automatic overlap/fit validation pass; saves at exactly 1080ร—1350

Graceful degradation: if every HF text-to-image model fails, create_ai_image() returns None and the campaign's creative is simply absent for that run โ€” logged clearly โ€” while caption/hashtags/meta.json are still saved.

Retired, unused, kept as a safety net: product_enhancer.py's segmentation/enhancement/blending helpers and image_composer.py's prepare_product_reference() / build_hybrid_creative() / build_reference_composite() implement the earlier real-photo pipeline (img2img primary, hybrid-compositor fallback). None of it is called by main.py any more โ€” see the note near the top of this README for why โ€” but it's left in place in case real-photo mode is worth reviving later.

Trend intelligence

Five independent sources โ€” one failure never blocks the others:

SourceNotes
Google News RSSOfficial syndication feed โ€” reliable
Bing News RSSOfficial syndication feed โ€” reliable
DuckDuckGo HTMLKeyless web search โ€” robust from datacenter IPs
Reddit JSONPublic read-only API โ€” occasionally blocked from DCs
pytrendsGoogle Trends unofficial API โ€” frequently 429s from Spaces

Model selection

Text โ€” `qwen/qwen3.6-27b` (Groq, open-weight): All models from the original priority list are currently unavailable (deprecated or already shut down as of 2026-06-21). qwen/qwen3.6-27b is the same Qwen3 family, open-weight, live on Groq, and Groq's own documented replacement for both qwen3-32b and llama-4-scout.

Images โ€” full-scene text-to-image via `FLUX.1-schnell โ†’ SDXL โ†’ SD3-medium` (HF Inference Providers): The ONLY image path now (see the note near the top of this README on why img2img was retired) โ€” no real photo, no conditioning image. The LLM picks a jewelry_type for the festival, generate_full_scene_prompt() writes a prompt describing that piece and its scene together, and this text-to-image chain generates the complete photograph in one call. FLUX.1-schnell preferred first for speed/quality; SDXL and SD3-medium are automatic fallbacks. HF_IMG2IMG_MODELS and the img2img code path are still in content_generator.py, gated behind the HF_IMG2IMG_ENABLED env var, in case a working provider/model combination is found later.

Segmentation โ€” `isnet-general-use` (via `rembg`): No longer called by main.py (see above) โ€” kept in product_enhancer.py for the retired real-photo pipeline. rembg's own recommended model for general product/object cutouts (distinct from u2net_human_seg, which targets people).


Output files

Each campaign writes to output/<festival-name>_<date>/:

FileContents
creative.pngFinal 1080ร—1350 px (4:5 Instagram portrait) creative
caption.txtFull caption + hashtags ready to paste
meta.jsonGreeting, headline, CTA, image prompt, timestamp
trends.jsonSource list, related queries, rising topics