gemini-cli-proxy 1.0.3__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gemini_cli_proxy/cli.py +14 -9
- gemini_cli_proxy/config.py +6 -3
- gemini_cli_proxy/gemini_client.py +187 -26
- gemini_cli_proxy/models.py +8 -1
- gemini_cli_proxy/openai_adapter.py +3 -1
- gemini_cli_proxy/server.py +8 -3
- {gemini_cli_proxy-1.0.3.dist-info → gemini_cli_proxy-1.1.0.dist-info}/METADATA +8 -3
- gemini_cli_proxy-1.1.0.dist-info/RECORD +12 -0
- gemini_cli_proxy-1.0.3.dist-info/RECORD +0 -12
- {gemini_cli_proxy-1.0.3.dist-info → gemini_cli_proxy-1.1.0.dist-info}/WHEEL +0 -0
- {gemini_cli_proxy-1.0.3.dist-info → gemini_cli_proxy-1.1.0.dist-info}/entry_points.txt +0 -0
- {gemini_cli_proxy-1.0.3.dist-info → gemini_cli_proxy-1.1.0.dist-info}/licenses/LICENSE +0 -0
gemini_cli_proxy/cli.py
CHANGED
@@ -20,12 +20,6 @@ import uvicorn
|
|
20
20
|
type=int,
|
21
21
|
help="Server port"
|
22
22
|
)
|
23
|
-
@click.option(
|
24
|
-
"--log-level",
|
25
|
-
default="info",
|
26
|
-
type=click.Choice(["debug", "info", "warning", "error", "critical"]),
|
27
|
-
help="Log level"
|
28
|
-
)
|
29
23
|
@click.option(
|
30
24
|
"--rate-limit",
|
31
25
|
default=60,
|
@@ -52,7 +46,6 @@ import uvicorn
|
|
52
46
|
def main(
|
53
47
|
host: str,
|
54
48
|
port: int,
|
55
|
-
log_level: str,
|
56
49
|
rate_limit: int,
|
57
50
|
max_concurrency: int,
|
58
51
|
timeout: float,
|
@@ -61,21 +54,33 @@ def main(
|
|
61
54
|
"""Start Gemini CLI Proxy server"""
|
62
55
|
|
63
56
|
# Set configuration
|
57
|
+
import os
|
64
58
|
from .config import config
|
59
|
+
|
60
|
+
# Set environment variable for reload mode
|
61
|
+
os.environ['GEMINI_CLI_PROXY_DEBUG'] = str(debug)
|
62
|
+
|
65
63
|
config.host = host
|
66
64
|
config.port = port
|
67
|
-
config.log_level =
|
65
|
+
config.log_level = "debug" if debug else "info"
|
68
66
|
config.rate_limit = rate_limit
|
69
67
|
config.max_concurrency = max_concurrency
|
70
68
|
config.timeout = timeout
|
71
69
|
config.debug = debug
|
72
70
|
|
71
|
+
# Update logging level based on configuration
|
72
|
+
import logging
|
73
|
+
# Set root logger level
|
74
|
+
logging.getLogger().setLevel(getattr(logging, config.log_level.upper()))
|
75
|
+
# Also set level for all gemini_cli_proxy loggers
|
76
|
+
logging.getLogger('gemini_cli_proxy').setLevel(getattr(logging, config.log_level.upper()))
|
77
|
+
|
73
78
|
# Start server
|
74
79
|
uvicorn.run(
|
75
80
|
"gemini_cli_proxy.server:app",
|
76
81
|
host=host,
|
77
82
|
port=port,
|
78
|
-
log_level=log_level,
|
83
|
+
log_level=config.log_level,
|
79
84
|
reload=debug
|
80
85
|
)
|
81
86
|
|
gemini_cli_proxy/config.py
CHANGED
@@ -9,15 +9,18 @@ class Config:
|
|
9
9
|
"""Application configuration class"""
|
10
10
|
|
11
11
|
def __init__(self):
|
12
|
+
import os
|
13
|
+
|
12
14
|
# Server configuration
|
13
15
|
self.host: str = "127.0.0.1"
|
14
16
|
self.port: int = 8765
|
15
|
-
|
16
|
-
self.debug: bool =
|
17
|
+
# Read from environment variable if available (for reload mode)
|
18
|
+
self.debug: bool = os.environ.get('GEMINI_CLI_PROXY_DEBUG', 'false').lower() == 'true'
|
19
|
+
self.log_level: str = "debug" if self.debug else "info"
|
17
20
|
|
18
21
|
# Gemini CLI configuration
|
19
22
|
self.gemini_command: str = "gemini" # Gemini CLI command path
|
20
|
-
self.timeout: float =
|
23
|
+
self.timeout: float = 120.0
|
21
24
|
|
22
25
|
# Limit configuration
|
23
26
|
self.rate_limit: int = 60 # Requests per minute
|
@@ -6,11 +6,16 @@ Handles interaction with Gemini CLI tool
|
|
6
6
|
|
7
7
|
import asyncio
|
8
8
|
import logging
|
9
|
-
|
9
|
+
import os
|
10
|
+
import tempfile
|
11
|
+
import uuid
|
12
|
+
import base64
|
13
|
+
import re
|
14
|
+
from typing import List, Optional, AsyncGenerator, Tuple
|
10
15
|
from .models import ChatMessage
|
11
16
|
from .config import config
|
12
17
|
|
13
|
-
logger = logging.getLogger(
|
18
|
+
logger = logging.getLogger('gemini_cli_proxy')
|
14
19
|
|
15
20
|
|
16
21
|
class GeminiClient:
|
@@ -19,9 +24,43 @@ class GeminiClient:
|
|
19
24
|
def __init__(self):
|
20
25
|
self.semaphore = asyncio.Semaphore(config.max_concurrency)
|
21
26
|
|
27
|
+
def _simplify_error_message(self, raw_error: str) -> Optional[str]:
|
28
|
+
"""
|
29
|
+
Convert Gemini CLI error messages to more readable user-friendly messages
|
30
|
+
|
31
|
+
Args:
|
32
|
+
raw_error: Raw error message from Gemini CLI
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
Simplified error message, or None if the error cannot be recognized
|
36
|
+
"""
|
37
|
+
if not raw_error:
|
38
|
+
return None
|
39
|
+
|
40
|
+
lower_err = raw_error.lower()
|
41
|
+
|
42
|
+
# Check for rate limiting related keywords
|
43
|
+
rate_limit_indicators = [
|
44
|
+
"code\": 429",
|
45
|
+
"status 429",
|
46
|
+
"ratelimitexceeded",
|
47
|
+
"resource_exhausted",
|
48
|
+
"quota exceeded",
|
49
|
+
"quota metric",
|
50
|
+
"requests per day",
|
51
|
+
"requests per minute",
|
52
|
+
"limit exceeded"
|
53
|
+
]
|
54
|
+
|
55
|
+
if any(keyword in lower_err for keyword in rate_limit_indicators):
|
56
|
+
return "Gemini CLI rate limit exceeded. Please run `gemini` directly to check."
|
57
|
+
|
58
|
+
return None
|
59
|
+
|
22
60
|
async def chat_completion(
|
23
61
|
self,
|
24
62
|
messages: List[ChatMessage],
|
63
|
+
model: str,
|
25
64
|
temperature: Optional[float] = None,
|
26
65
|
max_tokens: Optional[int] = None,
|
27
66
|
**kwargs
|
@@ -31,6 +70,7 @@ class GeminiClient:
|
|
31
70
|
|
32
71
|
Args:
|
33
72
|
messages: List of chat messages
|
73
|
+
model: Model name to use
|
34
74
|
temperature: Temperature parameter
|
35
75
|
max_tokens: Maximum number of tokens
|
36
76
|
**kwargs: Other parameters
|
@@ -44,12 +84,13 @@ class GeminiClient:
|
|
44
84
|
"""
|
45
85
|
async with self.semaphore:
|
46
86
|
return await self._execute_gemini_command(
|
47
|
-
messages, temperature, max_tokens, **kwargs
|
87
|
+
messages, model, temperature, max_tokens, **kwargs
|
48
88
|
)
|
49
89
|
|
50
90
|
async def chat_completion_stream(
|
51
91
|
self,
|
52
92
|
messages: List[ChatMessage],
|
93
|
+
model: str,
|
53
94
|
temperature: Optional[float] = None,
|
54
95
|
max_tokens: Optional[int] = None,
|
55
96
|
**kwargs
|
@@ -59,6 +100,7 @@ class GeminiClient:
|
|
59
100
|
|
60
101
|
Args:
|
61
102
|
messages: List of chat messages
|
103
|
+
model: Model name to use
|
62
104
|
temperature: Temperature parameter
|
63
105
|
max_tokens: Maximum number of tokens
|
64
106
|
**kwargs: Other parameters
|
@@ -68,7 +110,7 @@ class GeminiClient:
|
|
68
110
|
"""
|
69
111
|
# First get complete response
|
70
112
|
full_response = await self.chat_completion(
|
71
|
-
messages, temperature, max_tokens, **kwargs
|
113
|
+
messages, model, temperature, max_tokens, **kwargs
|
72
114
|
)
|
73
115
|
|
74
116
|
# Split by lines and yield one by one
|
@@ -82,6 +124,7 @@ class GeminiClient:
|
|
82
124
|
async def _execute_gemini_command(
|
83
125
|
self,
|
84
126
|
messages: List[ChatMessage],
|
127
|
+
model: str,
|
85
128
|
temperature: Optional[float] = None,
|
86
129
|
max_tokens: Optional[int] = None,
|
87
130
|
**kwargs
|
@@ -91,6 +134,7 @@ class GeminiClient:
|
|
91
134
|
|
92
135
|
Args:
|
93
136
|
messages: List of chat messages
|
137
|
+
model: Model name to use
|
94
138
|
temperature: Temperature parameter
|
95
139
|
max_tokens: Maximum number of tokens
|
96
140
|
**kwargs: Other parameters
|
@@ -98,14 +142,12 @@ class GeminiClient:
|
|
98
142
|
Returns:
|
99
143
|
Command output result
|
100
144
|
"""
|
101
|
-
# Build command arguments
|
102
|
-
|
145
|
+
# Build command arguments and get temporary files
|
146
|
+
prompt, temp_files = self._build_prompt_with_images(messages)
|
103
147
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
# Use --prompt parameter to pass prompt text
|
108
|
-
cmd_args.extend(["--prompt", prompt])
|
148
|
+
cmd_args = [config.gemini_command]
|
149
|
+
cmd_args.extend(["-m", model])
|
150
|
+
cmd_args.extend(["-p", prompt])
|
109
151
|
|
110
152
|
# Note: Real gemini CLI doesn't support temperature and max_tokens parameters
|
111
153
|
# We ignore these parameters here but log them
|
@@ -133,42 +175,161 @@ class GeminiClient:
|
|
133
175
|
# Check return code
|
134
176
|
if process.returncode != 0:
|
135
177
|
error_msg = stderr.decode('utf-8').strip()
|
136
|
-
|
178
|
+
|
179
|
+
# Try to simplify error message to more user-friendly format
|
180
|
+
simplified_msg = self._simplify_error_message(error_msg)
|
181
|
+
if simplified_msg:
|
182
|
+
logger.warning(f"Gemini CLI error (simplified): {simplified_msg}")
|
183
|
+
raise RuntimeError(simplified_msg)
|
184
|
+
else:
|
185
|
+
logger.warning(f"Gemini CLI execution failed: {error_msg}")
|
186
|
+
raise RuntimeError(f"Gemini CLI execution failed (exit code: {process.returncode}): {error_msg}")
|
137
187
|
|
138
188
|
# Return standard output
|
139
189
|
result = stdout.decode('utf-8').strip()
|
140
|
-
logger.debug(f"
|
190
|
+
logger.debug(f"Gemini CLI response: {result}")
|
141
191
|
return result
|
142
192
|
|
143
193
|
except asyncio.TimeoutError:
|
144
194
|
logger.error(f"Gemini CLI command timeout ({config.timeout}s)")
|
195
|
+
raise RuntimeError(f"Gemini CLI execution timeout ({config.timeout} seconds), please retry later or check your network connection")
|
196
|
+
except RuntimeError:
|
197
|
+
# Re-raise already processed RuntimeError
|
145
198
|
raise
|
146
199
|
except Exception as e:
|
147
200
|
logger.error(f"Error executing Gemini CLI command: {e}")
|
148
|
-
raise
|
201
|
+
raise RuntimeError(f"Error executing Gemini CLI command: {str(e)}")
|
202
|
+
finally:
|
203
|
+
# Clean up temporary files (skip in debug mode)
|
204
|
+
if not config.debug:
|
205
|
+
for temp_file in temp_files:
|
206
|
+
try:
|
207
|
+
if os.path.exists(temp_file):
|
208
|
+
os.unlink(temp_file)
|
209
|
+
except Exception as e:
|
210
|
+
logger.warning(f"Failed to clean up temp file {temp_file}: {e}")
|
149
211
|
|
150
|
-
def
|
212
|
+
def _build_prompt_with_images(self, messages: List[ChatMessage]) -> Tuple[str, List[str]]:
|
151
213
|
"""
|
152
|
-
Build prompt text
|
214
|
+
Build prompt text with image processing
|
153
215
|
|
154
216
|
Args:
|
155
217
|
messages: List of chat messages
|
156
218
|
|
157
219
|
Returns:
|
158
|
-
|
220
|
+
Tuple of (formatted prompt text, list of temporary file paths)
|
159
221
|
"""
|
160
|
-
# Simplified implementation: format all messages by role
|
161
222
|
prompt_parts = []
|
223
|
+
temp_files = []
|
224
|
+
|
225
|
+
for i, message in enumerate(messages):
|
226
|
+
if isinstance(message.content, str):
|
227
|
+
# Simple string content
|
228
|
+
if message.role == "system":
|
229
|
+
prompt_parts.append(f"System: {message.content}")
|
230
|
+
elif message.role == "user":
|
231
|
+
prompt_parts.append(f"User: {message.content}")
|
232
|
+
elif message.role == "assistant":
|
233
|
+
prompt_parts.append(f"Assistant: {message.content}")
|
234
|
+
else:
|
235
|
+
# List of content parts (vision support)
|
236
|
+
content_parts = []
|
237
|
+
|
238
|
+
for j, part in enumerate(message.content):
|
239
|
+
if part.type == "text" and part.text:
|
240
|
+
content_parts.append(part.text)
|
241
|
+
elif part.type == "image_url" and part.image_url:
|
242
|
+
url = part.image_url.get("url", "")
|
243
|
+
if url.startswith("data:"):
|
244
|
+
# Process base64 image
|
245
|
+
temp_file_path = self._save_base64_image(url)
|
246
|
+
temp_files.append(temp_file_path)
|
247
|
+
content_parts.append(f"@{temp_file_path}")
|
248
|
+
else:
|
249
|
+
# For regular URLs, we'll just pass them through for now
|
250
|
+
# TODO: Download and save remote images if needed
|
251
|
+
content_parts.append(f"<image_url>{url}</image_url>")
|
252
|
+
|
253
|
+
combined_content = " ".join(content_parts)
|
254
|
+
if message.role == "system":
|
255
|
+
prompt_parts.append(f"System: {combined_content}")
|
256
|
+
elif message.role == "user":
|
257
|
+
prompt_parts.append(f"User: {combined_content}")
|
258
|
+
elif message.role == "assistant":
|
259
|
+
prompt_parts.append(f"Assistant: {combined_content}")
|
260
|
+
|
261
|
+
final_prompt = "\n".join(prompt_parts)
|
262
|
+
logger.debug(f"Prompt sent to Gemini CLI: {final_prompt}")
|
162
263
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
elif message.role == "assistant":
|
169
|
-
prompt_parts.append(f"Assistant: {message.content}")
|
264
|
+
return final_prompt, temp_files
|
265
|
+
|
266
|
+
def _save_base64_image(self, data_url: str) -> str:
|
267
|
+
"""
|
268
|
+
Save base64 image data to temporary file
|
170
269
|
|
171
|
-
|
270
|
+
Args:
|
271
|
+
data_url: Data URL in format "data:image/type;base64,..."
|
272
|
+
|
273
|
+
Returns:
|
274
|
+
Path to temporary file
|
275
|
+
|
276
|
+
Raises:
|
277
|
+
ValueError: Invalid data URL format
|
278
|
+
"""
|
279
|
+
try:
|
280
|
+
# Parse data URL
|
281
|
+
if not data_url.startswith("data:"):
|
282
|
+
raise ValueError("Invalid data URL format")
|
283
|
+
|
284
|
+
# Extract MIME type and base64 data
|
285
|
+
header, data = data_url.split(",", 1)
|
286
|
+
mime_info = header.split(";")[0].split(":")[1] # e.g., "image/png"
|
287
|
+
|
288
|
+
# Determine file extension
|
289
|
+
if "png" in mime_info.lower():
|
290
|
+
ext = ".png"
|
291
|
+
elif "jpeg" in mime_info.lower() or "jpg" in mime_info.lower():
|
292
|
+
ext = ".jpg"
|
293
|
+
elif "gif" in mime_info.lower():
|
294
|
+
ext = ".gif"
|
295
|
+
elif "webp" in mime_info.lower():
|
296
|
+
ext = ".webp"
|
297
|
+
else:
|
298
|
+
ext = ".png" # Default to PNG
|
299
|
+
|
300
|
+
# Decode base64 data
|
301
|
+
image_data = base64.b64decode(data)
|
302
|
+
|
303
|
+
# Create .gemini-cli-proxy directory in project root
|
304
|
+
temp_dir = ".gemini-cli-proxy"
|
305
|
+
os.makedirs(temp_dir, exist_ok=True)
|
306
|
+
|
307
|
+
# Create temporary file with simplified name
|
308
|
+
filename = f"{uuid.uuid4().hex[:8]}{ext}"
|
309
|
+
temp_file_path = os.path.join(temp_dir, filename)
|
310
|
+
|
311
|
+
# Write image data
|
312
|
+
with open(temp_file_path, 'wb') as f:
|
313
|
+
f.write(image_data)
|
314
|
+
|
315
|
+
return temp_file_path
|
316
|
+
|
317
|
+
except Exception as e:
|
318
|
+
logger.error(f"Error saving base64 image: {e}")
|
319
|
+
raise ValueError(f"Failed to save base64 image: {e}")
|
320
|
+
|
321
|
+
def _build_prompt(self, messages: List[ChatMessage]) -> str:
|
322
|
+
"""
|
323
|
+
Build prompt text (legacy method, kept for compatibility)
|
324
|
+
|
325
|
+
Args:
|
326
|
+
messages: List of chat messages
|
327
|
+
|
328
|
+
Returns:
|
329
|
+
Formatted prompt text
|
330
|
+
"""
|
331
|
+
prompt, _ = self._build_prompt_with_images(messages)
|
332
|
+
return prompt
|
172
333
|
|
173
334
|
|
174
335
|
# Global client instance
|
gemini_cli_proxy/models.py
CHANGED
@@ -10,10 +10,17 @@ import time
|
|
10
10
|
import uuid
|
11
11
|
|
12
12
|
|
13
|
+
class ChatContentPart(BaseModel):
|
14
|
+
"""Chat content part model for vision support"""
|
15
|
+
type: Literal["text", "image_url"]
|
16
|
+
text: Optional[str] = None
|
17
|
+
image_url: Optional[Dict[str, str]] = None # {"url": "..."}
|
18
|
+
|
19
|
+
|
13
20
|
class ChatMessage(BaseModel):
|
14
21
|
"""Chat message model"""
|
15
22
|
role: Literal["system", "user", "assistant"]
|
16
|
-
content: str
|
23
|
+
content: Union[str, List[ChatContentPart]]
|
17
24
|
|
18
25
|
|
19
26
|
class ChatCompletionRequest(BaseModel):
|
@@ -20,7 +20,7 @@ from .models import (
|
|
20
20
|
)
|
21
21
|
from .gemini_client import gemini_client
|
22
22
|
|
23
|
-
logger = logging.getLogger(
|
23
|
+
logger = logging.getLogger('gemini_cli_proxy')
|
24
24
|
|
25
25
|
|
26
26
|
class OpenAIAdapter:
|
@@ -42,6 +42,7 @@ class OpenAIAdapter:
|
|
42
42
|
# Call Gemini CLI
|
43
43
|
response_text = await gemini_client.chat_completion(
|
44
44
|
messages=request.messages,
|
45
|
+
model=request.model,
|
45
46
|
temperature=request.temperature,
|
46
47
|
max_tokens=request.max_tokens
|
47
48
|
)
|
@@ -89,6 +90,7 @@ class OpenAIAdapter:
|
|
89
90
|
# Get streaming data generator
|
90
91
|
stream_generator = gemini_client.chat_completion_stream(
|
91
92
|
messages=request.messages,
|
93
|
+
model=request.model,
|
92
94
|
temperature=request.temperature,
|
93
95
|
max_tokens=request.max_tokens
|
94
96
|
)
|
gemini_cli_proxy/server.py
CHANGED
@@ -29,12 +29,12 @@ from .models import (
|
|
29
29
|
)
|
30
30
|
from .openai_adapter import openai_adapter
|
31
31
|
|
32
|
-
# Configure logging
|
32
|
+
# Configure logging (will be updated when config is set)
|
33
33
|
logging.basicConfig(
|
34
|
-
level=
|
34
|
+
level=logging.INFO, # Default level, will be updated in CLI
|
35
35
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
36
36
|
)
|
37
|
-
logger = logging.getLogger(
|
37
|
+
logger = logging.getLogger('gemini_cli_proxy')
|
38
38
|
|
39
39
|
# Create rate limiter
|
40
40
|
limiter = Limiter(key_func=get_remote_address)
|
@@ -43,8 +43,13 @@ limiter = Limiter(key_func=get_remote_address)
|
|
43
43
|
@asynccontextmanager
|
44
44
|
async def lifespan(app: FastAPI):
|
45
45
|
"""Application lifecycle management"""
|
46
|
+
# Ensure logging level is applied after uvicorn starts
|
47
|
+
import logging
|
48
|
+
logging.getLogger('gemini_cli_proxy').setLevel(getattr(logging, config.log_level.upper()))
|
49
|
+
|
46
50
|
logger.info(f"Starting Gemini CLI Proxy v{__version__}")
|
47
51
|
logger.info(f"Configuration: port={config.port}, rate_limit={config.rate_limit}/min, concurrency={config.max_concurrency}")
|
52
|
+
logger.debug(f"Debug logging is enabled (log_level={config.log_level})")
|
48
53
|
yield
|
49
54
|
logger.info("Shutting down Gemini CLI Proxy")
|
50
55
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: gemini-cli-proxy
|
3
|
-
Version: 1.0
|
3
|
+
Version: 1.1.0
|
4
4
|
Summary: OpenAI-compatible API wrapper for Gemini CLI
|
5
5
|
Author: nettee
|
6
6
|
License: MIT
|
@@ -59,6 +59,12 @@ gemini -p "Hello, Gemini"
|
|
59
59
|
|
60
60
|
### Start Gemini CLI Proxy
|
61
61
|
|
62
|
+
Method 1: Direct startup
|
63
|
+
```bash
|
64
|
+
uvx gemini-cli-proxy
|
65
|
+
```
|
66
|
+
|
67
|
+
Method 2: Clone this repository and run:
|
62
68
|
```bash
|
63
69
|
uv run gemini-cli-proxy
|
64
70
|
```
|
@@ -122,11 +128,10 @@ gemini-cli-proxy --help
|
|
122
128
|
Available options:
|
123
129
|
- `--host`: Server host address (default: 127.0.0.1)
|
124
130
|
- `--port`: Server port (default: 8765)
|
125
|
-
- `--log-level`: Log level (debug/info/warning/error/critical)
|
126
131
|
- `--rate-limit`: Max requests per minute (default: 60)
|
127
132
|
- `--max-concurrency`: Max concurrent subprocesses (default: 4)
|
128
133
|
- `--timeout`: Gemini CLI command timeout in seconds (default: 30.0)
|
129
|
-
- `--debug`: Enable debug mode
|
134
|
+
- `--debug`: Enable debug mode (enables debug logging and file watching)
|
130
135
|
|
131
136
|
## ❓ FAQ
|
132
137
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
gemini_cli_proxy/__init__.py,sha256=fOI3EtGmmggiMc3uGV8lGLsbzXmM3ADfnFbaHnwrKtg,257
|
2
|
+
gemini_cli_proxy/cli.py,sha256=3_ZgcvEDzRtY6AVA0-8VbQtUWOcpn3waoAsH-soOQ18,1863
|
3
|
+
gemini_cli_proxy/config.py,sha256=Ly7_eGTssitIbSVhxq7Tq-bQao83oDWzIyI-t1PJpvM,1043
|
4
|
+
gemini_cli_proxy/gemini_client.py,sha256=wWIZ4gaT1CqFbZaFsUKRiDf0RHWpou1AZGYwGeuw_1o,12180
|
5
|
+
gemini_cli_proxy/models.py,sha256=SiyesskO4J3rLb4nayxHc4cpbTKQQ6j7osTOsTo7Ems,3033
|
6
|
+
gemini_cli_proxy/openai_adapter.py,sha256=oi4W4SjuFjii-v42KVWqh1UGzaTXI_KvrUIhBTcOiFM,5482
|
7
|
+
gemini_cli_proxy/server.py,sha256=a25eSsTUWyTLxNjFfcOFiL2TUgykaQRc3dBgfp6pC-8,5696
|
8
|
+
gemini_cli_proxy-1.1.0.dist-info/METADATA,sha256=ujEj4r8IPPIfqHnA2HfsWOYjfOj6XCBhh9aWAnKzl3E,4085
|
9
|
+
gemini_cli_proxy-1.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
10
|
+
gemini_cli_proxy-1.1.0.dist-info/entry_points.txt,sha256=wDLl4ePzvEWNQMSxoE7rKV5k8_MpK6yQwpYdiaXjcWI,63
|
11
|
+
gemini_cli_proxy-1.1.0.dist-info/licenses/LICENSE,sha256=-LKYkZXXzjCmYRVwR74fDmMHP3gNlKIW_UUuEbY9hq8,1068
|
12
|
+
gemini_cli_proxy-1.1.0.dist-info/RECORD,,
|
@@ -1,12 +0,0 @@
|
|
1
|
-
gemini_cli_proxy/__init__.py,sha256=fOI3EtGmmggiMc3uGV8lGLsbzXmM3ADfnFbaHnwrKtg,257
|
2
|
-
gemini_cli_proxy/cli.py,sha256=hc83w1AUobLN_-ONITforwdyQBx_0jwGyn-M_zbh4LQ,1555
|
3
|
-
gemini_cli_proxy/config.py,sha256=5erCL5v5sb2Kz-Kje-luCw7EJUB8oq5wU3fdBnMS3H0,854
|
4
|
-
gemini_cli_proxy/gemini_client.py,sha256=nfRbgzTHvh7w4QsLzG-s5B964JIBzwt--PE3jvQT0R4,5735
|
5
|
-
gemini_cli_proxy/models.py,sha256=3FNvKk4CuLUU7MrFM0X12HeEN5paRPrRoJO0083KLfQ,2779
|
6
|
-
gemini_cli_proxy/openai_adapter.py,sha256=x_8dUcob1DOLnKbTLQBsmN_e1dO6mX0DFaXqUmzcBzY,5394
|
7
|
-
gemini_cli_proxy/server.py,sha256=6u6vEc4nqrh6eocflmx8JuHTZuWoH4uKJdfdblMnJfo,5383
|
8
|
-
gemini_cli_proxy-1.0.3.dist-info/METADATA,sha256=vi5AG8BOBCWU25lJyIUXmKybadk4GzwOcZ8mJzhSzrg,4006
|
9
|
-
gemini_cli_proxy-1.0.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
10
|
-
gemini_cli_proxy-1.0.3.dist-info/entry_points.txt,sha256=wDLl4ePzvEWNQMSxoE7rKV5k8_MpK6yQwpYdiaXjcWI,63
|
11
|
-
gemini_cli_proxy-1.0.3.dist-info/licenses/LICENSE,sha256=-LKYkZXXzjCmYRVwR74fDmMHP3gNlKIW_UUuEbY9hq8,1068
|
12
|
-
gemini_cli_proxy-1.0.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|