ollama-proxy/AGENTS.md
Atte149 4fe15324c8 feat: multi-provider support — Ollama Cloud + OpenCode Go
- Account.Provider field (ollama-cloud | opencode-go), backward compatible
- Model-based routing: common models served by combined pool, unique models
  routed to their provider only
- /go/v1/* path forces OpenCode Go provider (prefix stripped upstream)
- Merged /v1/models endpoint returns union of both catalogs (44 models)
- Failover: 429/402 → cooldown + failover; 5xx → retry without cooldown
- CLI: accounts add --provider flag, list shows provider column
- Body buffering: request body buffered (8 MiB cap) for failover replay
- opencode integration: unified provider 'oc' with all merged models
- 64 tests pass (unit + integration)
- Verified: glm-5 → ollama, mimo-v2.5 → go, gpt-oss:20b → ollama
2026-06-24 15:37:28 +03:00

8.5 KiB
Raw Permalink Blame History

AGENTS.md — ollama-proxy

Контекст

ollama-proxy — Go-прокси для Ollama Cloud (https://ollama.com) и OpenCode Go (https://opencode.ai/zen/go/v1). Балансирует запросы по нескольким API-ключам с round-robin и failover на 429/402/5xx. Модели общие для обоих провайдеров идут одним пулом, уникальные — маршрутизируются к нужному провайдеру. Слушает 127.0.0.1:11435, экспонирует OpenAI-совместимый (/v1/*) и нативный Ollama (/api/*) API. Деплой — systemd unit ollama-proxy.service. Используется opencode через кастомный провайдер oc.

Стек

  • Go 1.22 (используется log/slog, net/http, net/http/httputil)
  • Единственная внешняя зависимость: golang.org/x/term (prompt без echo в accounts add)
  • Go 1.18 из apt НЕ подходит — нужен ~/.local/go/bin/go (Go 1.22.10). Перед сборкой: export PATH="$HOME/.local/go/bin:$PATH"
  • Сборка: make build./ollama-proxy
  • Тесты: go test ./... (43 теста, включая integration-тесты с httptest)
  • Линт: go vet ./..., gofmt -l .

Структура

main.go                        — точка входа, делегирует в cli.Run
internal/config/
  accounts.go                  — Account, AccountsFile, JSON load/save (chmod 0600, atomic)
  server.go                    — ServerConfig (addr, base_url, cooldown, retries, log_level)
internal/log/logger.go         — slog JSON logger с configurable level
internal/proxy/
  balancer.go                  — round-robin + per-account cooldown (RWMutex)
  handler.go                   — HTTP handler: reverse-proxy, Authorization подмена, SSE flush
  retry.go                     — документация стриминг-инварианта
internal/cli/
  root.go                      — dispatch подкоманд (serve / accounts / version)
  accounts.go                  — add / list / remove / set-base-url
  serve.go                     — запуск HTTP-сервера с signal handling
systemd/ollama-proxy.service   — systemd unit

Конфигурация

  • Аккаунты: ~/.config/ollama-proxy/accounts.json (XDG_CONFIG_HOME, chmod 0600, atomic write)
  • Флаги serve: --addr, --base-url, --cooldown, --retries, --log-level
  • Env vars: OLLAMA_PROXY_ADDR, OLLAMA_PROXY_BASE_URL, OLLAMA_PROXY_COOLDOWN, OLLAMA_PROXY_RETRIES, OLLAMA_PROXY_LOG_LEVEL

Типичные задачи

  • Добавить аккаунт: ollama-proxy accounts add --name <alias> --provider ollama-cloud|opencode-go (промпт) или ... add <key> --name <alias> --provider <P>
  • Перезапустить после правки аккаунтов: sudo systemctl restart ollama-proxy (прокси читает accounts.json при старте; runtime-обновления не поддерживаются)
  • Посмотреть логи: journalctl -u ollama-proxy -f — JSON-структурированные (account, account_id, provider, path, status, latency_ms, stream)
  • Проверить, что прокси жив: curl http://127.0.0.1:11435/api/version → должен вернуть JSON от ollama.com
  • Проверить список моделей: curl http://127.0.0.1:11435/v1/models | jq '.data | length' (~44 модели, merged из Ollama Cloud + OpenCode Go)
  • Запустить opencode с пулом: openlama (обёртка в ~/.local/bin/openlama, выставляет NO_PROXY и запускает opencode)
  • Обновить список моделей в opencode-конфиге: curl -sS http://127.0.0.1:11435/v1/models | jq -r '.data[].id' → обновить models блок в ~/.config/opencode/openlama.json

Интеграция с opencode

Два отдельных конфига: opencode видит только marg/fireworks, openlama видит только oc (объединённый пул Ollama Cloud + OpenCode Go).

  • ~/.config/opencode/opencode.json — глобальный конфиг с marg + fireworks (без oc)
  • ~/.config/opencode/openlama.json — отдельный конфиг с oc + "enabled_providers": ["oc"]. Содержит те же instructions/mcp/plugins/agent что глобальный, но только провайдер oc.
  • ~/.local/bin/openlama — обёртка, выставляет OPENCODE_CONFIG=~/.config/opencode/openlama.json + NO_PROXY=127.0.0.1,localhost,::1 + exec opencode "$@"

openlama.json (ключевая часть):

{
  "enabled_providers": ["oc"],
  "provider": {
    "oc": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Ollama + OpenCode Go (pool)",
      "options": {
        "baseURL": "http://127.0.0.1:11435/v1",
        "apiKey": "proxy"
      },
      "models": {
        "glm-5": { "name": "glm-5" },
        "gpt-oss:20b": { "name": "gpt-oss:20b (ollama)" },
        "mimo-v2.5": { "name": "mimo-v2.5 (go)" }
      }
    }
  }
}

OPENCODE_CONFIG мерджится с глобальным конфигом, но enabled_providers: ["oc"] перекрывает глобальный — marg/fireworks скрыты.

Маршрутизация моделей: прокси парсит model из тела запроса и выбирает eligible провайдеров:

  • Общие модели (glm-5, kimi-k2.6, deepseek-v4-flash и др.) → пул всех аккаунтов обоих провайдеров
  • Уникальные Ollama (gpt-oss:20b, qwen3-coder, gemma, nemotron) → только ollama-cloud аккаунты
  • Уникальные Go (mimo-v2.5, qwen3.5-plus, qwen3.7-max) → только opencode-go аккаунты
  • Также: /go/v1/* path force-рует opencode-go провайдер

Модель не сделана дефолтной — выбирается через /models в TUI.

ВАЖНО: NO_PROXY. Если в окружении установлен HTTP_PROXY/HTTPS_PROXY (например http://127.0.0.1:2080), opencode (Bun runtime) направит запросы к прокси через HTTP-прокси → 502 Bad Gateway. Нужно явно исключить localhost:

# В ~/.config/environment.d/no-proxy-local.conf (systemd/desktop sessions):
NO_PROXY=127.0.0.1,localhost,::1
no_proxy=127.0.0.1,localhost,::1

Или запускать opencode с NO_PROXY=127.0.0.1,localhost opencode ....

Модели в конфиге: opencode НЕ умеет динамически резолвить модели через /v1/models для @ai-sdk/openai-compatiblemodels: {} даёт ProviderModelNotFoundError. Нужно явно перечислить модели. Обновить список: curl -sS http://127.0.0.1:11435/v1/models | jq -r '.data[].id' → вставить в models блок.

WARN

  • Никогда не запускай прокси на 0.0.0.0 без auth — он не имеет аутентификации, любой может использовать твои ключи
  • accounts.json содержит ключи в plaintext — файл chmod 0600, не коммить в git
  • После правки accounts.json нужен sudo systemctl restart ollama-proxy (прокси не перечитывает файл в рантайме)
  • Если локальная ollama запущена на 11434 — это НЕ конфликтует (прокси на 11435), но OLLAMA_HOST=127.0.0.1:11435 нужно явно указывать
  • Стриминг-инвариант: после первого flush-а 2xx ответа переключение аккаунта НЕ происходит. Mid-stream ошибки → truncated response клиенту. Это by design (нельзя дублировать вывод)
  • 5xx НЕ вызывает cooldown аккаунта (только 429). 5xx → failover на следующий аккаунт, но без penalty

План отката

  • sudo systemctl stop ollama-proxy && sudo systemctl disable ollama-proxy
  • sudo rm /usr/local/bin/ollama-proxy /etc/systemd/system/ollama-proxy.service && sudo systemctl daemon-reload
  • Убрать блок ocp из ~/.config/opencode/opencode.json