Spaces:
Running
Running
Commit
·
cb01b8b
1
Parent(s):
db708af
hf test
Browse files- .env.example +13 -7
- LICENSE +1 -1
- README.md +142 -188
- requirements.txt +2 -2
- run.py +4 -1
- src/auth.py +17 -12
- src/config.py +1 -1
- src/main.py +6 -0
.env.example
CHANGED
@@ -1,9 +1,15 @@
|
|
1 |
-
#
|
2 |
-
|
3 |
-
GEMINI_PROJECT_ID=
|
4 |
|
5 |
-
#
|
6 |
-
|
7 |
|
8 |
-
#
|
9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Required: Authentication password for API access
|
2 |
+
GEMINI_AUTH_PASSWORD=123456
|
|
|
3 |
|
4 |
+
# Option 1: Credentials as JSON string (highest priority - overrides file-based credentials)
|
5 |
+
# GEMINI_CREDENTIALS={"client_id":"your-client-id","client_secret":"your-client-secret","token":"your-access-token","refresh_token":"your-refresh-token","scopes":["https://www.googleapis.com/auth/cloud-platform"],"token_uri":"https://oauth2.googleapis.com/token"}
|
6 |
|
7 |
+
# Option 2: Path to credentials file (only used if GEMINI_CREDENTIALS is not set)
|
8 |
+
GOOGLE_APPLICATION_CREDENTIALS=oauth_creds.json
|
9 |
+
|
10 |
+
# Optional: Google Cloud Project ID (if not in credentials)
|
11 |
+
# GOOGLE_CLOUD_PROJECT=your-project-id
|
12 |
+
|
13 |
+
# Server configuration (optional)
|
14 |
+
# HOST=0.0.0.0
|
15 |
+
# PORT=8888 # Default compatibility port (use 7860 for Hugging Face)
|
LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
MIT License
|
2 |
|
3 |
-
Copyright (c)
|
4 |
|
5 |
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
of this software and associated documentation files (the "Software"), to deal
|
|
|
1 |
MIT License
|
2 |
|
3 |
+
Copyright (c) 2024 Gemini CLI to API Proxy
|
4 |
|
5 |
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
of this software and associated documentation files (the "Software"), to deal
|
README.md
CHANGED
@@ -1,229 +1,183 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
-
|
22 |
-
2. Install the required dependencies:
|
23 |
-
```bash
|
24 |
-
pip install -r requirements.txt
|
25 |
-
```
|
26 |
|
27 |
-
|
|
|
|
|
28 |
|
29 |
-
###
|
|
|
|
|
|
|
|
|
30 |
|
31 |
-
|
32 |
-
|
33 |
-
```bash
|
34 |
-
# Edit the .env file
|
35 |
-
GEMINI_PROJECT_ID=your-project-id
|
36 |
-
|
37 |
-
# Optional: Change the default port
|
38 |
-
GEMINI_PORT=8888
|
39 |
-
```
|
40 |
|
41 |
-
|
42 |
-
```bash
|
43 |
-
python gemini_proxy.py
|
44 |
-
```
|
45 |
|
46 |
-
|
47 |
-
- On first run, the proxy will display an authentication URL
|
48 |
-
- Open the URL in your browser and sign in with your Google account
|
49 |
-
- Grant the necessary permissions
|
50 |
-
- The browser will show "Authentication successful!" when complete
|
51 |
-
- The proxy will automatically save your credentials for future use
|
52 |
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
|
58 |
-
|
59 |
|
60 |
-
After initial setup, simply run:
|
61 |
```bash
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
```
|
70 |
|
71 |
-
###
|
72 |
|
73 |
-
Configure your Gemini API client to use `http://localhost:8888` as the base URL. The proxy accepts standard Gemini API requests and handles the authentication automatically.
|
74 |
-
|
75 |
-
Example request:
|
76 |
```bash
|
77 |
-
|
78 |
-
|
79 |
-
-d '{
|
80 |
-
"contents": [{
|
81 |
-
"parts": [{"text": "Hello, how are you?"}]
|
82 |
-
}]
|
83 |
-
}'
|
84 |
-
```
|
85 |
-
|
86 |
-
**Note:** The proxy supports multiple authentication methods. The `key` query parameter is the most compatible with standard Gemini clients.
|
87 |
-
|
88 |
-
### Safety Settings
|
89 |
-
|
90 |
-
The proxy automatically sets default safety settings to `BLOCK_NONE` for all categories if no safety settings are specified in the request. This provides maximum flexibility for content generation. The default categories are:
|
91 |
|
92 |
-
|
93 |
-
-
|
94 |
-
|
95 |
-
- `HARM_CATEGORY_DANGEROUS_CONTENT`
|
96 |
-
|
97 |
-
You can override these defaults by including your own `safetySettings` in the request payload.
|
98 |
-
|
99 |
-
## Authentication
|
100 |
-
|
101 |
-
The proxy supports multiple authentication methods for maximum compatibility:
|
102 |
|
103 |
-
|
104 |
-
- **Configuration:** Set `GEMINI_AUTH_PASSWORD` in `.env` file to change the password
|
105 |
|
106 |
-
|
107 |
|
108 |
-
1.
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
|
115 |
-
|
116 |
-
```bash
|
117 |
-
curl -X POST http://localhost:8888/v1/models/gemini-pro:generateContent \
|
118 |
-
-H "Authorization: Bearer 123456" \
|
119 |
-
-H "Content-Type: application/json" \
|
120 |
-
-d '{"contents": [{"parts": [{"text": "Hello!"}]}]}'
|
121 |
-
```
|
122 |
|
123 |
-
|
124 |
-
```bash
|
125 |
-
curl -u "user:123456" -X POST http://localhost:8888/v1/models/gemini-pro:generateContent \
|
126 |
-
-H "Content-Type: application/json" \
|
127 |
-
-d '{"contents": [{"parts": [{"text": "Hello!"}]}]}'
|
128 |
-
```
|
129 |
|
130 |
-
**Python examples:**
|
131 |
```python
|
132 |
-
import
|
133 |
-
from requests.auth import HTTPBasicAuth
|
134 |
|
135 |
-
#
|
136 |
-
|
137 |
-
"http://localhost:8888/v1
|
138 |
-
|
139 |
)
|
140 |
|
141 |
-
#
|
142 |
-
response =
|
143 |
-
"
|
144 |
-
|
145 |
-
|
|
|
|
|
146 |
)
|
147 |
|
148 |
-
|
149 |
-
|
150 |
-
"http://localhost:8888/v1/models/gemini-pro:generateContent",
|
151 |
-
auth=HTTPBasicAuth("user", "123456"),
|
152 |
-
json={"contents": [{"parts": [{"text": "Hello!"}]}]}
|
153 |
-
)
|
154 |
```
|
155 |
|
156 |
-
##
|
157 |
|
158 |
-
|
159 |
-
|
160 |
-
- **Credential file:** `oauth_creds.json` (automatically created)
|
161 |
-
- **Configuration file:** `.env` (optional settings)
|
162 |
-
- **Scopes:** Cloud Platform, User Info (email/profile), OpenID
|
163 |
-
|
164 |
-
### Configuration File (.env)
|
165 |
|
166 |
-
|
|
|
|
|
|
|
167 |
|
168 |
-
|
169 |
-
|
170 |
-
|
|
|
|
|
|
|
|
|
|
|
171 |
|
172 |
-
|
173 |
-
|
|
|
|
|
|
|
174 |
|
175 |
-
|
176 |
-
GEMINI_AUTH_PASSWORD=your-secure-password
|
177 |
```
|
178 |
|
179 |
-
|
180 |
-
|
181 |
-
## File Structure
|
182 |
-
|
183 |
-
- `gemini_proxy.py` - Main proxy server
|
184 |
-
- `oauth_creds.json` - Cached OAuth credentials and project ID (auto-generated)
|
185 |
-
- `requirements.txt` - Python dependencies
|
186 |
-
- `.env` - Configuration file (optional, create as needed)
|
187 |
-
- `.gitignore` - Prevents credential and config files from being committed
|
188 |
-
|
189 |
-
## Troubleshooting
|
190 |
-
|
191 |
-
### Port Already in Use
|
192 |
-
If you see "error while attempting to bind on address", another instance is already running. Stop the existing process or use a different port.
|
193 |
-
|
194 |
-
### Authentication Issues (Google OAuth)
|
195 |
-
- Delete `oauth_creds.json` and restart to re-authenticate
|
196 |
-
- Ensure your Google account has access to Google Cloud and Gemini API
|
197 |
-
- Check that the required scopes are granted during authentication
|
198 |
-
|
199 |
-
### Authentication Issues (Proxy Access)
|
200 |
-
- If you get 401 Unauthorized errors, check your authentication method
|
201 |
-
- Default password is `123456` unless changed in `.env` file
|
202 |
-
- Try different authentication methods:
|
203 |
-
- Query parameter: `?key=123456`
|
204 |
-
- Bearer token: `Authorization: Bearer 123456`
|
205 |
-
- Basic auth: `Authorization: Basic base64(user:123456)`
|
206 |
-
- Most Gemini clients work best with the query parameter method (`?key=password`)
|
207 |
-
|
208 |
-
### Project ID Issues
|
209 |
-
- The proxy automatically detects your project ID on first run
|
210 |
-
- If detection fails, you can manually set `GEMINI_PROJECT_ID` in the `.env` file
|
211 |
-
- Check your Google Cloud project permissions if auto-detection fails
|
212 |
-
- Delete `oauth_creds.json` to force re-detection
|
213 |
-
- Project ID in `.env` file takes priority over cached and auto-detected project IDs
|
214 |
|
215 |
-
|
|
|
|
|
|
|
|
|
216 |
|
217 |
-
|
218 |
-
- The `.gitignore` file is configured to prevent accidental commits
|
219 |
-
- Credentials are stored locally and refreshed automatically when expired
|
220 |
-
- The proxy runs on localhost only for security
|
221 |
|
222 |
-
|
223 |
|
224 |
-
|
225 |
-
- **Input:** Standard Gemini API format
|
226 |
-
- **Output:** Standard Gemini API responses
|
227 |
-
- **Internal:** Google's Cloud Code Assist API format
|
228 |
|
229 |
-
|
|
|
1 |
+
---
|
2 |
+
title: Gemini CLI to API Proxy
|
3 |
+
emoji: 🤖
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: purple
|
6 |
+
sdk: docker
|
7 |
+
pinned: false
|
8 |
+
license: mit
|
9 |
+
app_port: 7860
|
10 |
+
---
|
11 |
+
|
12 |
+
# Gemini CLI to API Proxy (geminicli2api)
|
13 |
+
|
14 |
+
A FastAPI-based proxy server that converts the Gemini CLI tool into both OpenAI-compatible and native Gemini API endpoints. This allows you to leverage Google's free Gemini API quota through familiar OpenAI API interfaces or direct Gemini API calls.
|
15 |
+
|
16 |
+
## 🚀 Features
|
17 |
+
|
18 |
+
- **OpenAI-Compatible API**: Drop-in replacement for OpenAI's chat completions API
|
19 |
+
- **Native Gemini API**: Direct proxy to Google's Gemini API
|
20 |
+
- **Streaming Support**: Real-time streaming responses for both API formats
|
21 |
+
- **Multimodal Support**: Text and image inputs
|
22 |
+
- **Authentication**: Multiple auth methods (Bearer, Basic, API key)
|
23 |
+
- **Docker Ready**: Containerized for easy deployment
|
24 |
+
- **Hugging Face Spaces**: Ready for deployment on Hugging Face
|
25 |
+
|
26 |
+
## 🔧 Environment Variables
|
27 |
+
|
28 |
+
### Required
|
29 |
+
- `GEMINI_AUTH_PASSWORD`: Authentication password for API access
|
30 |
+
|
31 |
+
### Optional Credential Sources (choose one)
|
32 |
+
- `GEMINI_CREDENTIALS`: JSON string containing Google OAuth credentials
|
33 |
+
- `GOOGLE_APPLICATION_CREDENTIALS`: Path to Google OAuth credentials file
|
34 |
+
- `GOOGLE_CLOUD_PROJECT`: Google Cloud project ID
|
35 |
+
- `GEMINI_PROJECT_ID`: Alternative project ID variable
|
36 |
+
|
37 |
+
### Example Credentials JSON
|
38 |
+
```json
|
39 |
+
{
|
40 |
+
"client_id": "your-client-id",
|
41 |
+
"client_secret": "your-client-secret",
|
42 |
+
"token": "your-access-token",
|
43 |
+
"refresh_token": "your-refresh-token",
|
44 |
+
"scopes": ["https://www.googleapis.com/auth/cloud-platform"],
|
45 |
+
"token_uri": "https://oauth2.googleapis.com/token"
|
46 |
+
}
|
47 |
+
```
|
48 |
|
49 |
+
## 📡 API Endpoints
|
|
|
|
|
|
|
|
|
50 |
|
51 |
+
### OpenAI-Compatible Endpoints
|
52 |
+
- `POST /v1/chat/completions` - Chat completions (streaming & non-streaming)
|
53 |
+
- `GET /v1/models` - List available models
|
54 |
|
55 |
+
### Native Gemini Endpoints
|
56 |
+
- `GET /v1beta/models` - List Gemini models
|
57 |
+
- `POST /v1beta/models/{model}:generateContent` - Generate content
|
58 |
+
- `POST /v1beta/models/{model}:streamGenerateContent` - Stream content
|
59 |
+
- All other Gemini API endpoints are proxied through
|
60 |
|
61 |
+
### Utility Endpoints
|
62 |
+
- `GET /health` - Health check for container orchestration
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
|
64 |
+
## 🔐 Authentication
|
|
|
|
|
|
|
65 |
|
66 |
+
The API supports multiple authentication methods:
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
+
1. **Bearer Token**: `Authorization: Bearer YOUR_PASSWORD`
|
69 |
+
2. **Basic Auth**: `Authorization: Basic base64(username:YOUR_PASSWORD)`
|
70 |
+
3. **Query Parameter**: `?key=YOUR_PASSWORD`
|
71 |
+
4. **Google Header**: `x-goog-api-key: YOUR_PASSWORD`
|
72 |
|
73 |
+
## 🐳 Docker Usage
|
74 |
|
|
|
75 |
```bash
|
76 |
+
# Build the image
|
77 |
+
docker build -t geminicli2api .
|
78 |
+
|
79 |
+
# Run on default port 8888 (compatibility)
|
80 |
+
docker run -p 8888:8888 \
|
81 |
+
-e GEMINI_AUTH_PASSWORD=your_password \
|
82 |
+
-e GEMINI_CREDENTIALS='{"client_id":"...","token":"..."}' \
|
83 |
+
-e PORT=8888 \
|
84 |
+
geminicli2api
|
85 |
+
|
86 |
+
# Run on port 7860 (Hugging Face compatible)
|
87 |
+
docker run -p 7860:7860 \
|
88 |
+
-e GEMINI_AUTH_PASSWORD=your_password \
|
89 |
+
-e GEMINI_CREDENTIALS='{"client_id":"...","token":"..."}' \
|
90 |
+
-e PORT=7860 \
|
91 |
+
geminicli2api
|
92 |
```
|
93 |
|
94 |
+
### Docker Compose
|
95 |
|
|
|
|
|
|
|
96 |
```bash
|
97 |
+
# Default setup (port 8888)
|
98 |
+
docker-compose up -d
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
|
100 |
+
# Hugging Face setup (port 7860)
|
101 |
+
docker-compose --profile hf up -d geminicli2api-hf
|
102 |
+
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
|
104 |
+
## 🤗 Hugging Face Spaces
|
|
|
105 |
|
106 |
+
This project is configured for Hugging Face Spaces deployment:
|
107 |
|
108 |
+
1. Fork this repository
|
109 |
+
2. Create a new Space on Hugging Face
|
110 |
+
3. Connect your repository
|
111 |
+
4. Set the required environment variables in Space settings:
|
112 |
+
- `GEMINI_AUTH_PASSWORD`
|
113 |
+
- `GEMINI_CREDENTIALS` (or other credential source)
|
114 |
|
115 |
+
The Space will automatically build and deploy using the included Dockerfile.
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
|
117 |
+
## 📝 OpenAI API Example
|
|
|
|
|
|
|
|
|
|
|
118 |
|
|
|
119 |
```python
|
120 |
+
import openai
|
|
|
121 |
|
122 |
+
# Configure client to use your proxy
|
123 |
+
client = openai.OpenAI(
|
124 |
+
base_url="http://localhost:8888/v1", # or 7860 for HF
|
125 |
+
api_key="your_password" # Your GEMINI_AUTH_PASSWORD
|
126 |
)
|
127 |
|
128 |
+
# Use like normal OpenAI API
|
129 |
+
response = client.chat.completions.create(
|
130 |
+
model="gemini-2.0-flash-exp",
|
131 |
+
messages=[
|
132 |
+
{"role": "user", "content": "Hello, how are you?"}
|
133 |
+
],
|
134 |
+
stream=True
|
135 |
)
|
136 |
|
137 |
+
for chunk in response:
|
138 |
+
print(chunk.choices[0].delta.content, end="")
|
|
|
|
|
|
|
|
|
139 |
```
|
140 |
|
141 |
+
## 🔧 Native Gemini API Example
|
142 |
|
143 |
+
```python
|
144 |
+
import requests
|
|
|
|
|
|
|
|
|
|
|
145 |
|
146 |
+
headers = {
|
147 |
+
"Authorization": "Bearer your_password",
|
148 |
+
"Content-Type": "application/json"
|
149 |
+
}
|
150 |
|
151 |
+
data = {
|
152 |
+
"contents": [
|
153 |
+
{
|
154 |
+
"role": "user",
|
155 |
+
"parts": [{"text": "Hello, how are you?"}]
|
156 |
+
}
|
157 |
+
]
|
158 |
+
}
|
159 |
|
160 |
+
response = requests.post(
|
161 |
+
"http://localhost:8888/v1beta/models/gemini-2.0-flash-exp:generateContent", # or 7860 for HF
|
162 |
+
headers=headers,
|
163 |
+
json=data
|
164 |
+
)
|
165 |
|
166 |
+
print(response.json())
|
|
|
167 |
```
|
168 |
|
169 |
+
## 🎯 Supported Models
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
|
171 |
+
- `gemini-2.0-flash-exp`
|
172 |
+
- `gemini-1.5-flash`
|
173 |
+
- `gemini-1.5-flash-8b`
|
174 |
+
- `gemini-1.5-pro`
|
175 |
+
- `gemini-1.0-pro`
|
176 |
|
177 |
+
## 📄 License
|
|
|
|
|
|
|
178 |
|
179 |
+
MIT License - see LICENSE file for details.
|
180 |
|
181 |
+
## 🤝 Contributing
|
|
|
|
|
|
|
182 |
|
183 |
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
requirements.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
fastapi
|
2 |
-
uvicorn
|
3 |
requests
|
4 |
python-dotenv
|
5 |
google-auth-oauthlib
|
6 |
-
|
|
|
1 |
fastapi
|
2 |
+
uvicorn[standard]
|
3 |
requests
|
4 |
python-dotenv
|
5 |
google-auth-oauthlib
|
6 |
+
pydantic
|
run.py
CHANGED
@@ -1,5 +1,8 @@
|
|
|
|
1 |
import uvicorn
|
2 |
from src.main import app
|
3 |
|
4 |
if __name__ == "__main__":
|
5 |
-
|
|
|
|
|
|
1 |
+
import os
|
2 |
import uvicorn
|
3 |
from src.main import app
|
4 |
|
5 |
if __name__ == "__main__":
|
6 |
+
host = os.getenv("HOST", "0.0.0.0")
|
7 |
+
port = int(os.getenv("PORT", "8888"))
|
8 |
+
uvicorn.run(app, host=host, port=port)
|
src/auth.py
CHANGED
@@ -22,6 +22,7 @@ from .config import (
|
|
22 |
credentials = None
|
23 |
user_project_id = None
|
24 |
onboarding_complete = False
|
|
|
25 |
|
26 |
security = HTTPBasic()
|
27 |
|
@@ -80,6 +81,11 @@ def authenticate_user(request: Request):
|
|
80 |
)
|
81 |
|
82 |
def save_credentials(creds, project_id=None):
|
|
|
|
|
|
|
|
|
|
|
83 |
|
84 |
creds_data = {
|
85 |
"client_id": CLIENT_ID,
|
@@ -116,17 +122,18 @@ def save_credentials(creds, project_id=None):
|
|
116 |
|
117 |
def get_credentials():
|
118 |
"""Loads credentials matching gemini-cli OAuth2 flow."""
|
119 |
-
global credentials
|
120 |
|
121 |
if credentials and credentials.token:
|
122 |
return credentials
|
123 |
|
124 |
-
|
125 |
-
|
|
|
126 |
try:
|
127 |
-
|
128 |
-
creds_data = json.load(f)
|
129 |
credentials = Credentials.from_authorized_user_info(creds_data, SCOPES)
|
|
|
130 |
|
131 |
if credentials.refresh_token:
|
132 |
try:
|
@@ -137,7 +144,8 @@ def get_credentials():
|
|
137 |
return credentials
|
138 |
except Exception as e:
|
139 |
pass # Fall through to file-based credentials
|
140 |
-
|
|
|
141 |
if os.path.exists(CREDENTIAL_FILE):
|
142 |
try:
|
143 |
with open(CREDENTIAL_FILE, "r") as f:
|
@@ -151,6 +159,8 @@ def get_credentials():
|
|
151 |
creds_data["scopes"] = creds_data["scope"].split()
|
152 |
|
153 |
credentials = Credentials.from_authorized_user_info(creds_data, SCOPES)
|
|
|
|
|
154 |
|
155 |
if credentials.refresh_token:
|
156 |
try:
|
@@ -208,6 +218,7 @@ def get_credentials():
|
|
208 |
try:
|
209 |
flow.fetch_token(code=auth_code)
|
210 |
credentials = flow.credentials
|
|
|
211 |
save_credentials(credentials)
|
212 |
print("Authentication successful! Credentials saved.")
|
213 |
return credentials
|
@@ -312,12 +323,6 @@ def get_user_project_id(creds):
|
|
312 |
save_credentials(creds, user_project_id)
|
313 |
return user_project_id
|
314 |
|
315 |
-
gemini_env_project_id = os.getenv("GEMINI_PROJECT_ID")
|
316 |
-
if gemini_env_project_id:
|
317 |
-
user_project_id = gemini_env_project_id
|
318 |
-
save_credentials(creds, user_project_id)
|
319 |
-
return user_project_id
|
320 |
-
|
321 |
if os.path.exists(CREDENTIAL_FILE):
|
322 |
try:
|
323 |
with open(CREDENTIAL_FILE, "r") as f:
|
|
|
22 |
credentials = None
|
23 |
user_project_id = None
|
24 |
onboarding_complete = False
|
25 |
+
credentials_from_env = False # Track if credentials came from environment variable
|
26 |
|
27 |
security = HTTPBasic()
|
28 |
|
|
|
81 |
)
|
82 |
|
83 |
def save_credentials(creds, project_id=None):
|
84 |
+
global credentials_from_env
|
85 |
+
|
86 |
+
# Don't save to file if credentials came from environment variable
|
87 |
+
if credentials_from_env:
|
88 |
+
return
|
89 |
|
90 |
creds_data = {
|
91 |
"client_id": CLIENT_ID,
|
|
|
122 |
|
123 |
def get_credentials():
|
124 |
"""Loads credentials matching gemini-cli OAuth2 flow."""
|
125 |
+
global credentials, credentials_from_env
|
126 |
|
127 |
if credentials and credentials.token:
|
128 |
return credentials
|
129 |
|
130 |
+
# Check for credentials in environment variable (JSON string)
|
131 |
+
env_creds_json = os.getenv("GEMINI_CREDENTIALS")
|
132 |
+
if env_creds_json:
|
133 |
try:
|
134 |
+
creds_data = json.loads(env_creds_json)
|
|
|
135 |
credentials = Credentials.from_authorized_user_info(creds_data, SCOPES)
|
136 |
+
credentials_from_env = True # Mark as environment credentials
|
137 |
|
138 |
if credentials.refresh_token:
|
139 |
try:
|
|
|
144 |
return credentials
|
145 |
except Exception as e:
|
146 |
pass # Fall through to file-based credentials
|
147 |
+
|
148 |
+
# Check for credentials file (CREDENTIAL_FILE now includes GOOGLE_APPLICATION_CREDENTIALS path if set)
|
149 |
if os.path.exists(CREDENTIAL_FILE):
|
150 |
try:
|
151 |
with open(CREDENTIAL_FILE, "r") as f:
|
|
|
159 |
creds_data["scopes"] = creds_data["scope"].split()
|
160 |
|
161 |
credentials = Credentials.from_authorized_user_info(creds_data, SCOPES)
|
162 |
+
# Mark as environment credentials if GOOGLE_APPLICATION_CREDENTIALS was used
|
163 |
+
credentials_from_env = bool(os.getenv("GOOGLE_APPLICATION_CREDENTIALS"))
|
164 |
|
165 |
if credentials.refresh_token:
|
166 |
try:
|
|
|
218 |
try:
|
219 |
flow.fetch_token(code=auth_code)
|
220 |
credentials = flow.credentials
|
221 |
+
credentials_from_env = False # Mark as file-based credentials
|
222 |
save_credentials(credentials)
|
223 |
print("Authentication successful! Credentials saved.")
|
224 |
return credentials
|
|
|
323 |
save_credentials(creds, user_project_id)
|
324 |
return user_project_id
|
325 |
|
|
|
|
|
|
|
|
|
|
|
|
|
326 |
if os.path.exists(CREDENTIAL_FILE):
|
327 |
try:
|
328 |
with open(CREDENTIAL_FILE, "r") as f:
|
src/config.py
CHANGED
@@ -21,7 +21,7 @@ SCOPES = [
|
|
21 |
|
22 |
# File Paths
|
23 |
SCRIPT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
24 |
-
CREDENTIAL_FILE = os.path.join(SCRIPT_DIR, "oauth_creds.json")
|
25 |
|
26 |
# Authentication
|
27 |
GEMINI_AUTH_PASSWORD = os.getenv("GEMINI_AUTH_PASSWORD", "123456")
|
|
|
21 |
|
22 |
# File Paths
|
23 |
SCRIPT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
24 |
+
CREDENTIAL_FILE = os.path.join(SCRIPT_DIR, os.getenv("GOOGLE_APPLICATION_CREDENTIALS", "oauth_creds.json"))
|
25 |
|
26 |
# Authentication
|
27 |
GEMINI_AUTH_PASSWORD = os.getenv("GEMINI_AUTH_PASSWORD", "123456")
|
src/main.py
CHANGED
@@ -48,5 +48,11 @@ async def handle_preflight(request: Request, full_path: str):
|
|
48 |
}
|
49 |
)
|
50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
app.include_router(openai_router)
|
52 |
app.include_router(gemini_router)
|
|
|
48 |
}
|
49 |
)
|
50 |
|
51 |
+
# Health check endpoint for Docker/Hugging Face
|
52 |
+
@app.get("/health")
|
53 |
+
async def health_check():
|
54 |
+
"""Health check endpoint for container orchestration."""
|
55 |
+
return {"status": "healthy", "service": "geminicli2api"}
|
56 |
+
|
57 |
app.include_router(openai_router)
|
58 |
app.include_router(gemini_router)
|