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
This commit is contained in:
parent
98bbc96bf5
commit
4fe15324c8
11 changed files with 973 additions and 125 deletions
35
AGENTS.md
35
AGENTS.md
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Контекст
|
||||
|
||||
`ollama-proxy` — Go-прокси для Ollama Cloud (`https://ollama.com`). Балансирует запросы по нескольким API-ключам с round-robin и failover на 429/5xx. Слушает `127.0.0.1:11435`, экспонирует нативный Ollama API (`/api/*`) и OpenAI-совместимый (`/v1/*`) passthrough. Деплой — systemd unit `ollama-proxy.service`. Используется opencode через кастомный провайдер `ocp`.
|
||||
`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`.
|
||||
|
||||
## Стек
|
||||
|
||||
|
|
@ -40,43 +40,50 @@ systemd/ollama-proxy.service — systemd unit
|
|||
|
||||
## Типичные задачи
|
||||
|
||||
- **Добавить аккаунт:** `ollama-proxy accounts add --name <alias>` (промпт) или `... add <key> --name <alias>`
|
||||
- **Добавить аккаунт:** `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, path, status, latency_ms, stream)
|
||||
- **Посмотреть логи:** `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'` (~35 моделей)
|
||||
- **Проверить список моделей:** `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/opencode.json`
|
||||
- **Обновить список моделей в 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` видит только ocp.
|
||||
**Два отдельных конфига:** `opencode` видит только marg/fireworks, `openlama` видит только `oc` (объединённый пул Ollama Cloud + OpenCode Go).
|
||||
|
||||
- `~/.config/opencode/opencode.json` — глобальный конфиг с `marg` + `fireworks` (без `ocp`)
|
||||
- `~/.config/opencode/openlama.json` — отдельный конфиг с `ocp` + `"enabled_providers": ["ocp"]`. Содержит те же instructions/mcp/plugins/agent что глобальный, но только провайдер `ocp`.
|
||||
- `~/.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": ["ocp"],
|
||||
"enabled_providers": ["oc"],
|
||||
"provider": {
|
||||
"ocp": {
|
||||
"oc": {
|
||||
"npm": "@ai-sdk/openai-compatible",
|
||||
"name": "Ollama Cloud (pool)",
|
||||
"name": "Ollama + OpenCode Go (pool)",
|
||||
"options": {
|
||||
"baseURL": "http://127.0.0.1:11435/v1",
|
||||
"apiKey": "proxy"
|
||||
},
|
||||
"models": {
|
||||
"gpt-oss:20b": { "name": "gpt-oss:20b" },
|
||||
"...": { "name": "..." }
|
||||
"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: ["ocp"]` перекрывает глобальный — `marg`/`fireworks` скрыты.
|
||||
`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.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue