Metadata-Version: 2.4
Name: hyperagent-unified-gateway
Version: 0.3.0
Summary: OpenAI-compatible unified gateway for Hyperagent.com (MCP upstream)
Author: tmq9999
License: MIT
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi>=0.110
Requires-Dist: uvicorn[standard]>=0.27
Requires-Dist: httpx>=0.26
Requires-Dist: python-multipart>=0.0.9
Requires-Dist: pydantic>=2.6
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Provides-Extra: tray
Requires-Dist: pystray>=0.19; extra == "tray"
Requires-Dist: pillow>=10; extra == "tray"
Dynamic: license-file

# Hyperagent Unified Gateway

**Biến Hyperagent.com thành một backend nói tiếng OpenAI.** Trỏ bất kỳ client OpenAI nào —
**Codex CLI**, Cursor, 9Router, LibreChat, thư viện `openai`… — vào gateway này, và "model"
trả lời là một agent Hyperagent chạy đúng model AI thật bạn chọn (Claude Fable 5, Claude
Opus, ChatGPT Sol 5.6, …).

> **v0.1.0** · 163 unit test xanh · đã verify end-to-end bằng **Codex CLI binary thật 10/10
> task** trên account Hyperagent thật (`docs/AUDIT.md`) · clean-room MIT.

```
Codex CLI · Cursor · 9Router · openai SDK          (client OpenAI bất kỳ)
        │      HTTP  http://127.0.0.1:8099/v1   (wire format OpenAI chuẩn)
        ▼
┌────────────────────────────────────────────────────────────┐
│              Hyperagent Unified Gateway (FastAPI)          │
│  /v1/*  OpenAI surface   ·  /admin  dashboard có sẵn       │
│  /admin/ui/  Web UI      ·  model map · multi-account      │
│  relay tool-calls kiểu Codex · SQLite state · API keys     │
└────────────────────────────────────────────────────────────┘
        │      MCP JSON-RPC 2.0 (OAuth 2.1 + PKCE, tự refresh token)
        ▼
   Hyperagent.com — các named agent "backend trần" (không tool)
   mỗi model AI thật = một agent:  claude-fable-5 · opus-5 · gpt-5.6-sol …
```

**Vì sao cần "một agent cho mỗi model"?** MCP của Hyperagent không cho chọn model theo từng
request — model là cấu hình của agent. Gateway giải quyết bằng **model map**: mỗi model một
agent gateway sạch, client cứ gọi `model=claude-opus` như với OpenAI. Xem
[Model map](#model-map--agent-gateway-chuẩn).

---

## Tính năng

| Nhóm | Có gì | Trạng thái |
| --- | --- | --- |
| **Chat Completions** `POST /v1/chat/completions` | stream + non-stream, `tools` → `tool_calls` (relay) | ✅ verify account thật |
| **Responses API** `POST /v1/responses` (+`GET`, `/cancel`) | stream (đủ vòng đời event cho Codex), chaining `previous_response_id`, `function_call` / `function_call_output`, continuity vào đúng thread | ✅ verify account thật, Codex CLI 10/10 |
| **Codex CLI** | `wire_api = "responses"`, tool loop nhiều lượt, apply_patch/shell | ✅ binary thật v0.146.0 |
| **Model map** | tên model THẬT ↔ agent; `/v1/models` liệt kê model; đổi runtime không restart | ✅ verify thật 8/8 |
| **Multi-account** | nhiều danh tính Hyperagent một gateway; chọn theo `X-Account-Id` / API key; continuity bám account | ✅ unit + single-account thật |
| **Admin** | `/admin` dashboard có sẵn (không cần build) + `/admin/ui/` Web UI đầy đủ (một lệnh build, cùng port) | ✅ contract 18/18 thật |
| **Vận hành** | API keys CRUD, usage stats, billing (cần web session), đổi port + self-restart, offline CSP | ✅ verify thật |
| **Provisioning** | `hga capture-session` → `hga provision`: tự tạo bộ agent gateway chuẩn trên account | ✅ contract thật; chạy trên máy bạn |
| Legacy / stub | `/v1/completions` (legacy), `/v1/embeddings` (hashing **không ngữ nghĩa**), `/v1/moderations` (heuristic) | ⚠️ stub tương thích, không phải bản thật |

Đường đi coding-agent (chat / responses / tool calling) là ưu tiên số 1 của dự án;
embeddings/moderations chỉ là stub giữ tương thích wire-format.

---

## Cài đặt nhanh

**Yêu cầu:** Python **3.11+** · tài khoản Hyperagent có ít nhất 1 named agent ·
(tuỳ chọn Web UI) Node.js ≥ 18 hoặc bun · (tuỳ chọn provisioning) Playwright.

### macOS / Linux

> **Chạy 1 click:** clone xong chỉ cần **đúp chuột `run.bat`** (Windows) hoặc `./run.sh`
> (macOS/Linux) — script tự `git pull` → tạo venv + cài đặt (nếu thiếu) → `hga login`
> (chỉ lần đầu) → `hga serve`. Thêm `--mock` để chạy echo offline. Các bước thủ công
> bên dưới dành cho ai muốn kiểm soát từng lệnh.
>
> **Chạy NỀN (ẩn console, giống 9router):** đúp **`run-hidden.vbs`** — gateway chạy
> nền không cửa sổ terminal; tương tác qua `http://127.0.0.1:8099/admin`; dừng bằng
> **`stop-gateway.bat`**.
>
> **Icon KHAY hệ thống (giống 9router):** đúp **`run-tray.bat`** — cài `[tray]` deps
> (pystray+pillow), login nếu cần, rồi hiện **biểu tượng khay**; chuột phải: **Mở
> Admin / Dừng**. Không console. (CLI: `hga tray`.)
>
> **Provision một lần là xong:** `hga provision` (hoặc nút Install trong admin) LƯU
> model map vào state (`~/.hyperagent-gateway/state.db`) — `hga serve` đọc lại tự
> động, không phải provision lại mỗi phiên.

```bash
git clone https://github.com/tmq9999/hyperagent-unified-gateway
cd hyperagent-unified-gateway
python3.11 -m venv .venv && source .venv/bin/activate
pip install -e .

hga login       # OAuth một lần → ~/.hyperagent-gateway/tokens.json
hga verify      # liệt kê agent = auth thật đã chạy
hga serve       # gateway tại http://127.0.0.1:8099 — mặc định nhắm account THẬT
```

### Windows (PowerShell)

```powershell
git clone https://github.com/tmq9999/hyperagent-unified-gateway
cd hyperagent-unified-gateway
py -3.11 -m venv .venv; .\.venv\Scripts\Activate.ps1
pip install -e .

hga login
hga verify
hga serve       # http://127.0.0.1:8099 — mặc định nhắm account THẬT
```

> Dev/đóng góp: `pip install -e ".[dev]"` rồi `pytest -q` (193 test, network-free).

### Thử ngay

```bash
curl -s http://127.0.0.1:8099/v1/models
curl -s http://127.0.0.1:8099/v1/chat/completions \
  -H 'content-type: application/json' \
  -d '{"model":"hyperagent-default","messages":[{"role":"user","content":"Xin chào"}]}'
```

```powershell
Invoke-RestMethod http://127.0.0.1:8099/v1/models
Invoke-RestMethod http://127.0.0.1:8099/v1/chat/completions -Method Post `
  -ContentType 'application/json' `
  -Body '{"model":"hyperagent-default","messages":[{"role":"user","content":"Xin chào"}]}'
```

Mở `http://127.0.0.1:8099/` để xem service card (endpoint nào ở đâu), `/admin` để vào
dashboard. Chi tiết đăng nhập/sự cố OAuth: `docs/LOGIN.md`.

### Admin UI (có sẵn, KHÔNG cần build — cùng port với API)

`/admin` là **UI gốc đa trang** (Paper Circuit): Tổng quan · Models · Tài khoản ·
Sử dụng · Cài đặt · Install. Static thuần đóng gói ngay trong package Python —
không npm/bun, không build step, CSP offline (trình duyệt chặn mọi egress).
Model-centric: chọn **model** từ catalog nhà cung cấp; agent chỉ là đích của
model map (ADR-0007).

```bash
hga serve        # → http://127.0.0.1:8099/admin (đặt GATEWAY_ADMIN_KEY thì thêm ?key=…)
```

SPA Lovable trong `webui/` vẫn build được (`hga ui build` → phục vụ tại
`/admin/ui/`) nhưng không còn là admin surface mặc định. Xem
`webui/GATEWAY_INTEGRATION.md`.

---

## Dùng với Codex CLI

`~/.codex/config.toml`:

```toml
model = "claude-fable-5"            # tên model trong model map (KHÔNG kèm prefix router)
model_provider = "hga"
model_context_window = 1000000      # tắt cảnh báo Codex "Model metadata not found"
model_max_output_tokens = 64000     # (Codex đọc metadata từ config, KHÔNG từ /v1/models)

[model_providers.hga]
name    = "Hyperagent Unified Gateway"
base_url = "http://127.0.0.1:8099/v1"
env_key  = "HGA_KEY"                # đọc key từ biến môi trường HGA_KEY
wire_api = "responses"
```

> **Cảnh báo `Model metadata for … not found`** là của Codex (dùng fallback, task vẫn chạy)
> — nó tra context window trong config, không phải từ `/v1/models`. Đặt 2 dòng
> `model_context_window` + `model_max_output_tokens` ở trên là hết. Nếu đi qua 9Router,
> prefix kiểu `hga-test/` là tên route của 9Router; `model` ở Codex nên là tên model thật.
> Gateway vẫn expose `context_window` trong `/v1/models` cho các client CÓ đọc (LiteLLM…).

Chạy gateway với relay tool-calls + client auth:

```bash
SHIM_API_KEYS=sk-your-key hga serve   # tool relay đã BẬT mặc định (ADR-0012)
# rồi:  HGA_KEY=sk-your-key codex "sửa bug trong repo này"
```

Đã verify bằng binary thật: trả lời no-tool, tạo/sửa file qua `shell` + `apply_patch`,
task nhiều bước, không lặp tool. Chi tiết: `docs/AUDIT.md`.

---

## Model map & agent gateway chuẩn

1. **Tạo agent gateway sạch** — "LLM backend trần": **không tool, không skill, không
   memory, learning tắt**, system prompt chuẩn trong `docs/AGENT_SETUP.md` (tin cấu hình
   client, trả lời sạch, hỗ trợ relay tool call). Tạo tay theo hướng dẫn đó, **hoặc** tự
   động:

   ```bash
   pip install playwright && playwright install chromium
   hga capture-session                    # bạn tự đăng nhập; tool chỉ giữ session cookie
   hga provision --models models.json     # mỗi model một agent → in GATEWAY_MODEL_MAP
   hga change-model --agent <id> --model opus-latest   # đổi model một agent (P4)
   ```

2. **Khai model map** để client chọn model như OpenAI thật:

   ```bash
   GATEWAY_MODEL_MAP='{"claude-fable-5":"<agent_id_1>","claude-opus":"<agent_id_2>"}'
   GATEWAY_DEFAULT_AGENT='claude-fable-5'   # default có thể là tên model
   ```

   Khi có map: `/v1/models` liệt kê **tên model thật**, `model=claude-opus` route đúng
   agent chạy Opus. Đổi map/default lúc chạy (không restart): Admin UI hoặc
   `POST /admin/api/config {"model_map": {...}, "default_agent": "..."}`.

**Chế độ tool — chọn 1 trong 2:**

| | Autonomous (`GATEWAY_TOOL_RELAY=0`) | Relay (**mặc định** — ADR-0012) |
| --- | --- | --- |
| Ai chạy tool? | Agent Hyperagent tự chạy tool CỦA NÓ (nếu agent có tool) | **Client** (Codex/Cursor) tự chạy tool của client |
| `tool_calls` trả về client | Không (câu trả lời cuối sạch) | Có — JSON theo đúng tên tool client khai báo |
| Dùng cho | Chat thường, agent có sẵn của bạn | Codex CLI, coding-agent loop |

---

## Multi-account

Một gateway phục vụ nhiều danh tính Hyperagent (`GATEWAY_ACCOUNTS_FILE`):

```json
{"accounts": [
  {"id": "personal", "token_file": "~/.hyperagent-gateway/personal.json", "default": true,
   "api_keys": ["sk-personal"]},
  {"id": "work", "token_file": "~/.hyperagent-gateway/work.json", "api_keys": ["sk-work"]}
]}
```

Chọn account theo header `X-Account-Id`, theo API key (map trong file), hoặc default.
`GET /v1/accounts` liệt kê. Mỗi hội thoại/thread **bám account đã tạo ra nó** — thiết kế
cho *continuity* (một phiên làm việc dính một account; đổi account là hành động chủ đích),
không phải xoay vòng mỗi request (ADR-0002).

---

## Cấu hình (biến môi trường)

| Biến | Mặc định | Ý nghĩa |
| --- | --- | --- |
| `GATEWAY_UPSTREAM` | `mcp` | `mcp` = account Hyperagent thật (mặc định); `mock` = echo offline để dev/test — chỉ bật TƯỜNG MINH (`hga serve --mock`), không bao giờ rơi vào âm thầm (ADR-0010) |
| `GATEWAY_STATE_PATH` | `~/.hyperagent-gateway/state.db` | SQLite cho model map / API key / port đổi trong UI — persist qua restart; `:memory:` = không lưu |
| `GATEWAY_BIND_HOST` / `GATEWAY_BIND_PORT` | `127.0.0.1` / `8099` | host/port khi `hga serve` (port đổi trong UI được lưu SQLite và thắng) |
| `HYPERAGENT_TOKEN_FILE` | `~/.hyperagent-gateway/tokens.json` | token bundle từ `hga login` |
| `GATEWAY_FILES_MAX_MB` | `25` | trần upload `/v1/files` (file lưu BLOB trong SQLite state, ADR-0013) |
| `GATEWAY_HTTP_TIMEOUT` | `180` | read timeout (giây) cho MCP upstream — nới rộng vì thread lớn trả cả transcript mỗi poll (ADR-0016) |
| `GATEWAY_AGENTS_TTL` | `30` | giây cache `list_agents` — cắt round-trip MCP thừa mỗi lượt tool-loop; agent mới provision hiện sau tối đa TTL (ADR-0014) |
| `HYPERAGENT_MCP_URL` | `https://hyperagent.com/api/mcp` | endpoint MCP |
| `GATEWAY_DEFAULT_AGENT` | *(trống)* | agent id **hoặc tên model** dùng cho `hyperagent-default` |
| `GATEWAY_MODEL_MAP` | *(trống)* | JSON `{tên model: agent id}` — xem Model map |
| `GATEWAY_TOOL_RELAY` | `1` | relay tool-calls kiểu Codex — **bật mặc định** (chỉ kích hoạt khi client gửi `tools`); `0` để tắt (ADR-0012) |
| `SHIM_API_KEYS` | *(trống)* | danh sách key client `sk-…` (phẩy); trống = dev mode mở; key quản lý trong Admin UI cũng được nhận |
| `GATEWAY_ACCOUNTS_FILE` | *(trống)* | JSON multi-account (xem trên) |
| `GATEWAY_STATE_PATH` | `:memory:` | SQLite (Responses store, settings, API keys, usage). **Đặt đường dẫn file để bền qua restart** |
| `GATEWAY_ADMIN_KEY` | *(trống)* | khoá `/admin*` (`?key=` hoặc Bearer); trống = mở (dev) |
| `GATEWAY_ADMIN_STATIC` | *(trống)* | **override** thư mục UI build; trống = tự phát hiện build trong `webui/` |
| `GATEWAY_WEBUI_DIR` | *(trống)* | override thư mục NGUỒN webui (hiếm khi cần) |
| `GATEWAY_ADMIN_CSP` | CSP offline | CSP cho `/admin*`; `off` để tắt (không khuyến nghị) |
| `GATEWAY_ADMIN_CORS` | *(trống)* | Origin được phép gọi `/admin/api/*` — chỉ cho dev UI tách port |
| `GATEWAY_POLL_INTERVAL` / `GATEWAY_RUN_TIMEOUT` | `1.0` / `1200` | chu kỳ poll thread + timeout MỘT lượt upstream (giây). Coding-agent chạy cả build+lint+test mỗi lượt → mặc định 1200s; nâng nữa nếu lượt nặng vẫn 504 |
| `GATEWAY_EMBEDDINGS` / `GATEWAY_EMBEDDINGS_DIM` | `fallback` / `256` | stub embeddings (`off` = 501) |
| `GATEWAY_PRICE_MAP` | *(trống)* | JSON `{model: {"in": $/1M, "out": $/1M}}` để ƯỚC LƯỢNG chi phí trong usage |

## CLI `hga`

| Lệnh | Làm gì |
| --- | --- |
| `hga login` | OAuth 2.1 + PKCE một lần → lưu token bundle (tự refresh khi chạy) |
| `hga discover` | in metadata OAuth server (chẩn đoán scope/lỗi login) |
| `hga verify` | liệt kê agent của account — chứng minh auth MCP thật chạy |
| `hga serve` | chạy gateway: **API + admin + Web UI trên MỘT port** |
| `hga ui build` | build Web UI một lệnh (bun/npm) → `hga serve` tự phục vụ tại `/admin/ui/` |
| `hga ui status` | xem đã có bản build chưa, nằm đâu, phục vụ ở đâu |
| `hga capture-session` | Playwright mở Chrome cho BẠN đăng nhập → giữ web session (provisioning + billing) |
| `hga capture-message-api` | ghi fetch API THẬT khi bạn gửi ẢNH trong web (upload + send) → contract JSON, không lưu cookie — bước 1 của đường REST gửi ảnh |
| `hga provision --models f.json` | tạo bộ agent gateway chuẩn (mỗi model một agent) → in `GATEWAY_MODEL_MAP` |
| `hga change-model --agent <id>` | đổi model/effort/thinking/fast của một agent gateway |

---

## Cấu trúc repo

```
AGENTS.md                  # cửa vào cho AI agent làm việc trên repo này
gateway/                   # toàn bộ code gateway (FastAPI, clean-room)
  app.py                   #   OpenAI surface /v1/* + service card /
  admin.py                 #   /admin dashboard + /admin/api/* + mount /admin/ui
  webui.py                 #   auto-detect + build Web UI (hga ui)
  cli.py                   #   hga: login/discover/verify/serve/ui/provision…
  upstream/                #   adapter MCP thật + mock tất định
  translate.py streaming.py responses.py toolcall.py state.py accounts.py …
webui/                     # nguồn Web UI (TanStack Start + React 19) — build 1 lệnh
tests/                     # 163 unit test (network-free, chạy được offline)
docs/                      # tài liệu người dùng + docs/agent/ (bộ nhớ dự án)
```

## Tài liệu

| File | Nội dung |
| --- | --- |
| `docs/LOGIN.md` | đăng nhập OAuth, token bundle, sự cố thường gặp |
| `docs/AGENT_SETUP.md` | tạo agent gateway chuẩn (system prompt + settings từng tab) |
| `webui/GATEWAY_INTEGRATION.md` | Web UI: build, phục vụ cùng port, chế độ dev |
| `docs/AUDIT.md` | kiểm toán v0.1.0 — bằng chứng verify thật từng khối |
| `docs/UI_SPEC.md` / `docs/INSTALLER_PLAN.md` | spec UI + thiết kế installer consent-first |
| `docs/agent/` + `AGENTS.md` | bộ nhớ dài hạn cho AI agent (STATE, PROGRESS_LOG, ADR…) |

## Trạng thái & giới hạn đã biết

**Đã verify trên account thật:** chat + responses (stream/chaining), relay tool loop 5/5,
Codex CLI binary 10/10 task, model map 8/8, đổi default runtime 8/8, API keys 9/9, usage
6/6, contract UI 18/18. **Stub:** embeddings (hashing không ngữ nghĩa), moderations
(heuristic). **Giới hạn:** streaming lượt tiếp nối là replay (chưa incremental); chưa hỗ
trợ parallel tool calls; `/v1/files` chưa có; billing thật cần web session (không lấy được
qua OAuth token); Hyperagent không stream token nên delta đến theo đợt poll (độ trễ giây).
Build UI và Playwright (capture-session/provision) chạy trên máy người vận hành.

## Bảo mật

- Secret nằm ngoài repo: `.gitignore` chặn `*tokens*.json`, `session*.json`, `*.env`.
  **Không bao giờ** dán token/cookie vào chat, log hay commit.
- `tokens.json` (OAuth) và `session.json` (web session) lưu `chmod 600` trong
  `~/.hyperagent-gateway/`; token tự refresh, session chỉ dùng cho provisioning/billing.
- Bật `SHIM_API_KEYS` (client) + `GATEWAY_ADMIN_KEY` (admin) khi mở ra ngoài localhost.
- `/admin*` mặc định gắn CSP offline — trình duyệt chặn mọi egress ra ngoài gateway.

## License

MIT — xem `LICENSE`. Toàn bộ code viết mới clean-room (ADR-0004), không vendor code
bên thứ ba không có license.
