Autonomous AI ops centre for trend-driven advertising β built on Google ADK 2.8.0, Gemini 3.7 Flash, and Google Cloud.
TrendJack monitors Google Trends (ZA), automatically runs every green trend through a 10-LLM-agent pipeline (plus deterministic policy and QA nodes), and stages approved campaigns across Google Ads and Meta Ads β with a deterministic policy engine and full OTel audit trail at every step.
Submitted to the All Things Agentic Hackathon β Fortified Enterprise Fleet track.
Google Trends (ZA) βββΆ Trend Radar βββΆ confidence β₯ 70?
β yes
βΌ
βββββββββββββββββββββββββββββββββββββββ
β DISCOVER β
β [context_analyst β authenticity] β
ββββββββββββββββ¬βββββββββββββββββββββββ
β β Policy Gate
ββββββββββββββββΌβββββββββββββββββββββββ
β INTELLIGENCE β
β [brand_guardian β audience β
β β risk_guardian] β
ββββββββββββββββ¬βββββββββββββββββββββββ
β β Policy Gate
ββββββββββββββββΌβββββββββββββββββββββββ
β CREATIVE β
β [creative_director β copywriter β
β β media_planner] β
ββββββββββββββββ¬βββββββββββββββββββββββ
β β Policy Gate
ββββββββββββββββΌβββββββββββββββββββββββ
β VERIFY β
β [compliance_reviewer β red_team β
β β technical_qa] β
ββββββββββββββββ¬βββββββββββββββββββββββ
β β Policy Engine Β· Final Verdict
ββββββββββββΌβββββββββββ
AUTO_STAGE REVIEW/HOLD REJECT
β
Google Ads + Meta Ads (mock gateways)
PostgreSQL (Cloud SQL) Β· Full audit trail
All LLM agents run on Vertex AI β Gemini 3.7 Flash (locations/global). Parallel agents within each stage run concurrently. technical_qa is a deterministic FunctionNode (no model call). The Policy Engine is fully deterministic β no model output can override a hard rule.
| Layer | Technology |
|---|---|
| AI Agents | Google ADK 2.8.0 Β· Gemini 3.7 Flash (Vertex AI Β· locations/global) |
| API | FastAPI Β· Python 3.11 Β· Uvicorn |
| Database | PostgreSQL (SQLModel + asyncpg) |
| Frontend | Next.js Β· React Β· TanStack Query Β· Tailwind CSS |
| Observability | OpenTelemetry SDK Β· Cloud Logging (structured JSON) |
| Cloud infra | Cloud Run (API) Β· Cloud SQL (Postgres) Β· Artifact Registry |
| Trend discovery | pytrends (Google Trends, ZA region) |
trendjack/
βββ apps/
β βββ api/ # FastAPI backend + ADK workflow
β β βββ app/
β β β βββ agents/ # ADK Workflow, LlmAgents, FunctionNodes
β β β βββ policy/ # Deterministic policy engine + rules
β β β βββ gateway/ # Google Ads + Meta Ads mock gateways
β β β βββ renderer/ # Creative asset renderer (Pillow + Imagen)
β β β βββ observability/# OTel tracer, structured logging, redact
β β β βββ routers/ # FastAPI route handlers
β β β βββ services/ # run_service, trend_scanner
β β β βββ models/ # Pydantic request/response models
β β β βββ repositories/ # DB access layer (brand, run, campaign)
β β β βββ db/ # SQLModel engine, table models
β β βββ tests/
β βββ web/ # Next.js dashboard
β βββ app/ # Next.js app-router pages
β βββ components/ # UI components (Runs, Campaigns, Brandsβ¦)
β βββ context/ # Theme, sidebar providers
β βββ providers/ # TanStack QueryProvider
β βββ lib/ # api-client, query hooks, utils
βββ packages/
βββ contracts/ # Shared TypeScript types
| Tool | Version |
|---|---|
| Python | 3.11+ |
| Node.js | 20+ |
| npm | 10+ |
| PostgreSQL | 15+ (local) or Cloud SQL |
git clone <repo-url>
cd trendjack
npm install # installs web + contracts workspacescd apps/api
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"Create apps/api/.env:
# Required
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/trendjack
# Gemini via Vertex AI (recommended)
GOOGLE_GENAI_USE_VERTEXAI=1
GOOGLE_CLOUD_PROJECT=your-project-id
GOOGLE_CLOUD_LOCATION=global # gemini-3.7-flash requires global; use us-central1 for older models
# -- or for AI Studio (local dev) --
GEMINI_API_KEY=your-gemini-api-key
# Agent execution β set to 1 to make real LLM calls; 0 = simulated responses only
TRENDJACK_AGENTS_ENABLED=1
# Optional overrides
TRENDJACK_AUTH_DISABLED=1 # skip API key header locally
TRENDJACK_DEMO_API_KEY=demo-key-2026
TRENDJACK_CORS_ORIGINS=http://localhost:3000,http://localhost:3001 # comma-separated; add deployed web URL in Cloud Run
TRENDJACK_BASE_URL=http://localhost:8000 # used for agent card URLs; set to public API URL in Cloud Run
GEMINI_MODEL=gemini-3.7-flash # model override
TRENDJACK_CI_FAKE=0 # set to 1 in CI to skip real API callsCreate apps/web/.env.local:
NEXT_PUBLIC_API_URL=http://localhost:8000Tables are created automatically on first startup via SQLModel.metadata.create_all β no migration step needed. Just ensure DATABASE_URL is set and the Postgres server is running before starting the API.
cd apps/api
uvicorn app.main:app --reloadAPI available at http://localhost:8000. Interactive docs at http://localhost:8000/docs.
cd apps/web
npm run devDashboard available at http://localhost:3000.
# Trigger a run manually
curl -X POST http://localhost:8000/api/v1/runs \
-H "Content-Type: application/json" \
-H "X-Demo-API-Key: demo-key-2026" \
-d '{
"trend_name": "Load Shedding Solutions",
"trend_confidence": 82,
"budget_total_zar": 50000,
"budget_daily_zar": 5000,
"flight_days": 10,
"platforms": ["google", "meta"],
"creative_headline": "Never Go Dark Again",
"creative_body": "Solar solutions for South African homes."
}'The scheduler runs automatically every 30 minutes β any trend from Google Trends (ZA) with confidence β₯ 70 is queued automatically.
cd apps/api
pytestCI-safe: set GEMINI_MODEL=fake (or TRENDJACK_CI_FAKE=1) to run the full workflow without paid API calls.
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/trends |
Current trend scan results |
POST |
/api/v1/runs |
Create a new agent workflow run |
GET |
/api/v1/runs |
List all runs |
GET |
/api/v1/runs/{id} |
Run detail + agent evidence |
GET |
/api/v1/audit/timeline |
All OTel audit timelines |
POST |
/api/v1/policy/evaluate |
Evaluate a trend against the policy engine |
GET |
/api/v1/brands |
List brand profiles |
GET |
/api/v1/brands/{id} |
Brand detail |
PATCH |
/api/v1/brands/{id} |
Update brand (name, guidelines, colors, etc.) |
GET |
/api/v1/campaigns |
List campaigns |
GET |
/api/v1/campaigns/{id} |
Campaign detail + asset manifest |
GET |
/api/v1/command-centre |
Live command centre data |
POST |
/api/v1/runs/{id}/approve |
Human approval of a held run |
POST |
/api/v1/runs/{id}/reject |
Human rejection of a held run |
GET |
/api/v1/runs/{id}/audit |
Per-run OTel audit timeline |
POST |
/api/v1/trends/scan |
Trigger a fresh Google Trends scan |
GET |
/ready |
Readiness check (DB + dependencies) |
GET |
/health |
Health check |
GCP project: hackathon-trend-jack Β· Region: us-central1 Β· Registry: us-central1-docker.pkg.dev/hackathon-trend-jack/trendjack/
The Cloud Build service account needs three roles (one-time):
SA="806222250471-compute@developer.gserviceaccount.com"
PROJECT="hackathon-trend-jack"
gcloud projects add-iam-policy-binding $PROJECT --member="serviceAccount:$SA" --role="roles/storage.objectAdmin"
gcloud projects add-iam-policy-binding $PROJECT --member="serviceAccount:$SA" --role="roles/artifactregistry.writer"
gcloud projects add-iam-policy-binding $PROJECT --member="serviceAccount:$SA" --role="roles/logging.logWriter"The Cloud Run service account needs Vertex AI and Secret Manager access:
gcloud services enable aiplatform.googleapis.com --project=$PROJECT
gcloud projects add-iam-policy-binding $PROJECT --member="serviceAccount:$SA" --role="roles/aiplatform.user"
gcloud secrets add-iam-policy-binding trendjack-db-url --member="serviceAccount:$SA" --role="roles/secretmanager.secretAccessor" --project=$PROJECTNote: IAM changes take ~30β60 seconds to propagate β no redeploy needed after granting.
gcloud artifacts repositories create trendjack --repository-format=docker --location=us-central1 --project=hackathon-trend-jackgcloud secrets create trendjack-db-url --replication-policy=automatic --project=hackathon-trend-jack
echo -n "postgresql://USER:PASS@HOST:5432/trendjack" | gcloud secrets versions add trendjack-db-url --data-file=- --project=hackathon-trend-jack# Build and push (run from repo root)
gcloud builds submit apps/api/ \
--tag us-central1-docker.pkg.dev/hackathon-trend-jack/trendjack/trendjack-api \
--project hackathon-trend-jack
# Deploy
gcloud run deploy trendjack-api \
--image us-central1-docker.pkg.dev/hackathon-trend-jack/trendjack/trendjack-api \
--region us-central1 \
--platform managed \
--allow-unauthenticated \
--set-env-vars "TRENDJACK_AGENTS_ENABLED=1,GOOGLE_CLOUD_PROJECT=hackathon-trend-jack,GOOGLE_CLOUD_LOCATION=global,GOOGLE_GENAI_USE_VERTEXAI=1,GEMINI_MODEL=gemini-3.7-flash,TRENDJACK_CORS_ORIGINS=https://trendjack-web-806222250471.us-central1.run.app" \
--set-secrets "DATABASE_URL=trendjack-db-url:latest" \
--memory 2Gi --cpu 2 --timeout 300 \
--project hackathon-trend-jackAfter deploy, set the public URL so agent cards use the correct endpoint, and confirm the CORS origin matches the frontend URL:
gcloud run services update trendjack-api \
--region us-central1 \
--update-env-vars "TRENDJACK_BASE_URL=https://trendjack-api-806222250471.us-central1.run.app,TRENDJACK_CORS_ORIGINS=https://trendjack-web-806222250471.us-central1.run.app" \
--project hackathon-trend-jackCORS note:
TRENDJACK_CORS_ORIGINSmust list every origin that the browser will use to call the API (comma-separated). Without it the API falls back to localhost-only origins and the deployed frontend will get403 ForbiddenCORS errors. Update it whenever the frontend URL changes (e.g. after a new Cloud Run revision assigns a different URL).To update an already-running service without a full redeploy:
gcloud run services update trendjack-api \ --region us-central1 \ --update-env-vars "TRENDJACK_CORS_ORIGINS=https://trendjack-web-806222250471.us-central1.run.app" \ --project hackathon-trend-jack
The frontend build bakes NEXT_PUBLIC_API_URL at compile time.
# Build and push (run from repo root)
gcloud builds submit . \
--config=cloudbuild-web.yaml \
--substitutions=_NEXT_PUBLIC_API_URL=https://trendjack-api-806222250471.us-central1.run.app \
--project hackathon-trend-jack
# Deploy
gcloud run deploy trendjack-web \
--image us-central1-docker.pkg.dev/hackathon-trend-jack/trendjack/trendjack-web \
--region us-central1 \
--platform managed \
--allow-unauthenticated \
--memory 1Gi --cpu 1 --timeout 60 \
--project hackathon-trend-jackTrendJack registers all 11 agents (1 fleet + 10 LLM specialists) in Vertex AI Agent Registry using the A2A v1.0 protocol. (technical_qa is a deterministic FunctionNode and is not separately registered.)
Agent card endpoints served by the API:
| Endpoint | Description |
|---|---|
GET /.well-known/agent-card.json |
Fleet A2A card (registry entry point) |
GET /api/v1/agents |
Catalog of all specialist agents |
GET /api/v1/agents/{id}/agent-card.json |
Individual agent A2A card |
Register or update all agents (upserts β safe to re-run after URL changes):
cd apps/api
python scripts/register_agents.py \
--project=hackathon-trend-jack \
--location=us-central1 \
--base-url=https://trendjack-api-806222250471.us-central1.run.appOptions: --fleet-only to register only the fleet card, --list to inspect current state, --dry-run to preview request bodies without calling the API.
Prerequisite: gcloud auth application-default login and gcloud services enable agentregistry.googleapis.com --project=hackathon-trend-jack.
Every run produces a full OTel-compatible audit trail (trace_id + span_id on every entry), written as structured JSON to Cloud Logging. The Governance page in the dashboard shows live audit timelines. Key invariant: rejected runs never produce a gateway call β the policy engine terminates the workflow before any ad platform is contacted. This is enforced in code and covered by TestRejectedRunNoGateway in the test suite.
See docs/OBSERVABILITY.md for the full trace model.
Fortified Enterprise Fleet β scalable multi-agent system with governance.
- 10 LLM agents (Google ADK 2.8.0) with parallel fan-out across four stages (Discover Β· Intelligence Β· Creative Β· Verify), plus deterministic policy and QA nodes
- Deterministic policy engine with scored rules (0β100) gating every stage transition β AI advises, rules decide
- Full OTel audit trail with structured JSON logging to Cloud Logging
- Automated trend scheduler running every 30 minutes without human intervention
- Mock Google Ads + Meta Ads gateways with simulated receipts (
simulated: true)