AI image generation monorepo with an A2A-compatible agent and a web UI.
User → Illustra UI (Express + Tailwind)
↓ /api/generate
Illustra Agent (LangChain + Gemini)
↓ tool call
Stability AI (image generation)
↓ upload
GCS Bucket → Public URL
| Package | Description | Port |
|---|---|---|
@illustra/agent |
LangChain + Gemini agent with Stability AI image generation via A2A protocol | 8080 |
@illustra/ui |
Express + Tailwind web interface that proxies requests to the agent | 8080 |
- Bun runtime
- Google Cloud project with GCS bucket
- Stability AI API key
- Google Gemini API key
-
Install dependencies:
bun install
-
Set up environment variables:
Agent (
agent/.env):GOOGLE_API_KEY=your_gemini_api_key STABILITY_KEY=your_stability_api_key GCS_BUCKET_NAME=illustra PORT=8080
UI (
ui/.env):PORT=3000 AGENT_URL=http://localhost:8080
-
Run both services:
bun dev
Or run individually:
bun dev:agent # starts agent on :8080 make run-ui # starts UI on :3000 (local dev override)
bun dev # run both services concurrently
bun dev:agent # run agent only
bun dev:ui # run UI only
bun check # lint + typecheck all packages
bun check:agent # lint + typecheck agent
bun check:ui # lint + typecheck UIGET /health- Health checkGET /.well-known/agent-card.json- A2A Agent Card discoveryPOST /a2a/invoke- A2A JSON-RPC endpoint
curl -X POST http://localhost:8080/a2a/invoke \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [{"type": "text", "text": "A cute cat"}]
}
}
}'Response (A2UI format):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"role": "assistant",
"parts": [
{
"kind": "data",
"data": {
"type": "Image",
"props": {
"url": "https://storage.googleapis.com/illustra/images/1234567890.png",
"alt": "A cute cat"
}
}
}
],
"messageId": "uuid"
}
}| Variable | Required | Description |
|---|---|---|
GOOGLE_API_KEY |
Yes | Google Gemini API key |
STABILITY_KEY |
Yes | Stability AI API key |
GCS_BUCKET_NAME |
Yes | GCS bucket for image storage |
PORT |
No | Server port (default: 8080) |
| Variable | Required | Description |
|---|---|---|
PORT |
No | Server port (default: 8080) |
AGENT_URL |
Yes (deployment) | Agent endpoint (default: http://localhost:8080) |
Each service uses its own env.yaml file for Cloud Run environment variables. Edit these files before deploying with your actual values.
Edit agent/env.yaml with your API keys:
GOOGLE_API_KEY: your_gemini_api_key
STABILITY_KEY: your_stability_api_key
GCS_BUCKET_NAME: illustraThen deploy:
make deployOr manually:
cd agent && gcloud run deploy illustra-agent \
--source . \
--region asia-south1 \
--port 8080 \
--env-vars-file env.yaml \
--allow-unauthenticatedEdit ui/env.yaml with your agent's Cloud Run URL:
AGENT_URL: https://illustra-agent-xxxxx.a.run.appFind your agent URL via:
gcloud run services describe illustra-agent --region asia-south1 --format="value(status.url)"Then deploy:
make deploy-uiOr manually:
cd ui && gcloud run deploy illustra-ui \
--source . \
--region asia-south1 \
--env-vars-file env.yaml \
--allow-unauthenticatedillustra/
├── agent/
│ ├── src/
│ │ ├── main.ts # Entry point
│ │ ├── a2a/
│ │ │ └── server.ts # A2A server with CORS
│ │ ├── agent/
│ │ │ ├── illustra_agent.ts # LangChain agent
│ │ │ └── tools/
│ │ │ └── stability_tool.ts # Stability AI tool
│ │ ├── config/
│ │ │ └── env.ts # Environment variables
│ │ └── utils/
│ │ ├── a2ui.ts # A2UI helpers
│ │ └── storage.ts # GCS upload utility
│ ├── env.yaml # Cloud Run env vars (gitignored)
│ ├── package.json
│ ├── tsconfig.json
│ ├── Dockerfile
│ └── .env.example
├── ui/
│ ├── src/
│ │ ├── index.ts # Express server entry
│ │ └── a2a/
│ │ └── client.ts # A2A proxy routes
│ ├── public/
│ │ └── index.html # Tailwind CSS SPA
│ ├── package.json
│ ├── tsconfig.json
│ ├── Dockerfile
│ ├── env.yaml # Cloud Run env vars (gitignored)
│ └── .env.example
├── scripts/
│ ├── test.sh # Health check + agent card test
│ └── test-a2a.sh # A2A invoke test
├── package.json # Root workspace config
├── biome.json # Linting/formatting
├── commitlint.config.js # Commit message conventions
├── Makefile # Build/deploy targets
└── .gitignore
MIT
Mayank (@mnkrana) — github.com/mnkrana
