# Chanakya v2 — Running the Agent Stack

Chanakya v2 is a multi-agent system of currently three Docker services:

| Service | Role | Port | Chat UI |
|---|---|---|---|
| `gmail-agent` | A2A sub-agent — searches dummy email data | 8001 | `http://localhost:8001/chat` |
| `jira-agent` | A2A sub-agent — searches Jira tickets | 8002 | `http://localhost:8002/chat` |
| `chanakya-ui` | Hub agent — A2A server + pylogue chat UI | 8010 | `http://localhost:8010/chat` |

All images are built with **uv** on `python3.11-bookworm-slim`.  
Gmail and Jira agents each serve two things on the same port: an A2A JSON-RPC endpoint (`POST /`) and a pylogue chat UI (`/chat`).

---

## Repository layout

```
backend/chanakya/
├── config.py            # AGENT_REGISTRY — single source of truth for hosts/ports
├── base.py              # EnterpriseAgent (extends pylogue PydanticAIResponder)
├── chanakya.py          # Hub agent, A2A client tools, chanakya_app (pylogue UI)
├── Makefile             # Launch recipes (bare-metal + docker compose shortcuts)
├── Dockerfile           # Hub image
├── requirements.txt     # Hub deps (pinned, uv)
├── gmail/
│   ├── agent.py         # Gmail agent + /chat mount + startup banner
│   ├── mock_data.py
│   ├── __init__.py
│   ├── Dockerfile
│   └── requirements.txt
└── jira/
    ├── agent.py         # Jira agent + /chat mount + startup banner
    ├── mock_data.py
    ├── __init__.py
    ├── Dockerfile
    └── requirements.txt
```

---

## Prerequisites

- Docker ≥ 24 with Compose v2 (`docker compose version`)
- A running LiteLLM proxy (or any OpenAI-compatible endpoint)
- `make` (optional, for shortcut recipes)

---

## 1 — Configure environment

From the **project root** (`ai-enterprise-brain/`):

```sh
cp .env.example .env
```

Edit `.env`:

```dotenv
# Required
LITELLM_PROVIDER_BASE_URL=http://your-litellm-proxy:4000
LITELLM_PROVIDER_MODEL_NAME=gemini/gemini-2.5-flash

# Optional
LITELLM_API_KEY=your_api_key
LOGFIRE_TOKEN=your_logfire_token

# Port overrides (defaults shown)
# GMAIL_AGENT_PORT=8001
# JIRA_AGENT_PORT=8002
# CHANAKYA_PORT=8010

# Override where chanakya-ui stores its SQLite chat history (default: /data/chat_history.db)
# PYLOGUE_DB_PATH=/data/chat_history.db
```

---

## 2 — Start the full stack

```sh
# From project root — always use --build to pick up code changes
docker compose up --build
```

On a successful start you will see output like:

```
gmail-agent-1  | INFO:  Uvicorn running on http://0.0.0.0:8001
gmail-agent-1  | INFO:  Gmail Agent ready:
gmail-agent-1  | INFO:    A2A   → http://0.0.0.0:8001/
gmail-agent-1  | INFO:    Chat  → http://0.0.0.0:8001/chat
gmail-agent-1  | INFO:    Card  → http://0.0.0.0:8001/.well-known/agent-card.json
jira-agent-1   | INFO:  Uvicorn running on http://0.0.0.0:8002
jira-agent-1   | INFO:    A2A   → http://0.0.0.0:8002/
jira-agent-1   | INFO:    Chat  → http://0.0.0.0:8002/chat
```

Both agents must pass their healthcheck (`GET /.well-known/agent-card.json → 200`)
before `chanakya` and `chanakya-ui` start.

---

## 3 — Access the UIs

| URL | What you get |
|---|---|
| `http://localhost:8001/chat` | Gmail agent chat frontend (pylogue) |
| `http://localhost:8002/chat` | Jira agent chat frontend (pylogue) |
| `http://localhost:8010/chat` | Chanakya hub chat frontend (pylogue) |
| `http://localhost:8001/` | Gmail A2A JSON-RPC endpoint |
| `http://localhost:8002/` | Jira A2A JSON-RPC endpoint |
| `http://localhost:8010/` | Chanakya A2A JSON-RPC endpoint |
| `http://localhost:8001/.well-known/agent-card.json` | Gmail A2A agent card |
| `http://localhost:8002/.well-known/agent-card.json` | Jira A2A agent card |
| `http://localhost:8010/.well-known/agent-card.json` | Chanakya A2A agent card |

---

## 4 — Chat with Chanakya

Open `http://localhost:8010/chat` in your browser. The pylogue UI is mounted directly on the Chanakya A2A server — no separate container needed.

Example question:

```
Are there any critical Jira tickets related to the coking coal procurement email from last week?
```

Chanakya routes to the appropriate sub-agent(s), synthesises results, and cites sources. Chat history is persisted in the `chanakya-chat-history` Docker volume and survives container restarts.

---

## 5 — Useful commands

```sh
# Rebuild and restart only one service after a code change
docker compose up --build chanakya-ui

# View logs for all services
docker compose logs -f

# View logs for a single service
docker compose logs -f gmail-agent

# Stop and remove all containers (chat history in data/ is always preserved)
docker compose down

# Stop and remove containers AND delete all volumes (data/ is unaffected — it's on disk)
docker compose down -v

# Check health status
docker compose ps

# Chat history lives in data/ at the project root — back it up like any directory
tar czf chat_history_backup.tar.gz data/
```

> **Important:** Always pass `--build` when you've changed Python source.
> Without it, Docker reuses the cached image and your changes won't be reflected.

### Local pylogue dev (live source, no rebuild)

A `docker-compose.override.yml` is included. When present, Docker Compose automatically merges it:

- Bind-mounts `../ai-pylogue/src` into every container at `/pylogue-src` (read-only)
- Prepends `/pylogue-src` to `PYTHONPATH` so local pylogue changes take effect immediately
- Preserves the `chanakya-chat-history` volume for `chanakya-ui`

To use the GitHub-pinned pylogue version instead (skipping the override):

```sh
docker compose -f docker-compose.yml up --build
```

---

## 6 — Running bare-metal (without Docker)

All recipes run from the **project root**: `make -f backend/chanakya/Makefile <recipe> ROOT=.`

**Bare-metal**

| Recipe | What it does |
|---|---|
| `gmail` | Run Gmail agent on port 8001 (with `--reload`) |
| `jira` | Run Jira agent on port 8002 (with `--reload`) |
| `agents` | Run Gmail + Jira agents in parallel (background) |
| `chanakya-ui` | Run Chanakya A2A + pylogue UI on port 8010 |
| `ui` | Run all agents + Chanakya UI |
| `stop` | Kill all bare-metal uvicorn processes |

**Docker Compose**

| Recipe | What it does |
|---|---|
| `docker-build` | Build all Docker images |
| `docker-up` | Build + start all services detached (uses override for local pylogue) |
| `docker-up-github` | Same, but skips override — uses GitHub-pinned pylogue |
| `docker-ui` | Alias for `docker-up` |
| `docker-run` | Build + start attached, log to `logs/YYYYMMDD-HHMMSS.log` |
| `docker-run-github` | Same, but skips override |
| `docker-down` | Stop and remove all containers |
| `docker-logs` | Tail logs for all services, write to `logs/YYYYMMDD-HHMMSS.log` |

---

## Architecture

```mermaid
graph TD
    subgraph compose["docker compose network"]
        gmail["gmail-agent :8001<br/>POST / — A2A<br/>GET /chat — UI"]
        jira["jira-agent :8002<br/>POST / — A2A<br/>GET /chat — UI"]
        chanakya["chanakya<br/>hub agent"]
        ui["chanakya-ui :8010<br/>pylogue chat UI<br/>volume: chat history → /data"]

        gmail -- "A2A JSON-RPC" --> chanakya
        jira  -- "A2A JSON-RPC" --> chanakya
        chanakya -- "same agent instance" --> ui
    end

    browser1(["Browser<br/>localhost:8001/chat"]) --> gmail
    browser2(["Browser<br/>localhost:8002/chat"]) --> jira
    browser3(["Browser<br/>localhost:8010"]) --> ui
```

Inside the Compose network, services reach each other by service name
(`http://gmail-agent:8001`, `http://jira-agent:8002`) — no `localhost` wiring needed.

---

## How the chat UI works

The pylogue chat UI is a FastHTML + WebSocket app. All three agents mount it **directly on their fasta2a FastAPI app** at `/chat`:

```python
app.mount("/chat", create_core_app(responder_factory=lambda: agent, ...))
```

FastAPI path-strips `/chat` before forwarding, so pylogue sees `/` internally —
WebSocket, static assets, and routing all work without any `base_path` configuration.

**Chat history storage:**

| Service | Host path | Docker mount | Shared? |
|---|---|---|---|
| `gmail-agent` | `data/gmail/chat_history.db` | `./data/gmail:/data` | **Yes** — same file in both environments |
| `jira-agent` | `data/jira/chat_history.db` | `./data/jira:/data` | **Yes** — same file in both environments |
| `chanakya-ui` | `data/chanakya/chat_history.db` | `./data/chanakya:/data` | **Yes** — same file in both environments |

All three paths are relative to the project root (`ai-enterprise-brain/`). The directories are pre-created with `.gitkeep`; the `.db` files are git-ignored. The `PYLOGUE_DB_PATH` env var overrides the path per-container if needed.

---

## Config registry

All agent hostnames, ports, and metadata live in one place:

```
backend/chanakya/config.py  →  AGENT_REGISTRY
```

To change a port, update `AGENT_REGISTRY` (or set the corresponding env var) and rebuild.
Neither the agent files nor `chanakya.py` contain hardcoded URLs.

---

## Adding a new agent

1. Create `backend/chanakya/<agent>/` with `agent.py`, `__init__.py`, `requirements.txt`, `Dockerfile`.
2. Add an entry to `backend/chanakya/config.py` → `AGENT_REGISTRY`.
3. In `agent.py`: call `to_a2a()`, mount pylogue at `/chat`, add a `@app.on_event("startup")` banner.
4. Add a service block in `docker-compose.yml` mirroring `gmail-agent` (same healthcheck pattern).
5. Add `ask_<agent>` tool in `chanakya.py` and a `depends_on` entry for the new service.
