English | Español | Português | 简体中文
Um proxy local, compatível com OpenAI, com failover real, aliasing multi-conta e zero segredos em disco. Um único binário fica entre o seu IDE/SDK e cada provider LLM que você usa — OpenAI, OpenRouter, Groq, Google Gemini, Azure OpenAI, Anthropic, e até a sua assinatura ChatGPT Plus/Pro. Também roda modelos GGUF totalmente locais, embeddings e voz (STT/TTS) sem nuvem nenhuma — um substituto do Ollama e um router para a nuvem no mesmo binário.
Important
OperatorLM escuta em 127.0.0.1:11434 por padrão — a mesma porta do Ollama. Qualquer ferramenta já apontada para o Ollama funciona sem mexer em nada. Se rodar os dois, troque uma em ~/.operatorlm/config.toml.
Só quer rodar? Baixe o último binário — sem Go toolchain, sem Docker.
Vá até a página de Releases e baixe o asset do seu sistema operacional:
| OS | Asset |
|---|---|
| Windows (x64) | OperatorLM-windows-amd64.exe |
| macOS (Apple silicon) | OperatorLM-darwin-arm64 |
| macOS (Intel) | OperatorLM-darwin-amd64 |
| Linux (x64) | OperatorLM-linux-amd64 |
Note
No macOS/Linux, dê permissão de execução: chmod +x OperatorLM-*.
- Windows: duplo clique em
OperatorLM-windows-amd64.exe. Aparece um ícone na bandeja (sem janela de console). Na primeira execução, o SmartScreen pode mostrar "O Windows protegeu seu PC" → clique em Mais informações → Executar mesmo assim (o binário não é assinado). - macOS desktop:
./OperatorLM-darwin-*— aparece o ícone na bandeja. Na primeira execução, o Gatekeeper vai bloquear → clique com o botão direito no binário no Finder → Abrir → Abrir de novo no diálogo (só precisa fazer uma vez). - Linux desktop:
./OperatorLM-linux-amd64— aparece o ícone na bandeja. - Linux headless:
OPERATORLM_NO_TRAY=1 ./OperatorLM-linux-amd64.
Acesse http://127.0.0.1:11434/admin/ → adicione um provider → cole uma API key → dispare um request de teste pela aba Try It.
Qualquer cliente compatível com OpenAI funciona — defina a base URL como http://127.0.0.1:11434/v1 e qualquer api_key não vazia (o OperatorLM injeta a real). Exemplos prontos pra copiar-colar em curl / Python / JavaScript estão em Use a partir de qualquer coisa que fale OpenAI mais abaixo.
Prefere buildar do código-fonte? Veja Build from source.
- Quick Start
- Por que esse projeto?
- Casos de uso
- Como funciona
- A killer feature: aliases multi-conta
- Failover que realmente faz failover
- Rode modelos localmente (chat, visão, embeddings, voz)
- API compatível com Anthropic
- ChatGPT Plus/Pro como backend (experimental)
- Mantendo atualizado
- Como o OperatorLM se compara
- Build from source
- Endpoints suportados
- Providers suportados
- Configuração & segredos
- Modelo de segurança
- Estrutura do repositório
- Status e contribuição
- 🔀 Aliasing multi-conta — empilhe 3 API keys da OpenAI, 2 contas do OpenRouter, e um Groq grátis como backup atrás de um único nome de modelo. O OperatorLM percorre a lista até uma funcionar.
- 🛡️ Failover de nível produção — circuit breaker por target (3 estados), retries com exponential backoff + jitter, RPM limiter com sliding window. Cooldowns diferentes para 429 / 5xx / erros de rede.
- 🔐 Zero segredos em disco — As API keys ficam no Windows Credential Manager / macOS Keychain / Linux Secret Service. O arquivo TOML só guarda referências, nunca as keys.
- 🖥️ Admin UI embutida e ao vivo — Gerencie providers, keys, aliases, ajustes de reliability, e veja o audit log em streaming — tudo a partir de
http://127.0.0.1:11434/admin/. Zero instalação: está embutida no binário comgo:embed. - 🧪 Audit log em JSONL — Cada request: modelo, attempt, URL upstream, status, duração. Headers
Authorizationredatados por padrão. Writer não-bloqueante. - 🤖 ChatGPT Plus/Pro como backend — Logue uma vez via OAuth (PKCE) e tenha a sua cota Plus/Pro plugada na mesma API compatível com OpenAI que as suas ferramentas já falam. (Experimental — leia o disclaimer abaixo.)
- 🖥️ Rode modelos localmente (llama.cpp embutido) — Aponte para uma pasta de arquivos
*.ggufe o OperatorLM sobe ollama-serversob demanda, sem daemon à parte. Um catálogo com downloads de um clique traz modelos de chat, visão e embeddings já curados. Um substituto do Ollama que também roteia para a nuvem. - 🧬 Embeddings locais —
/v1/embeddingsservido por um modelo GGUF local (Qwen3-Embedding, EmbeddingGemma) num sidecar dedicado — ótimo para RAG / busca semântica offline. Embeddings na nuvem (OpenAI, Azure, Gemini) funcionam pelo mesmo endpoint. - 🎙️ Voz local (STT + TTS) — Transcreva com Whisper (
/v1/audio/transcriptions), sintetize com Piper (/v1/audio/speech), ou faça chat-para-voz em tempo real por streaming (/v1/audio/speech/realtime). Catálogo de vozes com downloads de um clique. - 🟣 Compatível com Anthropic — Um endpoint nativo
/v1/messagesmais um provideranthropiccom tradução bidirecional OpenAI↔Anthropic, então ferramentas que falam a API do Claude (como o Claude Code) também podem apontar para o OperatorLM. - ⬆️ Auto-update OTA — "Check for updates" pela bandeja baixa o asset da release do seu OS, verifica o SHA-256, troca o binário no lugar e se re-executa.
- 🪶 Um binário pequeno, pouca RAM em idle — Go nativo. Sem
node_modules, sem Python, sem Docker. Sobe em milissegundos. - 🛰️ Modo headless —
OPERATORLM_NO_TRAY=1para rodar numa máquina Linux sem sessão de desktop. - 🪟 Sem flash de console no Windows — buildado com
-H=windowsgui. É uma tray app de verdade.
- Esticar tiers grátis / baratos — encadeie Groq → OpenRouter → OpenAI atrás de um único alias. O dia a dia bate no free tier e só transborda para o pago quando o grátis é rate-limited.
- Várias contas pessoais/de trabalho sob um único nome de modelo — mantenha as keys
openai_personal,openai_workeopenai_sideseparadas no OS keyring, exponha para o Cursor/Continue como um único modelogpt-4o, e deixe o router percorrer todas em caso de 429. - ChatGPT Plus/Pro em vez de créditos de API — logue uma vez via OAuth e roteie chamadas para modelos Codex / GPT-5.x pela sua cota Plus/Pro existente, sem billing de API (experimental — leia o disclaimer).
- Substituto drop-in para Ollama — o OperatorLM escuta em
127.0.0.1:11434, então qualquer coisa já apontada para o Ollama (Continue, Cline, Open WebUI, Zed, …) continua funcionando sem mudanças mas agora alcança OpenAI / OpenRouter / Gemini / Azure / Bedrock / etc. - Totalmente offline / air-gapped — rode modelos GGUF locais para chat, visão, embeddings e voz sem nenhum provider de nuvem configurado; o mesmo binário depois se espalha para a nuvem assim que você adiciona uma key.
- RAG / busca semântica local — sirva embeddings a partir de um modelo local (Qwen3-Embedding / EmbeddingGemma) por
/v1/embeddings, mantendo os seus documentos na máquina. - Tráfego LLM auditável numa máquina de dev ou estação compartilhada — todo request cai em JSONL redatado, as keys ficam no OS keyring (nunca em disco), e o admin UI é loopback-only com validação de host-header e uma API key local opcional.
- Gateway self-hosted em modo headless — rode numa VM Linux / NAS / home server com
OPERATORLM_NO_TRAY=1, alcance127.0.0.1:11434via WireGuard ou Tailscale do seu laptop, e centralize as suas keys e o audit log num único lugar.
- Recebe um request no formato OpenAI em
127.0.0.1:11434. - Resolve o campo
model→ ou por match de prefixo (openai/gpt-4o,groq/llama-3.3-70b-versatile), ou por um alias definido pelo usuário que se espalha entre várias contas/providers. - Injeta a API key correta do OS keyring a cada attempt.
- Tenta, faz retry e abre — exponential backoff entre tentativas; abre o circuit breaker do target em falhas repetidas; respeita
Retry-After. - Audita cada attempt num log JSONL redatado.
A maioria dos proxies locais roteia um nome de modelo para um único upstream. O OperatorLM deixa um nome de modelo se espalhar para N upstreams em ordem de prioridade, com rate limits por target e failover automático.
# ~/.operatorlm/config.toml
[[providers]]
name = "openai"
type = "openai"
base_url = "https://api.openai.com/v1"
prefix = "openai/"
api_key_ref = "operatorlm:openai_personal" # key padrão
[[providers.keys]]
name = "work"
api_key_ref = "operatorlm:openai_work"
[[providers.keys]]
name = "side-project"
api_key_ref = "operatorlm:openai_side"
[[aliases]]
name = "gpt-4o"
strategy = "order"
[[aliases.targets]]
provider = "openai"
key = "default" # key pessoal primeiro
upstream_model = "gpt-4o"
order = 1
rpm = 60
[[aliases.targets]]
provider = "openai"
key = "work" # cai para a key do trabalho em falha / 429
upstream_model = "gpt-4o"
order = 2
rpm = 60
[[aliases.targets]]
provider = "openai"
key = "side-project" # último recurso
upstream_model = "gpt-4o"
order = 3Agora o seu IDE só diz model: "gpt-4o" e o OperatorLM percorre as keys até uma funcionar. Bateu num 429 na key #1? O circuit breaker abre por 15 s e a key #2 assume na hora.
[[aliases]]
name = "fast-llama"
strategy = "order"
[[aliases.targets]]
provider = "groq" # grátis + mais rápido, tente primeiro
upstream_model = "llama-3.3-70b-versatile"
order = 1
rpm = 30 # respeita o RPM do free tier do Groq
[[aliases.targets]]
provider = "openrouter" # fallback pago se o Groq estiver rate-limited
upstream_model = "meta-llama/llama-3.3-70b-instruct"
order = 2Mande model: "fast-llama" — pega Groq quando disponível, OpenRouter quando não. Zero mudanças no client.
| Mecanismo | O que faz | Default |
|---|---|---|
| Retry + jitter | Retries por target com exponential backoff, full jitter, respeita Retry-After |
2 retries, 500 ms base, 10 s cap |
| Circuit breaker | Abre por target após N falhas consecutivas; closed → open → half-open | 3 falhas |
| Cooldown 429 | Cooldown quando o upstream rate-limitar | 15 s |
| Cooldown 5xx | Cooldown em erros de servidor upstream | 60 s |
| Cooldown de rede | Cooldown em falhas de DNS / TCP / timeout | 90 s |
| RPM limiter | Sliding window de 60 segundos por target — skip em vez de bloquear | configurável por target |
| Timeout por attempt | Cap duro por chamada upstream | 60 s (180 s total) |
| Stream idle timeout | Aborta um stream SSE morto | 30 s |
Tudo isso é ajustável ao vivo pela aba Reliability do admin UI — sem restart.
O OperatorLM tem um motor llama.cpp embutido — sem daemon do Ollama à parte. Aponte para uma pasta de arquivos *.gguf (na aba Local models do admin UI) e ele expõe cada um como local/<model>, subindo o llama-server sob demanda e trocando modelos automaticamente. Tudo roda em 127.0.0.1, offline.
| Capacidade | Endpoint | Motor | Notas |
|---|---|---|---|
| Chat local | /v1/chat/completions |
llama.cpp | Qualquer GGUF; carga sob demanda, swap automático, GPU (-ngl) |
| Visão local | /v1/chat/completions |
llama.cpp | Modelos multimodais com projector mmproj (entradas de imagem) |
| Embeddings locais | /v1/embeddings |
llama.cpp | Sidecar dedicado; coexiste com o modelo de chat |
| Speech-to-text | /v1/audio/transcriptions, /v1/audio/translations |
whisper.cpp | Transcrição/tradução com Whisper local |
| Text-to-speech | /v1/audio/speech |
Piper | Vozes locais; sample rate por voz; streaming PCM |
| Chat→voz em realtime | /v1/audio/speech/realtime |
llama.cpp + Piper | SSE: os tokens do modelo são sintetizados em áudio conforme chegam |
A aba Local models traz um catálogo curado — você escolhe um modelo e o OperatorLM baixa (com o projector de visão ou config de voz quando aplicável) para a sua pasta de modelos com defaults sensatos:
- Chat / visão / agentic: Qwen2.5-VL 3B, Gemma 3 4B, Qwen2.5 3B, Llama 3.2 3B, Phi-4 Mini, SmallThinker 3B, Gemma 4 E2B.
- Embeddings: Qwen3-Embedding 0.6B (o melhor em multilíngue para o seu tamanho, 100+ idiomas) e EmbeddingGemma 300M (o modelo leve on-device do Google). Ambos rodam em CPU por padrão para não competir com o modelo de chat por VRAM.
- Voz: Whisper Base (STT) e vozes do Piper (TTS).
Note
A inferência local precisa do binário llama-server (llama.cpp). O admin UI tem um download de um clique, ou defina local_models.llama_server_path para o seu próprio build. Whisper e Piper têm os próprios botões de download.
Como escuta na porta do Ollama e fala a API da OpenAI, qualquer ferramenta local-first (Continue, Cline, Zed, Open WebUI…) alcança os seus modelos locais sem mudanças — enquanto o mesmo proxy pode fazer failover para a nuvem.
O OperatorLM fala a API Messages da Anthropic nativamente em POST /v1/messages, e o provider anthropic traduz bidirecionalmente entre os esquemas da OpenAI e da Anthropic. Duas consequências:
- Ferramentas feitas para a API do Claude (incluindo o Claude Code) podem apontar a base URL para o OperatorLM e rotear por toda a sua maquinaria de failover, aliasing e auditoria.
- Você pode misturar ecossistemas livremente: chame um modelo da Anthropic por
/v1/chat/completions, ou um modelo da OpenAI/local por/v1/messages— o OperatorLM converte conforme necessário.
⚠️ Leia esse disclaimer antes de habilitar o provider chatgpt-codex
[!WARNING] O provider
chatgpt-codexnão é oficial nem endossado pela OpenAI. Ele reusa o client ID público de OAuth do Codex CLI oficial da OpenAI (app_EMoamEEZ73f0CkXaXp7hrann).
- A OpenAI pode rotacionar ou revogar esse ID a qualquer momento e quebrar esse provider.
- O uso pode violar os Termos de Serviço da OpenAI.
- Só
/v1/responsesé suportado (sem chat/completions, sem imagens).- Use por sua conta e risco. Para um caminho suportado, use o provider
openaicom a sua própria API key.
Se você aceita o risco: abra o admin UI, adicione um provider chatgpt-codex, clique em Login with ChatGPT — um navegador abre, você loga, e os tokens são guardados no seu OS keyring com refresh automático. A partir daí, modelos Codex / GPT-5.x ficam acessíveis pelo mesmo endpoint /v1/responses de qualquer outro provider.
O OperatorLM se auto-atualiza pelo GitHub Releases. Escolha Check for updates no menu da bandeja (ou POST /admin/update/check): ele busca a última release, baixa o asset do seu OS/arch mais o checksums.txt, verifica o SHA-256, troca o binário em execução no lugar e se re-executa na nova versão. Builds de dev (sem versão embutida) são pulados.
| Feature | OperatorLM | LiteLLM proxy | OmniRoute |
|---|---|---|---|
| Binário único, sem runtime | ✅ | ❌ (Python) | ❌ |
| Multi-conta / rotação de keys por provider | ✅ | ✅ | ✅ |
| Circuit breaker + retry + RPM limiter | ✅ | parcial | parcial |
| Keys no OS keyring (sem plaintext) | ✅ | ❌ | ❌ |
| Admin UI embutida | ✅ | ✅ | ✅ |
| Tray app nativa | ✅ | ❌ | ❌ |
| Audit log (JSONL, redatado) | ✅ | ✅ | ✅ |
| Inferência local embutida (GGUF/llama.cpp) | ✅ | ❌ | ❌ |
| Embeddings + voz locais (STT/TTS) | ✅ | ❌ | ❌ |
Compatibilidade Anthropic /v1/messages |
✅ | ✅ | parcial |
| ChatGPT Plus/Pro como backend | ✅ (exp.) | ❌ | ❌ |
Escolha o OperatorLM se quer um proxy desktop-first, de um único binário, que faz failover e routing multi-conta como um serviço de produção — sem rodar um serviço Python nem mandar as suas keys pela cloud de outra pessoa.
# Windows
.\build.ps1# Linux / macOS
./build.shNote
CGO é necessário (para system tray + OS keyring).
Linux: instale gcc libgtk-3-dev libayatana-appindicator3-dev (Debian/Ubuntu) ou os equivalentes de Fedora/RHEL listados em build.sh.
# Windows
.\OperatorLM.exe
# Linux / macOS (desktop)
./OperatorLM
# Linux (servidor headless, sem tray)
OPERATORLM_NO_TRAY=1 ./OperatorLMAparece um ícone na bandeja. O admin UI fica em http://127.0.0.1:11434/admin/.
- Abra o admin UI.
- Providers → adicione um provider, escolha o tipo (
openai,openrouter,groq,gemini,azure-openai,anthropic,chatgpt-codex,custom, …). - Keys → cole a sua API key. Ela é gravada no OS keyring; o TOML só guarda a referência.
- Aliases (opcional) → monte failover multi-conta / multi-provider.
- Local models (opcional) → aponte para uma pasta
*.gguf(ou baixe um modelo do catálogo com um clique) para rodar chat / embeddings / voz localmente. - Try It → dispare um request inline para verificar.
curl http://127.0.0.1:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"groq/llama-3.3-70b-versatile","messages":[{"role":"user","content":"hi"}]}'from openai import OpenAI
client = OpenAI(
base_url="http://127.0.0.1:11434/v1",
api_key="not-needed", # OperatorLM injeta a key real
)
print(client.chat.completions.create(
model="groq/llama-3.3-70b-versatile",
messages=[{"role": "user", "content": "hi"}],
).choices[0].message.content)import OpenAI from "openai";
const openai = new OpenAI({
baseURL: "http://127.0.0.1:11434/v1",
apiKey: "not-needed",
});
const chat = await openai.chat.completions.create({
model: "groq/llama-3.3-70b-versatile",
messages: [{ role: "user", content: "hi" }],
});
console.log(chat.choices[0].message.content);Aponte Cursor / Continue / qualquer cliente compatível com OpenAI para http://127.0.0.1:11434/v1 e pronto.
| Endpoint | Status |
|---|---|
POST /v1/chat/completions |
✅ Completo, com streaming |
POST /v1/messages |
✅ API Messages da Anthropic (OpenAI↔Anthropic) |
POST /v1/responses |
✅ (usado pelo chatgpt-codex) |
POST /v1/embeddings |
✅ Nuvem (OpenAI/Azure/Gemini) + GGUF local |
POST /v1/images/generations |
✅ (sem streaming) |
POST /v1/audio/transcriptions |
✅ Whisper (local) + STT de providers |
POST /v1/audio/translations |
✅ Whisper (local) |
POST /v1/audio/speech |
✅ Piper (local) + TTS de providers |
POST /v1/audio/speech/realtime |
✅ Chat→voz por streaming (SSE) |
GET /v1/models |
✅ Agregado entre os providers configurados |
GET /health |
✅ Checagem de liveness pública |
Nuvem / API: openai · openrouter · groq · gemini · azure-openai · anthropic · mistral · nvidia-nim · bedrock · opencode-zen · custom (qualquer upstream compatível com OpenAI).
Local (sem nuvem): motor llama.cpp embutido (chat + visão + embeddings), whisper.cpp (STT), piper (TTS).
Experimental: chatgpt-codex (ChatGPT Plus/Pro via OAuth) · antigravity (Gemini via uma sessão local do Antigravity).
- Config:
~/.operatorlm/config.toml - Logs:
~/.operatorlm/operatorlm.log - Audit Log:
~/.operatorlm/audit.log(JSONL, redatado)
| OS | Backend | Onde inspecionar |
|---|---|---|
| Windows | Credential Manager | Painel de Controle → Gerenciador de Credenciais |
| macOS | Keychain | App Acesso às Chaves (Keychain Access) |
| Linux | Secret Service (D-Bus) | seahorse (GNOME) ou kwalletmanager (KDE) |
O TOML referencia as keys por nome (operatorlm:openai_work) — nunca o segredo em si.
Note
Linux headless: requer um daemon de Secret Service rodando (ex.: gnome-keyring-daemon --components=secrets) e uma sessão D-Bus válida.
- Só loopback — escuta em
127.0.0.1por padrão. - Validação de host header na admin API (defesa contra DNS rebinding).
- Header custom obrigatório em endpoints admin mutantes (
X-OperatorLM-Admin). - Sem CORS por padrão.
- Auth local opcional — ative uma API key local pelo admin UI para restringir acesso em máquinas compartilhadas.
- Redação no audit —
Authorizatione outros headers sensíveis são sempre redatados antes de irem para o audit log.
internal/
config/ # TOML + integração com OS keyring
providers/ # providers de nuvem (openai · anthropic · gemini · azure · …) +
# motor local embutido (llama.cpp · whisper · piper) + catálogo/downloader
router/ # alias resolver · retry · circuit breaker · rate limiter
server/ # handlers HTTP + admin UI embutido (web/)
audit/ # audit logger JSONL não-bloqueante
update/ # auto-update OTA pelo GitHub Releases (verificado por SHA-256)
tray/ # system tray cross-platform
main.go # entrypointA codebase é pequena o bastante para ser auditada numa tarde. Esse é o ponto.
Projeto pessoal, liberado como está — mas ativamente usado e mantido.
Se o OperatorLM te economiza tempo ou facilita o seu setup, uma ⭐ no GitHub é o "obrigado" mais gentil e ajuda outros devs a encontrarem o projeto.
Bug reports, pull requests e integrações de providers são muito bem-vindos.
Licença: MIT


