Gateway HTTP OpenAI-compatible (/v1/chat/completions, /v1/models) que expõe
o Antigravity CLI (agy) como se fosse um backend de LLM
qualquer — dá pra apontar qualquer client/SDK que fale API da OpenAI (curl, código,
extensões de editor, etc.) direto pro agy rodando localmente.
Projeto irmão: agy-gateway — mesma ideia, só que como bridge de Telegram em vez de API HTTP.
Cada request HTTP dispara um processo agy --print (modo sem TUI), captura a saída
e devolve no formato chat.completion da OpenAI. Por trás disso:
agy trava um banco local por processo; o gateway
usa um semáforo (AGY_MAX_CONCURRENT, default 1) pra nunca rodar dois ao mesmo
tempo.user (padrão OpenAI) e o
gateway mantém sua conversa separada das dos outros clientes.stream: true devolve Server-Sent Events incrementais,
não só o texto todo de uma vez.agy pedir permissão pra rodar algo no meio do
caminho, o gateway não trava: devolve um approval_id e o cliente aprova/nega via
outro endpoint.AGY_API_KEY liga checagem de Authorization: Bearer.Detalhes de arquitetura e invariantes internas estão em CLAUDE.md.
git clone [email protected]:ataliba/agy-gateway-ng.git
cd agy-gateway-ng
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
cp .env.example .env
Pré-requisito: agy (Antigravity CLI) instalado e autenticado na máquina
(agy rodado ao menos uma vez pra validar login).
.env)| Var | Default | Uso |
|---|---|---|
AGY_BIN | agy | binário/caminho do CLI |
AGY_CONTINUE | true | continua a última conversa quando não há user no request |
MODELS_FILE | models.yaml | registro de aliases de modelo |
AGY_TIMEOUT | 300 | timeout de processo travado e de espera por aprovação |
BRAIN_DIR | ~/.gemini/antigravity-cli/brain | pasta onde o agy guarda conversas |
AGY_MAX_CONCURRENT | 1 | quantos agy podem rodar ao mesmo tempo |
AGY_API_KEY | (vazio) | se setado, exige Bearer token em /v1/* |
Modelos expostos ficam em models.yaml, mapeando um alias (agy-claude-sonnet-4-6)
pro nome real que o --model do agy espera.
.venv/bin/uvicorn main:app --reload --port 8000
Ou em produção via systemd (unit já pronta em agy-gateway.service):
mkdir -p ~/.config/systemd/user
cp agy-gateway.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now agy-gateway
A imagem já vem com o Antigravity CLI instalado (instalador oficial, dentro do
Dockerfile). Login e conversas ficam num volume próprio (gemini_data),
isolado do host — sobrevive a restart/rebuild, só some com
docker compose down -v.
cp .env.example .env
docker compose up -d --build
Pra usar a imagem já publicada no Docker Hub (cybernetus/agy-gateway-ng), sem
build local:
cp .env.example .env
docker compose -f docker-compose.prod.yml up -d
Na primeira vez, entre no container e faça o login do Google (o agy mostra uma
URL pra abrir num navegador):
docker compose exec agy-gateway agy
Depois disso o gateway já usa essa sessão autenticada normalmente — não precisa
logar de novo a não ser que o volume gemini_data seja apagado.
BRAIN_DIR do .env é sobrescrito pelo compose pro caminho de dentro do
container (/root/.gemini/antigravity-cli/brain) — não precisa mexer nessa
variável pra rodar via Docker.
Instalador standalone (não depende do framework community-scripts) que cria um
LXC Debian 13 e instala tudo nativo (venv + systemd, sem Docker, em
/opt/api-gateway-ng). Roda no shell do host Proxmox:
bash -c "$(curl -fsSL https://raw.githubusercontent.com/ataliba/agy-gateway-ng/main/ct/agy-gateway-ng.sh)"
Customiza via env var antes do comando — mesma convenção do
community-scripts/ProxmoxVE (var_cpu, var_ram, var_disk, var_version,
var_unprivileged), mais CTID, HOSTNAME, STORAGE, NET_CONFIG,
GATEWAY_PORT (ver cabeçalho de ct/agy-gateway-ng.sh):
var_cpu=2 var_ram=1024 var_version=13 bash -c "$(curl -fsSL https://raw.githubusercontent.com/ataliba/agy-gateway-ng/main/ct/agy-gateway-ng.sh)"
No fim, falta só logar o agy dentro do container:
pct exec <CTID> -- agy
.venv/bin/pip install -r requirements-dev.txt
.venv/bin/python -m pytest -q
curl http://127.0.0.1:8000/v1/models
curl -X POST http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "agy-claude-sonnet-4-6",
"messages": [{"role": "user", "content": "oi"}],
"user": "ataliba"
}'
user é o que faz o gateway lembrar da sua conversa entre chamadas — sem ele, cada
request sem contexto cai no --continue (segue a última conversa global do agy).
curl -N -X POST http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "agy-claude-sonnet-4-6",
"messages": [{"role": "user", "content": "oi"}],
"stream": true,
"user": "ataliba"
}'
Uma resposta pode vir com HTTP 202:
{
"status": "permission_required",
"approval_id": "45e08c8581d34915abf6151021c35de4",
"command": "Run this command? [y/N]"
}
Aprovar ou negar:
curl -X POST http://127.0.0.1:8000/v1/approvals/45e08c8581d34915abf6151021c35de4 \
-H "Content-Type: application/json" \
-d '{"approved": true}'
Em modo streaming, o mesmo aviso chega como um chunk SSE extra
("permission_required": {...}) e o stream original continua depois que a
aprovação chegar por essa mesma rota.
AGY_API_KEY configuradocurl http://127.0.0.1:8000/v1/models -H "Authorization: Bearer <sua-chave>"
Content type
Image
Digest
sha256:2ee518f99…
Size
114.5 MB
Last updated
7 days ago
docker pull cybernetus/agy-gateway-ng