返回提交历史
Modified
g4f/Provider/github/GithubCopilot.py
+6
-6
Modified
g4f/Provider/needs_auth/Antigravity.py
+4
-4
Modified
g4f/Provider/needs_auth/GeminiCLI.py
+5
-5
Modified
g4f/Provider/qwen/QwenCode.py
+5
-5
Modified
g4f/cli/__init__.py
+97
-17
Modified
g4f/gui/gui_parser.py
+2
-2
Modified
g4f_cli.py
+0
-19
Modified
setup.py
+0
-4
XFEstudio/gpt4free
Refactor CLI authentication commands and improve argument parsing
5100c19c
代码差异
8 个文件
+119
-62
@@ -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__":
@@ -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__":
@@ -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__":
@@ -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__":
@@ -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.")
@@ -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")
@@ -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__":
@@ -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