👗 Flutter, ADK Go āĻ“ Gemini āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻāĻ•āϟāĻŋ āĻ­āĻžāĻ°ā§āϚ⧁āϝāĻŧāĻžāϞ āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ āĻāĻŦāĻ‚ āĻāφāχ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ

ā§§. āĻ­ā§‚āĻŽāĻŋāĻ•āĻž

āφāĻĒāύāĻŋ āϝāĻž āϤ⧈āϰāĻŋ āĻ•āϰāĻŦ⧇āύ

āĻāχ āϕ⧋āĻĄāĻ˛ā§āϝāĻžāĻŦ⧇, āφāĻĒāύāĻŋ āĻāĻ•āϜāύ āĻĄā§‡āϭ⧇āϞāĻĒāĻžāϰ⧇āϰ āĻ­ā§‚āĻŽāĻŋāĻ•āĻžāϝāĻŧ āĻ…āĻŦāϤ⧀āĻ°ā§āĻŖ āĻšāϝāĻŧ⧇ āĻāĻ•āϟāĻŋ āĻ•āĻžāĻ˛ā§āĻĒāύāĻŋāĻ• āϰāĻŋāĻŸā§‡āχāϞ āĻŦā§āĻ°ā§āϝāĻžāĻ¨ā§āĻĄā§‡āϰ āϜāĻ¨ā§āϝ ‘āĻĢā§āϝāĻžāĻļāύ āĻ…ā§āϝāĻžāĻĒ’ āύāĻžāĻŽā§‡āϰ āĻāĻ•āϟāĻŋ āĻĢā§āϞāĻžāϟāĻžāϰ āĻļāĻĒāĻŋāĻ‚ āĻ…ā§āϝāĻžāĻĒ āϤ⧈āϰāĻŋ āĻ•āϰāĻŦ⧇āύāĨ¤ āφāĻĒāύāĻžāϰ āĻ•āĻžāϜ āĻšāϞ⧋: āĻĻ⧁āϟāĻŋ āĻāφāχ-āϚāĻžāϞāĻŋāϤ āĻĢāĻŋāϚāĻžāϰ āϝ⧋āĻ— āĻ•āϰāĻž, āϝāĻž āĻ…āύāϞāĻžāχāύ āĻļāĻĒāĻŋāĻ‚-āĻāϰ āĻ…āĻ­āĻŋāĻœā§āĻžāϤāĻžāϕ⧇ āφāĻŽā§‚āϞ āĻŦāĻĻāϞ⧇ āĻĻ⧇āĻŦ⧇āĨ¤

  1. āĻ­āĻžāĻ°ā§āϚ⧁āϝāĻŧāĻžāϞ āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ — āĻāĻ•āϜāύ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āύāĻŋāĻœā§‡āϰ āĻāĻ•āϟāĻŋ āĻ›āĻŦāĻŋ āφāĻĒāϞ⧋āĻĄ āĻ•āϰ⧇āύ, āĻāĻ•āϟāĻŋ āĻĒā§‹āĻļāĻžāĻ• āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻ•āϰ⧇āύ āĻāĻŦāĻ‚ āϏ⧇āχ āĻĒā§‹āĻļāĻžāĻ•āϟāĻŋ āĻĒāϰāĻž āĻ…āĻŦāĻ¸ā§āĻĨāĻžāϝāĻŧ āĻāφāχ āĻĻā§āĻŦāĻžāϰāĻž āϤ⧈āϰāĻŋ āύāĻŋāĻœā§‡āϰ āĻāĻ•āϟāĻŋ āĻ›āĻŦāĻŋ āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāύāĨ¤
  2. āĻāφāχ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ — āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āĻ…āĻŦāĻ¸ā§āĻĨāĻžāύ, āωāĻĒāϞāĻ•ā§āώ āĻāĻŦāĻ‚ āĻ¸ā§āϟāĻžāχāϞ⧇āϰ āĻĒāĻ›āĻ¨ā§āĻĻ⧇āϰ āωāĻĒāϰ āĻ­āĻŋāĻ¤ā§āϤāĻŋ āĻ•āϰ⧇ āĻāĻ•āϟāĻŋ āĻāφāχ āĻāĻœā§‡āĻ¨ā§āϟ āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻĒā§‹āĻļāĻžāϕ⧇āϰ āϏ⧁āĻĒāĻžāϰāĻŋāĻļ āϤ⧈āϰāĻŋ āĻ•āϰ⧇ āĻĻ⧇āϝāĻŧ — āĻāĻŦāĻ‚ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āĻ•āĻĨā§‹āĻĒāĻ•āĻĨāύ⧇āϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āϏ⧇āϗ⧁āϞ⧋āϕ⧇ āφāϰāĻ“ āĻĒāϰāĻŋāĻŽāĻžāĻ°ā§āϜāύ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤

āĻŽā§‚āϞ āϧāĻžāϰāĻŖāĻžāϟāĻŋ āϖ⧁āĻŦāχ āϏāĻšāϜ: āĻŽāĻžāύ⧁āώ āϝāĻ–āύ āĻŸā§āϰāĻžāϝāĻŧāĻžāϞ āϰ⧁āĻŽā§‡ āĻĒā§‹āĻļāĻžāĻ• āĻĒāϰ⧇ āĻĻ⧇āϖ⧇, āϤāĻ–āύ āϤāĻžāĻĻ⧇āϰ āϏ⧇āϗ⧁āϞ⧋ āϕ⧇āύāĻžāϰ āϏāĻŽā§āĻ­āĻžāĻŦāύāĻž āĻ…āύ⧇āĻ• āĻŦ⧇āĻļāĻŋ āĻĨāĻžāϕ⧇āĨ¤ āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻ…āύāϞāĻžāχāύ⧇? āϏ⧇āϟāĻž āϕ⧇āĻŦāϞāχ āφāĻ¨ā§āĻĻāĻžāϜ āĻ•āϰāĻžāĨ¤ āĻāχ āĻĒā§āϰāĻ•āĻ˛ā§āĻĒāϟāĻŋ āĻ•ā§ƒāĻ¤ā§āϰāĻŋāĻŽ āĻŦ⧁āĻĻā§āϧāĻŋāĻŽāĻ¤ā§āϤāĻžāϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āϏ⧇āχ āĻŦā§āϝāĻŦāϧāĻžāύāϟāĻŋ āĻĒā§‚āϰāĻŖ āĻ•āϰ⧇āĨ¤

āĻ¸ā§āĻĨāĻžāĻĒāĻ¤ā§āϝ⧇āϰ āĻāĻ• āĻāϞāĻ•

Flutter App  ──── HTTP/REST ────â–ļ  ADK Go Backend
                                       │
                            ┌──────────â”ŧ──────────┐
                       Fitting Room  Stylist    Catalog
                         Agent       Agent      Agent
                                       │
                            Gemini API + Cloud Storage

āĻŽā§‚āϞ āĻĒā§āϰāϝ⧁āĻ•ā§āϤāĻŋ

āωāĻĒāĻžāĻĻāĻžāύ

āĻĒā§āϰāϝ⧁āĻ•ā§āϤāĻŋ

āωāĻĻā§āĻĻ⧇āĻļā§āϝ

āĻāĻœā§‡āĻ¨ā§āϟ āĻĢā§āϰ⧇āĻŽāĻ“āϝāĻŧāĻžāĻ°ā§āĻ•

Go-āĻāϰ āϜāĻ¨ā§āϝ ADK (āĻāĻœā§‡āĻ¨ā§āϟ āĻĄā§‡āϭ⧇āϞāĻĒāĻŽā§‡āĻ¨ā§āϟ āĻ•āĻŋāϟ)

āĻŽāĻžāĻ˛ā§āϟāĻŋ-āĻāĻœā§‡āĻ¨ā§āϟ āĻ…āĻ°ā§āϕ⧇āĻ¸ā§āĻŸā§āϰ⧇āĻļāύ, āϏ⧇āĻļāύ, āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟ

āĻāĻœā§‡āĻ¨ā§āϟ āϝ⧁āĻ•ā§āϤāĻŋ (āĻĒā§āϰ⧋)

āĻœā§‡āĻŽāĻŋāύāĻŋ ā§Š.ā§§ āĻĒā§āϰ⧋ āĻĒā§āϰāĻŋāĻ­āĻŋāω

āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ āĻāĻŦāĻ‚ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāĻœā§‡āĻ¨ā§āϟāĻĻ⧇āϰ āĻ•ā§āώāĻŽāϤāĻž āĻĻ⧇āϝāĻŧ

āĻāĻœā§‡āĻ¨ā§āϟ āϝ⧁āĻ•ā§āϤāĻŋ (āĻĢā§āĻ˛ā§āϝāĻžāĻļ)

āĻœā§‡āĻŽāĻŋāύāĻŋ ā§Š āĻĢā§āĻ˛ā§āϝāĻžāĻļ āĻĒā§āϰāĻŋāĻ­āĻŋāω

āϰ⧁āϟ āĻāĻŦāĻ‚ āĻ•ā§āϝāĻžāϟāĻžāϞāĻ— āĻāĻœā§‡āĻ¨ā§āϟāĻĻ⧇āϰ (āϞāĻžāχāϟāĻ“āϝāĻŧ⧇āϟ āϰāĻžāωāϟāĻŋāĻ‚/āϞ⧁āĻ•āφāĻĒ) āĻļāĻ•ā§āϤāĻŋ āϝ⧋āĻ—āĻžāϝāĻŧāĨ¤

āϚāĻŋāĻ¤ā§āϰ āϤ⧈āϰāĻŋ

āĻœā§‡āĻŽāĻŋāύāĻŋ ⧍.ā§Ģ āĻĢā§āĻ˛ā§āϝāĻžāĻļ āχāĻŽā§‡āϜ

āĻŸā§āϰāĻžāχ-āĻ…āύ āĻāĻŦāĻ‚ āĻĒā§‹āĻļāĻžāϕ⧇āϰ āĻ›āĻŦāĻŋ āϤ⧈āϰāĻŋ āĻ•āϰ⧇

āĻĢā§āϰāĻ¨ā§āϟāĻāĻ¨ā§āĻĄ

āĻĢā§āϞāĻžāϟāĻžāϰ (āĻĄāĻžāĻ°ā§āϟ)

āĻ•ā§āϰāϏ-āĻĒā§āĻ˛ā§āϝāĻžāϟāĻĢāĻ°ā§āĻŽ āĻ…ā§āϝāĻžāĻĒ (āĻ“āϝāĻŧ⧇āĻŦ, āφāχāĻ“āĻāϏ, āĻ…ā§āϝāĻžāĻ¨ā§āĻĄā§āϰāϝāĻŧ⧇āĻĄ)

āĻ¸ā§āĻŸā§‹āϰ⧇āϜ

āϗ⧁āĻ—āϞ āĻ•ā§āϞāĻžāωāĻĄ āĻ¸ā§āĻŸā§‹āϰ⧇āϜ

āĻĒāĻŖā§āϝ⧇āϰ āĻ›āĻŦāĻŋ āĻāĻŦāĻ‚ āϤ⧈āϰāĻŋ āĻ•āϰāĻž āύāĻŋāĻĻāĻ°ā§āĻļāύāϗ⧁āϞāĻŋ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰ⧇

āĻšā§‹āĻ¸ā§āϟāĻŋāĻ‚

āĻ•ā§āϞāĻžāωāĻĄ āϰāĻžāύ

āϏāĻžāĻ°ā§āĻ­āĻžāϰāĻŦāĻŋāĻšā§€āύ āĻ•āĻ¨ā§āĻŸā§‡āχāύāĻžāϰ āĻ¸ā§āĻĨāĻžāĻĒāύ

⧍. đŸ“Ļ āĻĒā§‚āĻ°ā§āĻŦāĻļāĻ°ā§āϤ āĻ“ āĻ•ā§āϞāĻžāωāĻĄ āĻļ⧇āϞ āϏ⧇āϟāφāĻĒ

ā§§. āĻ•ā§āϞāĻžāωāĻĄ āĻļ⧇āϞ āĻāĻĄāĻŋāϟāϰ āϖ⧁āϞ⧁āύ

👉 āφāĻĒāύāĻžāϰ āĻŦā§āϰāĻžāωāϜāĻžāϰ⧇ āĻ•ā§āϞāĻžāωāĻĄ āĻļ⧇āϞ āĻāĻĄāĻŋāϟāϰ āϖ⧁āϞ⧁āύāĨ¤

āϝāĻĻāĻŋ āϟāĻžāĻ°ā§āĻŽāĻŋāύāĻžāϞāϟāĻŋ āĻ¸ā§āĻ•ā§āϰāĻŋāύ⧇āϰ āύ⧀āĻšā§‡ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āύāĻž āĻšāϝāĻŧ:

  • āĻ­āĻŋāω → āϟāĻžāĻ°ā§āĻŽāĻŋāύāĻžāϞ-āĻ āĻ•ā§āϞāĻŋāĻ• āĻ•āϰ⧁āύ

⧍. āĻĢā§āϞāĻžāϟāĻžāϰ āĻāϏāĻĄāĻŋāϕ⧇ āϏ⧇āϟ āφāĻĒ āĻ•āϰ⧁āύ

āĻ•ā§āϞāĻžāωāĻĄ āĻļ⧇āϞ-āĻāϰ āϏāĻžāĻĨ⧇ /google/flutter āĻ āĻĢā§āϞāĻžāϟāĻžāϰ āφāϗ⧇ āĻĨ⧇āϕ⧇āχ āχāύāĻ¸ā§āϟāϞ āĻ•āϰāĻž āĻĨāĻžāϕ⧇āĨ¤ āϝ⧇āĻšā§‡āϤ⧁ āĻ“āχ āĻĄāĻŋāϰ⧇āĻ•ā§āϟāϰāĻŋāϟāĻŋ āĻ…āĻ¨ā§āϝ āĻāĻ•āϜāύ āϏāĻŋāĻ¸ā§āĻŸā§‡āĻŽ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āĻŽāĻžāϞāĻŋāĻ•āĻžāύāĻžāϧ⧀āύ, āϤāĻžāχ āφāĻĒāύāĻŋ āĻĒā§āϰāĻĨāĻŽāĻŦāĻžāϰ flutter āϚāĻžāϞāĻžāύ⧋āϰ āϏāĻŽāϝāĻŧ āĻāĻ•āϟāĻŋ fatal: detected dubious ownership āĻ¤ā§āϰ⧁āϟāĻŋ āĻĒāĻžāĻŦ⧇āύāĨ¤ āĻāϟāĻŋāϕ⧇ āĻāĻ•āĻŦāĻžāϰ āĻ—āĻŋāϟ-āĻāϰ āύāĻŋāϰāĻžāĻĒāĻĻ-āĻĄāĻŋāϰ⧇āĻ•ā§āϟāϰāĻŋ āϤāĻžāϞāĻŋāĻ•āĻžāϝāĻŧ āϝ⧋āĻ— āĻ•āϰ⧁āύ:

git config --global --add safe.directory /google/flutter

āĻĢā§āϞāĻžāϟāĻžāϰ āφāĻĒāύāĻžāϰ PATH āĻ āφāϛ⧇ āĻāĻŦāĻ‚ āĻ•āĻžāϜ āĻ•āϰāϛ⧇ āĻ•āĻŋāύāĻž āϤāĻž āϝāĻžāϚāĻžāχ āĻ•āϰ⧁āύ:

flutter --version

āĻĒā§āϰāĻĨāĻŽāĻŦāĻžāϰ āϚāĻžāϞāĻžāϞ⧇ āĻĄāĻžāĻ°ā§āϟ āĻāϏāĻĄāĻŋāϕ⧇ āĻĄāĻžāωāύāϞ⧋āĻĄ āĻšāϝāĻŧ āĻāĻŦāĻ‚ āĻĢā§āϞāĻžāϟāĻžāϰ āϟ⧁āϞāϟāĻŋ āĻŦāĻŋāĻ˛ā§āĻĄ āĻšāϝāĻŧ — āĻāĻ• āĻŽāĻŋāύāĻŋāϟ āĻ…āĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰ⧁āύāĨ¤ āφāĻĒāύāĻŋ Flutter 3.x â€ĸ channel stable . āĻāϰ āĻŽāϤ⧋ āĻ•āĻŋāϛ⧁ āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāĻŦ⧇āύāĨ¤

ā§Š. āϰāĻŋāĻĒā§‹āϜāĻŋāϟāϰāĻŋ āĻ•ā§āϞ⧋āύ āĻ•āϰ⧁āύ

cd ~
git clone https://github.com/gca-americas/fashion-app-demo
cd fashion_app_demo

ā§Ē. āĻĒā§āϰāĻ•āĻ˛ā§āĻĒ⧇āϰ āĻ•āĻžāĻ āĻžāĻŽā§‹ āĻ…āĻ¨ā§āĻŦ⧇āώāĻŖ āĻ•āϰ⧁āύ

fashion_app_demo/
├── adk_backend/                 # Go backend with ADK agents
│   ├── main.go                  # Entry point — wires all agents + REST server
│   ├── catalog/                 # Catalog Agent — product lookup
│   │   ├── agent.go
│   │   ├── catalog.yaml         # Product database (YAML)
│   │   └── instructions.md      # Agent persona prompt
│   ├── fittingroom/             # Fitting Room Agent — virtual try-on
│   │   ├── agent.go
│   │   ├── instructions.md      # Agent persona prompt
│   │   └── tool_instructions.md # Image generation prompt
│   ├── stylist/                 # Stylist Agent — outfit curation
│   │   ├── agent.go
│   │   └── instructions.md      # Agent persona + output format
│   ├── rootagent/               # Root Agent — routes to the right agent
│   │   └── agent.go
│   └── tools/                   # Shared tools
│       ├── imagetool.go         # getProductImage — loads product images
│       ├── outfit_gen_tool.go   # generate_outfit_image — creates outfit images
│       └── cors_helper.go      # CORS middleware + request logging
│
├── flutter_frontend/            # Flutter cross-platform app
│   ├── lib/
│   │   ├── main.dart            # App entry point + Provider setup
│   │   ├── app_config.dart      # Backend URL configuration
│   │   ├── core_app/            # Pre-built shopping app (browse, cart, etc.)
│   │   └── workshop_tasks/      # AI feature code
│   │       ├── step_1_try_it_on/  # Virtual Try-On flow
│   │       │   ├── providers/     # TryItOnProvider (state management)
│   │       │   ├── services/      # AdkFittingRoomService (HTTP calls)
│   │       │   └── ui/            # Screens (product detail → try on → fitting room)
│   │       └── step_2_style_me/   # Style Me flow
│   │           ├── models/        # Outfit, StyleRequest data classes
│   │           ├── providers/     # StylingProvider (state management)
│   │           ├── services/      # AdkStylingService (HTTP calls)
│   │           └── ui/            # Screens (form sheet → outfit carousel)
│   └── assets/images/           # Product catalog images

ā§Š. â˜ī¸ āϗ⧁āĻ—āϞ āĻ•ā§āϞāĻžāωāĻĄ āĻĒā§āϰāĻœā§‡āĻ•ā§āϟ āϏ⧇āϟāφāĻĒ

ā§§. āĻāĻ•āϟāĻŋ āύāϤ⧁āύ āĻĒā§āϰāĻ•āĻ˛ā§āĻĒ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ

gcloud projects create fashion-app-demo --name="Fashion App Demo"
gcloud config set project fashion-app-demo

āφāĻĒāύāĻžāϰ āĻŦāĻŋāϞāĻŋāĻ‚ āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āϟāϗ⧁āϞāĻŋāϰ āϤāĻžāϞāĻŋāĻ•āĻž āĻĻāĻŋāύ:

gcloud billing accounts list

āĻĻ⧇āϖ⧁āύ

OPEN

āĻ•āϞāĻžāĻŽāĨ¤ āĻāĻ–āĻžāύ⧇ āĻ…āĻŦāĻļā§āϝāχ True āϞ⧇āĻ–āĻž āĻĨāĻžāĻ•āϤ⧇ āĻšāĻŦ⧇āĨ¤ āϝāĻĻāĻŋ False āϞ⧇āĻ–āĻž āĻĨāĻžāϕ⧇ (āϝāĻž āĻŽā§‡āϝāĻŧāĻžāĻĻā§‹āĻ¤ā§āϤ⧀āĻ°ā§āĻŖ āĻĢā§āϰāĻŋ āĻŸā§āϰāĻžāϝāĻŧāĻžāϞ⧇āϰ āĻ•ā§āώ⧇āĻ¤ā§āϰ⧇ āϏāĻžāϧāĻžāϰāĻŖ), āϤāĻžāĻšāϞ⧇ āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āϟāϟāĻŋ āĻŦāĻ¨ā§āϧ āĻšāϝāĻŧ⧇ āϗ⧇āϛ⧇ āĻāĻŦāĻ‚ āφāĻĒāύāĻŋ āφāϏāϞ⧇ āϕ⧋āύ⧋ āĻ•āĻŋāϛ⧁āϰ āϜāĻ¨ā§āϝ āĻ…āĻ°ā§āĻĨ āĻĒā§āϰāĻĻāĻžāύ āĻ•āϰāĻŦ⧇āύ āύāĻž — āĻāĻ—āĻŋāϝāĻŧ⧇ āϝāĻžāĻ“āϝāĻŧāĻžāϰ āφāϗ⧇ āύāĻŋāĻšā§‡āϰ āĻŸā§āϰāĻžāĻŦāϞāĻļ⧁āϟāĻŋāĻ‚ āĻŦā§āϞāϕ⧇ āϚāϞ⧇ āϝāĻžāύāĨ¤

āĻāĻ•āϟāĻŋ OPEN: True āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āĻŸā§‡āϰ ACCOUNT_ID (āϝāĻž 0X0X0X-0X0X0X-0X0X0X āĻāϰ āĻŽāϤ⧋ āĻĻ⧇āĻ–āϤ⧇) āĻ•āĻĒāĻŋ āĻ•āϰ⧁āύ āĻāĻŦāĻ‚ āφāĻĒāύāĻžāϰ āĻĒā§āϰ⧋āĻœā§‡āĻ•ā§āĻŸā§‡āϰ āϏāĻžāĻĨ⧇ āϞāĻŋāĻ™ā§āĻ• āĻ•āϰ⧁āύ:

gcloud billing projects link fashion-app-demo \
 --billing-account=YOUR_BILLING_ACCOUNT_ID

āϞāĻŋāĻ™ā§āĻ•āϟāĻŋ āϝāĻžāϚāĻžāχ āĻ•āϰ⧁āύ:

gcloud billing projects describe fashion-app-demo

āφāĻĒāύāĻžāϰ billingEnabled: true āĻĻ⧇āĻ–āĻž āωāϚāĻŋāϤāĨ¤ āϞāĻŋāĻ™ā§āĻ• āĻ•āϰāĻžāϰ āĻĒāϰ⧇āĻ“ āϝāĻĻāĻŋ āφāĻĒāύāĻŋ billingEnabled: false āĻĻ⧇āϖ⧇āύ, āϤāĻžāĻšāϞ⧇ āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āϟāϟāĻŋ āĻŦāĻ¨ā§āϧ āφāϛ⧇ ( OPEN: False ) — āύāĻŋāĻšā§‡āϰ āĻŸā§āϰāĻžāĻŦāϞāĻļ⧁āϟāĻŋāĻ‚ āĻŦā§āϞāĻ•āϟāĻŋ āĻĻ⧇āϖ⧁āύāĨ¤

ā§Š. āĻĒā§āϰāϝāĻŧā§‹āϜāύ⧀āϝāĻŧ āĻāĻĒāĻŋāφāχāϗ⧁āϞ⧋ āϏāĻ•ā§āϰāĻŋāϝāĻŧ āĻ•āϰ⧁āύ

gcloud services enable \
 aiplatform.googleapis.com \
 storage.googleapis.com \
 run.googleapis.com \
 cloudbuild.googleapis.com \
 artifactregistry.googleapis.com

āĻāĻĒāĻŋāφāχ

āωāĻĻā§āĻĻ⧇āĻļā§āϝ

aiplatform.googleapis.com

āĻ­āĻžāĻ°ā§āĻŸā§‡āĻ•ā§āϏ āĻāφāχ — fitting_tool āĻ­āĻžāĻ°ā§āĻŸā§‡āĻ•ā§āϏ āĻāφāχ-āĻāϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻœā§‡āĻŽāĻŋāύāĻŋāϰ āχāĻŽā§‡āϜ āĻœā§‡āύāĻžāϰ⧇āĻļāύāϕ⧇ āĻ•āϞ āĻ•āϰ⧇āĨ¤

storage.googleapis.com

āĻ•ā§āϞāĻžāωāĻĄ āĻ¸ā§āĻŸā§‹āϰ⧇āϜ — āĻĒāĻŖā§āϝ⧇āϰ āĻ•ā§āϝāĻžāϟāĻžāϞāϗ⧇āϰ āĻ›āĻŦāĻŋ āĻāĻŦāĻ‚ āϤ⧈āϰāĻŋ āĻ•āϰāĻž āĻŸā§āϰāĻžāχ-āĻ…āύ āĻĢāϞāĻžāĻĢāϞ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰ⧇

run.googleapis.com

āĻ•ā§āϞāĻžāωāĻĄ āϰāĻžāύ — āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄāϕ⧇ āĻāĻ•āϟāĻŋ āϏāĻžāĻ°ā§āĻ­āĻžāϰāϞ⧇āϏ āĻ•āĻ¨ā§āĻŸā§‡āχāύāĻžāϰ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻšā§‹āĻ¸ā§āϟ āĻ•āϰ⧇

cloudbuild.googleapis.com

āĻ•ā§āϞāĻžāωāĻĄ āĻŦāĻŋāĻ˛ā§āĻĄ — āϏ⧋āĻ°ā§āϏ āĻĨ⧇āϕ⧇ āĻĄāĻ•āĻžāϰ āχāĻŽā§‡āϜ āϤ⧈āϰāĻŋ āĻ•āϰ⧇

artifactregistry.googleapis.com

āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟ āϰ⧇āϜāĻŋāĻ¸ā§āĻŸā§āϰāĻŋ — āύāĻŋāĻ°ā§āĻŽāĻŋāϤ āĻĄāĻ•āĻžāϰ āχāĻŽā§‡āϜ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰ⧇

ā§Ē. āĻāĻ•āϟāĻŋ GCS āĻŦāĻžāϕ⧇āϟ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ

export PROJECT_ID=$(gcloud config get-value project)
gcloud storage buckets create gs://fashion-app-$PROJECT_ID \
 --location=us-central1 \
 --uniform-bucket-level-access

ā§Ģ. āĻĒāĻŖā§āϝ⧇āϰ āĻ•ā§āϝāĻžāϟāĻžāϞāϗ⧇āϰ āĻ›āĻŦāĻŋ āφāĻĒāϞ⧋āĻĄ āĻ•āϰ⧁āύ

āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄā§‡āϰ getProductImage āϟ⧁āϞāϟāĻŋ gs://$GCS_BUCKET/catalog-assets/images/ āĻĨ⧇āϕ⧇ āĻĄā§‡āϟāĻž āĻĒāĻĄāĻŧ⧇āĨ¤ gs://$GCS_BUCKET/catalog-assets/images/ āĻ•ā§āϝāĻžāϟāĻžāϞāϗ⧇āϰ āĻ›āĻŦāĻŋāϗ⧁āϞ⧋ āĻ āĻŋāĻ• āĻ“āχ āĻĒāĻžāĻĨ⧇ āφāĻĒāϞ⧋āĻĄ āĻ•āϰ⧁āύ:

cd ~/fashion_app_demo
gcloud storage cp flutter_frontend/assets/images/*.png \
 gs://fashion-app-$PROJECT_ID/catalog-assets/images/

āφāĻĒāϞ⧋āĻĄāϟāĻŋ āϝāĻžāϚāĻžāχ āĻ•āϰ⧁āύ (āφāĻĒāύāĻŋ .png āĻĢāĻžāχāϞāϗ⧁āϞāĻŋāϰ āĻāĻ•āϟāĻŋ āϤāĻžāϞāĻŋāĻ•āĻž āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāĻŦ⧇āύ):

gcloud storage ls gs://fashion-app-$PROJECT_ID/catalog-assets/images/

ā§Ŧ. .env āĻĢāĻžāχāϞāϟāĻŋ āĻ•āύāĻĢāĻŋāĻ—āĻžāϰ āĻ•āϰ⧁āύāĨ¤

cd ~/fashion_app_demo/adk_backend
cat > .env << EOF
GOOGLE_CLOUD_PROJECT=$PROJECT_ID
GCS_BUCKET=fashion-app-$PROJECT_ID
EOF

ā§­. āĻ…ā§āϝāĻžāĻĒā§āϞāĻŋāϕ⧇āĻļāύ⧇āϰ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻ•ā§āϰ⧇āĻĄā§‡āύāĻļāĻŋāϝāĻŧāĻžāϞ āĻĻāĻŋāϝāĻŧ⧇ āĻĒā§āϰāĻŽāĻžāĻŖā§€āĻ•āϰāĻŖ āĻ•āϰ⧁āύ

āĻ¸ā§āĻĨāĻžāύ⧀āϝāĻŧāĻ­āĻžāĻŦ⧇ āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄ āϚāĻžāϞ⧁ āĻ•āϰāĻžāϰ āφāϗ⧇ āφāĻĒāύāĻžāϕ⧇ āĻ…āĻŦāĻļā§āϝāχ āĻāϟāĻŋ āϚāĻžāϞāĻžāϤ⧇ āĻšāĻŦ⧇āĨ¤ āĻ—ā§‹ āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄāϟāĻŋ āĻ­āĻžāĻ°ā§āĻŸā§‡āĻ•ā§āϏ āĻāφāχ (āĻœā§‡āĻŽāĻŋāύāĻŋ) āĻāĻŦāĻ‚ āĻ•ā§āϞāĻžāωāĻĄ āĻ¸ā§āĻŸā§‹āϰ⧇āĻœā§‡ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻ•āϞ āĻĒā§āϰāĻŽāĻžāĻŖā§€āĻ•āϰāϪ⧇āϰ āϜāĻ¨ā§āϝ āĻāĻĄāĻŋāϏāĻŋ (ADC) āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇āĨ¤ āĻāĻĄāĻŋāϏāĻŋ āĻ›āĻžāĻĄāĻŧāĻž, āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄāϟāĻŋ āϚāĻžāϞ⧁ āĻšāĻŦ⧇ āĻ āĻŋāĻ•āχ, āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻŸā§āϰāĻžāχ-āĻ…āύ āϰāĻŋāϕ⧋āϝāĻŧ⧇āĻ¸ā§āϟ ā§Ēā§Ļā§§ CREDENTIALS_MISSING ) āĻ¤ā§āϰ⧁āϟāĻŋāϰ āĻ•āĻžāϰāϪ⧇ āĻŦā§āϝāĻ°ā§āĻĨ āĻšāĻŦ⧇āĨ¤

āĻāĻ•āϟāĻŋ āĻĒāϰāĻŋāϚāϝāĻŧāĻĒāĻ¤ā§āϰ āĻĻāĻŋāϝāĻŧ⧇āχ āωāĻ­āϝāĻŧ āĻĒāϰāĻŋāώ⧇āĻŦāĻž āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āϝāĻžāĻŦ⧇āĨ¤ āĻāχ āĻĻ⧁āϟāĻŋ āĻ•āĻŽāĻžāĻ¨ā§āĻĄ āĻ•ā§āϰāĻŽāĻžāύ⧁āϏāĻžāϰ⧇ āϚāĻžāϞāĻžāύ:

# 1. Log in (opens a browser; in Cloud Shell, paste the verification code back)
gcloud auth application-default login

# 2. Attach your project as the quota / billing project for ADC
gcloud auth application-default set-quota-project $(gcloud config get-value project)

ADC āϏ⧁āĻ¸ā§āĻĨ āφāϛ⧇ āĻ•āĻŋāύāĻž āϝāĻžāϚāĻžāχ āĻ•āϰ⧁āύ:

gcloud auth application-default print-access-token | head -c 20 && echo "..."

āφāĻĒāύāĻŋ āĻāĻ•āϟāĻŋ āĻŸā§‹āϕ⧇āύ⧇āϰ āĻĒā§āϰāĻžāϝāĻŧ ⧍ā§ĻāϟāĻŋ āĻ…āĻ•ā§āώāϰ āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāĻŦ⧇āύ, āϝāĻžāϰ āĻĒāϰ⧇ ... āĻĨāĻžāĻ•āĻŦ⧇āĨ¤ āϝāĻĻāĻŋ āϕ⧋āύ⧋ āĻ¤ā§āϰ⧁āϟāĻŋ āĻšāϝāĻŧ, āϤāĻžāϰ āĻŽāĻžāύ⧇ āϞāĻ—āχāύāϟāĻŋ āϏāĻĢāϞ āĻšāϝāĻŧāύāĻŋ — āϧāĻžāĻĒ ā§§ āφāĻŦāĻžāϰ āϚāĻžāϞāĻžāύāĨ¤

ā§Ē. đŸ—ī¸ āĻ¸ā§āĻĨāĻžāĻĒāĻ¤ā§āϝ⧇āϰ āϏāĻ‚āĻ•ā§āώāĻŋāĻĒā§āϤ āĻŦāĻŋāĻŦāϰāĻŖ

āĻāĻ–āύ āϝ⧇āĻšā§‡āϤ⧁ āĻĒāϰāĻŋāĻŦ⧇āĻļ āĻĒā§āϰāĻ¸ā§āϤ⧁āϤ, āϕ⧋āĻĄ āĻĻ⧇āĻ–āĻžāϰ āφāϗ⧇ āϚāϞ⧁āύ āϏāĻŋāĻ¸ā§āĻŸā§‡āĻŽāϟāĻŋ āϕ⧀āĻ­āĻžāĻŦ⧇ āĻ•āĻžāϜ āĻ•āϰ⧇ āϤāĻž āĻŦ⧁āĻā§‡ āύ⧇āĻ“āϝāĻŧāĻž āϝāĻžāĻ•āĨ¤

āϚāĻžāϰ-āĻāĻœā§‡āĻ¨ā§āϟ āϏāĻŋāĻ¸ā§āĻŸā§‡āĻŽ

āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄāϟāĻŋ Go-āĻāϰ āϜāĻ¨ā§āϝ ADK (āĻāĻœā§‡āĻ¨ā§āϟ āĻĄā§‡āϭ⧇āϞāĻĒāĻŽā§‡āĻ¨ā§āϟ āĻ•āĻŋāϟ) āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻāĻ•āϟāĻŋ āĻŽāĻžāĻ˛ā§āϟāĻŋ-āĻāĻœā§‡āĻ¨ā§āϟ āϏāĻŋāĻ¸ā§āĻŸā§‡āĻŽ āĻšāĻŋāϏ⧇āĻŦ⧇ āϤ⧈āϰāĻŋ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇āĨ¤ āϚāĻžāϰāϟāĻŋ āĻāĻœā§‡āĻ¨ā§āϟ āĻāĻ•āϏāĻžāĻĨ⧇ āĻ•āĻžāϜ āĻ•āϰ⧇, āϝāĻžāĻĻ⧇āϰ āĻĒā§āϰāĻ¤ā§āϝ⧇āϕ⧇āϰ āĻāĻ•āϟāĻŋ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻĻāĻžāϝāĻŧāĻŋāĻ¤ā§āĻŦ āϰāϝāĻŧ⧇āϛ⧇:

                   ┌──────────────┐
                   │ Root Agent   │  ← Routes requests to the right agent
                   │ (gemini-3-   │
                   │  flash-      │
                   │  preview)    │
                   └──────â”Ŧ───────┘
                          │
           ┌──────────────â”ŧ──────────────┐
           â–ŧ              â–ŧ              â–ŧ
  ┌────────────────┐ ┌──────────┐ ┌────────────────┐
  │ Fitting Room   │ │ Catalog  │ │ Stylist        │
  │ Agent          │ │ Agent    │ │ Agent          │
  │ (gemini-3.1-   │ │ (gemini- │ │ (gemini-3.1-   │
  │  pro-preview)  │ │  3-flash-│ │  pro-preview)  │
  │                │ │  preview)│ │                │
  │ Tools:         │ │          │ │ Tools:         │
  │ â€ĸ fitting_tool │ │ Tools:   │ │ â€ĸ fitting_tool │
  │ â€ĸ getProduct   │ │ â€ĸ list   │ │ â€ĸ getProduct   │
  │   Image        │ │ Products │ │   Image        │
  │ â€ĸ catalog_agent│ │ â€ĸ get    │ │ â€ĸ catalog_agent│
  │   (delegation) │ │ Product  │ │   (delegation) │
  │                │ │  Image   │ │ â€ĸ generate_    │
  └────────────────┘ └──────────┘ │   outfit_image │
                                  └────────────────┘

āĻāĻœā§‡āĻ¨ā§āϟ

āĻŽāĻĄā§‡āϞ

āĻ­ā§‚āĻŽāĻŋāĻ•āĻž

āĻŽā§‚āϞ āĻāĻœā§‡āĻ¨ā§āϟ

gemini-3-flash-preview

āĻŸā§āϰāĻžāĻĢāĻŋāĻ• āĻĒ⧁āϞāĻŋāĻļāĨ¤ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āĻŦāĻžāĻ°ā§āϤāĻž āĻĒāĻĄāĻŧ⧇ āĻāĻŦāĻ‚ āϏāĻ āĻŋāĻ• āĻŦāĻŋāĻļ⧇āώāĻœā§āĻž āĻāĻœā§‡āĻ¨ā§āĻŸā§‡āϰ āĻ•āĻžāϛ⧇ āĻĻāĻžāϝāĻŧāĻŋāĻ¤ā§āĻŦ āĻ…āĻ°ā§āĻĒāĻŖ āĻ•āϰ⧇āĨ¤ āĻāϟāĻŋ āĻāĻ•āϟāĻŋ āĻĻā§āϰ⧁āϤ āĻ“ āĻšāĻžāϞāĻ•āĻž āĻŽāĻĄā§‡āϞ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇, āĻ•āĻžāϰāĻŖ āĻāϕ⧇ āĻļ⧁āϧ⧁ āĻĒāĻĨ āύāĻŋāĻ°ā§āϧāĻžāϰāϪ⧇āϰ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āύāĻŋāϤ⧇ āĻšāϝāĻŧāĨ¤

āĻ•ā§āϝāĻžāϟāĻžāϞāĻ— āĻāĻœā§‡āĻ¨ā§āϟ

gemini-3-flash-preview

āĻĒāĻŖā§āϝ āĻŦāĻŋāĻļ⧇āώāĻœā§āĻžāĨ¤ āĻāĻ•āϟāĻŋ YAML āĻĢāĻžāχāϞ āĻĨ⧇āϕ⧇ āĻĒāĻŖā§āϝ⧇āϰ āĻ•ā§āϝāĻžāϟāĻžāϞāĻ— āϞ⧋āĻĄ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āĻĒāĻŖā§āϝ āϏāĻŽā§āĻĒāĻ°ā§āĻ•āĻŋāϤ āĻĒā§āϰāĻļā§āύ⧇āϰ āωāĻ¤ā§āϤāϰ āĻĻ⧇āϝāĻŧāĨ¤ āĻāϟāĻŋ āĻŦ⧇āĻļ āĻšāĻžāϞāĻ•āĻž āĻĒā§āϰāĻ•ā§ƒāϤāĻŋāϰ — āĻāϰ āĻ•āĻžāϜ āĻļ⧁āϧ⧁ āĻĄā§‡āϟāĻž āϖ⧁āρāĻœā§‡ āĻŦ⧇āϰ āĻ•āϰāĻžāĨ¤

āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ āĻāĻœā§‡āĻ¨ā§āϟ

gemini-3.1-pro-preview

āĻ­āĻžāĻ°ā§āϚ⧁āϝāĻŧāĻžāϞ āĻŸā§āϰāĻžāχ-āĻ…āύ āĻ¸ā§āĻĒ⧇āĻļāĻžāϞāĻŋāĻ¸ā§āϟāĨ¤ āĻāϟāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āĻ›āĻŦāĻŋ āĻ“ āĻĒāĻŖā§āϝ⧇āϰ āĻ›āĻŦāĻŋ āύāĻŋāϝāĻŧ⧇, āϏ⧇āχ āĻĒāĻŖā§āϝāϟāĻŋ āĻĒāϰāĻŋāĻšāĻŋāϤ āĻŦā§āϝāĻ•ā§āϤāĻŋāϰ āĻāĻ•āϟāĻŋ āϏāĻŽāĻ¨ā§āĻŦāĻŋāϤ āĻ›āĻŦāĻŋ āϤ⧈āϰāĻŋ āĻ•āϰ⧇āĨ¤ āĻāϟāĻŋ āφāϰāĻ“ āωāĻ¨ā§āύāϤ āĻāĻ•āϟāĻŋ āĻŽāĻĄā§‡āϞ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇, āĻ•āĻžāϰāĻŖ āĻāϟāĻŋāϕ⧇ āĻ›āĻŦāĻŋ āĻŦāĻŋāĻļā§āϞ⧇āώāĻŖ āĻ•āϰ⧇ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āύāĻŋāϤ⧇ āĻšāϝāĻŧāĨ¤

āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāĻœā§‡āĻ¨ā§āϟ

gemini-3.1-pro-preview

āĻĢā§āϝāĻžāĻļāύ āωāĻĒāĻĻ⧇āĻˇā§āϟāĻžāĨ¤ āĻ¸ā§āĻĨāĻžāύ, āωāĻĒāϞāĻ•ā§āώ āĻāĻŦāĻ‚ āĻĒāĻ›āĻ¨ā§āĻĻ⧇āϰ āωāĻĒāϰ āĻ­āĻŋāĻ¤ā§āϤāĻŋ āĻ•āϰ⧇ āĻāϟāĻŋ āĻ•ā§āϝāĻžāϟāĻžāϞāĻ— āĻĨ⧇āϕ⧇ ā§ŠāϟāĻŋ āĻĒā§‹āĻļāĻžāϕ⧇āϰ āϏāĻŽāĻ¨ā§āĻŦāϝāĻŧ āϤ⧈āϰāĻŋ āĻ•āϰ⧇āĨ¤ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻĒā§‹āĻļāĻžāϕ⧇āϰ āϜāĻ¨ā§āϝ āĻŸā§āϰāĻžāχ-āĻ…āύ āĻ›āĻŦāĻŋ āϤ⧈āϰāĻŋ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤ āĻāĻ›āĻžāĻĄāĻŧāĻžāĻ“, āϏ⧃āϜāύāĻļā§€āϞ āϝ⧁āĻ•ā§āϤāĻŋāϰ āϜāĻ¨ā§āϝ āϏāĻ•ā§āώāĻŽ āĻŽāĻĄā§‡āϞāϟāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇āĨ¤

āĻĒā§āϰāĻŦ⧇āĻļ āĻŦāĻŋāĻ¨ā§āĻĻ⧁: main.go

āϏāĻŦāĻ•āĻŋāϛ⧁ main.go āĻĨ⧇āϕ⧇ āĻļ⧁āϰ⧁ āĻšāϝāĻŧ, āϝāĻž āĻāĻœā§‡āĻ¨ā§āϟāϗ⧁āϞ⧋āϕ⧇ āĻāĻ•āĻ¤ā§āϰāĻŋāϤ āĻ•āϰ⧇ āĻāĻŦāĻ‚ HTTP āϏāĻžāĻ°ā§āĻ­āĻžāϰ āϚāĻžāϞ⧁ āĻ•āϰ⧇:

// main.go — simplified for clarity


func main() {
   godotenv.Load()  // Load .env file


   // 1. Create the artifact storage (GCS-backed)
   artifacts, _ := gcsartifact.NewService(ctx, bucket)


   // 2. Build agents bottom-up (dependencies first)
   catagent, _    := catalog.NewCatalogAgent(apikey, "catalog/catalog.yaml")
   fitagent, _    := fittingroom.NewFittingRoomAgent(apikey, catagent)
   stylistAgent, _ := stylist.NewStylistAgent(apikey, catagent)
   ragent, _      := rootagent.NewRootAgent(apikey, fitagent, catagent, stylistAgent)


   // 3. Register all agents with a multi-loader
   loader, _ := agent.NewMultiLoader(ragent, fitagent, catagent, stylistAgent)


   // 4. Create the ADK REST server
   restHandler, _ := adkrest.NewServer(adkrest.ServerConfig{
       SessionService:  session.InMemoryService(),
       MemoryService:   memory.InMemoryService(),
       AgentLoader:     loader,
       ArtifactService: artifacts,
   })


   // 5. Mount behind /api/ with CORS support
   r := mux.NewRouter()
   r.Use(tools.LocalhostCORS)
   r.PathPrefix("/api/").Handler(
       http.StripPrefix("/api", tools.LogHandler(restHandler)))


   http.Server{Addr: ":8080", Handler: r}.ListenAndServe()
}

āĻ•āϝāĻŧ⧇āĻ•āϟāĻŋ āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻŦāĻŋāώāϝāĻŧ āϞāĻ•ā§āώāĻŖā§€āϝāĻŧ:

  • āĻāĻœā§‡āĻ¨ā§āϟāϗ⧁āϞ⧋ āύāĻŋāϚ āĻĨ⧇āϕ⧇ āωāĻĒāϰ⧇ āϤ⧈āϰāĻŋ āĻ•āϰāĻž āĻšāϝāĻŧ : āĻ•ā§āϝāĻžāϟāĻžāϞāĻ— āĻāĻœā§‡āĻ¨ā§āϟāϟāĻŋ āĻĒā§āϰāĻĨāĻŽā§‡ āϤ⧈āϰāĻŋ āĻ•āϰāĻž āĻšāϝāĻŧ, āĻ•āĻžāϰāĻŖ āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ āĻāĻŦāĻ‚ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāĻœā§‡āĻ¨ā§āϟ āωāĻ­āϝāĻŧāχ āĻāϰ āωāĻĒāϰ āύāĻŋāĻ°ā§āĻ­āϰāĻļā§€āϞ (āϤāĻžāϰāĻž āĻĒāĻŖā§āϝ āĻ–ā§‹āρāϜāĻžāϰ āĻ•āĻžāϜāϟāĻŋ āĻāϰ āωāĻĒāϰ āĻ…āĻ°ā§āĻĒāĻŖ āĻ•āϰ⧇)āĨ¤
  • agent.NewMultiLoader āϚāĻžāϰāϟāĻŋ āĻāĻœā§‡āĻ¨ā§āϟāϕ⧇āχ āϰ⧇āϜāĻŋāĻ¸ā§āϟāĻžāϰ āĻ•āϰ⧇, āϝāĻžāϤ⧇ REST API āύāĻžāĻŽā§‡āϰ āϏāĻžāĻšāĻžāĻ¯ā§āϝ⧇ āϏ⧇āϗ⧁āϞ⧋āϰ āϝ⧇āϕ⧋āύ⧋āϟāĻŋāϤ⧇ āϰāĻžāωāϟ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤
  • adkrest.NewServer āĻ¸ā§āĻŦāϝāĻŧāĻ‚āĻ•ā§āϰāĻŋāϝāĻŧāĻ­āĻžāĻŦ⧇ REST API āĻĒā§āϰāĻĻāĻžāύ āĻ•āϰ⧇ — āφāĻĒāύāĻžāϕ⧇ āύāĻŋāĻœā§‡ āĻĨ⧇āϕ⧇ āĻāĻ¨ā§āĻĄāĻĒāϝāĻŧ⧇āĻ¨ā§āϟ āĻšā§āϝāĻžāĻ¨ā§āĻĄāϞāĻžāϰ āϞāĻŋāĻ–āϤ⧇ āĻšāϝāĻŧ āύāĻžāĨ¤ ADK āφāĻĒāύāĻžāϕ⧇ āϏ⧇āĻļāύ āĻŽā§āϝāĻžāύ⧇āϜāĻŽā§‡āĻ¨ā§āϟ, āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟ āĻ¸ā§āĻŸā§‹āϰ⧇āϜ āĻāĻŦāĻ‚ āĻāĻœā§‡āĻ¨ā§āϟ āĻāĻ•ā§āϏāĻŋāĻ•āĻŋāωāĻļāύ⧇āϰ āϏ⧁āĻŦāĻŋāϧāĻž āϏāϰāĻžāϏāϰāĻŋ āĻĻāĻŋāϝāĻŧ⧇ āĻĨāĻžāϕ⧇āĨ¤
  • session.InMemoryService() āϏ⧇āĻļāύāϗ⧁āϞ⧋āϕ⧇ āĻŽā§‡āĻŽāϰāĻŋāϤ⧇ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰ⧇āĨ¤ āĻāϰ āĻŽāĻžāύ⧇ āĻšāϞ⧋, āϏāĻžāĻ°ā§āĻ­āĻžāϰ āϰāĻŋāĻ¸ā§āϟāĻžāĻ°ā§āϟ āĻšāϞ⧇ āϏ⧇āĻļāύāϗ⧁āϞ⧋ āĻšāĻžāϰāĻŋāϝāĻŧ⧇ āϝāĻžāϝāĻŧ, āϝāĻž āĻĄā§‡āĻŽā§‹āϰ āϜāĻ¨ā§āϝ āĻ āĻŋāĻ• āφāϛ⧇āĨ¤ āĻĒā§āϰ⧋āĻĄāĻžāĻ•āĻļāύ⧇, āφāĻĒāύāĻžāϰ āĻāĻ•āϟāĻŋ āĻĒāĻžāϰāϏāĻŋāĻ¸ā§āĻŸā§‡āĻ¨ā§āϟ āĻ¸ā§āĻŸā§‹āϰ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āωāϚāĻŋāϤāĨ¤
  • gcsartifact.NewService āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟ (āϤ⧈āϰāĻŋ āĻ•āϰāĻž āĻ›āĻŦāĻŋ) āϗ⧁āĻ—āϞ āĻ•ā§āϞāĻžāωāĻĄ āĻ¸ā§āĻŸā§‹āϰ⧇āĻœā§‡ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰ⧇, āĻĢāϞ⧇ āϏ⧇āϗ⧁āϞ⧋ āĻāĻ•āĻžāϧāĻŋāĻ• āĻ…āύ⧁āϰ⧋āϧ⧇āϰ āĻĒāϰ⧇āĻ“ āĻ…āĻ•ā§āώāϤ āĻĨāĻžāϕ⧇ āĻāĻŦāĻ‚ GCS URI-āĻāϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻļ⧇āϝāĻŧāĻžāϰ āĻ•āϰāĻž āϝāĻžāϝāĻŧāĨ¤

ā§Ģ. 🤖 āĻāĻĄāĻŋāϕ⧇ (āĻāĻœā§‡āĻ¨ā§āϟ āĻĄā§‡āϭ⧇āϞāĻĒāĻŽā§‡āĻ¨ā§āϟ āĻ•āĻŋāϟ) āύāĻŋāϝāĻŧ⧇ āĻŦāĻŋāĻ¸ā§āϤāĻžāϰāĻŋāϤ āφāϞ⧋āϚāύāĻž

ADK āϕ⧀?

āĻāĻœā§‡āĻ¨ā§āϟ āĻĄā§‡āϭ⧇āϞāĻĒāĻŽā§‡āĻ¨ā§āϟ āĻ•āĻŋāϟ (ADK) āĻšāϞ⧋ āϗ⧁āĻ—āϞ⧇āϰ āĻāĻ•āϟāĻŋ āĻ“āĻĒ⧇āύ-āϏ⧋āĻ°ā§āϏ āĻĢā§āϰ⧇āĻŽāĻ“āϝāĻŧāĻžāĻ°ā§āĻ•, āϝāĻž āĻĻāĻŋāϝāĻŧ⧇ Go (āĻāĻŦāĻ‚ Python/Java) āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻāφāχ āĻāĻœā§‡āĻ¨ā§āϟ āϤ⧈āϰāĻŋ āĻ•āϰāĻž āϝāĻžāϝāĻŧāĨ¤ āĻāϟāĻŋ āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĒā§āϞāĻŋāϕ⧇āĻļāύ āĻāĻŦāĻ‚ āĻœā§‡āĻŽāĻŋāύāĻŋ āĻāĻĒāĻŋāφāχ-āĻāϰ āĻŽāĻ§ā§āϝāĻŦāĻ°ā§āϤ⧀ āĻ¸ā§āϤāϰ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻ•āĻžāϜ āĻ•āϰ⧇āĨ¤

āφāĻĒāύāĻŋ āϏāϰāĻžāϏāϰāĻŋ āĻœā§‡āĻŽāĻŋāύāĻŋ āĻāĻĒāĻŋāφāχ āĻ•āϞ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύ āĨ¤ āĻ•āĻŋāĻ¨ā§āϤ⧁ āϝāĻ–āύ āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĒ⧇āϰ āĻĒā§āϰāϝāĻŧā§‹āϜāύ āĻšāĻŦ⧇:

  • āĻ•ā§āϝāĻžāϟāĻžāϞāĻ— āĻĨ⧇āϕ⧇ āĻĒāĻŖā§āϝ āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻ•āϰ⧁āύ
  • āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āϤ⧋āϞāĻž āĻ›āĻŦāĻŋāϰ āωāĻĒāϰ āĻ­āĻŋāĻ¤ā§āϤāĻŋ āĻ•āϰ⧇ āχāĻŽā§‡āϜ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύāĨ¤
  • āĻŽāύ⧇ āĻ•āϰ⧁āύ āφāϗ⧇ āϕ⧋āύ āĻĒā§‹āĻļāĻžāĻ•āϗ⧁āϞ⧋āϰ āĻĒāϰāĻžāĻŽāĻ°ā§āĻļ āĻĻ⧇āĻ“āϝāĻŧāĻž āĻšāϝāĻŧ⧇āĻ›āĻŋāϞāĨ¤
  • āĻāĻ•āĻžāϧāĻŋāĻ• āĻāφāχ āĻāĻœā§‡āĻ¨ā§āĻŸā§‡āϰ āĻŽāĻ§ā§āϝ⧇ āϏāĻŽāĻ¨ā§āĻŦāϝāĻŧ āϏāĻžāϧāύ āĻ•āϰ⧁āύ

āφāĻĒāύāĻžāϰ āĻāĻ•āϟāĻŋ āĻ•āĻžāĻ āĻžāĻŽā§‹ āĻĒā§āϰāϝāĻŧā§‹āϜāύāĨ¤ ADK āϏ⧇āχ āĻ•āĻžāĻ āĻžāĻŽā§‹āϟāĻŋ āĻĒā§āϰāĻĻāĻžāύ āĻ•āϰ⧇āĨ¤

āĻāĻœā§‡āĻ¨ā§āϟ āϞ⧁āĻĒ

āĻĒā§āϰāϤāĻŋāϟāĻŋ ADK āĻāĻœā§‡āĻ¨ā§āϟ āĻāĻ•āϟāĻŋ āϚāĻ•ā§āϰ āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰ⧇:

1. Receive a message (from user or another agent)
2. Think — the LLM reasons about what to do
3. Act — call a tool, delegate to a sub-agent, or respond
4. Return — send the result back

āĻāχ āϞ⧁āĻĒāϟāĻŋ āĻāĻ•āϟāĻŋāĻŽāĻžāĻ¤ā§āϰ āĻ…āύ⧁āϰ⧋āϧ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āĻāĻ•āĻžāϧāĻŋāĻ•āĻŦāĻžāϰ āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤāĻŋ āĻšāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤ āωāĻĻāĻžāĻšāϰāĻŖāĻ¸ā§āĻŦāϰ⧂āĻĒ, āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāĻœā§‡āĻ¨ā§āϟ āϝāĻž āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇:

  1. "āϏ⧈āĻ•āϤ āĻ­ā§āϰāĻŽāϪ⧇āϰ āϜāĻ¨ā§āϝ āφāĻŽāĻžāϕ⧇ āϏāĻžāϜāĻŋāϝāĻŧ⧇ āĻĻāĻŋāύ" āĻ—ā§āϰāĻšāĻŖ āĻ•āϰ⧁āύ
  2. āĻĒāĻŖā§āϝ⧇āϰ āϤāĻžāϞāĻŋāĻ•āĻž āĻĒ⧇āϤ⧇ catalog_agent āϟ⧁āϞāϟāĻŋ āĻ•āϞ āĻ•āϰ⧁āύāĨ¤
  3. ā§ŠāϟāĻŋ āĻĒā§‹āĻļāĻžāϕ⧇āϰ āϏāĻ‚āĻŽāĻŋāĻļā§āϰāĻŖ āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻ•āϰ⧁āύ
  4. āĻ›āĻŦāĻŋ āϤ⧈āϰāĻŋ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻĒā§‹āĻļāĻžāϕ⧇āϰ āϜāĻ¨ā§āϝ fitting_tool āĻ•āϞ āĻ•āϰ⧁āύāĨ¤
  5. āĻ•āĻžāĻ āĻžāĻŽā§‹āĻ—āϤ JSON āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻž āĻĢ⧇āϰāϤ āĻĻāĻŋāύ

āĻŽā§‚āϞ āϧāĻžāϰāĻŖāĻž (āĻāχ āϰāĻŋāĻĒā§‹ āĻĨ⧇āϕ⧇ āύ⧇āĻ“āϝāĻŧāĻž āϕ⧋āĻĄ āϏāĻš)

āĻāϞāĻāϞāĻāĻŽ āĻāĻœā§‡āĻ¨ā§āϟ

āĻĒā§āϰāĻžāĻĨāĻŽāĻŋāĻ• āĻ—āĻ āύ āĻāĻ•āĻ•āĨ¤ llmagent.New() āĻĻāĻŋāϝāĻŧ⧇ āϤ⧈āϰāĻŋ :

// From catalog/agent.go
agent, err := llmagent.New(llmagent.Config{
   Name:        "catalog_agent",                    // Unique identifier
   Model:       m,                                  // Which LLM to use
   Description: "An agent that can search and list products from the catalog",
   Instruction: instructions,                       // Persona prompt (embedded from .md file)
   Tools:       []tool.Tool{listTool, imageTool},   // What the agent can do
})

Instruction āĻĢāĻŋāĻ˛ā§āĻĄāϟāĻŋ āĻšāϞ⧋ āĻāĻœā§‡āĻ¨ā§āĻŸā§‡āϰ āĻĒāϰāĻŋāϚāϝāĻŧ — āĻāϟāĻŋ āĻāϞāĻāϞāĻāĻŽ-āϕ⧇ āĻŦāϞ⧇ āĻĻ⧇āϝāĻŧ āϝ⧇ āϏ⧇ āϕ⧇ āĻāĻŦāĻ‚ āϤāĻžāϰ āφāϚāϰāĻŖ āϕ⧇āĻŽāύ āĻšāĻŦ⧇āĨ¤ āĻāχ āϰ⧇āĻĒā§‹āϤ⧇, āύāĻŋāĻ°ā§āĻĻ⧇āĻļāĻžāĻŦāϞ⧀ āĻŽāĻžāĻ°ā§āĻ•āĻĄāĻžāωāύ āĻĢāĻžāχāϞ āĻšāĻŋāϏ⧇āĻŦ⧇ āϞ⧇āĻ–āĻž āĻšāϝāĻŧ āĻāĻŦāĻ‚ Go-āĻāϰ //go:embed āĻĄāĻŋāϰ⧇āĻ•ā§āϟāĻŋāĻ­ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻ•āĻŽā§āĻĒāĻžāχāϞ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ āĻāĻŽāĻŦ⧇āĻĄ āĻ•āϰāĻž āĻšāϝāĻŧ:

//go:embed instructions.md
var instructions string

āĻāϰ āĻĢāϞ⧇ āĻĒā§āϰāĻŽā§āĻĒāϟāϗ⧁āϞ⧋ āχāύāϞāĻžāχāύ āĻ¸ā§āĻŸā§āϰāĻŋāĻ‚ āĻšāĻŋāϏ⧇āĻŦ⧇ āύāĻž āĻĨ⧇āϕ⧇ āφāϞāĻžāĻĻāĻž, āϏāĻ‚āĻ¸ā§āĻ•āϰāĻŖāϝ⧋āĻ—ā§āϝ āύāĻĨāĻŋ āĻšāĻŋāϏ⧇āĻŦ⧇ āϏāĻ‚āϰāĻ•ā§āώāĻŋāϤ āĻĨāĻžāϕ⧇āĨ¤

āϏāϰāĻžā§āϜāĻžāĻŽ

āϟ⧁āϞ āĻšāϞ⧋ āĻ—ā§‹ āĻĢāĻžāĻ‚āĻļāύ āϝāĻž āĻāϞāĻāϞāĻāĻŽ āĻ•āϞ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤ āĻāĻĄāĻŋāϕ⧇ āĻāϞāĻāϞāĻāĻŽ-āĻāϰ āϟ⧁āϞ-āĻ•āϞāĻŋāĻ‚ āĻĢāϰāĻŽā§āϝāĻžāϟ āĻāĻŦāĻ‚ āφāĻĒāύāĻžāϰ āϟāĻžāχāĻĒ āĻ•āϰāĻž āĻ—ā§‹ āĻĢāĻžāĻ‚āĻļāύ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āĻ…āύ⧁āĻŦāĻžāĻĻ⧇āϰ āĻ•āĻžāϜāϟāĻŋ āĻĒāϰāĻŋāϚāĻžāϞāύāĻž āĻ•āϰ⧇:

// From catalog/agent.go
type ListProductsArgs struct{}                    // Input (can be empty)
type ListProductsResult struct {
   Products []Product `json:"products"`          // Output
}


func ListProducts(ctx tool.Context, args ListProductsArgs) (ListProductsResult, error) {
   return ListProductsResult{Products: catalogProducts}, nil
}


// Register it:
listTool, _ := functiontool.New(functiontool.Config{
   Name:        "listProducts",
   Description: "list all products in the catalog",
}, ListProducts)

ADK āĻ¸ā§āĻŦāϝāĻŧāĻ‚āĻ•ā§āϰāĻŋāϝāĻŧāĻ­āĻžāĻŦ⧇ āφāĻĒāύāĻžāϰ Go struct-āϗ⧁āϞ⧋ āĻĨ⧇āϕ⧇ āĻāĻ•āϟāĻŋ JSON āĻ¸ā§āĻ•āĻŋāĻŽāĻž āϤ⧈āϰāĻŋ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āϏ⧇āϟāĻŋ LLM-āĻ āĻĒāĻžāĻ āĻžāϝāĻŧāĨ¤ āϝāĻ–āύ LLM listProducts āĻĢāĻžāĻ‚āĻļāύāϟāĻŋ āĻ•āϞ āĻ•āϰāĻžāϰ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āύ⧇āϝāĻŧ, āϤāĻ–āύ ADK āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟāϗ⧁āϞ⧋āϕ⧇ āĻĄāĻŋāϏāĻŋāϰāĻŋāϝāĻŧāĻžāϞāĻžāχāϜ āĻ•āϰ⧇, āφāĻĒāύāĻžāϰ āĻĢāĻžāĻ‚āĻļāύāϟāĻŋ āĻ•āϞ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āĻĢāϞāĻžāĻĢāϞāϟāĻŋ āĻĢ⧇āϰāϤ āĻĒāĻžāĻ āĻžāϝāĻŧāĨ¤

tool.Context āĻĒā§āϝāĻžāϰāĻžāĻŽāĻŋāϟāĻžāϰāϟāĻŋ āϟ⧁āϞāϗ⧁āϞāĻŋāϕ⧇ ADK-āĻāϰ āϰāĻžāύāϟāĻžāχāĻŽ āĻĒāϰāĻŋāώ⧇āĻŦāĻžāϗ⧁āϞāĻŋāϤ⧇ āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻĻ⧇āϝāĻŧ — āϝāĻžāϰ āĻŽāĻ§ā§āϝ⧇ āϏāĻŦāĻšā§‡āϝāĻŧ⧇ āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻšāϞ⧋ āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟāϗ⧁āϞāĻŋ :

// Save an image as an artifact
ctx.Artifacts().Save(ctx, "my_image", imagePart)


// Load an artifact
resp, _ := ctx.Artifacts().Load(ctx, "my_image")

āωāĻĒ-āĻāĻœā§‡āĻ¨ā§āϟ āĻĒā§āϰāϤāĻŋāύāĻŋāϧāĻŋāĻĻāϞ

āĻāĻ•āϜāύ āĻāĻœā§‡āĻ¨ā§āϟ agenttool.New() āĻāϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻ…āĻ¨ā§āϝ āĻāĻ•āϜāύ āĻāĻœā§‡āĻ¨ā§āϟāϕ⧇ āϟ⧁āϞ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇:

// From fittingroom/agent.go
Tools: []tool.Tool{
   loadartifactstool.New(),              // List available artifacts
   imgtool,                              // Get product images
   agenttool.New(catalogAgent, nil),     // Delegate to catalog agent
   fittingTool,                          // Generate try-on image
},

āϝāĻ–āύ āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ āĻāĻœā§‡āĻ¨ā§āĻŸā§‡āϰ āĻĒāĻŖā§āϝ⧇āϰ āϤāĻĨā§āϝ⧇āϰ āĻĒā§āϰāϝāĻŧā§‹āϜāύ āĻšāϝāĻŧ, āϤāĻ–āύ āϏ⧇ āĻ•ā§āϝāĻžāϟāĻžāϞāĻ— āĻāĻœā§‡āĻ¨ā§āϟāϕ⧇ āĻāĻ•āϟāĻŋ āϏāĻžāϧāĻžāϰāĻŖ āϟ⧁āϞ⧇āϰ āĻŽāϤ⧋āχ āĻĄāĻžāĻ•āϤ⧇ āĻĒāĻžāϰ⧇āĨ¤ āĻāϞāĻāϞāĻāĻŽ āĻāϟāĻŋāϕ⧇ āϟ⧁āϞ⧇āϰ āϤāĻžāϞāĻŋāĻ•āĻžāϝāĻŧ āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāϝāĻŧ āĻāĻŦāĻ‚ āĻāϟāĻŋāϕ⧇ āϚāĻžāϞ⧁ āĻ•āϰāĻžāϰ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āύāĻŋāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤

āĻ…āϧāĻŋāĻŦ⧇āĻļāύ

āϏ⧇āĻļāύāϗ⧁āϞ⧋ āĻ•āĻĨā§‹āĻĒāĻ•āĻĨāύ⧇āϰ āχāϤāĻŋāĻšāĻžāϏ āĻŸā§āĻ°ā§āϝāĻžāĻ• āĻ•āϰ⧇āĨ¤ ADK-āĻāϰ REST API āĻ¸ā§āĻŦāϝāĻŧāĻ‚āĻ•ā§āϰāĻŋāϝāĻŧāĻ­āĻžāĻŦ⧇ āĻāϗ⧁āϞ⧋ āĻĒāϰāĻŋāϚāĻžāϞāύāĻž āĻ•āϰ⧇:

POST /api/apps/{appName}/users/{userId}/sessions  →  Creates a new session
POST /api/run  (with sessionId)                   →  Runs agent within that session

āĻāχ āĻ…ā§āϝāĻžāĻĒāϟāĻŋāϰ āĻāĻ•āϟāĻŋ āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻĄāĻŋāϜāĻžāχāύ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āĻšāϞ⧋: āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻ…āύ⧁āϰ⧋āϧ⧇āϰ āϜāĻ¨ā§āϝ āĻāĻ•āϟāĻŋ āύāϤ⧁āύ āϏ⧇āĻļāύ āϤ⧈āϰāĻŋ āĻ•āϰ⧇ (āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻŸā§āϰāĻžāχ-āĻ…āύ āĻ¸ā§āĻŦāϤāĻ¨ā§āĻ¤ā§āϰ), āĻ…āĻ¨ā§āϝāĻĻāĻŋāϕ⧇ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāĻ•āχ āϏ⧇āĻļāύ āĻĒ⧁āύāϰāĻžāϝāĻŧ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇āύ (āĻĢāϞ⧇ āĻāϟāĻŋ āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āϤ⧀ āĻĒāϰāĻžāĻŽāĻ°ā§āĻļāϗ⧁āϞ⧋ āĻŽāύ⧇ āϰāĻžāϖ⧇ āĻāĻŦāĻ‚ āĻĒā§āϰāĻžāĻĒā§āϤ āĻŽāϤāĻžāĻŽāϤ⧇āϰ āĻ­āĻŋāĻ¤ā§āϤāĻŋāϤ⧇ āϏ⧇āϟāĻŋāϕ⧇ āĻĒāϰāĻŋāĻŽāĻžāĻ°ā§āϜāύ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇)āĨ¤

āϰāĻžāĻœā§āϝ

āĻ¸ā§āĻŸā§‡āϟ āĻšāϞ⧋ āϏ⧇āĻļāύ⧇āϰ āϏāĻžāĻĨ⧇ āϏāĻ‚āϝ⧁āĻ•ā§āϤ āĻāĻ•āϟāĻŋ āϕ⧀-āĻ­ā§āϝāĻžāϞ⧁ āĻ¸ā§āĻŸā§‹āϰāĨ¤ āĻāĻœā§‡āĻ¨ā§āϟāϰāĻž āϏāĻŽāĻ¨ā§āĻŦāϝāĻŧ āϏāĻžāϧāύ⧇āϰ āϜāĻ¨ā§āϝ āĻ¸ā§āĻŸā§‡āϟ āϰāĻŋāĻĄ āĻ“ āϰāĻžāχāϟ āĻ•āϰ⧇:

// Write to state
ctx.State().Set("previously_used_products", "[\"id_bomber\",\"id_hat\"]")


// Read from state
val, err := ctx.State().Get("previously_used_products")

āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāĻœā§‡āĻ¨ā§āϟ āϤāĻžāϰ āĻ¸ā§āĻŸā§‡āϟ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻŽāύ⧇ āϰāĻžāϖ⧇ āϝ⧇ āϏ⧇ āφāϗ⧇ āϕ⧋āύ āĻĒā§āϰ⧋āĻĄāĻžāĻ•ā§āϟāϗ⧁āϞ⧋ āϏāĻžāĻœā§‡āĻ¸ā§āϟ āĻ•āϰ⧇āϛ⧇, āϝāĻžāϤ⧇ āĻĒāϰ⧇āϰāĻŦāĻžāϰ āϏ⧇ āĻ­āĻŋāĻ¨ā§āύ āĻĒā§āϰ⧋āĻĄāĻžāĻ•ā§āϟ āĻŦ⧇āϛ⧇ āύāĻŋāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤

āĻĒā§āϰāĻ¤ā§āύāĻŦāĻ¸ā§āϤ⧁

āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟ āĻšāϞ⧋ āύāĻžāĻŽāϝ⧁āĻ•ā§āϤ āĻŦāĻžāχāύāĻžāϰāĻŋ āĻ…āĻŦāĻœā§‡āĻ•ā§āϟ (āϏāĻžāϧāĻžāϰāĻŖāϤ āĻ›āĻŦāĻŋ), āϝāĻž āĻĒā§āϰāϤāĻŋ āϏ⧇āĻļāύ⧇ āϏāĻ‚āϰāĻ•ā§āώāĻŋāϤ āĻĨāĻžāϕ⧇āĨ¤ āĻŸā§‡āĻ•ā§āϏāϟ āϰ⧇āϏāĻĒāĻ¨ā§āϏ⧇āϰ āĻŽāϤ⧋ āύāϝāĻŧ, āĻāϗ⧁āϞ⧋ āφāϞāĻžāĻĻāĻžāĻ­āĻžāĻŦ⧇ āϏāĻ‚āϰāĻ•ā§āώāĻŋāϤ āĻšāϝāĻŧ āĻāĻŦāĻ‚ āύāĻžāĻŽ āĻĻāĻŋāϝāĻŧ⧇ āϖ⧁āρāĻœā§‡ āĻŦ⧇āϰ āĻ•āϰāĻž āĻšāϝāĻŧ:

// Save a generated image as an artifact
artName := fmt.Sprintf("generated_fitting_%s_%s", ctx.InvocationID(), uuid.NewString()[:8])
ctx.Artifacts().Save(ctx, artName, imagePart)


// The frontend fetches it via:
// GET /api/apps/{app}/users/{user}/sessions/{session}/artifacts/{artName}

āĻāϰ āĻĢāϞ⧇ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻžāϗ⧁āϞ⧋ āĻšāĻžāϞāĻ•āĻž āĻĨāĻžāϕ⧇ — āĻāĻœā§‡āĻ¨ā§āϟ āĻļ⧁āϧ⧁ āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āĻŸā§‡āϰ āύāĻžāĻŽāϟāĻŋ āĻĢ⧇āϰāϤ āĻĻ⧇āϝāĻŧ āĻāĻŦāĻ‚ āĻĢā§āϰāĻ¨ā§āϟāĻāĻ¨ā§āĻĄ āφāϞāĻžāĻĻāĻžāĻ­āĻžāĻŦ⧇ āĻŦāĻžāχāύāĻžāϰāĻŋ āχāĻŽā§‡āϜ āĻĄā§‡āϟāĻž āϏāĻ‚āĻ—ā§āϰāĻš āĻ•āϰ⧇āĨ¤

āĻ•āϞāĻŦā§āϝāĻžāĻ•

āĻ•āϞāĻŦā§āϝāĻžāĻ• āĻšāϞ⧋ āĻšā§āĻ• āϝāĻž āĻāĻœā§‡āĻ¨ā§āϟ āϞ⧁āĻĒ⧇āϰ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻĒāϝāĻŧ⧇āĻ¨ā§āĻŸā§‡ āϚāϞ⧇āĨ¤ āĻāϗ⧁āϞ⧋ āĻāĻ•ā§āϏāĻŋāĻ•āĻŋāωāĻļāύāϕ⧇ āύāĻŋāϰ⧀āĻ•ā§āώāĻž, āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻŦāĻž āϏāĻ‚āĻ•ā§āώāĻŋāĻĒā§āϤ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇:

llmagent.Config{
   // Runs before the agent starts — used to save uploaded images
   BeforeAgentCallbacks: []agent.BeforeAgentCallback{SaveIncomingBlobs},


   // Runs before each LLM call — used to inject context
   BeforeModelCallbacks: []llmagent.BeforeModelCallback{
       ExtractAndInjectUserImage,
       InjectPreviousProducts,
   },


   // Runs after each LLM response — used to extract data
   AfterModelCallbacks: []llmagent.AfterModelCallback{SaveSelectedProducts},
}

āϝāĻĻāĻŋ āϕ⧋āύ⧋ āĻ•āϞāĻŦā§āϝāĻžāĻ• āĻāĻ•āϟāĻŋ āύāύ-āύāĻŋāϞ āϰ⧇āϏāĻĒāĻ¨ā§āϏ āϰāĻŋāϟāĻžāĻ°ā§āύ āĻ•āϰ⧇, āϤāĻžāĻšāϞ⧇ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āφāϚāϰāĻŖāϟāĻŋ āĻāĻĄāĻŧāĻŋāϝāĻŧ⧇ āϝāĻžāĻ“āϝāĻŧāĻž āĻšāϝāĻŧāĨ¤ āωāĻĻāĻžāĻšāϰāĻŖāĻ¸ā§āĻŦāϰ⧂āĻĒ, āĻāĻ•āϟāĻŋ BeforeModelCallback āϝāĻž āĻāĻ•āϟāĻŋ āĻ•ā§āϝāĻžāĻļāĻĄ āϰ⧇āϏāĻĒāĻ¨ā§āϏ āϰāĻŋāϟāĻžāĻ°ā§āύ āĻ•āϰ⧇, āϏ⧇āϟāĻŋ āĻĒā§āϰāĻ•ā§ƒāϤ LLM āĻ•āϞāϟāĻŋ āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖāϰ⧂āĻĒ⧇ āĻāĻĄāĻŧāĻŋāϝāĻŧ⧇ āϝāĻžāĻŦ⧇āĨ¤

JSON āĻ¸ā§āĻ•āĻŋāĻŽāĻž āĻĒā§āϰāϝāĻŧā§‹āĻ—

āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ āĻāĻŦāĻ‚ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāĻœā§‡āĻ¨ā§āϟ āωāĻ­āϝāĻŧāχ āĻāϞāĻāϞāĻāĻŽ-āϕ⧇ āĻ¸ā§āĻŸā§āϰāĻžāĻ•āϚāĻžāĻ°ā§āĻĄ JSON-āĻ āωāĻ¤ā§āϤāϰ āĻĻāĻŋāϤ⧇ āĻŦāĻžāĻ§ā§āϝ āĻ•āϰ⧇:

GenerateContentConfig: &genai.GenerateContentConfig{
   ResponseMIMEType:   "application/json",
   ResponseJsonSchema: fittingSchemaMap(),  // Defines the expected structure
}

āĻāϟāĻŋ āύāĻŋāĻļā§āϚāĻŋāϤ āĻ•āϰ⧇ āϝ⧇ āĻĢā§āϞāĻžāϟāĻžāϰ āĻĢā§āϰāĻ¨ā§āϟāĻāĻ¨ā§āĻĄ āϏāĻ°ā§āĻŦāĻĻāĻž āĻĒāĻžāĻ°ā§āϏāϝ⧋āĻ—ā§āϝ āĻĄā§‡āϟāĻž āĻ—ā§āϰāĻšāĻŖ āĻ•āϰ⧇, āϕ⧋āύ⧋ āĻ…āĻĒāϰāĻŋāĻ•āĻ˛ā§āĻĒāĻŋāϤ āĻŸā§‡āĻ•ā§āϏāϟ āύāϝāĻŧāĨ¤

āĻ•ā§āϝāĻžāϟāĻžāϞāĻ— āĻāĻœā§‡āĻ¨ā§āϟ: āϏāϰāϞāϤāĻŽ āωāĻĻāĻžāĻšāϰāĻŖ

āĻ•ā§āϝāĻžāϟāĻžāϞāĻ— āĻāĻœā§‡āĻ¨ā§āϟ ( catalog/agent.go ) āĻšāϞ⧋ āϏāĻŋāĻ¸ā§āĻŸā§‡āĻŽā§‡āϰ āϏāĻŦāĻšā§‡āϝāĻŧ⧇ āϏāϰāϞ āĻāĻœā§‡āĻ¨ā§āϟ — ADK āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύāϗ⧁āϞ⧋ āĻŦā§‹āĻāĻžāϰ āϜāĻ¨ā§āϝ āĻāϟāĻŋ āĻāĻ•āϟāĻŋ āĻ­āĻžāϞ⧋ āϏ⧂āϚāύāĻžāĨ¤

āĻāϤ⧇ āĻĻ⧁āϟāĻŋ āϏāϰāĻžā§āϜāĻžāĻŽ āφāϛ⧇:

  1. listProducts — āĻāĻ•āϟāĻŋ YAML āĻĢāĻžāχāϞ āĻĨ⧇āϕ⧇ āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻĒāĻŖā§āϝ āĻ•ā§āϝāĻžāϟāĻžāϞāĻ— āĻĢ⧇āϰāϤ āĻĻ⧇āϝāĻŧāĨ¤
  2. getProductImage — GCS (āĻŦāĻž āĻ¸ā§āĻĨāĻžāύ⧀āϝāĻŧ āĻĢāϞāĻŦā§āϝāĻžāĻ•) āĻĨ⧇āϕ⧇ āĻāĻ•āϟāĻŋ āĻĒāĻŖā§āϝ⧇āϰ āĻ›āĻŦāĻŋ āϞ⧋āĻĄ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āĻāϟāĻŋāϕ⧇ āĻāĻ•āϟāĻŋ āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟ āĻšāĻŋāϏāĻžāĻŦ⧇ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰ⧇āĨ¤

getProductImage āϟ⧁āϞāϟāĻŋ āĻāĻ•āϟāĻŋ āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āĻĻ⧇āĻ–āĻžāϝāĻŧ — āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟ āĻ•ā§āϝāĻžāĻļāĻŋāĻ‚ āϏāĻš āĻŽāĻžāĻ˛ā§āϟāĻŋ-āϏ⧋āĻ°ā§āϏ āϞ⧋āĻĄāĻŋāĻ‚ :

// From tools/imagetool.go — simplified
func GetProductImage(ctx tool.Context, args GetProductImageArgs) (GetProductImageResult, error) {
   // First: check if it's already an artifact
   _, err := ctx.Artifacts().Load(ctx, args.ImageName)
   if err == nil {
       return GetProductImageResult{Image: args.ImageName}, nil  // Cache hit!
   }


   // Second: try loading from GCS bucket
   rc, err := client.Bucket(bucket).Object("catalog-assets/images/" + args.ImageName).NewReader(ctx)
   if err != nil {
       // Third: fall back to local filesystem
       localData, _ := os.ReadFile("../flutter_frontend/assets/images/" + args.ImageName)
       ctx.Artifacts().Save(ctx, args.ImageName, &genai.Part{InlineData: ...})
       return GetProductImageResult{Image: args.ImageName}, nil
   }


   // Save to artifact cache and return
   ctx.Artifacts().Save(ctx, args.ImageName, &genai.Part{InlineData: ...})
   return GetProductImageResult{Image: args.ImageName}, nil
}

āϟ⧁āϞāϟāĻŋ āĻĒā§āϰāĻĨāĻŽā§‡ āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟ, āϤāĻžāϰāĻĒāϰ GCS, āĻāĻŦāĻ‚ āϏāĻŦāĻļ⧇āώ⧇ āϞ⧋āĻ•āĻžāϞ āĻĢāĻžāχāϞāϗ⧁āϞ⧋ āĻšā§‡āĻˇā§āϟāĻž āĻ•āϰ⧇āĨ¤ āĻāĻ•āĻŦāĻžāϰ āϞ⧋āĻĄ āĻšāϝāĻŧ⧇ āϗ⧇āϞ⧇, āĻ›āĻŦāĻŋāϟāĻŋ āĻāĻ•āϟāĻŋ āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻ•ā§āϝāĻžāĻļ āĻ•āϰāĻž āĻšāϝāĻŧ, āĻĢāϞ⧇ āĻĒāϰāĻŦāĻ°ā§āϤ⧀ āĻ•āϞāϗ⧁āϞ⧋ āϤāĻžā§ŽāĻ•ā§āώāĻŖāĻŋāĻ•āĻ­āĻžāĻŦ⧇ āϏāĻŽā§āĻĒāĻ¨ā§āύ āĻšāϝāĻŧāĨ¤

ā§Ŧ. đŸ§Ē āĻāφāχ āĻĒāĻžāχāĻĒāϞāĻžāχāύ: āĻāĻœā§‡āĻ¨ā§āϟāĻĻ⧇āϰ āĻ•āĻžāĻ°ā§āϝāĻ•āϞāĻžāĻĒ

āĻāĻŦāĻžāϰ āϚāϞ⧁āύ āϏāĻŦāĻšā§‡āϝāĻŧ⧇ āĻ…āĻ¤ā§āϝāĻžāϧ⧁āύāĻŋāĻ• āĻĻ⧁āϟāĻŋ āĻāĻœā§‡āĻ¨ā§āϟ āύāĻŋāϝāĻŧ⧇ āφāϞ⧋āϚāύāĻž āĻ•āϰāĻž āϝāĻžāĻ• — āϝ⧇āϗ⧁āϞ⧋ āĻĒā§āϰāĻ•ā§ƒāϤāĻĒāĻ•ā§āώ⧇ āĻ›āĻŦāĻŋ āϤ⧈āϰāĻŋ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āĻĒā§‹āĻļāĻžāĻ• āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻ•āϰ⧇āĨ¤

ā§Ŧ.ā§§ āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ āĻāĻœā§‡āĻ¨ā§āϟ

āĻĢāĻžāχāϞ:

adk_backend/fittingroom/agent.go

āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ āĻāĻœā§‡āĻ¨ā§āϟ āĻšāϞ⧋ 'āĻ­āĻžāĻ°ā§āϚ⧁āϝāĻŧāĻžāϞ āĻŸā§āϰāĻžāχ-āĻ…āύ'-āĻāϰ āĻŽā§‚āϞ āϚāĻžāϞāĻŋāĻ•āĻžāĻļāĻ•ā§āϤāĻŋāĨ¤ āϝāĻ–āύ āϕ⧋āύ⧋ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āϤāĻžāϰ āĻ›āĻŦāĻŋ āφāĻĒāϞ⧋āĻĄ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āĻāĻ•āϟāĻŋ āĻĒāĻŖā§āϝ āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āϰ⧇āύ, āϤāĻ–āύ āĻāχ āĻāĻœā§‡āĻ¨ā§āϟāϟāĻŋ āϏ⧇āχ āĻĒāĻŖā§āϝāϟāĻŋ āĻĒāϰāĻŋāĻšāĻŋāϤ āĻŦā§āϝāĻ•ā§āϤāĻŋāϰ āĻāĻ•āϟāĻŋ āϏāĻŽāĻ¨ā§āĻŦāĻŋāϤ āĻ›āĻŦāĻŋ āϤ⧈āϰāĻŋ āĻ•āϰ⧇āĨ¤

fitting_tool — āϧāĻžāĻĒ⧇ āϧāĻžāĻĒ⧇

āĻŽā§‚āϞ āϞāϜāĻŋāĻ•āϟāĻŋ doFitting āĻĢāĻžāĻ‚āĻļāύ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āĻĨāĻžāϕ⧇āĨ¤ āĻāĻœā§‡āĻ¨ā§āϟ āϝāĻ–āύ āĻāϟāĻŋāϕ⧇ āĻ•āϞ āĻ•āϰ⧇, āϤāĻ–āύ āϝāĻž āϘāĻŸā§‡ āϤāĻž āύāĻŋāĻšā§‡ āĻĻ⧇āĻ“āϝāĻŧāĻž āĻšāϞ⧋:

āϧāĻžāĻĒ ā§§: āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āĻ›āĻŦāĻŋāϟāĻŋ āϏāĻŽāĻžāϧāĻžāύ āĻ•āϰ⧁āύ

func doFitting(ctx tool.Context, args FittingToolArgs) (FittingToolResult, error) {
   if len(args.Accessories) > 2 {
       args.Accessories = args.Accessories[:2]  // Safety limit: max 2 items
   }


   var userPart *genai.Part
   if strings.HasPrefix(args.UserImage, "gs://") {
       // If we have a GCS URI from a previous fitting, use it directly
       userPart = &genai.Part{FileData: &genai.FileData{
           FileURI:  args.UserImage,
           MIMEType: gcsURIMimeType(args.UserImage),
       }}
   } else {
       // Otherwise, load the image from artifact storage
       userImgResp, err := ctx.Artifacts().Load(ctx, args.UserImage)
       userPart = userImgResp.Part
   }

āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āĻ›āĻŦāĻŋ āĻĻ⧁āϟāĻŋ āĻ‰ā§ŽāϏ āĻĨ⧇āϕ⧇ āφāϏāϤ⧇ āĻĒāĻžāϰ⧇:

  • āĻāĻ•āϟāĻŋ āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟ āύāĻžāĻŽ (āϝ⧇āĻŽāύ upload_abc123_1 ) — āĻāϟāĻŋ āĻšāϞ⧋ āĻĒā§āϰāĻžāĻĨāĻŽāĻŋāĻ• āφāĻĒāϞ⧋āĻĄ, āϝāĻž SaveIncomingBlobs āĻ•āϞāĻŦā§āϝāĻžāĻ• āĻĻā§āĻŦāĻžāϰāĻž āϏāĻ‚āϰāĻ•ā§āώāĻŋāϤ āĻšāϝāĻŧāĨ¤
  • āĻāĻ•āϟāĻŋ gs:// URI — āĻāϟāĻŋ āĻĒā§‚āĻ°ā§āĻŦ⧇ āϤ⧈āϰāĻŋ āĻ•āϰāĻž āĻāĻ•āϟāĻŋ āĻĢāĻŋāϟāĻŋāĻ‚ āĻĢāϞāĻžāĻĢāϞ, āϝāĻž āĻŦāĻŋāĻ­āĻŋāĻ¨ā§āύ āϏ⧇āĻļāύ⧇ āĻĒ⧁āύāϰāĻžāϝāĻŧ āĻŦā§āϝāĻŦāĻšāĻžāϰ⧇āϰ āϜāĻ¨ā§āϝ GCS-āĻ āϏāĻ‚āϰāĻ•ā§āώāĻŋāϤ āĻĨāĻžāϕ⧇āĨ¤

āĻāχ āĻĻā§āĻŦ⧈āϤ-āĻĒāĻĨ⧇āϰ āύāĻ•āĻļāĻžāϟāĻŋ āχāĻšā§āĻ›āĻžāĻ•ā§ƒāϤ: āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāĻœā§‡āĻ¨ā§āϟ āϝāĻ–āύ āĻĒāϰāĻŦāĻ°ā§āϤ⧀āϤ⧇ āĻĒā§‹āĻļāĻžāĻ• āĻŸā§āϰāĻžāχ-āĻ…āύ āϤ⧈āϰāĻŋ āĻ•āϰ⧇, āϤāĻ–āύ āĻāϟāĻŋ āĻĒā§āϰāĻžāĻĨāĻŽāĻŋāĻ• āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽā§‡āϰ āĻĢāϞāĻžāĻĢāϞ āĻĨ⧇āϕ⧇ GCS URL-āϟāĻŋ āĻĒ⧁āύāϰāĻžāϝāĻŧ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇, āϝāĻžāϤ⧇ āϏāĻŽāĻ¸ā§āϤ āĻĒā§‹āĻļāĻžāϕ⧇āϰ āĻ•ā§āώ⧇āĻ¤ā§āϰ⧇ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āĻĒāϰāĻŋāϚāϝāĻŧ āĻāĻ•āχ āĻĨāĻžāϕ⧇āĨ¤

āϧāĻžāĻĒ ā§¨: āĻŽāĻžāĻ˛ā§āϟāĻŋāĻŽā§‹āĻĄāĻžāϞ āĻĒā§āϰāĻŽā§āĻĒāϟāϟāĻŋ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ

   parts := []*genai.Part{
       genai.NewPartFromText(toolInstructions),           // Identity preservation prompt
       genai.NewPartFromText("Reference Person Photo:"),
       userPart,                                          // The user's photo
   }


   for _, acc := range args.Accessories {
       accResp, _ := ctx.Artifacts().Load(ctx, acc)       // Load product image artifact
       parts = append(parts, genai.NewPartFromText("Product Image to Apply:"))
       parts = append(parts, accResp.Part)                // The product photo
   }

toolInstructions ( tool_instructions.md āĻĨ⧇āϕ⧇ āĻāĻŽāĻŦ⧇āĻĄ āĻ•āϰāĻž) āĻ…āĻ¤ā§āϝāĻ¨ā§āϤ āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ — āĻāϟāĻŋ āĻœā§‡āĻŽāĻŋāύāĻŋāϕ⧇ āύāĻŋāĻ°ā§āĻĻ⧇āĻļ āĻĻ⧇āϝāĻŧ āϝ⧇āύ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āĻĒāϰāĻŋāϚāϝāĻŧ (āĻŽā§āĻ–, āĻļāĻžāϰ⧀āϰāĻŋāĻ• āĻ—āĻ āύ, āĻ¤ā§āĻŦāϕ⧇āϰ āϰāĻ™, āϚ⧁āϞ) āĻ…āĻ•ā§āώ⧁āĻŖā§āĻŖ āϰ⧇āϖ⧇ āĻļ⧁āϧ⧁āĻŽāĻžāĻ¤ā§āϰ āĻĒā§‹āĻļāĻžāĻ•āϟāĻŋ āĻĒā§āϰāϝāĻŧā§‹āĻ— āĻ•āϰāĻž āĻšāϝāĻŧāĨ¤ āĻāχ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āύāĻŋāĻ°ā§āĻĻ⧇āĻļāύāĻžāϟāĻŋ āĻ›āĻžāĻĄāĻŧāĻž, āĻŽāĻĄā§‡āϞāϟāĻŋ āĻšāϝāĻŧāϤ⧋ āĻŦā§āϝāĻ•ā§āϤāĻŋāϟāĻŋāϰ āĻšā§‡āĻšāĻžāϰāĻž āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰ⧇ āĻĢ⧇āϞāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤

āϧāĻžāĻĒ ā§Š: āĻ›āĻŦāĻŋ āϤ⧈āϰāĻŋāϰ āϜāĻ¨ā§āϝ āĻœā§‡āĻŽāĻŋāύāĻŋāϕ⧇ āĻĄāĻžāϕ⧁āύ

   client, _ := genai.NewClient(ctx, &genai.ClientConfig{
       Backend:  genai.BackendVertexAI,                 // Vertex AI endpoint
       Project:  os.Getenv("GOOGLE_CLOUD_PROJECT"),     // From your .env
       Location: "global",                              // Multi-region endpoint
   })


   resp, _ := client.Models.GenerateContent(ctx, "gemini-2.5-flash-image",
       []*genai.Content{genai.NewContentFromParts(parts, "user")},
       &genai.GenerateContentConfig{
           ResponseModalities: []string{"TEXT", "IMAGE"},  // Request both text and image output
           Temperature:        genai.Ptr(float32(0.2)),    // Low temperature for consistency
       })

āϚāĻžāϰāϟāĻŋ āĻāĻœā§‡āĻ¨ā§āϟ āĻāĻŦāĻ‚ āχāĻŽā§‡āϜ-āĻœā§‡āύ āϟ⧁āĻ˛â€”āϏāĻŦāϗ⧁āϞ⧋āχ āĻāĻ•āϟāĻŋāĻŽāĻžāĻ¤ā§āϰ āĻ…āĻĨ⧇āύāϟāĻŋāϕ⧇āĻļāύ āĻĒāĻžāĻĨ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇: Backend: genai.BackendVertexAI , āϝāĻž āĻĒā§āϰāĻœā§‡āĻ•ā§āϟ āφāχāĻĄāĻŋ āĻĻā§āĻŦāĻžāϰāĻž āĻ…ā§āϝāĻžāĻĒā§āϞāĻŋāϕ⧇āĻļāύ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻ•ā§āϰ⧇āĻĄā§‡āύāĻļāĻŋāϝāĻŧāĻžāϞāϏ⧇āϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻ…āĻĨ⧇āύāϟāĻŋāϕ⧇āĻŸā§‡āĻĄ āĻšāϝāĻŧāĨ¤ āĻ…āĻ°ā§āϕ⧇āĻ¸ā§āĻŸā§āϰ⧇āĻļāύ āĻŽāĻĄā§‡āϞāϗ⧁āϞ⧋ ( gemini-3.1-pro-preview , gemini-3-flash-preview ) āĻāĻŦāĻ‚ āχāĻŽā§‡āϜ āĻŽāĻĄā§‡āϞ ( gemini-2.5-flash-image ) āϏāĻŦāϗ⧁āϞ⧋āχ āĻāĻ•āχ Vertex AI āĻāĻ¨ā§āĻĄāĻĒāϝāĻŧ⧇āĻ¨ā§āĻŸā§‡āϰ āĻĒ⧇āĻ›āύ⧇ āĻĨāĻžāϕ⧇ āĻāĻŦāĻ‚ āĻāĻ•āχ ADC āĻ•ā§āϞāĻžāωāĻĄ āĻ¸ā§āĻŸā§‹āϰ⧇āϜ āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ⧇āϰ āĻ…āύ⧁āĻŽā§‹āĻĻāύāĻ“ āĻĻ⧇āϝāĻŧ—āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻ•āϞ⧇āϰ āϜāĻ¨ā§āϝ āĻāĻ•āϟāĻŋāĻŽāĻžāĻ¤ā§āϰ āĻ•ā§āϰ⧇āĻĄā§‡āύāĻļāĻŋāϝāĻŧāĻžāϞāĨ¤

āϧāĻžāĻĒ ā§Ē: āĻĢāϞāĻžāĻĢāϞāϟāĻŋ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰ⧁āύāĨ¤

   // Find the image in the response (may contain both text + image parts)
   var genPart *genai.Part
   for _, p := range resp.Candidates[0].Content.Parts {
       if p.InlineData != nil {
           genPart = p
           break
       }
   }


   // Save as an ADK artifact
   artName := fmt.Sprintf("generated_fitting_%s_%s", ctx.InvocationID(), uuid.NewString()[:8])
   ctx.Artifacts().Save(ctx, artName, genPart)


   // Also upload to GCS for cross-session reuse
   objectName := fmt.Sprintf("generated-fittings/%s.jpg", ctx.InvocationID())
   w := storageClient.Bucket(bucket).Object(objectName).NewWriter(ctx)
   w.Write(genPart.InlineData.Data)
   gcsURI := fmt.Sprintf("gs://%s/%s", bucket, objectName)


   return FittingToolResult{ArtifactName: artName, GCSUrl: gcsURI}, nil

āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ āĻāĻŦāĻ‚ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āĻŸā§‡āϰ āĻŽāĻ§ā§āϝ⧇ āĻāĻœā§‡āĻ¨ā§āϟ āĻšāĻ¸ā§āϤāĻžāĻ¨ā§āϤāϰ⧇āϰ āĻŽā§‚āϞ āϚāĻžāĻŦāĻŋāĻ•āĻžāĻ āĻŋ āĻšāϞ⧋ āĻĄā§āϝāĻŧāĻžāϞ-āϏ⧇āĻ­ (āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟ + āϜāĻŋāϏāĻŋāĻāϏ)āĨ¤ āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟāϟāĻŋ āĻŦāĻ°ā§āϤāĻŽāĻžāύ āϏ⧇āĻļāύ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āϤāĻžā§ŽāĻ•ā§āώāĻŖāĻŋāĻ• āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻĒā§āϰāĻĻāĻžāύ āĻ•āϰ⧇, āĻ…āĻ¨ā§āϝāĻĻāĻŋāϕ⧇ āϜāĻŋāϏāĻŋāĻāϏ āχāωāφāϰāφāχ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟāϕ⧇ (āϝāĻŋāύāĻŋ āĻāĻ•āϟāĻŋ āĻ­āĻŋāĻ¨ā§āύ āϏ⧇āĻļāύ⧇ āĻ•āĻžāϜ āĻ•āϰ⧇āύ) āĻĒāϰāĻŦāĻ°ā§āϤ⧀āϤ⧇ āĻāĻ•āχ āĻ›āĻŦāĻŋāϟāĻŋ āϰ⧇āĻĢāĻžāϰ⧇āĻ¨ā§āϏ āĻ•āϰāĻžāϰ āϏ⧁āϝ⧋āĻ— āĻĻ⧇āϝāĻŧāĨ¤

SaveIncomingBlobs āĻ•āϞāĻŦā§āϝāĻžāĻ•

āĻāĻœā§‡āĻ¨ā§āϟ āϝ⧁āĻ•ā§āϤāĻŋ-āϤāĻ°ā§āĻ• āĻļ⧁āϰ⧁ āĻ•āϰāĻžāϰ āφāϗ⧇āχ, āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āφāĻĒāϞ⧋āĻĄ āĻ•āϰāĻž āĻ›āĻŦāĻŋāϗ⧁āϞ⧋ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āĻāχ BeforeAgentCallback āϚāϞ⧇:

func SaveIncomingBlobs(ctx agent.CallbackContext) (*genai.Content, error) {
   for pindex, p := range ctx.UserContent().Parts {
       if p.InlineData != nil {
           aname := fmt.Sprintf("upload_%s_%d", ctx.InvocationID(), pindex)
           ctx.Artifacts().Save(ctx, aname, p)
       }
   }
   return nil, nil  // Return nil to proceed with normal agent execution
}

(nil, nil) āϰāĻŋāϟāĻžāĻ°ā§āύ āĻ•āϰāĻžāϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡, āĻ•āϞāĻŦā§āϝāĻžāĻ•āϟāĻŋ āϏāĻ‚āϕ⧇āϤ āĻĻ⧇āϝāĻŧ āϝ⧇, "āφāĻŽāĻžāϰ āĻĒā§āϰāĻŋāĻĒā§āϰāϏ⧇āϏāĻŋāĻ‚ āĻļ⧇āώ — āĻāĻ–āύ āĻāĻœā§‡āĻ¨ā§āϟāϟāĻŋāϕ⧇ āĻ¸ā§āĻŦāĻžāĻ­āĻžāĻŦāĻŋāĻ•āĻ­āĻžāĻŦ⧇ āϚāĻžāϞāĻžāύāĨ¤" āϝāĻĻāĻŋ āĻāϟāĻŋ nil āĻ›āĻžāĻĄāĻŧāĻž āĻ…āĻ¨ā§āϝ āϕ⧋āύ⧋ āĻ•āĻ¨ā§āĻŸā§‡āĻ¨ā§āϟ āϰāĻŋāϟāĻžāĻ°ā§āύ āĻ•āϰāϤ, āϤāĻŦ⧇ āĻāϟāĻŋ āĻāĻœā§‡āĻ¨ā§āϟāϟāĻŋāϕ⧇ āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖāϰ⧂āĻĒ⧇ āĻļāĻ°ā§āϟ-āϏāĻžāĻ°ā§āĻ•āĻŋāϟ āĻ•āϰ⧇ āĻĻāĻŋāϤāĨ¤

ā§Ŧ.⧍ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāĻœā§‡āĻ¨ā§āϟ

āĻĢāĻžāχāϞ:

adk_backend/stylist/agent.go

āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāĻœā§‡āĻ¨ā§āϟāϟāĻŋ āĻāχ āϏāĻŋāĻ¸ā§āĻŸā§‡āĻŽā§‡āϰ āϏāĻŦāĻšā§‡āϝāĻŧ⧇ āĻ…āĻ¤ā§āϝāĻžāϧ⧁āύāĻŋāĻ• āĻŦā§āϝāĻŦāĻ¸ā§āĻĨāĻžāĨ¤ āĻāϟāĻŋ āĻŦā§āϝāĻ•ā§āϤāĻŋāĻ—āϤ āĻĒā§āϰāϝāĻŧā§‹āϜāύ āĻ…āύ⧁āϝāĻžāϝāĻŧā§€ āĻĒā§‹āĻļāĻžāϕ⧇āϰ āϏ⧁āĻĒāĻžāϰāĻŋāĻļ āϤ⧈āϰāĻŋ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āĻ•āĻĨā§‹āĻĒāĻ•āĻĨāύ⧇āϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻŦāĻžāϰāĻŦāĻžāϰ āĻĒāϰāĻŋāĻŽāĻžāĻ°ā§āϜāύ⧇ āϏāĻšāĻžāϝāĻŧāϤāĻž āĻ•āϰ⧇āĨ¤

āϤāĻŋāύāϟāĻŋ āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤāĻŋ — āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āĻŸā§‡āϰ āĻ¸ā§āĻŽā§ƒāϤāĻŋ

āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāĻ•āĻžāϧāĻŋāĻ• āĻĒāĻžāϞāĻžāĻŦāĻŋāĻļāĻŋāĻˇā§āϟ āĻ•āĻĨā§‹āĻĒāĻ•āĻĨāύ⧇ āĻĒā§āϰāĻžāϏāĻ™ā§āĻ—āĻŋāĻ•āϤāĻž āĻŦāϜāĻžāϝāĻŧ āϰāĻžāĻ–āϤ⧇ āϤāĻŋāύāϟāĻŋ āϰ⧇āĻĢāĻžāϰ⧇āĻ¨ā§āϏ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇āύ:

āĻ•āϞāĻŦā§āϝāĻžāĻ• ā§§:

InjectPreviousProducts (āĻŦāĻŋāĻĢā§‹āϰāĻŽāĻĄā§‡āϞ)

āϏāĻŽāĻ¸ā§āϝāĻžāϟāĻŋ āĻšāϞ⧋: āϝāĻĻāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āĻŦāϞ⧇āύ "āφāĻŽāĻžāϕ⧇ āĻŦāĻŋāĻ­āĻŋāĻ¨ā§āύ āĻŦāĻŋāĻ•āĻ˛ā§āĻĒ āĻĻ⧇āĻ–āĻžāĻ“," āϤāĻžāĻšāϞ⧇ āĻāϞāĻāϞāĻāĻŽ (LLM) āĻšāϝāĻŧāϤ⧋ āφāĻŦāĻžāϰāĻ“ āĻāĻ•āχ āĻĒāĻŖā§āϝāϗ⧁āϞ⧋āχ āϏ⧁āĻĒāĻžāϰāĻŋāĻļ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇, āĻ•āĻžāϰāĻŖ āĻāϟāĻŋ āφāϗ⧇ āϕ⧀ āϏ⧁āĻĒāĻžāϰāĻŋāĻļ āĻ•āϰ⧇āϛ⧇ āϤāĻž āϏāĻšāϜāĻžāϤāĻ­āĻžāĻŦ⧇ āĻŸā§āĻ°ā§āϝāĻžāĻ• āĻ•āϰ⧇ āύāĻžāĨ¤

āϏāĻŽāĻžāϧāĻžāύ: āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻžāϰ āĻĒāϰ⧇, āĻĒā§āϰ⧋āĻĄāĻžāĻ•ā§āϟ āφāχāĻĄāĻŋāϗ⧁āϞāĻŋ āϏ⧇āĻļāύ āĻ¸ā§āĻŸā§‡āĻŸā§‡ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰāĻž āĻšāϝāĻŧāĨ¤ āĻĒāϰāĻŦāĻ°ā§āϤ⧀ LLM āĻ•āϞ āĻ•āϰāĻžāϰ āφāϗ⧇, āĻāχ āĻ•āϞāĻŦā§āϝāĻžāĻ•āϟāĻŋ āϏ⧇āϗ⧁āϞāĻŋ āĻĒāĻĄāĻŧ⧇ āĻāĻŦāĻ‚ āĻāĻ•āϟāĻŋ āχāĻ™ā§āĻ—āĻŋāϤ āϝ⧋āĻ— āĻ•āϰ⧇:

func InjectPreviousProducts(ctx agent.CallbackContext, req *model.LLMRequest) (*model.LLMResponse, error) {
   prev, err := ctx.State().Get(stateKeyPreviousProducts)  // Read from session state
   if err != nil {
       return nil, nil  // No previous state — first run
   }


   // Append hint to the user's message
   for i := len(req.Contents) - 1; i >= 0; i-- {
       if req.Contents[i].Role == "user" {
           req.Contents[i].Parts = append(req.Contents[i].Parts,
               genai.NewPartFromText(fmt.Sprintf(
                   "IMPORTANT: You previously suggested these products: %s. "+
                   "You MUST pick DIFFERENT complementary products this time.", prev)))
           break
       }
   }
   return nil, nil  // Continue to LLM call
}

āĻ•āϞāĻŦā§āϝāĻžāĻ• ⧍:

ExtractAndInjectUserImage (āĻŽāĻĄā§‡āϞ⧇āϰ āφāϗ⧇)

āϏāĻŽāĻ¸ā§āϝāĻžāϟāĻŋ āĻšāϞ⧋: āϝāĻ–āύ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āĻŽāϤāĻžāĻŽāϤ āĻĻ⧇āύ ("āφāϰāĻ“ āĻ•ā§āϝāĻžāϜ⧁āϝāĻŧāĻžāϞ āĻ•āϰ⧁āύ"), āϤāĻ–āύ āĻĒāϰāĻŦāĻ°ā§āϤ⧀ āĻŽā§‡āϏ⧇āĻœā§‡ āϤāĻžāρāϰ āĻ›āĻŦāĻŋ āφāϰ āϝ⧁āĻ•ā§āϤ āĻ•āϰāĻž āĻšāϝāĻŧ āύāĻžāĨ¤ āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻĢāĻŋāϟāĻŋāĻ‚ āϟ⧁āϞāϟāĻŋāϰ āϜāĻ¨ā§āϝ āĻ›āĻŦāĻŋāϟāĻŋ āĻĒā§āϰāϝāĻŧā§‹āϜāύāĨ¤

āϏāĻŽāĻžāϧāĻžāύ: āĻĒā§āϰāĻĨāĻŽ āĻ…āύ⧁āϰ⧋āϧ⧇, āĻāχ āĻ•āϞāĻŦā§āϝāĻžāĻ•āϟāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āĻ›āĻŦāĻŋāϰ āϰ⧇āĻĢāĻžāϰ⧇āĻ¨ā§āϏ āĻŦ⧇āϰ āĻ•āϰ⧇ āĻ¸ā§āĻŸā§‡āĻŸā§‡ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰ⧇āĨ¤ āĻĒāϰāĻŦāĻ°ā§āϤ⧀ āĻ…āύ⧁āϰ⧋āϧāϗ⧁āϞ⧋āϤ⧇, āĻāϟāĻŋ āĻĒ⧁āύāϰāĻžāϝāĻŧ āϤāĻž āϝ⧁āĻ•ā§āϤ āĻ•āϰ⧇:

func ExtractAndInjectUserImage(ctx agent.CallbackContext, req *model.LLMRequest) (*model.LLMResponse, error) {
   var foundImgStr string


   // Search for user image in the latest message
   for i := len(req.Contents) - 1; i >= 0; i-- {
       if req.Contents[i].Role == "user" {
           for _, part := range req.Contents[i].Parts {
               if strings.Contains(part.Text, "User try-on base image") {
                   foundImgStr = part.Text  // Found the GCS URI reference
               }
           }
           break
       }
   }


   if foundImgStr != "" {
       ctx.State().Set(stateKeyUserImageStr, foundImgStr)  // Save for later
   } else {
       // Not in current message — retrieve from state and inject
       val, _ := ctx.State().Get(stateKeyUserImageStr)
       if savedImgStr, ok := val.(string); ok {
           // Inject into the latest user message
           req.Contents[last].Parts = append(req.Contents[last].Parts,
               genai.NewPartFromText("REMINDER: Use this image: " + savedImgStr))
       }
   }
   return nil, nil
}

āĻ•āϞāĻŦā§āϝāĻžāĻ• ā§Š:

SaveSelectedProducts (āĻŽāĻĄā§‡āϞ⧇āϰ āĻĒāϰ⧇)

LLM āĻĒā§‹āĻļāĻžāϕ⧇āϰ āĻĒāϰāĻžāĻŽāĻ°ā§āĻļ āĻĻāĻŋāϝāĻŧ⧇ āϏāĻžāĻĄāĻŧāĻž āĻĻ⧇āĻ“āϝāĻŧāĻžāϰ āĻĒāϰ, āĻāχ āĻ•āϞāĻŦā§āϝāĻžāĻ•āϟāĻŋ JSON āĻĒāĻžāĻ°ā§āϏ āĻ•āϰ⧇ āĻĒā§āϰ⧋āĻĄāĻžāĻ•ā§āϟ āφāχāĻĄāĻŋāϗ⧁āϞ⧋ āĻŦ⧇āϰ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āĻĒāϰāĻŦāĻ°ā§āϤ⧀ āϏāĻŽāϝāĻŧ⧇ āĻŦā§āϝāĻŦāĻšāĻžāϰ⧇āϰ āϜāĻ¨ā§āϝ InjectPreviousProducts āĻ•āϞāĻŦā§āϝāĻžāϕ⧇āϰ āϜāĻ¨ā§āϝ āϏ⧇āϗ⧁āϞ⧋ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰ⧇ āϰāĻžāϖ⧇:

func SaveSelectedProducts(ctx agent.CallbackContext, resp *model.LLMResponse, respErr error) (*model.LLMResponse, error) {
   for _, part := range resp.Content.Parts {
       ids := extractProductIDs(part.Text)  // Parse JSON → extract product IDs
       if len(ids) > 0 {
           data, _ := json.Marshal(ids)
           ctx.State().Set(stateKeyPreviousProducts, string(data))  // Save to state
       }
   }
   return nil, nil  // Don't modify the response
}

āĻāĻ•āĻ¤ā§āϰ⧇, āĻāχ āϤāĻŋāύāϟāĻŋ āĻ•āϞāĻŦā§āϝāĻžāĻ• āĻāĻ•āϟāĻŋ āĻĢāĻŋāĻĄāĻŦā§āϝāĻžāĻ• āϞ⧁āĻĒ āϤ⧈āϰāĻŋ āĻ•āϰ⧇:

Request 1:  User sends styling request + user image
           → ExtractAndInjectUserImage SAVES image to state
           → LLM generates 3 outfits
           → SaveSelectedProducts SAVES product IDs to state


Request 2:  User says "make it more casual"
           → ExtractAndInjectUserImage INJECTS saved image into prompt
           → InjectPreviousProducts INJECTS "don't reuse these IDs"
           → LLM generates 3 NEW outfits
           → SaveSelectedProducts UPDATES product IDs in state

ā§Ŧ.ā§Š āĻŽā§‚āϞ āĻāĻœā§‡āĻ¨ā§āϟ

āĻĢāĻžāχāϞ:

adk_backend/rootagent/agent.go

āϏāĻŦāĻšā§‡āϝāĻŧ⧇ āϏāϰāϞ āĻāĻœā§‡āĻ¨ā§āϟ — āĻŽāĻžāĻ¤ā§āϰ ā§Šā§§ āϞāĻžāχāύ:

func NewRootAgent(project string, fittingAgent, catalogAgent, stylistAgent agent.Agent) (agent.Agent, error) {
   m, _ := gemini.NewModel(ctx, "gemini-3-flash-preview", &genai.ClientConfig{
       Backend:  genai.BackendVertexAI,
       Project:  project,
       Location: "global",
   })


   return llmagent.New(llmagent.Config{
       Name:        "root_agent",
       Model:       m,
       Description: "A root agent that delegates to other agents",
       Instruction: "You are a helpful shopping assistant. If the user asks about fitting " +
           "items or generating images, delegate to the fitting room agent. If the user " +
           "asks about products, delegate to the catalog agent. If the user asks for " +
           "styling advice, delegate to the stylist agent.",
       SubAgents: []agent.Agent{fittingAgent, catalogAgent, stylistAgent},
   })
}

āĻāϟāĻŋ gemini-3-flash-preview (āĻĻā§āϰ⧁āϤāϤāĻŽ āĻŽāĻĄā§‡āϞ) āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇, āĻ•āĻžāϰāĻŖ āϰāĻžāωāϟāĻŋāĻ‚ āϏāĻ‚āĻ•ā§āϰāĻžāĻ¨ā§āϤ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤāϗ⧁āϞ⧋ āϏāĻšāϜ — LLM-āϕ⧇ āĻļ⧁āϧ⧁ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āĻ…āĻ­āĻŋāĻĒā§āϰāĻžāϝāĻŧ āĻĒāĻĄāĻŧāϤ⧇ āĻšāϝāĻŧ āĻāĻŦāĻ‚ āϏāĻ āĻŋāĻ• āϏāĻžāĻŦ-āĻāĻœā§‡āĻ¨ā§āϟ āĻŦ⧇āϛ⧇ āύāĻŋāϤ⧇ āĻšāϝāĻŧāĨ¤ āϕ⧋āύ⧋ āϟ⧁āϞ⧇āϰ āĻĒā§āϰāϝāĻŧā§‹āϜāύ āύ⧇āχ; SubAgents āĻ¸ā§āĻŦāϝāĻŧāĻ‚āĻ•ā§āϰāĻŋāϝāĻŧāĻ­āĻžāĻŦ⧇ āĻĻāĻžāϝāĻŧāĻŋāĻ¤ā§āĻŦ āĻ…āĻ°ā§āĻĒāϪ⧇āϰ āĻ•āĻžāϜāϟāĻŋ āĻ•āϰ⧇āĨ¤

ā§­. 📱 āĻĢā§āϞāĻžāϟāĻžāϰ āĻĢā§āϰāĻ¨ā§āϟāĻāĻ¨ā§āĻĄ āφāĻ°ā§āĻ•āĻŋāĻŸā§‡āĻ•āϚāĻžāϰ

āĻĢā§āϞāĻžāϟāĻžāϰ āĻĢā§āϰāĻ¨ā§āϟāĻāĻ¨ā§āĻĄāϟāĻŋ āĻāĻ•āϟāĻŋ āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻ•āĻžāĻ°ā§āϝāĻ•āϰ⧀ āϰāĻŋāĻŸā§‡āχāϞ āĻļāĻĒāĻŋāĻ‚ āĻ…ā§āϝāĻžāĻĒāĨ¤ āĻāφāχ āĻĢāĻŋāϚāĻžāϰāϗ⧁āϞ⧋ flutter_frontend/lib/workshop_tasks/ āĻĢā§‹āĻ˛ā§āĻĄāĻžāϰ⧇ āĻĨāĻžāϕ⧇, āϝāĻž core_app/ āĻ āĻĨāĻžāĻ•āĻž āφāϗ⧇ āĻĨ⧇āϕ⧇ āϤ⧈āϰāĻŋ āĻļāĻĒāĻŋāĻ‚ āĻ…āĻ­āĻŋāĻœā§āĻžāϤāĻž āĻĨ⧇āϕ⧇ āφāϞāĻžāĻĻāĻžāĨ¤

MVVM āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ

āĻ…ā§āϝāĻžāĻĒāϟāĻŋ Provider āĻĒā§āϝāĻžāϕ⧇āϜ āϏāĻš Model-View-ViewModel āφāĻ°ā§āĻ•āĻŋāĻŸā§‡āĻ•āϚāĻžāϰ āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰ⧇:

┌──────────────────┐    ┌────────────────────┐    ┌──────────────────┐
│  View (Widget)   │◀───│ ViewModel (Provider)│◀───│  Service (HTTP)  │
│                  │    │                     │    │                  │
│ â€ĸ Renders UI     │    │ â€ĸ Holds state       │    │ â€ĸ Makes API calls│
│ â€ĸ User gestures  │───â–ļ│ â€ĸ Business logic    │───â–ļ│ â€ĸ Parses response│
│ â€ĸ Listens for    │    │ â€ĸ notifyListeners() │    │ â€ĸ Returns data   │
│   state changes  │    │                     │    │                  │
└──────────────────┘    └────────────────────┘    └──────────────────┘

āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻ¸ā§āϤāϰ⧇āϰ āĻāĻ•āϟāĻŋ āϏ⧁āĻ¸ā§āĻĒāĻˇā§āϟ āĻ­ā§‚āĻŽāĻŋāĻ•āĻž āϰāϝāĻŧ⧇āϛ⧇:

  • āĻŽāĻĄā§‡āϞ : Product , Outfit , StyleRequest āĻŽāϤ⧋ āĻĄā§‡āϟāĻž āĻ•ā§āϞāĻžāϏ āĻāĻŦāĻ‚ TryOnState āĻŽāϤ⧋ āĻāύāĻžāĻŽāĨ¤
  • ViewModel ( ChangeNotifier ): āĻŦāĻ°ā§āϤāĻŽāĻžāύ āĻ…āĻŦāĻ¸ā§āĻĨāĻž āϧāĻžāϰāĻŖ āĻ•āϰ⧇ āĻāĻŦāĻ‚ notifyListeners() āĻāϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ UI-āϤ⧇ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύāϏāĻŽā§‚āĻš āĻĒā§āϰāϚāĻžāϰ āĻ•āϰ⧇āĨ¤
  • āĻ­āĻŋāω (āωāχāĻœā§‡āϟ): context.watch () āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ ViewModel-āĻ āϏāĻžāĻŦāĻ¸ā§āĻ•ā§āϰāĻžāχāĻŦ āĻ•āϰ⧇āĨ¤ context.watch () āĻāĻŦāĻ‚ āĻ…āĻŦāĻ¸ā§āĻĨāĻžāϰ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻšāϞ⧇ āĻĒ⧁āύāĻ°ā§āύāĻŋāĻ°ā§āĻŽāĻžāĻŖ āĻ•āϰ⧇
  • āĻĒāϰāĻŋāώ⧇āĻŦāĻž : ADK āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄā§‡ HTTP āĻ•āϞ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āϟāĻžāχāĻĒ āĻ•āϰāĻž āĻĄā§‡āϟāĻž āĻĢ⧇āϰāϤ āĻĻ⧇āϝāĻŧāĨ¤

āϏāĻžāĻ°ā§āĻ­āĻŋāϏ āϞ⧇āϝāĻŧāĻžāϰ

āϏāĻžāĻ°ā§āĻ­āĻŋāϏāϗ⧁āϞ⧋āϕ⧇ āĻ…ā§āϝāĻžāĻŦāĻ¸ā§āĻŸā§āϰāĻžāĻ•ā§āϟ āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ āĻšāĻŋāϏ⧇āĻŦ⧇ āϏāĻ‚āĻœā§āĻžāĻžāϝāĻŧāĻŋāϤ āĻ•āϰāĻž āĻšāϝāĻŧ, āϝ⧇āϗ⧁āϞ⧋āϰ ADK-āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āχāĻŽāĻĒā§āϞāĻŋāĻŽā§‡āĻ¨ā§āĻŸā§‡āĻļāύ āϰāϝāĻŧ⧇āϛ⧇:

// Abstract interface — defines WHAT the service does
abstract class TryItOnService {
 Future<(Uint8List?, String?)> generateTryOnImage(
   Uint8List userImageBytes,
   Uint8List productImageBytes,
 );
}


// Concrete implementation — defines HOW (via ADK REST API)
class AdkFittingRoomService implements TryItOnService { ... }

āĻāχ āĻĒ⧃āĻĨāϕ⧀āĻ•āϰāϪ⧇āϰ āĻ…āĻ°ā§āĻĨ āĻšāϞ⧋, āφāĻĒāύāĻŋ āĻ…ā§āϝāĻžāĻĒ⧇āϰ āĻŦāĻžāĻ•āĻŋ āĻ…āĻ‚āĻļ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āύāĻž āĻ•āϰ⧇āχ ADK āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄā§‡āϰ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤ⧇ Firebase AI, āĻāĻ•āϟāĻŋ āĻŽāĻ• āϏāĻžāĻ°ā§āĻ­āĻŋāϏ āĻŦāĻž āĻ…āĻ¨ā§āϝ āϝ⧇āϕ⧋āύ⧋ āχāĻŽāĻĒā§āϞāĻŋāĻŽā§‡āĻ¨ā§āĻŸā§‡āĻļāύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŦ⧇āύāĨ¤

ā§Š-āϧāĻžāĻĒ⧇āϰ āĻāĻĒāĻŋāφāχ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ

AdkFittingRoomService āĻāĻŦāĻ‚ AdkStylingService āωāĻ­āϝāĻŧāχ ADK āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄā§‡āϰ āϏāĻžāĻĨ⧇ āϝ⧋āĻ—āĻžāϝ⧋āϗ⧇āϰ āϜāĻ¨ā§āϝ āĻāĻ•āχ āĻĒāĻĻā§āϧāϤāĻŋ āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰ⧇:

āϧāĻžāĻĒ ā§§: āĻāĻ•āϟāĻŋ āϏ⧇āĻļāύ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ

Future<String> _createSession() async {
 final url = Uri.parse(
   '$_baseUrl/apps/${Uri.encodeComponent(_appName)}/users/$_userId/sessions');
 final response = await _client.post(url,
   headers: {'Content-Type': 'application/json'},
   body: jsonEncode({}));
 final body = jsonDecode(response.body) as Map<String, dynamic>;
 return body['id'] as String;  // Returns the session ID
}

āϧāĻžāĻĒ ā§¨: āĻāĻœā§‡āĻ¨ā§āϟāϟāĻŋ āϚāĻžāϞāĻžāύ

Future<(String?, String?)> _runAgent({required String sessionId, ...}) async {
 final requestBody = jsonEncode({
   'appName': _appName,
   'userId': _userId,
   'sessionId': sessionId,
   'newMessage': {
     'role': 'user',
     'parts': [
       {'text': 'Generate a virtual try-on...'},
       {'inlineData': {'mimeType': 'image/jpeg', 'data': base64Encode(userImageBytes)}},
       {'inlineData': {'mimeType': 'image/png', 'data': base64Encode(productImageBytes)}},
     ],
   },
 });
 final response = await _client.post(Uri.parse('$_baseUrl/run'), body: requestBody);
 // Parse response events for artifact name and GCS URL...
}

āϧāĻžāĻĒ ā§Š: āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟāϟāĻŋ āύāĻŋāϝāĻŧ⧇ āφāϏ⧁āύ

Future<Uint8List?> _loadArtifact({required String sessionId, required String artifactName}) async {
 final url = Uri.parse(
   '$_baseUrl/apps/$_appName/users/$_userId/sessions/$sessionId/artifacts/$artifactName');
 final response = await _client.get(url);
 final part = jsonDecode(response.body) as Map<String, dynamic>;
 final data = part['inlineData']['data'] as String;
 return base64Decode(data);  // Returns raw image bytes
}

āĻĄāĻŋāϜāĻžāχāύ⧇ āĻāĻ•āϟāĻŋ āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āĻĒāĻžāĻ°ā§āĻĨāĻ•ā§āϝ āĻšāϞ⧋: āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ āϏāĻžāĻ°ā§āĻ­āĻŋāϏ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻ…āύ⧁āϰ⧋āϧ⧇āϰ āϜāĻ¨ā§āϝ āĻāĻ•āϟāĻŋ āύāϤ⧁āύ āϏ⧇āĻļāύ āϤ⧈āϰāĻŋ āĻ•āϰ⧇ (āĻĒā§āϰāϤāĻŋāĻŦāĻžāϰ _createSession() āĻ•āϞ āĻ•āϰāĻž āĻšāϝāĻŧ), āĻ…āĻ¨ā§āϝāĻĻāĻŋāϕ⧇ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ‚ āϏāĻžāĻ°ā§āĻ­āĻŋāϏ āĻāĻ•āχ āϏ⧇āĻļāύ āĻĒ⧁āύāϰāĻžāϝāĻŧ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ ( _sessionId ??= await _createSession() ) āϝāĻžāϤ⧇ āĻāĻ•āĻžāϧāĻŋāĻ•āĻŦāĻžāϰ āĻ•āĻĨā§‹āĻĒāĻ•āĻĨāύ āϏāĻŽā§āĻ­āĻŦ āĻšāϝāĻŧāĨ¤

āĻ¸ā§āĻŸā§‡āϟ āĻŽā§āϝāĻžāύ⧇āϜāĻŽā§‡āĻ¨ā§āϟ: āĻĻā§āϝ āĻŸā§āϰāĻžāχāχāϟāĻ…āύāĻĒā§āϰ⧋āĻ­āĻžāχāĻĄāĻžāϰ

āĻĢāĻžāχāϞ:

workshop_tasks/step_1_try_it_on/providers/try_it_on_provider.dart

TryItOnProvider āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻŸā§āϰāĻžāχ-āĻ…āύ āĻĒā§āϰāĻ•ā§āϰāĻŋāϝāĻŧāĻžāϟāĻŋ āĻĒāϰāĻŋāϚāĻžāϞāύāĻž āĻ•āϰ⧇āĨ¤ āĻāϟāĻŋ āĻāĻ•āϟāĻŋ āĻ¸ā§āĻŸā§‡āϟ āĻŽā§‡āĻļāĻŋāύ āĻšāĻŋāϏ⧇āĻŦ⧇ TryOnState enum āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇:

enum TryOnState { initial, imagePicked, generating, success, error }


class TryItOnProvider with ChangeNotifier {
 TryOnState _state = TryOnState.initial;
 Uint8List? _userImageBytes;
 Uint8List? _generatedImage;
 String? _errorMessage;

āĻĒā§āϰāĻžāχāϭ⧇āϟ āĻ¸ā§āĻŸā§‡āϟ āĻŸā§āϰāĻžāύāϜāĻŋāĻļāύ āϏāĻžāĻŽāĻžā§āϜāĻ¸ā§āϝ āύāĻŋāĻļā§āϚāĻŋāϤ āĻ•āϰ⧇ — āφāĻĒāύāĻŋ āĻ•āĻ–āύ⧋āχ āĻĒ⧁āϰāύ⧋ āĻĄā§‡āϟāĻž āĻŽā§āϛ⧇ āύāĻž āĻĢ⧇āϞ⧇ āĻāĻŦāĻ‚ UI-āϕ⧇ āĻ…āĻŦāĻšāĻŋāϤ āύāĻž āĻ•āϰ⧇ āĻ¸ā§āĻŸā§‡āϟ āφāĻĒāĻĄā§‡āϟ āĻ•āϰ⧇āύ āύāĻž:

 void _setGenerating() {
   _state = TryOnState.generating;
   _errorMessage = null;            // Clear any previous error
   _wasLastGenerationCached = false;
   notifyListeners();               // Tell the UI to rebuild
 }


 void _setSuccess(Uint8List image, {bool isCached = false}) {
   _generatedImage = image;
   _errorMessage = null;
   _wasLastGenerationCached = isCached;
   _state = TryOnState.success;
   notifyListeners();
 }

āĻĒā§āϰāϧāĻžāύ āĻ‰ā§ŽāĻĒāĻžāĻĻāύ āĻĒāĻĻā§āϧāϤāĻŋāϟāĻŋāχ āϏāĻŦāĻ•āĻŋāϛ⧁āϕ⧇ āĻāĻ•āĻ¤ā§āϰāĻŋāϤ āĻ•āϰ⧇:

 Future<String?> generateTryOnImage(String productImagePath) async {
   final userImageBytes = _userImageBytes;
   if (userImageBytes == null) {
     _setError('No image selected.');
     return _errorMessage;
   }


   // Check local cache first
   final cachedImage = ImageUtils.getCachedImage(_sessionCache, userImageBytes, productUint8List);
   if (cachedImage != null) {
     _setSuccess(cachedImage, isCached: true);
     return null;
   }


   _setGenerating();  // Triggers loading state in UI


   try {
     final (generatedBytes, gcsUrl) = await _aiService.generateTryOnImage(
       userImageBytes, productUint8List);
     if (generatedBytes != null) {
       _fittingGcsUrl = gcsUrl;     // Save for the stylist agent later
       _setSuccess(generatedBytes);
     }
   } catch (e) {
     _setError(e.toString());
   }
   return _state == TryOnState.success ? null : _errorMessage;
 }

UI: āĻ¸ā§āĻŸā§‡āϟ āϰāĻžāωāϟāĻžāϰ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻ¸ā§āĻ•ā§āϰāĻŋāύ

āĻĢāĻžāχāϞ:

workshop_tasks/step_1_try_it_on/ui/2_try_it_on_screen.dart

āĻŸā§āϰāĻžāχ-āĻ…āύ āĻ¸ā§āĻ•ā§āϰāĻŋāύāϟāĻŋ āĻĒā§āϰ⧋āĻ­āĻžāχāĻĄāĻžāϰ⧇āϰ āĻ…āĻŦāĻ¸ā§āĻĨāĻžāϰ āωāĻĒāϰ āĻ­āĻŋāĻ¤ā§āϤāĻŋ āĻ•āϰ⧇ āϏāĻžāĻŦ-āĻ¸ā§āĻ•ā§āϰāĻŋāύāϗ⧁āϞāĻŋāϰ āĻŽāĻ§ā§āϝ⧇ āϰāĻžāωāϟāĻŋāĻ‚ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āĻĄāĻžāĻ°ā§āϟ ā§Š-āĻāϰ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āĻŽā§āϝāĻžāϚāĻŋāĻ‚ āĻ“ AnimatedSwitcher āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇:

class TryItOnScreen extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   final tryOnProvider = context.watch<TryItOnProvider>();


   return ScaffoldWithBackgroundNoise(
     appBar: const TryOnAppBar(),
     body: AnimatedSwitcher(
       duration: AppDurations.medium,
       child: switch (tryOnProvider.state) {
         TryOnState.initial || TryOnState.error => const ChooseImageScreen(),
         TryOnState.imagePicked || TryOnState.generating => LoadingScreen(
           userImage: tryOnProvider.userImageBytes),
         TryOnState.success => const FittingRoomScreen(),
       },
     ),
   );
 }
}

context.watch () āĻĒā§āϰ⧋āĻ­āĻžāχāĻĄāĻžāϰāϕ⧇ āϏāĻžāĻŦāĻ¸ā§āĻ•ā§āϰāĻžāχāĻŦ āĻ•āϰ⧇āĨ¤ āϝāĻ–āύāχ notifyListeners() āĻ•āϞ āĻ•āϰāĻž āĻšāϝāĻŧ, āĻāχ āωāχāĻœā§‡āϟāϟāĻŋ āϰāĻŋāĻŦāĻŋāĻ˛ā§āĻĄ āĻšāϝāĻŧ āĻāĻŦāĻ‚ AnimatedSwitcher āĻŽāϏ⧃āĻŖāĻ­āĻžāĻŦ⧇ āĻ¸ā§āĻ•ā§āϰāĻŋāύāϗ⧁āϞ⧋āϰ āĻŽāĻ§ā§āϝ⧇ āĻŸā§āϰāĻžāύāϜāĻŋāĻļāύ āĻ•āϰ⧇āĨ¤ āĻāĻ–āĻžāύ⧇ Navigator.push āύ⧇āχ — āĻ¸ā§āĻŸā§‡āϟ āĻāύāĻžāĻŽā§‡āϰ āωāĻĒāϰ āĻ­āĻŋāĻ¤ā§āϤāĻŋ āĻ•āϰ⧇ āĻ¸ā§āĻ•ā§āϰāĻŋāύ⧇āϰ āĻ•āĻ¨ā§āĻŸā§‡āĻ¨ā§āϟ āχāύ-āĻĒā§āϞ⧇āϏ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāĻŋāϤ āĻšāϝāĻŧāĨ¤

āĻāĻœā§‡āĻ¨ā§āϟāĻŋāĻ• āĻšāĻ¸ā§āϤāĻžāĻ¨ā§āϤāϰ: āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ → āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ

āϏāĻŦāĻšā§‡āϝāĻŧ⧇ āφāĻ•āĻ°ā§āώāĻŖā§€āϝāĻŧ āχāωāĻāĻ•ā§āϏ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύāϟāĻŋ āĻšāϞ⧋, āĻ…ā§āϝāĻžāĻĒāϟāĻŋ āϕ⧀āĻ­āĻžāĻŦ⧇ āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ āĻāĻœā§‡āĻ¨ā§āϟ āĻĨ⧇āϕ⧇ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāĻœā§‡āĻ¨ā§āĻŸā§‡āϰ āĻ•āĻžāϛ⧇ āĻ•āύāĻŸā§‡āĻ•ā§āϏāϟ āĻĒā§āϰ⧇āϰāĻŖ āĻ•āϰ⧇āĨ¤

5_fitting_room.dart āĻĢāĻžāχāϞ⧇, āĻŸā§āϰāĻžāχ-āĻ…āύ āχāĻŽā§‡āϜāϟāĻŋ āϤ⧈āϰāĻŋ āĻšāĻ“āϝāĻŧāĻžāϰ āĻĒāϰ, "Style Me" āĻŦāĻžāϟāύāϟāĻŋ āĻāĻ•āϟāĻŋ āĻĢāĻ°ā§āĻŽ āĻ–ā§‹āϞ⧇āĨ¤ āϝāĻ–āύ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āϏāĻžāĻŦāĻŽāĻŋāϟ āĻ•āϰ⧇āύ:

// From 1_style_me_form_sheet.dart
Navigator.pop(context, StyleRequest(
 location: _locationController.text.trim(),
 occasion: _occasionController.text.trim(),
 notes: _notesController.text.trim(),
 gcsUserImageUrl: provider.fittingGcsUrl,          // GCS URI from fitting result
 userImageData: provider.fittingGcsUrl == null
     ? provider.userImageBytes : null,              // Fallback to raw bytes
 selectedProductId: provider.selectedProduct?.id,   // Product they already tried on
 selectedProductTitle: provider.selectedProduct?.title,
));

StyleRequest āĻāϰ āĻŽāĻ§ā§āϝ⧇ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āĻŸā§‡āϰ āĻĒā§āϰāϝāĻŧā§‹āϜāύ⧀āϝāĻŧ āϏāĻŦāĻ•āĻŋāϛ⧁ āĻ…āĻ¨ā§āϤāĻ°ā§āϭ⧁āĻ•ā§āϤ āϰāϝāĻŧ⧇āϛ⧇:

  • āĻ…āĻŦāĻ¸ā§āĻĨāĻžāύ āĻ“ āωāĻĒāϞāĻ•ā§āώ — āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ‚āϝāĻŧ⧇āϰ āϜāĻ¨ā§āϝ āĻŸā§‡āĻ•ā§āϏāĻŸā§‡āϰ āĻĒā§āϰ⧇āĻ•ā§āώāĻžāĻĒāϟ
  • GCS āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āĻ›āĻŦāĻŋāϰ URL — āϝāĻžāϤ⧇ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻšā§āĻŦāĻšā§ āĻāĻ•āχ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āωāĻĒāĻ¸ā§āĻĨāĻžāĻĒāύāĻž āĻĒ⧁āύāϰāĻžāϝāĻŧ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤
  • āύāĻŋāĻ°ā§āĻŦāĻžāϚāĻŋāϤ āĻĒāĻŖā§āϝ — āϤāĻžāχ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāϟāĻŋ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻĒā§‹āĻļāĻžāϕ⧇ āĻ…āĻ¨ā§āϤāĻ°ā§āϭ⧁āĻ•ā§āϤ āĻ•āϰ⧇āύāĨ¤

āĻāϟāĻžāχ āĻšāϞ⧋ āĻāĻœā§‡āĻ¨ā§āϟāĻŋāĻ• āĻšā§āϝāĻžāĻ¨ā§āĻĄāĻ…āĻĢ â€” āϝāĻžāϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻāĻ•āϟāĻŋ āĻāφāχ āĻāĻœā§‡āĻ¨ā§āϟ āĻĨ⧇āϕ⧇ āĻ…āĻ¨ā§āϝāϟāĻŋāϤ⧇ āĻŽāĻžāĻ˛ā§āϟāĻŋāĻŽā§‹āĻĄāĻžāϞ āĻ•āύāĻŸā§‡āĻ•ā§āϏāϟ āύāĻŋāĻ°ā§āĻŦāĻŋāĻ˜ā§āύ⧇ āĻ¸ā§āĻĨāĻžāύāĻžāĻ¨ā§āϤāϰ āĻ•āϰāĻž āĻšāϝāĻŧ, āĻāĻŦāĻ‚ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āĻļ⧁āϧ⧁āĻŽāĻžāĻ¤ā§āϰ āĻāĻ•āϟāĻŋ āϏāĻžāϧāĻžāϰāĻŖ āĻĢāĻ°ā§āĻŽ āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāύāĨ¤

āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ‚ āĻĒā§āϰāĻŦāĻžāĻš: āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ‚āĻĒā§āϰ⧋āĻ­āĻžāχāĻĄāĻžāϰ

āĻĢāĻžāχāϞ:

workshop_tasks/step_2_style_me/providers/styling_provider.dart

StylingProvider TryItOnProvider āĻšā§‡āϝāĻŧ⧇ āϏāϰāϞ, āĻ•āĻžāϰāĻŖ āĻāϟāĻŋ āĻ…āϧāĻŋāĻ•āĻžāĻ‚āĻļ āϜāϟāĻŋāϞāϤāĻž āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄā§‡āϰ āĻ“āĻĒāϰ āĻ…āĻ°ā§āĻĒāĻŖ āĻ•āϰ⧇:

class StylingProvider with ChangeNotifier {
 StylingState _state = StylingState.initial;
 List<Outfit> _outfits = [];


 Future<bool> getStyleSuggestions(StyleRequest request) async {
   if (_state == StylingState.loading) return false;  // Prevent spam
   _currentRequest = request;
   _setLoading();
   try {
     final suggestions = await _stylingService.getStyleSuggestions(request);
     _setSuccess(suggestions);
     return true;
   } catch (e) {
     _setError('Something went wrong.');
   }
   return false;
 }


 // Multi-turn refinement — uses the same session
 Future<bool> refineWithFeedback(String feedback) async {
   if (_state == StylingState.loading) return false;
   _setLoading();
   try {
     final suggestions = await _stylingService.refineWithFeedback(feedback);
     _setSuccess(suggestions);
     return true;
   } catch (e) {
     _setError('Something went wrong.');
   }
   return false;
 }
}

refineWithFeedback āĻŽā§‡āĻĨāĻĄāϟāĻŋ āĻāĻ•āχ āϏ⧇āĻļāύ⧇ āĻāĻ•āϟāĻŋ āĻĒā§āϞ⧇āχāύ āĻŸā§‡āĻ•ā§āϏāϟ āĻŽā§‡āϏ⧇āϜ āĻĒāĻžāĻ āĻžāϝāĻŧ — āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄā§‡āϰ ` InjectPreviousProducts āĻāĻŦāĻ‚ ExtractAndInjectUserImage āĻ•āϞāĻŦā§āϝāĻžāĻ•āϗ⧁āϞ⧋ āĻ¸ā§āĻŦāϝāĻŧāĻ‚āĻ•ā§āϰāĻŋāϝāĻŧāĻ­āĻžāĻŦ⧇ āϏāĻŽāĻ¸ā§āϤ āĻ•āύāĻŸā§‡āĻ•ā§āϏāϟ āĻŽā§āϝāĻžāύ⧇āϜāĻŽā§‡āĻ¨ā§āϟ āϏāĻžāĻŽāϞ⧇ āύ⧇āϝāĻŧāĨ¤

ā§Ž. 🚀 āĻ…ā§āϝāĻžāĻĒāϟāĻŋ āĻ¸ā§āĻĨāĻžāύ⧀āϝāĻŧāĻ­āĻžāĻŦ⧇ āϚāĻžāϞāĻžāύ

āĻ•ā§āϞāĻžāωāĻĄ āĻļ⧇āϞ-āĻāϰ āĻŽāϏ⧃āĻŖ āĻ…āĻ­āĻŋāĻœā§āĻžāϤāĻžāϰ āϜāĻ¨ā§āϝ, āĻ—ā§‹ āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄ āĻ•āĻŽā§āĻĒāĻžāχāϞ āĻ•āϰāĻž āĻĢā§āϞāĻžāϟāĻžāϰ āĻ“āϝāĻŧ⧇āĻŦ āĻ…ā§āϝāĻžāĻĒāϟāĻŋ āĻāĻ•āχ āĻĒā§‹āĻ°ā§āϟ (ā§Žā§Ļā§Žā§Ļ) āĻĨ⧇āϕ⧇ āĻĒāϰāĻŋāĻŦ⧇āĻļāύ āĻ•āϰ⧇āĨ¤ āĻāĻ•āϟāĻŋ āĻĒā§āϰāϏ⧇āϏ, āĻāĻ•āϟāĻŋ āĻĒā§āϰāĻŋāĻ­āĻŋāω āχāωāφāϰāĻāϞ, āĻ•ā§āϰāϏ-āĻ…āϰāĻŋāϜāĻŋāύ āϏāĻ‚āĻ•ā§āϰāĻžāĻ¨ā§āϤ āϕ⧋āύ⧋ āĻāĻžāĻŽā§‡āϞāĻž āύ⧇āχ, āĻ•āύāĻĢāĻŋāĻ—āĻžāϰ⧇āĻļāύ āĻĢāĻžāχāϞ āϏāĻŽā§āĻĒāĻžāĻĻāύāĻžāϰāĻ“ āĻĒā§āϰāϝāĻŧā§‹āϜāύ āύ⧇āχāĨ¤

āĻļ⧁āϰ⧁ āĻ•āϰāĻžāϰ āφāϗ⧇ — ADC-āϟāĻŋ āϝ⧌āĻ•ā§āϤāĻŋāĻ•āĻ­āĻžāĻŦ⧇ āϝāĻžāϚāĻžāχ āĻ•āϰ⧇ āύāĻŋāύāĨ¤

Vertex AI-āϕ⧇ āĻ•āϞ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄā§‡āϰ āĻ…ā§āϝāĻžāĻĒā§āϞāĻŋāϕ⧇āĻļāύ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻ•ā§āϰ⧇āĻĄā§‡āύāĻļāĻŋāϝāĻŧāĻžāϞ āĻĒā§āϰāϝāĻŧā§‹āϜāύāĨ¤ āφāĻĒāύāĻŋ āϝāĻĻāĻŋ āĻāχ āĻ•ā§āϞāĻžāωāĻĄ āĻļ⧇āϞ āϏ⧇āĻļāύ āĻāĻŦāĻ‚ āĻāχ āϗ⧁āĻ—āϞ āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āĻŸā§‡ āĻĒā§āϰāĻœā§‡āĻ•ā§āϟ āϏ⧇āϟāφāĻĒ⧇āϰ āϧāĻžāĻĒ ā§­ āϏāĻŽā§āĻĒāĻ¨ā§āύ āĻ•āϰ⧇ āĻĨāĻžāϕ⧇āύ, āϤāĻžāĻšāϞ⧇ āφāϰ āĻ•āĻŋāϛ⧁ āĻ•āϰāĻžāϰ āĻĒā§āϰāϝāĻŧā§‹āϜāύ āύ⧇āχāĨ¤ āφāĻĒāύāĻŋ āϝāĻĻāĻŋ āĻŦāĻŋāϰāϤāĻŋāϰ āĻĒāϰ āĻĢāĻŋāϰ⧇ āφāϏ⧇āύ, āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āϟ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰ⧇ āĻĨāĻžāϕ⧇āύ, āĻŦāĻž āύāĻŋāĻļā§āϚāĻŋāϤ āύāĻž āĻšāύ, āϤāĻžāĻšāϞ⧇ āϝāĻžāϚāĻžāχ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ ā§Ģ āϏ⧇āϕ⧇āĻ¨ā§āĻĄ āϏāĻŽāϝāĻŧ āύāĻŋāύ:

gcloud auth application-default print-access-token | head -c 20 && echo "..."

āϝāĻĻāĻŋ āĻāϟāĻŋ āĻāĻ•āϟāĻŋ āĻŸā§‹āϕ⧇āύ⧇āϰ āĻĒā§āϰāĻžāϝāĻŧ ⧍ā§ĻāϟāĻŋ āĻ…āĻ•ā§āώāϰ āĻĒā§āϰāĻŋāĻ¨ā§āϟ āĻ•āϰ⧇, āϤāĻžāĻšāϞ⧇ āφāĻĒāύāĻžāϰ āĻ•āĻžāϜ āĻšāϝāĻŧ⧇ āϗ⧇āϛ⧇āĨ¤ āϝāĻĻāĻŋ āϕ⧋āύ⧋ āĻ¤ā§āϰ⧁āϟāĻŋ āĻšāϝāĻŧ, āϤāĻžāĻšāϞ⧇ āĻĒā§āϰāĻœā§‡āĻ•ā§āϟ āϏ⧇āϟāφāĻĒ⧇āϰ āϧāĻžāĻĒ ā§­ āφāĻŦāĻžāϰ āϚāĻžāϞāĻžāύ :

gcloud auth application-default login
gcloud auth application-default set-quota-project $(gcloud config get-value project)

āφāĻĒāύāĻŋ āĻĻ⧁āϟāĻŋ āĻ•ā§āϞāĻžāωāĻĄ āĻļ⧇āϞ āϟāĻžāĻ°ā§āĻŽāĻŋāύāĻžāϞ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻŦ⧇āύ:

  • āϟāĻžāĻ°ā§āĻŽāĻŋāύāĻžāϞ A — āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄ āĻ…āĻŦāĻŋāϰāĻžāĻŽ āϚāĻžāϞ⧁ āϰāĻžāϖ⧇ ( ./run.sh )āĨ¤ āĻāϟāĻŋ āĻ–ā§‹āϞāĻž āϰāĻžāϖ⧁āύāĨ¤
  • āϟāĻžāĻ°ā§āĻŽāĻŋāύāĻžāϞ āĻŦāĻŋ — āĻĢā§āϞāĻžāϟāĻžāϰ āĻ“āϝāĻŧ⧇āĻŦ āĻŦāĻŋāĻ˛ā§āĻĄ āĻāĻ•āĻŦāĻžāϰ āϚāĻžāϞāĻžāϝāĻŧ ( flutter build web )āĨ¤ āĻ•āĻžāϜ āĻļ⧇āώ āĻšāϞ⧇ āĻāϟāĻŋ āĻŦāĻ¨ā§āϧ āĻšāϝāĻŧ⧇ āϝāĻžāϝāĻŧāĨ¤

āĻ•ā§āϰāĻŽāϟāĻŋ āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āύāϝāĻŧ — āφāĻĒāύāĻŋ āϝ⧇āϕ⧋āύ⧋ āĻāĻ•āϟāĻŋ āĻĒā§āϰāĻĨāĻŽā§‡ āĻļ⧁āϰ⧁ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤ āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻĒā§āϰāĻĨāĻŽāĻŦāĻžāϰ āϚāĻžāϞāĻžāύ⧋āϰ āϏāĻŦāĻšā§‡āϝāĻŧ⧇ āĻŽāϏ⧃āĻŖ āĻ…āĻ­āĻŋāĻœā§āĻžāϤāĻžāϰ āϜāĻ¨ā§āϝ, āĻĒā§āϰāĻĨāĻŽā§‡ āĻĢā§āϞāĻžāϟāĻžāϰ āĻŦāĻŋāĻ˛ā§āĻĄ āĻ•āϰ⧁āύ, āϝāĻžāϤ⧇ āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄ āϚāĻžāϞ⧁ āĻšāĻ“āϝāĻŧāĻžāϰ āĻŽā§āĻšā§‚āĻ°ā§āϤ āĻĨ⧇āϕ⧇āχ āĻĒāϰāĻŋāĻŦ⧇āĻļāύ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āĻāĻ•āϟāĻŋ UI āĻĒā§āϰāĻ¸ā§āϤ⧁āϤ āĻĨāĻžāϕ⧇āĨ¤

ā§§. āϟāĻžāĻ°ā§āĻŽāĻŋāύāĻžāϞ āĻŦāĻŋ — āĻĢā§āϞāĻžāϟāĻžāϰ āĻ“āϝāĻŧ⧇āĻŦ āĻŦāĻžāĻ¨ā§āĻĄā§‡āϞ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ (āĻāĻ•āĻ•āĻžāϞ⧀āύ)

āĻāĻ•āϟāĻŋ āύāϤ⧁āύ āĻ•ā§āϞāĻžāωāĻĄ āĻļ⧇āϞ āĻŸā§āϝāĻžāĻŦ āϖ⧁āϞ⧁āύ (āϟāĻžāĻ°ā§āĻŽāĻŋāύāĻžāϞ āĻĒā§āϝāĻžāύ⧇āϞ⧇āϰ āωāĻĒāϰ⧇āϰ + āϚāĻŋāĻšā§āύ⧇ āĻ•ā§āϞāĻŋāĻ• āĻ•āϰ⧁āύ), āϤāĻžāϰāĻĒāϰ:

cd ~/fashion_app_demo/flutter_frontend
flutter pub get
flutter build web

āĻāϟāĻŋ flutter_frontend/build/web/ āύāĻžāĻŽā§‡ āĻāĻ•āϟāĻŋ āĻĄāĻŋāϰ⧇āĻ•ā§āϟāϰāĻŋ āϤ⧈āϰāĻŋ āĻ•āϰ⧇, āϝ⧇āĻ–āĻžāύ⧇ āĻ¸ā§āĻŸā§āϝāĻžāϟāĻŋāĻ• āĻĢāĻžāχāϞ (HTML, JS, assets) āĻĨāĻžāϕ⧇ āĻāĻŦāĻ‚ āĻ•āĻžāϜ āĻļ⧇āώ āĻšāϞ⧇ āĻāϟāĻŋ āĻŦāĻ¨ā§āϧ āĻšāϝāĻŧ⧇ āϝāĻžāϝāĻŧāĨ¤ āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄ āĻĄāĻŋāϰ⧇āĻ•ā§āϟāϰāĻŋāϟāĻŋ āĻĻ⧇āĻ–āĻžāĻŽāĻžāĻ¤ā§āϰāχ āĻĢāĻžāχāϞāϗ⧁āϞ⧋ āĻĒāϰāĻŋāĻŦ⧇āĻļāύ āĻ•āϰāĻŦ⧇āĨ¤

⧍. āϟāĻžāĻ°ā§āĻŽāĻŋāύāĻžāϞ āĻ — āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄ āϚāĻžāϞ⧁ āĻ•āϰ⧁āύ (āĻĻā§€āĻ°ā§āϘāĻ¸ā§āĻĨāĻžāϝāĻŧā§€ āĻĒā§āϰāĻ•ā§āϰāĻŋāϝāĻŧāĻž)

āφāĻĒāύāĻžāϰ āĻŽā§‚āϞ āĻ•ā§āϞāĻžāωāĻĄ āĻļ⧇āϞ āϟāĻžāĻ°ā§āĻŽāĻŋāύāĻžāϞ⧇:

cd ~/fashion_app_demo/adk_backend
./run.sh

āφāĻĒāύāĻŋ āĻāχāϰāĻ•āĻŽ āĻ•āĻŋāϛ⧁ āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāĻŦ⧇āύ:

Serving Flutter web build from ../flutter_frontend/build/web

āĻāχ āϟāĻžāĻ°ā§āĻŽāĻŋāύāĻžāϞāϟāĻŋ āϚāĻžāϞ⧁ āϰāĻžāϖ⧁āύ — āϝāϤāĻ•ā§āώāĻŖ run.sh āĻĨāĻžāĻ•āĻŦ⧇, āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄāĻ“ āϤāϤāĻ•ā§āώāĻŖ āϏāϚāϞ āĻĨāĻžāĻ•āĻŦ⧇āĨ¤ āĻāϟāĻŋ āĻŦāĻ¨ā§āϧ āĻ•āϰāϤ⧇ Ctrl+C āϚāĻžāĻĒ⧁āύāĨ¤

āϏāĻžāĻ°ā§āĻ­āĻžāϰāϟāĻŋ ā§Žā§Ļā§Žā§Ļ āĻĒā§‹āĻ°ā§āĻŸā§‡ āϏāĻŦāĻ•āĻŋāϛ⧁ āωāĻ¨ā§āĻŽā§āĻ•ā§āϤ āĻ•āϰ⧇ āĻĻ⧇āϝāĻŧ:

  • / āĻ“āϝāĻŧ⧇āĻŦ āĻ…ā§āϝāĻžāĻĒ (āĻļāĻĒāĻŋāĻ‚ UI)
  • /api/ — ADK REST āĻāĻ¨ā§āĻĄāĻĒāϝāĻŧ⧇āĻ¨ā§āϟ (āĻĢā§āϞāĻžāϟāĻžāϰ āĻ…ā§āϝāĻžāĻĒ āĻĻā§āĻŦāĻžāϰāĻž āĻ•āϞ āĻ•āϰāĻž āĻšāϝāĻŧ)
  • ADK Dev UI — āĻĢā§āϞāĻžāϟāĻžāϰ āĻŦāĻŋāĻ˛ā§āĻĄ āύāĻž āĻĨāĻžāĻ•āϞ⧇ āĻāϟāĻŋ / āĻāĻ“ āĻĒāĻžāĻ“āϝāĻŧāĻž āϝāĻžāϝāĻŧ; āϏāϰāĻžāϏāϰāĻŋ āĻāĻœā§‡āĻ¨ā§āϟ āĻĄāĻŋāĻŦāĻžāĻ—āĻŋāĻ‚āϝāĻŧ⧇āϰ āϜāĻ¨ā§āϝ āωāĻĒāϝ⧋āĻ—ā§€āĨ¤

ā§Š. āĻ“āϝāĻŧ⧇āĻŦ āĻĒā§āϰāĻŋāĻ­āĻŋāω āϖ⧁āϞ⧁āύ

  1. āĻ•ā§āϞāĻžāωāĻĄ āĻļ⧇āϞ⧇, āĻ“āϝāĻŧ⧇āĻŦ āĻĒā§āϰāĻŋāĻ­āĻŋāω āφāχāĻ•āύ⧇ (āωāĻĒāϰ⧇āϰ-āĻĄāĻžāύāĻĻāĻŋāϕ⧇) āĻ•ā§āϞāĻŋāĻ• āĻ•āϰ⧁āύ → āĻĒā§‹āĻ°ā§āϟ ā§Žā§Ļā§Žā§Ļ-āϤ⧇ āĻĒā§āϰāĻŋāĻ­āĻŋāω āĻĻ⧇āϖ⧁āύāĨ¤
  2. āĻĢā§āϞāĻžāϟāĻžāϰ āĻļāĻĒāĻŋāĻ‚ āĻ…ā§āϝāĻžāĻĒāϟāĻŋ āĻāĻ•āϟāĻŋ āύāϤ⧁āύ āĻŸā§āϝāĻžāĻŦ⧇ āϞ⧋āĻĄ āĻšāϝāĻŧāĨ¤
  3. āĻĒāĻŖā§āϝ⧇āϰ āĻ•ā§āϝāĻžāϟāĻžāϞāĻ— āĻŦā§āϰāĻžāωāϜ āĻ•āϰ⧁āύ āĻāĻŦāĻ‚ āĻāĻ•āϟāĻŋ āφāχāĻŸā§‡āĻŽ āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻ•āϰ⧁āύāĨ¤
  4. āĻŸā§āϰāĻžāχ-āĻ…āύ āĻĒā§āϰāĻ•ā§āϰāĻŋāϝāĻŧāĻž āĻļ⧁āϰ⧁ āĻ•āϰāϤ⧇ āĻŦā§āϝāĻ•ā§āϤāĻŋāϰ āφāχāĻ•āύ⧇ (👤) āĻŸā§āϝāĻžāĻĒ āĻ•āϰ⧁āύāĨ¤
  5. āĻāĻ•āϟāĻŋ āĻ›āĻŦāĻŋ āφāĻĒāϞ⧋āĻĄ āĻ•āϰ⧁āύ āĻāĻŦāĻ‚ āĻĻ⧇āϖ⧁āύ āϕ⧀āĻ­āĻžāĻŦ⧇ āĻāφāχ āĻāĻ•āϟāĻŋ āĻŸā§āϰāĻžāχ-āĻ…āύ āχāĻŽā§‡āϜ āϤ⧈āϰāĻŋ āĻ•āϰ⧇āĨ¤
  6. āĻĒā§‹āĻļāĻžāϕ⧇āϰ āĻĒāϰāĻžāĻŽāĻ°ā§āĻļ āĻĒ⧇āϤ⧇ 'āĻ¸ā§āϟāĻžāχāϞ āĻŽāĻŋ'-āϤ⧇ āĻŸā§āϝāĻžāĻĒ āĻ•āϰ⧁āύāĨ¤
  7. ‘āĻāϟāĻŋāϕ⧇ āφāϰāĻ“ āĻ…āύāĻžāύ⧁āĻˇā§āĻ āĻžāύāĻŋāĻ• āĻ•āϰ⧁āĻ¨â€™-āĻāϰ āĻŽāϤ⧋ āĻĢāϞ⧋-āφāĻĒ āĻĢāĻŋāĻĄāĻŦā§āϝāĻžāĻ• āϟāĻžāχāĻĒ āĻ•āϰ⧁āύ — āĻāĻ•āχ āϏ⧇āĻļāύ⧇ āĻĒāϰāĻŋāĻŽāĻžāĻ°ā§āϜāύ

⧝. â˜ī¸ āĻ•ā§āϞāĻžāωāĻĄ āϰāĻžāύ⧇ āĻĄā§‡āĻĒā§āϞāϝāĻŧ āĻ•āϰ⧁āύ

āĻĢā§āϞāĻžāϟāĻžāϰ āĻŦāĻŋāĻ˛ā§āĻĄāϟāĻŋāϕ⧇ āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄā§‡ āĻŦāĻžāĻ¨ā§āĻĄāϞ āĻ•āϰ⧁āύ

āĻ•ā§āϞāĻžāωāĻĄ āϰāĻžāύ āĻ•āĻ¨ā§āĻŸā§‡āχāύāĻžāϰāϟāĻŋ āĻāĻĒāĻŋāφāχ āĻāĻŦāĻ‚ āχāωāφāχ āωāĻ­āϝāĻŧāχ āĻāĻ•āϟāĻŋ āχāĻŽā§‡āϜ āĻĨ⧇āϕ⧇ āϏāϰāĻŦāϰāĻžāĻš āĻ•āϰ⧇āĨ¤ āĻĢā§āϞāĻžāϟāĻžāϰ āĻ“āϝāĻŧ⧇āĻŦ āĻŦāĻŋāĻ˛ā§āĻĄāϟāĻŋ adk_backend/flutter_web/ -āĻ āĻ•āĻĒāĻŋ āĻ•āϰ⧁āύ — āϕ⧋āύ āχāωāφāχ āĻĒāϰāĻŋāĻŦ⧇āĻļāύ āĻ•āϰāĻž āĻšāĻŦ⧇ āϤāĻž āĻŦ⧇āϛ⧇ āύ⧇āĻ“āϝāĻŧāĻžāϰ āϏāĻŽāϝāĻŧ āĻ—ā§‹ āϏāĻžāĻ°ā§āĻ­āĻžāϰ āĻĒā§āϰāĻĨāĻŽā§‡ āĻāχ āĻĒāĻžāĻĨāϟāĻŋāχ āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰ⧇:

cd ~/fashion_app_demo/flutter_frontend
flutter build web
rm -rf ../adk_backend/flutter_web
cp -r build/web ../adk_backend/flutter_web

(āφāĻĒāύāĻŋ āϝāĻĻāĻŋ āĻ¸ā§āĻĨāĻžāύ⧀āϝāĻŧāĻ­āĻžāĻŦ⧇ āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤāĻŋ āĻ•āϰ⧇ āĻĨāĻžāϕ⧇āύ, āϤāĻžāĻšāϞ⧇ Run-Locally āϧāĻžāĻĒ āĻĨ⧇āϕ⧇ āφāĻĒāύāĻžāϰ āĻ•āĻžāϛ⧇ āχāϤāĻŋāĻŽāĻ§ā§āϝ⧇āχ build/web āĻĨāĻžāĻ•āϤ⧇ āĻĒāĻžāϰ⧇āĨ¤ flutter build web āĻĒ⧁āύāϰāĻžāϝāĻŧ āϚāĻžāϞāĻžāϞ⧇āĻ“ āϕ⧋āύ⧋ āϏāĻŽāĻ¸ā§āϝāĻž āύ⧇āχāĨ¤)

āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄ āĻ¸ā§āĻĨāĻžāĻĒāύ āĻ•āϰ⧁āύ (āĻāĻĒāĻŋāφāχ + āχāωāφāχ āĻĒāϰāĻŋāĻŦ⧇āĻļāύ āĻ•āϰ⧇)

cd ~/fashion_app_demo/adk_backend

gcloud run deploy fashion-app-backend \
 --source . \
 --region us-central1 \
 --allow-unauthenticated \
 --set-env-vars "GOOGLE_CLOUD_PROJECT=$PROJECT_ID,GCS_BUCKET=fashion-app-$PROJECT_ID" \
 --memory 1Gi \
 --cpu 2 \
 --timeout 300s \
 --min-instances 0 \
 --max-instances 3

āĻĄāĻŋāĻĒā§āϞāϝāĻŧāĻŽā§‡āĻ¨ā§āϟ āĻļ⧇āώ āĻšāϞ⧇, āφāĻĒāύāĻŋ https://fashion-app-backend-xyz-uc.a.run.app āĻāϰ āĻŽāϤ⧋ āĻāĻ•āϟāĻŋ āϏāĻžāĻ°ā§āĻ­āĻŋāϏ āχāωāφāϰāĻāϞ āĻĒāĻžāĻŦ⧇āύāĨ¤ āĻāϟāĻŋ āĻāĻ•āϟāĻŋ āĻŦā§āϰāĻžāωāϜāĻžāϰ⧇ āϖ⧁āϞ⧁āύ — āĻĢā§āϞāĻžāϟāĻžāϰ āĻļāĻĒāĻŋāĻ‚ āĻ…ā§āϝāĻžāĻĒāϟāĻŋ / āĻĨ⧇āϕ⧇ āϞ⧋āĻĄ āĻšāϝāĻŧ, āĻāĻŦāĻ‚ āĻāϰ āĻāĻĒāĻŋāφāχ āĻ•āϞāϗ⧁āϞ⧋ āĻāĻ•āχ āĻšā§‹āĻ¸ā§āĻŸā§‡āϰ /api/ āϤ⧇ āϝāĻžāϝāĻŧāĨ¤ āϕ⧋āύ⧋ āĻĢā§āϰāĻ¨ā§āϟāĻāĻ¨ā§āĻĄ āĻ•āύāĻĢāĻŋāĻ—āĻžāϰ⧇āĻļāύ āϏāĻŽā§āĻĒāĻžāĻĻāύāĻžāϰ āĻĒā§āϰāϝāĻŧā§‹āϜāύ āύ⧇āχ, āϕ⧋āύ⧋ āĻāĻĒāĻŋāφāχ āϕ⧀-āĻ“ āĻĒāĻžāϏ āĻ•āϰāϤ⧇ āĻšāĻŦ⧇ āύāĻžāĨ¤

āĻĄāĻŋāĻĒā§āϞāϝāĻŧāĻŽā§‡āĻ¨ā§āϟ āϝāĻžāϚāĻžāχ āĻ•āϰ⧁āύ

āφāĻĒāύāĻžāϰ āĻŦā§āϰāĻžāωāϜāĻžāϰ⧇ āĻ•ā§āϞāĻžāωāĻĄ āϰāĻžāύ āχāωāφāϰāĻāϞāϟāĻŋ āϖ⧁āϞ⧁āύ āĻāĻŦāĻ‚ āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻĒā§āϰāĻ•ā§āϰāĻŋāϝāĻŧāĻžāϟāĻŋ āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰ⧁āύ:

  1. āĻŦā§āϰāĻžāωāϜ āĻ•āϰ⧁āύ → āĻāĻ•āϟāĻŋ āĻĒāĻŖā§āϝ āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻ•āϰ⧁āύ
  2. āĻŸā§āϰāĻžāχ āĻ…āύ → āφāĻĒāύāĻžāϰ āĻ›āĻŦāĻŋ āφāĻĒāϞ⧋āĻĄ āĻ•āϰ⧁āύ → āĻāφāχ-āϤ⧈āϰāĻŋ āĻ›āĻŦāĻŋāϟāĻŋ āĻĻ⧇āϖ⧁āύ
  3. āĻ¸ā§āϟāĻžāχāϞ āĻŽāĻŋ → āĻ¸ā§āĻĨāĻžāύ/āĻ…āύ⧁āĻˇā§āĻ āĻžāύ āĻĒā§‚āϰāĻŖ āĻ•āϰ⧁āύ → āύāĻŋāĻ°ā§āĻŦāĻžāϚāĻŋāϤ āĻĒā§‹āĻļāĻžāĻ•āϗ⧁āϞ⧋ āĻĻ⧇āϖ⧁āύ
  4. āĻŽāϤāĻžāĻŽāϤ āĻĻāĻŋāύ → 'āφāϰāĻ“ āĻ•ā§āϝāĻžāϜ⧁āϝāĻŧāĻžāϞ āĻ•āϰ⧁āύ' āϟāĻžāχāĻĒ āĻ•āϰ⧁āύ → āφāĻĒāĻĄā§‡āϟ āĻ•āϰāĻž āĻĒā§‹āĻļāĻžāĻ•āϗ⧁āϞ⧋ āĻĻ⧇āϖ⧁āύ
  5. āĻŦā§āϝāĻžāϗ⧇ āϝ⧋āĻ— āĻ•āϰ⧁āύ → āϕ⧇āύāĻžāĻ•āĻžāϟāĻž āϏāĻŽā§āĻĒāĻ¨ā§āύ āĻ•āϰ⧁āύ

ā§§ā§Ļ. 🎉 āωāĻĒāϏāĻ‚āĻšāĻžāϰ

āφāĻĒāύāĻŋ āϝāĻž āϤ⧈āϰāĻŋ āĻ•āϰ⧇āϛ⧇āύ

āφāĻĒāύāĻŋ āĻāĻ•āϟāĻŋ āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻāφāχ-āϚāĻžāϞāĻŋāϤ āϖ⧁āϚāϰāĻž āϕ⧇āύāĻžāĻ•āĻžāϟāĻžāϰ āĻ…āĻ­āĻŋāĻœā§āĻžāϤāĻž āĻ…āĻ¨ā§āĻŦ⧇āώāĻŖ āĻ•āϰ⧇āϛ⧇āύ:

  • ✅ āĻāĻ•āϟāĻŋ āĻŽāĻžāĻ˛ā§āϟāĻŋ-āĻāĻœā§‡āĻ¨ā§āϟ āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄ āϝ⧇āĻ–āĻžāύ⧇ ā§Ē āϜāύ āĻŦāĻŋāĻļ⧇āώāĻžāϝāĻŧāĻŋāϤ āĻāĻœā§‡āĻ¨ā§āϟ āĻāĻ•āϏāĻžāĻĨ⧇ āĻ•āĻžāϜ āĻ•āϰ⧇
  • ✅ āĻāĻ•āϟāĻŋ āĻ­āĻžāĻ°ā§āϚ⧁āϝāĻŧāĻžāϞ āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ āϝāĻž āφāĻĒāύāĻžāϰ āĻĒāĻ›āĻ¨ā§āĻĻ āĻ…āύ⧁āϝāĻžāϝāĻŧā§€ āĻŸā§āϰāĻžāχ-āĻ…āύ āĻ›āĻŦāĻŋ āϤ⧈āϰāĻŋ āĻ•āϰ⧇ āĻĻ⧇āϝāĻŧ
  • ✅ āĻāĻ•āϜāύ āĻāφāχ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ , āϝ⧇ āĻ•āĻĨā§‹āĻĒāĻ•āĻĨāύ⧇āϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻĒā§‹āĻļāĻžāϕ⧇āϰ āϏāĻŽā§āĻ­āĻžāϰ āĻŦāĻžāĻ›āĻžāχ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āϏ⧇āϗ⧁āϞ⧋āϕ⧇ āφāϰāĻ“ āĻĒāϰāĻŋāĻŽāĻžāĻ°ā§āϜāĻŋāϤ āĻ•āϰ⧇āĨ¤
  • ✅ āĻāĻ•āϟāĻŋ āĻ•ā§āϰāϏ-āĻĒā§āĻ˛ā§āϝāĻžāϟāĻĢāĻ°ā§āĻŽ āĻĢā§āϞāĻžāϟāĻžāϰ āĻ…ā§āϝāĻžāĻĒ āϝāĻž āĻāĻœā§‡āĻ¨ā§āϟ āĻŦā§āϝāĻžāĻ•āĻāĻ¨ā§āĻĄā§‡āϰ āϏāĻžāĻĨ⧇ āϏāĻ‚āϝ⧁āĻ•ā§āϤ āĻšāϝāĻŧ
  • ✅ āĻĒāϰāĻŋāĻŦāĻ°ā§āϧāύāϝ⧋āĻ—ā§āϝ āĻ“ āϏāĻžāĻ°ā§āĻ­āĻžāϰāĻŦāĻŋāĻšā§€āύ āĻšā§‹āĻ¸ā§āϟāĻŋāĻ‚āϝāĻŧ⧇āϰ āϜāĻ¨ā§āϝ āĻ•ā§āϞāĻžāωāĻĄ āϰāĻžāύ āĻĄā§‡āĻĒā§āϞāϝāĻŧāĻŽā§‡āĻ¨ā§āϟ

āĻŽā§‚āϞ āϧāĻžāϰāĻŖāĻž

āϧāĻžāϰāĻŖāĻž

āϝ⧇āĻ–āĻžāύ⧇ āφāĻĒāύāĻŋ āĻāϟāĻŋ āĻĻ⧇āϖ⧇āϛ⧇āύ

ADK āĻŽāĻžāĻ˛ā§āϟāĻŋ-āĻāĻœā§‡āĻ¨ā§āϟ āĻ…āĻ°ā§āϕ⧇āĻ¸ā§āĻŸā§āϰ⧇āĻļāύ

āĻĢāĻŋāϟāĻŋāĻ‚ āϰ⧁āĻŽ, āĻ•ā§āϝāĻžāϟāĻžāϞāĻ— āĻāĻŦāĻ‚ āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻāĻœā§‡āĻ¨ā§āϟāĻĻ⧇āϰ āĻ•āĻžāϛ⧇ āϰ⧁āϟ āĻāĻœā§‡āĻ¨ā§āϟ āϰāĻžāωāϟāĻŋāĻ‚

āĻœā§‡āĻŽāĻŋāύāĻŋ āĻŽāĻžāĻ˛ā§āϟāĻŋāĻŽā§‹āĻĄāĻžāϞ āϚāĻŋāĻ¤ā§āϰ āϤ⧈āϰāĻŋ

āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āĻ›āĻŦāĻŋāϰ āϏāĻžāĻĨ⧇ āĻĒāĻŖā§āϝ⧇āϰ āĻ›āĻŦāĻŋ āĻāĻ•āĻ¤ā§āϰāĻŋāϤ āĻ•āϰāĻžāϰ fitting_tool

āĻ•āĻĨā§‹āĻĒāĻ•āĻĨāύāĻŽā§‚āϞāĻ• āĻāφāχ-āĻāϰ āϜāĻ¨ā§āϝ āϏ⧇āĻļāύ āĻ…āĻŦāĻ¸ā§āĻĨāĻž

āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āϟ āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤāĻŋāĻŽā§‚āϞāĻ• āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻžāϰ āϜāĻ¨ā§āϝ āϏ⧇āĻļāύāϗ⧁āϞ⧋ āĻĒ⧁āύāϰāĻžāϝāĻŧ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϛ⧇āύ

āĻŦāĻžāχāύāĻžāϰāĻŋ āĻĄā§‡āϟāĻžāϰ āϜāĻ¨ā§āϝ āφāĻ°ā§āϟāĻŋāĻĢā§āϝāĻžāĻ•ā§āϟ āĻ¸ā§āĻŸā§‹āϰ⧇āϜ

āĻŸā§‡āĻ•ā§āϏāϟ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻž āĻĨ⧇āϕ⧇ āĻ›āĻŦāĻŋāϰ āĻ¸ā§āĻŸā§‹āϰ⧇āϜ āφāϞāĻžāĻĻāĻž āĻ•āϰāĻž

āĻŽāĻŋāĻĄāϞāĻ“āϝāĻŧā§āϝāĻžāϰ āϞāϜāĻŋāϕ⧇āϰ āϜāĻ¨ā§āϝ āĻ•āϞāĻŦā§āϝāĻžāĻ•

SaveIncomingBlobs , InjectPreviousProducts , SaveSelectedProducts

āĻĢā§āϞāĻžāϟāĻžāϰ⧇ MVVM + āĻĒā§āϰ⧋āĻ­āĻžāχāĻĄāĻžāϰ

ChangeNotifier āϏāĻžāĻĨ⧇ TryItOnProvider āĻāĻŦāĻ‚ StylingProvider

āĻāĻœā§‡āĻ¨ā§āϟāĻŋāĻ• āĻšāĻ¸ā§āϤāĻžāĻ¨ā§āϤāϰ

StyleRequest āĻāĻœā§‡āĻ¨ā§āϟāĻĻ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āĻŽāĻžāĻ˛ā§āϟāĻŋāĻŽā§‹āĻĄāĻžāϞ āĻ•āύāĻŸā§‡āĻ•ā§āϏāϟ āĻĒā§āϰ⧇āϰāĻŖ āĻ•āϰāϛ⧇

āĻĒāϰāĻŦāĻ°ā§āϤ⧀ āĻĒāĻĻāĻ•ā§āώ⧇āĻĒ

  • 🎨 āĻāĻœā§‡āĻ¨ā§āĻŸā§‡āϰ āύāĻŋāĻ°ā§āĻĻ⧇āĻļāĻžāĻŦāϞ⧀ āύāĻŋāĻœā§‡āϰ āĻŽāϤ⧋ āĻ•āϰ⧇ āϏāĻžāϜāĻžāύ — āĻ¸ā§āϟāĻžāχāϞāĻŋāĻ¸ā§āĻŸā§‡āϰ āĻŦā§āϝāĻ•ā§āϤāĻŋāĻ¤ā§āĻŦ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāϤ⧇ instructions.md āϏāĻŽā§āĻĒāĻžāĻĻāύāĻž āĻ•āϰ⧁āύāĨ¤
  • đŸ›ī¸ āφāϰāĻ“ āĻĒāĻŖā§āϝ āϝ⧋āĻ— āĻ•āϰ⧁āύ — āύāϤ⧁āύ āφāχāĻŸā§‡āĻŽāϗ⧁āϞ⧋ āĻĻāĻŋāϝāĻŧ⧇ catalog.yaml āφāĻĒāĻĄā§‡āϟ āĻ•āϰ⧁āύāĨ¤
  • 📱 āĻŽā§‹āĻŦāĻžāχāϞ⧇āϰ āϜāĻ¨ā§āϝ āĻŦāĻŋāĻ˛ā§āĻĄ āĻ•āϰ⧁āύ — flutter build ios āĻ…āĻĨāĻŦāĻž flutter build apk āϰāĻžāύ āĻ•āϰ⧁āύ
  • 🔄 āĻ¸ā§āĻĨāĻžāϝāĻŧā§€ āϏ⧇āĻļāύ āϝ⧋āĻ— āĻ•āϰ⧁āύ — InMemoryService āĻāĻ•āϟāĻŋ āĻĄāĻžāϟāĻžāĻŦ⧇āϏ-āϏāĻŽāĻ°ā§āĻĨāĻŋāϤ āχāĻŽāĻĒā§āϞāĻŋāĻŽā§‡āĻ¨ā§āĻŸā§‡āĻļāύ āĻĻāĻŋāϝāĻŧ⧇ āĻĒā§āϰāϤāĻŋāĻ¸ā§āĻĨāĻžāĻĒāύ āĻ•āϰ⧁āύ
  • 🔒 āĻĒā§āϰāĻŽāĻžāĻŖā§€āĻ•āϰāĻŖ āϝ⧋āĻ— āĻ•āϰ⧁āύ — IAM āĻĻāĻŋāϝāĻŧ⧇ āĻ•ā§āϞāĻžāωāĻĄ āϰāĻžāύ āĻāĻ¨ā§āĻĄāĻĒāϝāĻŧ⧇āĻ¨ā§āϟ āϏ⧁āϰāĻ•ā§āώāĻŋāϤ āĻ•āϰ⧁āύ

āϏāĻŽā§āĻĒāĻĻ