API de Gemini: conectar el SDK de Google directamente al gateway local
Qué podrás hacer al completar este tutorial
- Conectar tus clientes usando los endpoints nativos de Gemini expuestos por Antigravity Tools (
/v1beta/models/*) - Llamar al gateway local con rutas de estilo Google
:generateContent/:streamGenerateContent - Entender por qué
x-goog-api-keyse puede usar directamente cuando la autenticación del proxy está habilitada
Tu problema actual
Es posible que ya hayas iniciado el proxy inverso local, pero te atasques cuando llega a Gemini:
- El SDK de Google llama por defecto a
generativelanguage.googleapis.com, ¿cómo cambiarlo ahttp://127.0.0.1:<port>? - Las rutas de Gemini incluyen dos puntos (
models/<model>:generateContent), muchos clientes obtienen 404 al concatenarlas - Habilitaste la autenticación del proxy, pero el cliente de Google no envía
x-api-key, por lo que siempre obtienes 401
Cuándo usar este enfoque
- Deseas usar el "protocolo nativo de Gemini" en lugar de la capa compatible con OpenAI/Anthropic
- Ya tienes clientes de estilo Google/de terceros de Gemini y quieres migrar al gateway local con el menor costo
🎒 Preparativos antes de comenzar
Requisitos previos
- Ya has agregado al menos 1 cuenta en la App (de lo contrario, el backend no obtendrá el token de acceso de upstream)
- Ya has iniciado el servicio de proxy inverso local y conoces el puerto de escucha (por defecto se usará
8045)
Idea central
Antigravity Tools expone rutas nativas de Gemini en el servidor Axum local:
- Lista:
GET /v1beta/models - Llamada:
POST /v1beta/models/<model>:generateContent - Streaming:
POST /v1beta/models/<model>:streamGenerateContent
El backend envolverá tu cuerpo de solicitud nativo de Gemini en una estructura v1internal (inyectando project, requestId, requestType, etc.), y luego lo reenviará al endpoint upstream de Google v1internal (junto con el token de acceso de la cuenta).(Código fuente: src-tauri/src/proxy/mappers/gemini/wrapper.rs, src-tauri/src/proxy/upstream/client.rs)
¿Por qué el ejemplo de integración rápida recomienda usar 127.0.0.1 como base URL?
El ejemplo de integración rápida de la App recomienda explícitamente 127.0.0.1 para "evitar problemas de latencia de resolución de IPv6 en algunos entornos".(Código fuente: src/pages/ApiProxy.tsx)
Sígueme paso a paso
Paso 1: Confirma que el gateway está en línea (/healthz)
Por qué Primero confirma que el servicio está en línea, ahorrarás mucho tiempo al solucionar problemas de protocolo/autenticación.
curl -s "http://127.0.0.1:8045/healthz"Invoke-RestMethod "http://127.0.0.1:8045/healthz"Deberías ver: devuelve JSON, contiene {"status":"ok"} (Código fuente: src-tauri/src/proxy/server.rs).
Paso 2: Lista los modelos de Gemini (/v1beta/models)
Por qué Necesitas confirmar primero cuál es el "ID de modelo expuesto externamente", todos los <model> posteriores se basarán en esto.
curl -s "http://127.0.0.1:8045/v1beta/models" | headDeberías ver: la respuesta tiene un array models, cada elemento name es similar a models/<id> (Código fuente: src-tauri/src/proxy/handlers/gemini.rs).
Importante
¿Qué campo del ID de modelo usar?
- ✅ Usa el campo
displayName(comogemini-2.0-flash) - ✅ O elimina el prefijo
models/del camponame - ❌ No copies el valor completo del campo
namedirectamente (causará error de ruta)
Si copias el campo name (como models/gemini-2.0-flash) como ID de modelo, la ruta de solicitud se convertirá en /v1beta/models/models/gemini-2.0-flash:generateContent, lo cual es incorrecto。(Código fuente: src-tauri/src/proxy/common/model_mapping.rs)
Importante
Actualmente /v1beta/models es un retorno que "disfraza la lista de modelos dinámica local como lista de modelos de Gemini", no se extrae de upstream en tiempo real。(Código fuente: src-tauri/src/proxy/handlers/gemini.rs)
Paso 3: Llama a generateContent (ruta con dos puntos)
Por qué La clave de la API REST nativa de Gemini es la "acción con dos puntos" como :generateContent. El backend analizará model:method en la misma ruta。(Código fuente: src-tauri/src/proxy/handlers/gemini.rs)
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"}]}
]
}'Deberías ver: la respuesta JSON contiene candidates (o la capa externa tiene response.candidates, el proxy lo desempaquetará).
Paso 4: Llama a streamGenerateContent (SSE)
Por qué El streaming es más estable para "salidas largas/modelos grandes"; el proxy reenviará el SSE de upstream a tu cliente y establecerá Content-Type: text/event-stream。(Código fuente: src-tauri/src/proxy/handlers/gemini.rs)
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"}]}
]
}'Deberías ver: el terminal sigue imprimiendo líneas en forma de data: {...}; en circunstancias normales, finalmente aparecerá data: [DONE] (indicando que el flujo ha terminado).
Nota
data: [DONE] es el marcador de finalización estándar de SSE, pero no necesariamente siempre aparece:
- Si upstream termina normalmente y envía
[DONE], el proxy lo reenviará - Si upstream se desconecta anormalmente, agota el tiempo o envía otra señal de finalización, el proxy no complementará
[DONE]
El código del cliente debe procesarse según el estándar SSE: encontrar data: [DONE] o que la conexión se desconecte debe considerarse como el final del flujo。(Código fuente: src-tauri/src/proxy/handlers/gemini.rs)
Paso 5: Usa el SDK de Google Python para conectar directamente al gateway local
Por qué Esta es la ruta de ejemplo de "integración rápida" proporcionada en la UI del proyecto: usa el paquete Google Generative AI Python para apuntar api_endpoint a tu dirección de proxy inverso local。(Código fuente: src/pages/ApiProxy.tsx)
# Necesitas instalar: 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)Deberías ver: el programa imprime un texto de respuesta del modelo.
Punto de control ✅
/healthzpuede devolver{"status":"ok"}/v1beta/modelspuede listar modelos (al menos 1):generateContentpuede devolvercandidates:streamGenerateContentdevuelveContent-Type: text/event-streamy puede continuar fluyendo
Advertencias de problemas comunes
- 401 que no se puede resolver: si habilitaste la autenticación, pero
proxy.api_keyestá vacío, el backend rechazará la solicitud directamente。(Código fuente:src-tauri/src/proxy/middleware/auth.rs) - ¿Qué key llevar en Header: el proxy reconocerá simultáneamente
Authorization,x-api-key,x-goog-api-key. Por lo tanto, "el cliente de estilo Google solo envíax-goog-api-key" también puede pasar。(Código fuente:src-tauri/src/proxy/middleware/auth.rs) - El resultado de countTokens siempre es 0: actualmente
POST /v1beta/models/<model>/countTokensdevuelve un valor fijo{"totalTokens":0}, que es una implementación de marcador de posición。(Código fuente:src-tauri/src/proxy/handlers/gemini.rs)
Resumen de este tutorial
- Lo que debes conectar es
/v1beta/models/*, no/v1/* - La forma clave de escribir rutas es
models/<modelId>:generateContent/:streamGenerateContent - Cuando la autenticación está habilitada,
x-goog-api-keyes un encabezado de solicitud admitido explícitamente por el proxy
Próximo tutorial
En el siguiente tutorial aprenderemos Imagen 3 generación de imágenes: mapeo automático de parámetros size/quality de Imágenes de OpenAI.
Apéndice: Referencia de código fuente
Haz clic para expandir y ver ubicación del código fuente
Última actualización: 2026-01-23
| Función | Ruta del archivo | Número de línea |
|---|---|---|
| Registro de rutas de Gemini (/v1beta/models/*) | src-tauri/src/proxy/server.rs | 170-181 |
Análisis de ID de modelo y enrutamiento (por qué el prefijo models/ causa error de enrutamiento) | src-tauri/src/proxy/common/model_mapping.rs | 58-77 |
Analizar model:method + lógica principal de generate/stream | src-tauri/src/proxy/handlers/gemini.rs | 14-337 |
Lógica de salida de SSE (reenviar [DONE] en lugar de complementar automáticamente) | src-tauri/src/proxy/handlers/gemini.rs | 161-183 |
Estructura de retorno de /v1beta/models (disfraz de lista de modelos dinámica) | src-tauri/src/proxy/handlers/gemini.rs | 39-71 |
Implementación de marcador de posición de countTokens (valor fijo 0) | src-tauri/src/proxy/handlers/gemini.rs | 73-79 |
| --- | --- | --- |
Ejemplo de SDK de Google Python (api_endpoint apunta al gateway local) | src/pages/ApiProxy.tsx | 692-734 |
| Huella de sesión de Gemini (session_id para adhesividad/caché) | src-tauri/src/proxy/session_manager.rs | 121-158 |
| Envoltura v1internal de solicitud de Gemini (inyecta project/requestId/requestType, etc.) | src-tauri/src/proxy/mappers/gemini/wrapper.rs | 5-160 |
| Endpoint v1internal upstream y fallback | src-tauri/src/proxy/upstream/client.rs | 8-182 |
Constantes clave:
MAX_RETRY_ATTEMPTS = 3: límite superior del número máximo de rotaciones para solicitudes de Gemini (Código fuente:src-tauri/src/proxy/handlers/gemini.rs)