TAUSIF
home
All projects

12 of 13

AI Tooling

Chitra AI Image Generation

Text-to-image generation with a private, paginated history. React front end, Django API, and a provider credential that never leaves the server. Shipped with a swappable provider interface — which is also how it broke in production.

Built at
TechnicalBind
Scale
Three output sizes, two quality tiers, per-user private history with preview, download, regenerate, and delete. Push to master runs the suite and then deploys the backend over SSH and the front end to Vercel.
Category
AI Tooling
Chitra AI Image Generation
Env line that broke production
1
Provider keys in the browser
0

Context

A prompt goes in, an image comes back, and it lands in a history only its owner can read. The provider key sits on the server and is never handed to the browser, which is the whole reason there is a backend here at all.

Challenge

Every generation on the live site came back a blue-to-green gradient. Instant, 7.3 KB, and completely wrong. The front end was rendering exactly what the API returned, and the API was doing exactly what it was configured to do.

Non-negotiables

  • The provider credential never reaches the browser
  • A provider outage returns a usable error, not a stack trace
  • Generated images are readable only by their owner

Calls I made

  1. 01

    Providers behind one interface

    The real provider and a deterministic stub that paints a gradient implement the same interface, so tests and local development never spend credits. The cost of that convenience is that a misconfigured environment produces a working application that is quietly wrong, rather than one that fails loudly.

  2. 02

    Provider errors become safe API responses

    A dedicated exception handler maps provider failures onto sanitised responses. A rate limit or a model timeout reaches the user as something they can act on, and nothing about the upstream call leaks out.

  3. 03

    Storage behind a swappable interface

    Images are on the filesystem, reached through an interface rather than directly. Moving to object storage is an implementation swap rather than a rewrite, and nothing above the interface knows where a file physically lives.

Trade-offs accepted

  • The bootstrap script writes a starter environment file with the provider set to `stub` and defers the switch to a manual post-install step. The deploy script never touches that file, so no amount of pushing to master could have corrected it. The abstraction that made the stub cheap is what let it reach production and stay there.
  • Generation is synchronous. It is honest about how long it takes and simple to reason about, but a long-running request is holding a worker the whole time, and that is the thing that would break first under concurrent load.

Outcome

The root cause was one line of configuration, not a line of code — the live backend was running the stub provider. Diagnosing it took one request to the options endpoint, which reports the active provider and model precisely so this question has an answer. The fix was smoke-tested with an inline override before the environment file was changed, so a bad token would have left the deployment exactly as it was.

A deployment pipeline that cannot set configuration cannot promise behaviour. If a stub can run in production, something in the boot path should refuse to start rather than serve a convincing placeholder.

What I took away

Stack

  • React 19
  • Vite
  • Tailwind CSS 4
  • React Router
  • Django 6
  • Django REST Framework
  • SimpleJWT
  • PostgreSQL
  • Hugging Face Inference Providers
  • FLUX.1-schnell
  • GitHub Actions
  • Vercel
  • Hostinger VPS

Got something similar in mind?

Send 3 lines. I reply within a day.

tausif1337@gmail.com