Skip to content

Подключение API Gemini: Пусть Google SDK напрямую подключается к локальному шлюзу

Чему вы научитесь

  • Использовать нативные конечные точки Gemini, предоставляемые Antigravity Tools (/v1beta/models/*) для подключения вашего клиента
  • Использовать пути в стиле Google :generateContent / :streamGenerateContent для вызова локального шлюза
  • При включении аутентификации Proxy понимать, почему x-goog-api-key можно напрямую использовать

Текущие проблемы

Вы могли уже запустить локальный прокси, но как дошли до Gemini, начали застревать:

  • Google SDK по умолчанию вызывает generativelanguage.googleapis.com, как изменить на ваш http://127.0.0.1:<port>?
  • Путь Gemini содержит двоеточие (models/<model>:generateContent), многие клиенты сразу склеивают и получают 404
  • Вы включили аутентификацию прокси, но Google-клиент не отправляет x-api-key, в итоге всегда 401

Когда использовать этот метод

  • Вы хотите использовать «нативный протокол Gemini», а не слой совместимости OpenAI/Anthropic
  • У вас уже есть клиент в стиле Google/сторонний Gemini, вы хотите с минимальной стоимостью мигрировать на локальный шлюз

🎒 Подготовка

Предварительные условия

  • Вы уже добавили хотя бы 1 аккаунт в App (иначе бэкенд не может получить токен доступа вышестоящего уровня)
  • Вы уже запустили службу локального прокси и знаете порт прослушивания (по умолчанию будет использовать 8045)

Основная идея

Antigravity Tools экспонирует нативные пути Gemini на локальном сервере Axum:

  • Список: GET /v1beta/models
  • Вызов: POST /v1beta/models/<model>:generateContent
  • Поток: POST /v1beta/models/<model>:streamGenerateContent

Бэкенд обернёт ваш нативный запрос тела Gemini структурой v1internal (вставит project, requestId, requestType и т.д.), а затем пересылает на конечную точку вышестоящего уровня v1internal (и приносит токен доступа аккаунта). (Исходный код: src-tauri/src/proxy/mappers/gemini/wrapper.rs, src-tauri/src/proxy/upstream/client.rs)

Почему в примерах учебника base URL рекомендует 127.0.0.1?

В примерах быстрой интеграции App жёстко рекомендуется использовать 127.0.0.1, причина — «избежать проблем с задержкой разрешения IPv6 в некоторых средах». (Исходный код: src/pages/ApiProxy.tsx)

Пошаговое руководство

Шаг 1: Подтвердите, что шлюз онлайн (/healthz)

Зачем Сначала подтвердите, что сервис онлайн, затем устраняйте проблемы с протоколом/аутентификацией, это сэкономит много времени.

bash
curl -s "http://127.0.0.1:8045/healthz"
powershell
Invoke-RestMethod "http://127.0.0.1:8045/healthz"

Что вы должны увидеть: Возвращается JSON, содержащий {"status":"ok"} (Исходный код: src-tauri/src/proxy/server.rs).

Шаг 2: Перечислите модели Gemini (/v1beta/models)

Зачем Вам нужно сначала подтвердить «какой идентификатор модели экспонируется», последующие <model> ориентируются на это.

bash
curl -s "http://127.0.0.1:8045/v1beta/models" | head

Что вы должны увидеть: В ответе есть массив models, name каждого элемента похож на models/<id> (Исходный код: src-tauri/src/proxy/handlers/gemini.rs).

Важно

Какой поле использовать для идентификатора модели?

  • ✅ Используйте поле displayName (например, gemini-2.0-flash)
  • ✅ Или уберите префикс models/ из поля name
  • ❌ Не копируйте напрямую полное значение поля name (это приведёт к ошибке пути)

Если вы скопируете поле name (например, models/gemini-2.0-flash) как идентификатор модели, путь запроса станет /v1beta/models/models/gemini-2.0-flash:generateContent, это неправильно. (Исходный код: src-tauri/src/proxy/common/model_mapping.rs)

Важно

Текущий /v1beta/models — это возврат, «который маскирует локальный динамический список моделей как список Gemini», а не реальная загрузка с вышестоящего уровня. (Исходный код: src-tauri/src/proxy/handlers/gemini.rs)

Шаг 3: Вызов generateContent (путь с двоеточием)

Зачем Ключ нативного REST API Gemini — это «action с двоеточием» типа :generateContent. Бэкенд проанализирует model:method в том же маршруте. (Исходный код: src-tauri/src/proxy/handlers/gemini.rs)

bash
curl -s \
  -H "Content-Type: application/json" \
  -X POST "http://127.0.0.1:8045/v1beta/models/<modelId>:generateContent" \
  -d '{
    "contents": [
      {"role": "user", "parts": [{"text": "Hello"}]}
    ]
  }'

Что вы должны увидеть: В JSON ответа есть candidates (или снаружи есть response.candidates, прокси распакует).

Шаг 4: Вызов streamGenerateContent (SSE)

Зачем Поток более стабилен для «длинного вывода/большой модели»; прокси пересылает вышестоящий SSE вашему клиенту и устанавливает Content-Type: text/event-stream. (Исходный код: src-tauri/src/proxy/handlers/gemini.rs)

bash
curl -N \
  -H "Content-Type: application/json" \
  -X POST "http://127.0.0.1:8045/v1beta/models/<modelId>:streamGenerateContent" \
  -d '{
    "contents": [
      {"role": "user", "parts": [{"text": "Tell me a short story"}]}
    ]
  }'

Что вы должны увидеть: Терминал постоянно выводит строки SSE в форме data: {...}, в нормальных условиях в конце появится data: [DONE] (указывает на конец потока).

Внимание

data: [DONE] — это стандартный знак окончания SSE, но не обязательно появится:

  • Если вышестоящий уровень нормально заканчивается и отправляет [DONE], прокси пересылает его
  • Если вышестоящий уровень аномально разрывается, тайм-аут или отправляет другие сигналы окончания, прокси не будет компенсировать [DONE]

Код клиента должен обрабатывать по стандарту SSE: при встрече data: [DONE] или разрыве соединения считать поток оконченным. (Исходный код: src-tauri/src/proxy/handlers/gemini.rs)

Шаг 5: Прямое подключение Google Python SDK к локальному шлюзу

Зачем Это путь примера «быстрая интеграция», предоставленный в UI проекта: используйте пакет Google Generative AI Python, чтобы указать api_endpoint на ваш адрес локального прокси. (Исходный код: src/pages/ApiProxy.tsx)

python
#Нужно установить: pip install google-generativeai
import google.generativeai as genai

genai.configure(
    api_key="YOUR_PROXY_API_KEY",
    transport='rest',
    client_options={'api_endpoint': 'http://127.0.0.1:8045'}
)

model = genai.GenerativeModel('<modelId>')
response = model.generate_content("Hello")
print(response.text)

Что вы должны увидеть: Программа выводит фрагмент ответа модели.

Контрольные точки ✅

  • /healthz может вернуть {"status":"ok"}
  • /v1beta/models может перечислить модели (минимум 1)
  • :generateContent может вернуть candidates
  • :streamGenerateContent возвращает Content-Type: text/event-stream и может постоянно выдавать поток

Частые ошибки

  • 401 всегда не проходит: Если вы включили аутентификацию, но proxy.api_key пуст, бэкенд напрямую отклонит запрос. (Исходный код: src-tauri/src/proxy/middleware/auth.rs)
  • Какой key нести в заголовке: Прокси одновременно распознаёт Authorization, x-api-key, x-goog-api-key. Поэтому «клиент в стиле Google только отправляет x-goog-api-key» тоже пройдёт. (Исходный код: src-tauri/src/proxy/middleware/auth.rs)
  • countTokens всегда 0: Текущий POST /v1beta/models/<model>/countTokens возвращает фиксированный {"totalTokens":0}, это реализация-заглушка. (Исходный код: src-tauri/src/proxy/handlers/gemini.rs)

Итог урока

  • Вы подключаетесь к /v1beta/models/*, а не /v1/*
  • Ключевая запись пути — models/<modelId>:generateContent / :streamGenerateContent
  • При включении аутентификации, x-goog-api-key — это заголовок запроса, который прокси явно поддерживает

Предпросмотр следующего урока

В следующем уроке мы изучим Генерация изображений Imagen 3: Автоматическое сопоставление параметров size/quality OpenAI Images.


Приложение: Справочник по исходному коду

Нажмите, чтобы раскрыть расположение исходного кода

Обновлено: 2026-01-23

ФункцияПуть к файлуСтроки
Регистрация маршрута Gemini (/v1beta/models/*)src-tauri/src/proxy/server.rs170-181
Разбор идентификатора модели и маршрутизация (почему префикс models/ вызывает ошибку маршрута)src-tauri/src/proxy/common/model_mapping.rs58-77
Разбор model:method + основная логика generate/streamsrc-tauri/src/proxy/handlers/gemini.rs14-337
Логика потока SSE (пересылка [DONE], а не автоматическая компенсация)src-tauri/src/proxy/handlers/gemini.rs161-183
Структура возврата /v1beta/models (маскировка динамического списка моделей)src-tauri/src/proxy/handlers/gemini.rs39-71
---------
---------
Пример Python SDK Google (api_endpoint указывает на локальный шлюз)src/pages/ApiProxy.tsx692-734
Отпечаток сессии Gemini (липкость/кэш использует session_id)src-tauri/src/proxy/session_manager.rs121-158
Обёртка запроса v1internal Gemini (вставка project/requestId/requestType и т.д.)src-tauri/src/proxy/mappers/gemini/wrapper.rs5-160
Конечная точка вышестоящего уровня v1internal и fallbacksrc-tauri/src/proxy/upstream/client.rs8-182

Ключевые константы:

  • MAX_RETRY_ATTEMPTS = 3: максимальное количество ротаций для запросов Gemini (Исходный код: src-tauri/src/proxy/handlers/gemini.rs)