# 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 --provider ollama-cloud|opencode-go` (промпт) или `... add --name --provider

` - **Перезапустить после правки аккаунтов:** `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 (ключевая часть): ```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: ```sh # В ~/.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-compatible` — `models: {}` даёт `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`