XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/gpt4free

Refactor CLI authentication commands and improve argument parsing

5100c19c
hlohaus <983577+hlohaus@users.noreply.github.com>
提交于

代码差异

8 个文件 +119 -62
Modified g4f/Provider/github/GithubCopilot.py +6 -6
@@ -25,7 +25,7 @@ class GithubCopilot(OpenaiTemplate):
25 25 allowing users to authenticate via browser without sharing credentials.
26 26
27 27 Usage:
28 1. Run `g4f-github-copilot login` to authenticate
28 1. Run `g4f auth github-copilot` to authenticate
29 29 2. Use the provider normally after authentication
30 30
31 31 Example:
@@ -270,7 +270,7 @@ class GithubCopilot(OpenaiTemplate):
270 270 usage = await resp.json()
271 271 return usage
272 272
273 async def main():
273 async def main(args: Optional[List[str]] = None):
274 274 """CLI entry point for GitHub Copilot OAuth authentication."""
275 275 import argparse
276 276
@@ -296,7 +296,7 @@ Examples:
296 296 # Logout command
297 297 subparsers.add_parser("logout", help="Remove saved credentials")
298 298
299 args = parser.parse_args()
299 args = parser.parse_args(args)
300 300
301 301 if args.command == "login":
302 302 try:
@@ -334,7 +334,7 @@ Examples:
334 334 print(f" (Could not read credential details: {e})")
335 335 else:
336 336 print("✗ No credentials found")
337 print(f"\nRun 'g4f-github-copilot login' to authenticate.")
337 print(f"\nRun 'g4f auth github-copilot' to authenticate.")
338 338
339 339 print()
340 340
@@ -370,9 +370,9 @@ Examples:
370 370 parser.print_help()
371 371
372 372
373 def cli_main():
373 def cli_main(args: Optional[List[str]] = None):
374 374 """Synchronous CLI entry point for setup.py console_scripts."""
375 asyncio.run(main())
375 asyncio.run(main(args))
376 376
377 377
378 378 if __name__ == "__main__":
Modified g4f/Provider/needs_auth/Antigravity.py +4 -4
@@ -1499,7 +1499,7 @@ class Antigravity(AsyncGeneratorProvider, ProviderModelMixin):
1499 1499 return cache_path
1500 1500
1501 1501
1502 async def main():
1502 async def main(args: Optional[List[str]] = None):
1503 1503 """CLI entry point for Antigravity authentication."""
1504 1504 import argparse
1505 1505
@@ -1537,7 +1537,7 @@ Examples:
1537 1537 # Logout command
1538 1538 subparsers.add_parser("logout", help="Remove saved credentials")
1539 1539
1540 args = parser.parse_args()
1540 args = parser.parse_args(args)
1541 1541
1542 1542 if args.command == "login":
1543 1543 try:
@@ -1616,9 +1616,9 @@ Examples:
1616 1616 parser.print_help()
1617 1617
1618 1618
1619 def cli_main():
1619 def cli_main(args: Optional[List[str]] = None):
1620 1620 """Synchronous CLI entry point for setup.py console_scripts."""
1621 asyncio.run(main())
1621 asyncio.run(main(args))
1622 1622
1623 1623
1624 1624 if __name__ == "__main__":
Modified g4f/Provider/needs_auth/GeminiCLI.py +5 -5
@@ -1171,7 +1171,7 @@ class GeminiCLI(AsyncGeneratorProvider, ProviderModelMixin):
1171 1171 return None
1172 1172
1173 1173
1174 async def main():
1174 async def main(args: Optional[List[str]] = None):
1175 1175 """CLI entry point for GeminiCLI authentication."""
1176 1176 import argparse
1177 1177
@@ -1203,7 +1203,7 @@ Examples:
1203 1203 # Logout command
1204 1204 subparsers.add_parser("logout", help="Remove saved credentials")
1205 1205
1206 args = parser.parse_args()
1206 args = parser.parse_args(args)
1207 1207
1208 1208 if args.command == "login":
1209 1209 try:
@@ -1241,7 +1241,7 @@ Examples:
1241 1241 print(f" (Could not read credential details: {e})")
1242 1242 else:
1243 1243 print("✗ No credentials found")
1244 print(f"\nRun 'g4f-geminicli login' to authenticate.")
1244 print(f"\nRun 'g4f auth gemini-cli login' to authenticate.")
1245 1245
1246 1246 print()
1247 1247
@@ -1274,9 +1274,9 @@ Examples:
1274 1274 parser.print_help()
1275 1275
1276 1276
1277 def cli_main():
1277 def cli_main(args: Optional[List[str]] = None):
1278 1278 """Synchronous CLI entry point for setup.py console_scripts."""
1279 asyncio.run(main())
1279 asyncio.run(main(args))
1280 1280
1281 1281
1282 1282 if __name__ == "__main__":
Modified g4f/Provider/qwen/QwenCode.py +5 -5
@@ -124,7 +124,7 @@ class QwenCode(OpenaiTemplate):
124 124 return None
125 125
126 126
127 async def main():
127 async def main(args: Optional[list[str]] = None):
128 128 """CLI entry point for QwenCode authentication."""
129 129 import argparse
130 130
@@ -150,7 +150,7 @@ Examples:
150 150 # Logout command
151 151 subparsers.add_parser("logout", help="Remove saved credentials")
152 152
153 args = parser.parse_args()
153 args = parser.parse_args(args)
154 154
155 155 if args.command == "login":
156 156 try:
@@ -188,7 +188,7 @@ Examples:
188 188 print(f" (Could not read credential details: {e})")
189 189 else:
190 190 print("✗ No credentials found")
191 print(f"\nRun 'g4f-qwencode login' to authenticate.")
191 print(f"\nRun 'g4f auth qwencode' to authenticate.")
192 192
193 193 print()
194 194
@@ -224,9 +224,9 @@ Examples:
224 224 parser.print_help()
225 225
226 226
227 def cli_main():
227 def cli_main(args: Optional[list[str]] = None):
228 228 """Synchronous CLI entry point for setup.py console_scripts."""
229 asyncio.run(main())
229 asyncio.run(main(args))
230 230
231 231
232 232 if __name__ == "__main__":
Modified g4f/cli/__init__.py +97 -17
@@ -13,6 +13,8 @@ runtime function depending on the CLI arguments.
13 13 """
14 14
15 15 import argparse
16 import os
17 import sys
16 18 from argparse import ArgumentParser
17 19
18 20 # Local imports (within g4f package)
@@ -20,6 +22,10 @@ from .client import get_parser, run_client_args
20 22 from ..requests import BrowserConfig
21 23 from ..gui.run import gui_parser, run_gui_args
22 24 from ..config import DEFAULT_PORT, DEFAULT_TIMEOUT, DEFAULT_STREAM_TIMEOUT
25 from ..Provider.needs_auth.Antigravity import cli_main as antigravity_cli_main
26 from ..Provider.qwen.QwenCode import cli_main as qwen_cli_main
27 from ..Provider.github.GithubCopilot import cli_main as github_cli_main
28 from g4f.Provider.needs_auth.GeminiCLI import cli_main as gemini_cli_main
23 29 from .. import Provider
24 30 from .. import cookies
25 31
@@ -27,12 +33,12 @@ from .. import cookies
27 33 # --------------------------------------------------------------
28 34 # API PARSER
29 35 # --------------------------------------------------------------
30 def get_api_parser() -> ArgumentParser:
36 def get_api_parser(exit_on_error: bool = True) -> ArgumentParser:
31 37 """
32 38 Creates and returns the argument parser used for:
33 39 g4f api ...
34 40 """
35 api_parser = ArgumentParser(description="Run the API and GUI")
41 api_parser = ArgumentParser(description="Run the API and GUI", exit_on_error=exit_on_error)
36 42
37 43 api_parser.add_argument(
38 44 "--bind",
@@ -228,12 +234,12 @@ def run_api_args(args):
228 234 # --------------------------------------------------------------
229 235 # MCP PARSER
230 236 # --------------------------------------------------------------
231 def get_mcp_parser() -> ArgumentParser:
237 def get_mcp_parser(exit_on_error: bool = True) -> ArgumentParser:
232 238 """
233 239 Parser for:
234 240 g4f mcp ...
235 241 """
236 mcp_parser = ArgumentParser(description="Run the MCP (Model Context Protocol) server")
242 mcp_parser = ArgumentParser(description="Run the MCP (Model Context Protocol) server", exit_on_error=exit_on_error)
237 243 mcp_parser.add_argument("--debug", "-d", action="store_true", help="Enable verbose logging.")
238 244 mcp_parser.add_argument("--http", action="store_true", help="Use HTTP instead of stdio.")
239 245 mcp_parser.add_argument("--host", default="0.0.0.0", help="HTTP server host.")
@@ -254,6 +260,11 @@ def run_mcp_args(args):
254 260 origin=args.origin
255 261 )
256 262
263 def get_auth_parser(exit_on_error: bool = True) -> ArgumentParser:
264 auth_parser = ArgumentParser(description="Manage authentication for providers", exit_on_error=exit_on_error)
265 auth_parser.add_argument("provider", choices=["gemini-cli", "antigravity", "qwencode", "github-copilot"], help="The provider to authenticate with")
266 auth_parser.add_argument("action", nargs="?", choices=["status", "login", "logout"], default="login", help="Action to perform (default: login)")
267 return auth_parser
257 268
258 269 # --------------------------------------------------------------
259 270 # MAIN ENTRYPOINT
@@ -264,31 +275,45 @@ def main():
264 275 Handles selecting: api / gui / client / mcp
265 276 """
266 277 parser = argparse.ArgumentParser(description="Run gpt4free", exit_on_error=False)
267
268 # Create sub-commands
269 subparsers = parser.add_subparsers(dest="mode", help="Mode to run g4f in.")
270 subparsers.add_parser("api", parents=[get_api_parser()], add_help=False)
271 subparsers.add_parser("gui", parents=[gui_parser()], add_help=False)
272 subparsers.add_parser("client", parents=[get_parser()], add_help=False)
273 subparsers.add_parser("mcp", parents=[get_mcp_parser()], add_help=False)
274
278 parser.add_argument("--install-autocomplete", action="store_true", help="Install Bash autocompletion for g4f CLI.")
279 args, remaining = parser.parse_known_args()
280 if args.install_autocomplete:
281 generate_autocomplete()
282 return
283
284
285 mode_parser = ArgumentParser(description="Select mode to run g4f in.", exit_on_error=False)
286 mode_parser.add_argument("mode", nargs="?", choices=["api", "gui", "client", "mcp", "auth"], default="api", help="Mode to run g4f in (default: api).")
287
288 args, remaining = mode_parser.parse_known_args(remaining)
275 289 try:
276 args = parser.parse_args()
277
278 # Mode routing
279 if args.mode == "api":
290 if args.mode == "auth":
291 parser = get_auth_parser()
292 args, remaining = parser.parse_known_args(remaining)
293 print(f"Handling auth for provider: {args.provider}, action: {args.action}")
294 handle_auth(args.provider, args.action, remaining)
295 return
296 elif args.mode == "api":
297 parser = get_api_parser()
298 args = parser.parse_args(remaining)
280 299 run_api_args(args)
281 300 elif args.mode == "gui":
301 parser = gui_parser()
302 args = parser.parse_args(remaining)
282 303 run_gui_args(args)
283 304 elif args.mode == "client":
305 parser = get_parser()
306 args = parser.parse_args(remaining)
284 307 run_client_args(args)
285 308 elif args.mode == "mcp":
309 parser = get_mcp_parser()
310 args = parser.parse_args(remaining)
286 311 run_mcp_args(args)
287 312 else:
288 313 # No mode provided
289 314 raise argparse.ArgumentError(
290 315 None,
291 "No valid mode specified. Use 'api', 'gui', 'client', or 'mcp'."
316 "No valid mode specified. Use 'api', 'gui', 'client', 'mcp', or 'auth'."
292 317 )
293 318
294 319 except argparse.ArgumentError:
@@ -302,3 +327,58 @@ def main():
302 327 except argparse.ArgumentError:
303 328 # 2. Try API mode with default arguments
304 329 run_api_args(get_api_parser().parse_args())
330
331 def generate_autocomplete():
332 # Top-level commands and their subcommands/options
333 commands = ["api", "gui", "client", "mcp", "auth"]
334 auth_providers = ["gemini-cli", "antigravity", "qwencode", "github-copilot"]
335 auth_subcommands = ["status", "login"]
336 # Options for each command
337 api_args = ["--bind", "--port", "--debug", "--gui", "--no-gui", "--model", "--provider", "--media-provider", "--proxy", "--workers", "--disable-colors", "--ignore-cookie-files", "--g4f-api-key", "--ignored-providers", "--cookie-browsers", "--reload", "--demo", "--timeout", "--stream-timeout", "--ssl-keyfile", "--ssl-certfile", "--log-config", "--access-log", "--no-access-log", "--browser-port", "--browser-host"]
338 gui_args = ["--debug"]
339 client_args = ["--debug"]
340 mcp_args = ["--debug", "--http", "--host", "--port", "--origin"]
341 global_args = ["--install-autocomplete"]
342 bash_completion_script = f"""
343 _g4f_completions() {{
344 local cur prev words cword
345 _get_comp_words_by_ref -n : cur prev words cword
346 if [[ $cword -eq 1 ]]; then
347 COMPREPLY=($(compgen -W '{' '.join(commands + global_args)}' -- "$cur"))
348 elif [[ $prev == auth && $cword -eq 2 ]]; then
349 COMPREPLY=($(compgen -W '{' '.join(auth_providers)}' -- "$cur"))
350 elif [[ $prev =~ ^(gemini-cli|antigravity|qwencode|github-copilot)$ && $cword -eq 3 ]]; then
351 COMPREPLY=($(compgen -W '{' '.join(auth_subcommands)}' -- "$cur"))
352 elif [[ $words[1] == api ]]; then
353 local opts="{' '.join(api_args)}"
354 COMPREPLY=($(compgen -W "$opts" -- "$cur"))
355 elif [[ $words[1] == gui ]]; then
356 local opts="{' '.join(gui_args)}"
357 COMPREPLY=($(compgen -W "$opts" -- "$cur"))
358 elif [[ $words[1] == client ]]; then
359 local opts="{' '.join(client_args)}"
360 COMPREPLY=($(compgen -W "$opts" -- "$cur"))
361 elif [[ $words[1] == mcp ]]; then
362 local opts="{' '.join(mcp_args)}"
363 COMPREPLY=($(compgen -W "$opts" -- "$cur"))
364 fi
365 }}
366 complete -F _g4f_completions g4f
367 """
368 completion_file = os.path.expanduser("~/.g4f_bash_completion")
369 with open(completion_file, "w") as f:
370 f.write(bash_completion_script)
371 print(f"Bash completion script written to {completion_file}. Source it in your .bashrc or .bash_profile.")
372
373
374 def handle_auth(provider, action, remaining):
375 if provider == "gemini-cli":
376 sys.exit(gemini_cli_main([action] + remaining))
377 elif provider == "antigravity":
378 sys.exit(antigravity_cli_main([action] + remaining))
379 elif provider == "qwencode":
380 sys.exit(qwen_cli_main([action] + remaining))
381 elif provider == "github-copilot":
382 sys.exit(github_cli_main([action] + remaining))
383 else:
384 print(f"Provider {provider} not supported yet.")
Modified g4f/gui/gui_parser.py +2 -2
@@ -3,8 +3,8 @@ from argparse import ArgumentParser
3 3 from ..cookies import BROWSERS
4 4 from .. import Provider
5 5
6 def gui_parser():
7 parser = ArgumentParser(description="Run the GUI")
6 def gui_parser(exit_on_error: bool = True):
7 parser = ArgumentParser(description="Run the GUI", exit_on_error=exit_on_error)
8 8 parser.add_argument("--host", type=str, default="0.0.0.0", help="hostname")
9 9 parser.add_argument("--port", "-p", type=int, default=8080, help="port")
10 10 parser.add_argument("--debug", "-d", "-debug", action="store_true", help="debug mode")
Modified g4f_cli.py +0 -19
@@ -7,25 +7,6 @@ This file is used as the main entry point for building executables with Nuitka
7 7 import g4f.debug
8 8 g4f.debug.enable_logging()
9 9
10 from g4f.client import Client
11 from g4f.errors import ModelNotFoundError
12
13 import g4f.Provider
14
15 try:
16 client = Client(provider=g4f.Provider.PollinationsAI)
17 response = client.chat.completions.create(
18 model="openai",
19 messages=[{"role": "user", "content": "Hello!"}],
20 stream=True,
21 raw=True
22 )
23 for r in response:
24 print(r)
25 except ModelNotFoundError as e:
26 print(f"Successfully")
27 exit(0)
28
29 10 import g4f.cli
30 11
31 12 if __name__ == "__main__":
Modified setup.py +0 -4
@@ -121,10 +121,6 @@ setup(
121 121 'console_scripts': [
122 122 'g4f=g4f.cli:main',
123 123 'g4f-mcp=g4f.mcp.server:main',
124 'g4f-antigravity=g4f.Provider.needs_auth.Antigravity:cli_main',
125 'g4f-geminicli=g4f.Provider.needs_auth.GeminiCLI:cli_main',
126 'g4f-qwencode=g4f.Provider.qwen.QwenCode:cli_main',
127 'g4f-github-copilot=g4f.Provider.github.GithubCopilot:cli_main',
128 124 ],
129 125 },
130 126 url='https://github.com/xtekky/gpt4free', # Link to your GitHub repository