From 3df24e27ae349beab5a80653eaae19239172b22a Mon Sep 17 00:00:00 2001 From: future Date: Sun, 15 Mar 2026 16:24:25 +0300 Subject: [PATCH] Add Ollama local model provider --- .env.example | 5 +++++ README.md | 5 ++++- app/core/ai.py | 25 ++++++++++++++++++++----- app/core/config.py | 7 +++++++ 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index 8c3dae5..ea382c2 100644 --- a/.env.example +++ b/.env.example @@ -27,6 +27,11 @@ ZAI_API_URL=https://api.z.ai/api/paas/v4/chat/completions ANTHROPIC_MODEL=claude-sonnet-4-20250514 ANTHROPIC_API_URL=https://api.anthropic.com/v1/messages ANTHROPIC_API_VERSION=2023-06-01 + +# Ollama (локально; без API key) +# AI_PROVIDER=ollama +OLLAMA_MODEL=llama3.1:8b +OLLAMA_API_URL=http://localhost:11434/v1/chat/completions DEEPGRAM_API_KEY=your_deepgram_api_key_here PORCUPINE_ACCESS_KEY=your_porcupine_access_key_here PORCUPINE_SENSITIVITY=0.8 diff --git a/README.md b/README.md index aef25a6..5136edd 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ flowchart TD - Системная роль ассистента и `ROLE_JSON` сохраняются для всех поддерживаемых AI-провайдеров. - Для AI используется строго один активный API key. Если в `.env` оставить несколько ключей, ассистент покажет ошибку конфигурации вместо случайного выбора. - Поддержка провайдеров сделана внутри одного модуля, но с разным форматом запросов для OpenAI-compatible API и Anthropic. +- Локальные модели через Ollama поддерживаются без API key (через OpenAI-compatible endpoint). ## Быстрый старт @@ -129,7 +130,7 @@ python run.py | Переменная | Обязательно | По умолчанию | Назначение | |---|---|---|---| -| `AI_PROVIDER` | Нет | `openrouter` | Опциональный провайдер AI (`openrouter`, `openai`, `gemini`, `zai`, `anthropic`; также понимает `claude`) | +| `AI_PROVIDER` | Нет | `openrouter` | Опциональный провайдер AI (`openrouter`, `openai`, `gemini`, `zai`, `anthropic`, `ollama`; также понимает `claude`) | | `OPENROUTER_API_KEY` | Да* | - | Ключ OpenRouter API (*если выбран OpenRouter и только этот AI ключ активен) | | `OPENROUTER_MODEL` | Нет | `openai/gpt-4o-mini` | Модель OpenRouter | | `OPENROUTER_API_URL` | Нет | `https://openrouter.ai/api/v1/chat/completions` | Endpoint OpenRouter Chat Completions | @@ -146,6 +147,8 @@ python run.py | `ANTHROPIC_MODEL` | Нет | `claude-sonnet-4-20250514` | Модель Claude | | `ANTHROPIC_API_URL` | Нет | `https://api.anthropic.com/v1/messages` | Endpoint Anthropic Messages API | | `ANTHROPIC_API_VERSION` | Нет | `2023-06-01` | Версия Anthropic API | +| `OLLAMA_MODEL` | Нет | `llama3.1:8b` | Модель Ollama (локально) | +| `OLLAMA_API_URL` | Нет | `http://localhost:11434/v1/chat/completions` | OpenAI-compatible endpoint Ollama | | `DEEPGRAM_API_KEY` | Да | - | Ключ Deepgram STT | | `PORCUPINE_ACCESS_KEY` | Да | - | Ключ PicoVoice Porcupine | | `PORCUPINE_SENSITIVITY` | Нет | `0.8` | Чувствительность wake word | diff --git a/app/core/ai.py b/app/core/ai.py index ee4aa4e..acbf2c9 100644 --- a/app/core/ai.py +++ b/app/core/ai.py @@ -15,6 +15,8 @@ from .config import ( GEMINI_API_KEY, GEMINI_API_URL, GEMINI_MODEL, + OLLAMA_API_URL, + OLLAMA_MODEL, OPENAI_API_KEY, OPENAI_API_URL, OPENAI_MODEL, @@ -81,6 +83,8 @@ _PROVIDER_ALIASES = { "claude_anthropic": "anthropic", "gemini": "gemini", "google": "gemini", + "olama": "ollama", + "ollama": "ollama", "openai": "openai", "openrouter": "openrouter", "z.ai": "zai", @@ -145,6 +149,18 @@ _PROVIDER_SETTINGS = { "key_var": "ANTHROPIC_API_KEY", "model_var": "ANTHROPIC_MODEL", }, + "ollama": { + "provider": "ollama", + "protocol": "openai_compatible", + # Ollama обычно локальный и не требует API key. + "api_key": None, + "requires_api_key": False, + "model": OLLAMA_MODEL, + "api_url": OLLAMA_API_URL, + "name": "Ollama", + "key_var": "OLLAMA_API_KEY", + "model_var": "OLLAMA_MODEL", + }, } @@ -221,7 +237,7 @@ def _content_to_text(content) -> str: def _get_provider_config_error(cfg) -> Optional[str]: if not cfg: return "Не настроен AI-провайдер. Проверьте файл .env." - if not cfg["api_key"]: + if cfg.get("requires_api_key", True) and not cfg.get("api_key"): return f"Не настроен {cfg['key_var']}. Проверьте файл .env." if not cfg["model"]: return f"Не настроен {cfg['model_var']}. Проверьте файл .env." @@ -236,10 +252,9 @@ def _build_headers(cfg): "Content-Type": "application/json", } - headers = { - "Authorization": f"Bearer {cfg['api_key']}", - "Content-Type": "application/json", - } + headers = {"Content-Type": "application/json"} + if cfg.get("api_key"): + headers["Authorization"] = f"Bearer {cfg['api_key']}" headers.update(cfg.get("extra_headers") or {}) return headers diff --git a/app/core/config.py b/app/core/config.py index 90ffeb4..7c96c6c 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -59,6 +59,13 @@ ANTHROPIC_API_URL = os.getenv( ) ANTHROPIC_API_VERSION = os.getenv("ANTHROPIC_API_VERSION", "2023-06-01") +# Ollama (локальные модели; OpenAI-compatible endpoint) +# Обычно Ollama слушает http://localhost:11434 и предоставляет /v1/chat/completions. +OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "llama3.1:8b") +OLLAMA_API_URL = os.getenv( + "OLLAMA_API_URL", "http://localhost:11434/v1/chat/completions" +) + # --- Настройки распознавания речи (Deepgram) --- # Ключ для облачного STT (Speech-to-Text) DEEPGRAM_API_KEY = os.getenv("DEEPGRAM_API_KEY")