ngpt 2.9.1__py3-none-any.whl → 2.10.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.
- ngpt/cli/args.py +161 -0
- ngpt/cli/main.py +42 -122
- ngpt/cli_config.py +27 -8
- {ngpt-2.9.1.dist-info → ngpt-2.10.0.dist-info}/METADATA +10 -8
- {ngpt-2.9.1.dist-info → ngpt-2.10.0.dist-info}/RECORD +8 -7
- {ngpt-2.9.1.dist-info → ngpt-2.10.0.dist-info}/WHEEL +0 -0
- {ngpt-2.9.1.dist-info → ngpt-2.10.0.dist-info}/entry_points.txt +0 -0
- {ngpt-2.9.1.dist-info → ngpt-2.10.0.dist-info}/licenses/LICENSE +0 -0
ngpt/cli/args.py
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
import argparse
|
2
|
+
import sys
|
3
|
+
from .. import __version__
|
4
|
+
from .formatters import COLORS, ColoredHelpFormatter
|
5
|
+
from .renderers import has_markdown_renderer, warn_if_no_markdown_renderer
|
6
|
+
|
7
|
+
def setup_argument_parser():
|
8
|
+
"""Set up and return a fully configured argument parser for nGPT CLI."""
|
9
|
+
# Colorize description - use a shorter description to avoid line wrapping issues
|
10
|
+
description = f"{COLORS['cyan']}{COLORS['bold']}nGPT{COLORS['reset']} - Interact with AI language models via OpenAI-compatible APIs"
|
11
|
+
|
12
|
+
# Minimalist, clean epilog design
|
13
|
+
epilog = f"\n{COLORS['yellow']}nGPT {COLORS['bold']}v{__version__}{COLORS['reset']} • {COLORS['green']}Docs: {COLORS['bold']}https://nazdridoy.github.io/ngpt/usage/cli_usage.html{COLORS['reset']}"
|
14
|
+
|
15
|
+
parser = argparse.ArgumentParser(description=description, formatter_class=ColoredHelpFormatter, epilog=epilog)
|
16
|
+
|
17
|
+
# Add custom error method with color
|
18
|
+
original_error = parser.error
|
19
|
+
def error_with_color(message):
|
20
|
+
parser.print_usage(sys.stderr)
|
21
|
+
parser.exit(2, f"{COLORS['bold']}{COLORS['yellow']}error: {COLORS['reset']}{message}\n")
|
22
|
+
parser.error = error_with_color
|
23
|
+
|
24
|
+
# Custom version action with color
|
25
|
+
class ColoredVersionAction(argparse.Action):
|
26
|
+
def __call__(self, parser, namespace, values, option_string=None):
|
27
|
+
print(f"{COLORS['green']}{COLORS['bold']}nGPT{COLORS['reset']} version {COLORS['yellow']}{__version__}{COLORS['reset']}")
|
28
|
+
parser.exit()
|
29
|
+
|
30
|
+
# Version flag
|
31
|
+
parser.add_argument('-v', '--version', action=ColoredVersionAction, nargs=0, help='Show version information and exit')
|
32
|
+
|
33
|
+
# Config options
|
34
|
+
config_group = parser.add_argument_group('Configuration Options')
|
35
|
+
config_group.add_argument('--config', nargs='?', const=True, help='Path to a custom config file or, if no value provided, enter interactive configuration mode to create a new config')
|
36
|
+
config_group.add_argument('--config-index', type=int, default=0, help='Index of the configuration to use or edit (default: 0)')
|
37
|
+
config_group.add_argument('--provider', help='Provider name to identify the configuration to use')
|
38
|
+
config_group.add_argument('--remove', action='store_true', help='Remove the configuration at the specified index (requires --config and --config-index)')
|
39
|
+
config_group.add_argument('--show-config', action='store_true', help='Show the current configuration(s) and exit')
|
40
|
+
config_group.add_argument('--all', action='store_true', help='Show details for all configurations (requires --show-config)')
|
41
|
+
config_group.add_argument('--list-models', action='store_true', help='List all available models for the current configuration and exit')
|
42
|
+
config_group.add_argument('--list-renderers', action='store_true', help='Show available markdown renderers for use with --prettify')
|
43
|
+
|
44
|
+
# Global options
|
45
|
+
global_group = parser.add_argument_group('Global Options')
|
46
|
+
global_group.add_argument('--api-key', help='API key for the service')
|
47
|
+
global_group.add_argument('--base-url', help='Base URL for the API')
|
48
|
+
global_group.add_argument('--model', help='Model to use')
|
49
|
+
global_group.add_argument('--web-search', action='store_true',
|
50
|
+
help='Enable web search capability (Note: Your API endpoint must support this feature)')
|
51
|
+
global_group.add_argument('-n', '--no-stream', action='store_true',
|
52
|
+
help='Return the whole response without streaming')
|
53
|
+
global_group.add_argument('--temperature', type=float, default=0.7,
|
54
|
+
help='Set temperature (controls randomness, default: 0.7)')
|
55
|
+
global_group.add_argument('--top_p', type=float, default=1.0,
|
56
|
+
help='Set top_p (controls diversity, default: 1.0)')
|
57
|
+
global_group.add_argument('--max_tokens', type=int,
|
58
|
+
help='Set max response length in tokens')
|
59
|
+
global_group.add_argument('--log', metavar='FILE',
|
60
|
+
help='Set filepath to log conversation to (For interactive modes)')
|
61
|
+
global_group.add_argument('--preprompt',
|
62
|
+
help='Set custom system prompt to control AI behavior')
|
63
|
+
global_group.add_argument('--prettify', action='store_const', const='auto',
|
64
|
+
help='Render markdown responses and code with syntax highlighting and formatting')
|
65
|
+
global_group.add_argument('--stream-prettify', action='store_true',
|
66
|
+
help='Enable streaming with markdown rendering (automatically uses Rich renderer)')
|
67
|
+
global_group.add_argument('--renderer', choices=['auto', 'rich', 'glow'], default='auto',
|
68
|
+
help='Select which markdown renderer to use with --prettify (auto, rich, or glow)')
|
69
|
+
|
70
|
+
# Mode flags (mutually exclusive)
|
71
|
+
mode_group = parser.add_argument_group('Modes (mutually exclusive)')
|
72
|
+
mode_exclusive_group = mode_group.add_mutually_exclusive_group()
|
73
|
+
mode_exclusive_group.add_argument('-i', '--interactive', action='store_true', help='Start an interactive chat session')
|
74
|
+
mode_exclusive_group.add_argument('-s', '--shell', action='store_true', help='Generate and execute shell commands')
|
75
|
+
mode_exclusive_group.add_argument('-c', '--code', action='store_true', help='Generate code')
|
76
|
+
mode_exclusive_group.add_argument('-t', '--text', action='store_true', help='Enter multi-line text input (submit with Ctrl+D)')
|
77
|
+
# Note: --show-config is handled separately and implicitly acts as a mode
|
78
|
+
|
79
|
+
# Language option for code mode
|
80
|
+
parser.add_argument('--language', default="python", help='Programming language to generate code in (for code mode)')
|
81
|
+
|
82
|
+
# Prompt argument
|
83
|
+
parser.add_argument('prompt', nargs='?', default=None, help='The prompt to send')
|
84
|
+
|
85
|
+
# Add CLI configuration command
|
86
|
+
config_group.add_argument('--cli-config', nargs='*', metavar='COMMAND',
|
87
|
+
help='Manage CLI configuration (set, get, unset, list)')
|
88
|
+
|
89
|
+
return parser
|
90
|
+
|
91
|
+
def parse_args():
|
92
|
+
"""Parse command line arguments using the configured parser."""
|
93
|
+
parser = setup_argument_parser()
|
94
|
+
return parser.parse_args()
|
95
|
+
|
96
|
+
def validate_args(args):
|
97
|
+
"""Validate parsed arguments for correctness and compatibility."""
|
98
|
+
# Validate --all usage
|
99
|
+
if args.all and not args.show_config:
|
100
|
+
raise ValueError("--all can only be used with --show-config")
|
101
|
+
|
102
|
+
# Check if --prettify is used with --stream-prettify (conflict)
|
103
|
+
if args.prettify and args.stream_prettify:
|
104
|
+
raise ValueError("--prettify and --stream-prettify cannot be used together. Choose one option.")
|
105
|
+
|
106
|
+
# Check if --stream-prettify is used but Rich is not available
|
107
|
+
if args.stream_prettify and not has_markdown_renderer('rich'):
|
108
|
+
raise ValueError("--stream-prettify requires Rich to be installed. Install with: pip install \"ngpt[full]\" or pip install rich")
|
109
|
+
|
110
|
+
return args
|
111
|
+
|
112
|
+
def validate_markdown_renderer(args):
|
113
|
+
"""Validate that required markdown renderers are available.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
args: The parsed command line arguments.
|
117
|
+
|
118
|
+
Returns:
|
119
|
+
tuple: (has_renderer, args)
|
120
|
+
- has_renderer: Boolean indicating if a renderer is available
|
121
|
+
- args: Potentially modified args with prettify disabled if no renderer is available
|
122
|
+
"""
|
123
|
+
has_renderer = True
|
124
|
+
if args.prettify:
|
125
|
+
has_renderer = warn_if_no_markdown_renderer(args.renderer)
|
126
|
+
if not has_renderer:
|
127
|
+
# Set a flag to disable prettify since we already warned the user
|
128
|
+
print(f"{COLORS['yellow']}Continuing without markdown rendering.{COLORS['reset']}")
|
129
|
+
args.prettify = False
|
130
|
+
|
131
|
+
return has_renderer, args
|
132
|
+
|
133
|
+
def handle_cli_config_args(args):
|
134
|
+
"""Process CLI configuration arguments and determine command parameters.
|
135
|
+
|
136
|
+
Args:
|
137
|
+
args: The parsed command line arguments.
|
138
|
+
|
139
|
+
Returns:
|
140
|
+
tuple: (should_handle, action, option, value)
|
141
|
+
- should_handle: True if --cli-config was specified and should be handled
|
142
|
+
- action: The action to perform (set, get, unset, list, help)
|
143
|
+
- option: The option name (or None)
|
144
|
+
- value: The option value (or None)
|
145
|
+
"""
|
146
|
+
if args.cli_config is None:
|
147
|
+
return (False, None, None, None)
|
148
|
+
|
149
|
+
# Show help if no arguments or "help" argument
|
150
|
+
if len(args.cli_config) == 0 or (len(args.cli_config) > 0 and args.cli_config[0].lower() == "help"):
|
151
|
+
return (True, "help", None, None)
|
152
|
+
|
153
|
+
action = args.cli_config[0].lower()
|
154
|
+
option = args.cli_config[1] if len(args.cli_config) > 1 else None
|
155
|
+
value = args.cli_config[2] if len(args.cli_config) > 2 else None
|
156
|
+
|
157
|
+
if action in ("set", "get", "unset", "list", "help"):
|
158
|
+
return (True, action, option, value)
|
159
|
+
else:
|
160
|
+
# Unknown action, show help
|
161
|
+
return (True, "help", None, None)
|
ngpt/cli/main.py
CHANGED
@@ -22,11 +22,13 @@ from .modes.chat import chat_mode
|
|
22
22
|
from .modes.code import code_mode
|
23
23
|
from .modes.shell import shell_mode
|
24
24
|
from .modes.text import text_mode
|
25
|
+
from .args import parse_args, validate_args, handle_cli_config_args, setup_argument_parser, validate_markdown_renderer
|
25
26
|
|
26
27
|
def show_cli_config_help():
|
27
28
|
"""Display help information about CLI configuration."""
|
28
29
|
print(f"\n{COLORS['green']}{COLORS['bold']}CLI Configuration Help:{COLORS['reset']}")
|
29
30
|
print(f" {COLORS['cyan']}Command syntax:{COLORS['reset']}")
|
31
|
+
print(f" {COLORS['yellow']}ngpt --cli-config help{COLORS['reset']} - Show this help message")
|
30
32
|
print(f" {COLORS['yellow']}ngpt --cli-config set OPTION VALUE{COLORS['reset']} - Set a default value for OPTION")
|
31
33
|
print(f" {COLORS['yellow']}ngpt --cli-config get OPTION{COLORS['reset']} - Get the current value of OPTION")
|
32
34
|
print(f" {COLORS['yellow']}ngpt --cli-config get{COLORS['reset']} - Show all CLI configuration settings")
|
@@ -83,16 +85,25 @@ def show_cli_config_help():
|
|
83
85
|
print(f" {COLORS['yellow']}ngpt --cli-config set language java{COLORS['reset']} - Set default language to java for code generation")
|
84
86
|
print(f" {COLORS['yellow']}ngpt --cli-config set temperature 0.9{COLORS['reset']} - Set default temperature to 0.9")
|
85
87
|
print(f" {COLORS['yellow']}ngpt --cli-config set no-stream true{COLORS['reset']} - Disable streaming by default")
|
88
|
+
print(f" {COLORS['yellow']}ngpt --cli-config get temperature{COLORS['reset']} - Check the current temperature setting")
|
89
|
+
print(f" {COLORS['yellow']}ngpt --cli-config get{COLORS['reset']} - Show all current CLI settings")
|
86
90
|
print(f" {COLORS['yellow']}ngpt --cli-config unset language{COLORS['reset']} - Remove language setting")
|
87
91
|
|
88
92
|
print(f"\n {COLORS['cyan']}Notes:{COLORS['reset']}")
|
89
|
-
print(f" - CLI configuration is stored in
|
93
|
+
print(f" - CLI configuration is stored in:")
|
94
|
+
print(f" • Linux: {COLORS['yellow']}~/.config/ngpt/ngpt-cli.conf{COLORS['reset']}")
|
95
|
+
print(f" • macOS: {COLORS['yellow']}~/Library/Application Support/ngpt/ngpt-cli.conf{COLORS['reset']}")
|
96
|
+
print(f" • Windows: {COLORS['yellow']}%APPDATA%\\ngpt\\ngpt-cli.conf{COLORS['reset']}")
|
90
97
|
print(f" - Settings are applied based on context (e.g., language only applies to code generation mode)")
|
91
98
|
print(f" - Command-line arguments always override CLI configuration")
|
92
99
|
print(f" - Some options are mutually exclusive and will not be applied together")
|
93
100
|
|
94
101
|
def handle_cli_config(action, option=None, value=None):
|
95
102
|
"""Handle CLI configuration commands."""
|
103
|
+
if action == "help":
|
104
|
+
show_cli_config_help()
|
105
|
+
return
|
106
|
+
|
96
107
|
if action == "list":
|
97
108
|
# List all available options
|
98
109
|
print(f"{COLORS['green']}{COLORS['bold']}Available CLI configuration options:{COLORS['reset']}")
|
@@ -161,109 +172,20 @@ def handle_cli_config(action, option=None, value=None):
|
|
161
172
|
show_cli_config_help()
|
162
173
|
|
163
174
|
def main():
|
164
|
-
#
|
165
|
-
|
166
|
-
|
167
|
-
# Minimalist, clean epilog design
|
168
|
-
epilog = f"\n{COLORS['yellow']}nGPT {COLORS['bold']}v{__version__}{COLORS['reset']} • {COLORS['green']}Docs: {COLORS['bold']}https://nazdridoy.github.io/ngpt/usage/cli_usage.html{COLORS['reset']}"
|
169
|
-
|
170
|
-
parser = argparse.ArgumentParser(description=description, formatter_class=ColoredHelpFormatter, epilog=epilog)
|
171
|
-
|
172
|
-
# Add custom error method with color
|
173
|
-
original_error = parser.error
|
174
|
-
def error_with_color(message):
|
175
|
-
parser.print_usage(sys.stderr)
|
176
|
-
parser.exit(2, f"{COLORS['bold']}{COLORS['yellow']}error: {COLORS['reset']}{message}\n")
|
177
|
-
parser.error = error_with_color
|
178
|
-
|
179
|
-
# Custom version action with color
|
180
|
-
class ColoredVersionAction(argparse.Action):
|
181
|
-
def __call__(self, parser, namespace, values, option_string=None):
|
182
|
-
print(f"{COLORS['green']}{COLORS['bold']}nGPT{COLORS['reset']} version {COLORS['yellow']}{__version__}{COLORS['reset']}")
|
183
|
-
parser.exit()
|
184
|
-
|
185
|
-
# Version flag
|
186
|
-
parser.add_argument('-v', '--version', action=ColoredVersionAction, nargs=0, help='Show version information and exit')
|
175
|
+
# Parse command line arguments using args.py
|
176
|
+
args = parse_args()
|
187
177
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
config_group.add_argument('--remove', action='store_true', help='Remove the configuration at the specified index (requires --config and --config-index)')
|
194
|
-
config_group.add_argument('--show-config', action='store_true', help='Show the current configuration(s) and exit')
|
195
|
-
config_group.add_argument('--all', action='store_true', help='Show details for all configurations (requires --show-config)')
|
196
|
-
config_group.add_argument('--list-models', action='store_true', help='List all available models for the current configuration and exit')
|
197
|
-
config_group.add_argument('--list-renderers', action='store_true', help='Show available markdown renderers for use with --prettify')
|
198
|
-
|
199
|
-
# Global options
|
200
|
-
global_group = parser.add_argument_group('Global Options')
|
201
|
-
global_group.add_argument('--api-key', help='API key for the service')
|
202
|
-
global_group.add_argument('--base-url', help='Base URL for the API')
|
203
|
-
global_group.add_argument('--model', help='Model to use')
|
204
|
-
global_group.add_argument('--web-search', action='store_true',
|
205
|
-
help='Enable web search capability (Note: Your API endpoint must support this feature)')
|
206
|
-
global_group.add_argument('-n', '--no-stream', action='store_true',
|
207
|
-
help='Return the whole response without streaming')
|
208
|
-
global_group.add_argument('--temperature', type=float, default=0.7,
|
209
|
-
help='Set temperature (controls randomness, default: 0.7)')
|
210
|
-
global_group.add_argument('--top_p', type=float, default=1.0,
|
211
|
-
help='Set top_p (controls diversity, default: 1.0)')
|
212
|
-
global_group.add_argument('--max_tokens', type=int,
|
213
|
-
help='Set max response length in tokens')
|
214
|
-
global_group.add_argument('--log', metavar='FILE',
|
215
|
-
help='Set filepath to log conversation to (For interactive modes)')
|
216
|
-
global_group.add_argument('--preprompt',
|
217
|
-
help='Set custom system prompt to control AI behavior')
|
218
|
-
global_group.add_argument('--prettify', action='store_const', const='auto',
|
219
|
-
help='Render markdown responses and code with syntax highlighting and formatting')
|
220
|
-
global_group.add_argument('--stream-prettify', action='store_true',
|
221
|
-
help='Enable streaming with markdown rendering (automatically uses Rich renderer)')
|
222
|
-
global_group.add_argument('--renderer', choices=['auto', 'rich', 'glow'], default='auto',
|
223
|
-
help='Select which markdown renderer to use with --prettify (auto, rich, or glow)')
|
224
|
-
|
225
|
-
# Mode flags (mutually exclusive)
|
226
|
-
mode_group = parser.add_argument_group('Modes (mutually exclusive)')
|
227
|
-
mode_exclusive_group = mode_group.add_mutually_exclusive_group()
|
228
|
-
mode_exclusive_group.add_argument('-i', '--interactive', action='store_true', help='Start an interactive chat session')
|
229
|
-
mode_exclusive_group.add_argument('-s', '--shell', action='store_true', help='Generate and execute shell commands')
|
230
|
-
mode_exclusive_group.add_argument('-c', '--code', action='store_true', help='Generate code')
|
231
|
-
mode_exclusive_group.add_argument('-t', '--text', action='store_true', help='Enter multi-line text input (submit with Ctrl+D)')
|
232
|
-
# Note: --show-config is handled separately and implicitly acts as a mode
|
233
|
-
|
234
|
-
# Language option for code mode
|
235
|
-
parser.add_argument('--language', default="python", help='Programming language to generate code in (for code mode)')
|
236
|
-
|
237
|
-
# Prompt argument
|
238
|
-
parser.add_argument('prompt', nargs='?', default=None, help='The prompt to send')
|
239
|
-
|
240
|
-
# Add CLI configuration command
|
241
|
-
config_group.add_argument('--cli-config', nargs='*', metavar='COMMAND',
|
242
|
-
help='Manage CLI configuration (set, get, unset, list)')
|
243
|
-
|
244
|
-
args = parser.parse_args()
|
178
|
+
try:
|
179
|
+
args = validate_args(args)
|
180
|
+
except ValueError as e:
|
181
|
+
print(f"{COLORS['bold']}{COLORS['yellow']}error: {COLORS['reset']}{str(e)}\n")
|
182
|
+
sys.exit(2)
|
245
183
|
|
246
184
|
# Handle CLI configuration command
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
return
|
252
|
-
|
253
|
-
action = args.cli_config[0].lower()
|
254
|
-
option = args.cli_config[1] if len(args.cli_config) > 1 else None
|
255
|
-
value = args.cli_config[2] if len(args.cli_config) > 2 else None
|
256
|
-
|
257
|
-
if action in ("set", "get", "unset", "list"):
|
258
|
-
handle_cli_config(action, option, value)
|
259
|
-
return
|
260
|
-
else:
|
261
|
-
show_cli_config_help()
|
262
|
-
return
|
263
|
-
|
264
|
-
# Validate --all usage
|
265
|
-
if args.all and not args.show_config:
|
266
|
-
parser.error("--all can only be used with --show-config")
|
185
|
+
should_handle_cli_config, action, option, value = handle_cli_config_args(args)
|
186
|
+
if should_handle_cli_config:
|
187
|
+
handle_cli_config(action, option, value)
|
188
|
+
return
|
267
189
|
|
268
190
|
# Handle --renderers flag to show available markdown renderers
|
269
191
|
if args.list_renderers:
|
@@ -283,15 +205,23 @@ def main():
|
|
283
205
|
effective_config_index = args.config_index
|
284
206
|
|
285
207
|
# Only apply CLI config for provider/config-index if not explicitly set on command line
|
286
|
-
|
208
|
+
# If --config-index is explicitly provided, we should ignore provider from CLI config
|
209
|
+
config_index_from_cli = '--config-index' in sys.argv
|
210
|
+
provider_from_cli = '--provider' in sys.argv
|
211
|
+
|
212
|
+
if not provider_from_cli and 'provider' in cli_config and not config_index_from_cli:
|
287
213
|
effective_provider = cli_config['provider']
|
288
214
|
|
289
|
-
if
|
215
|
+
if not config_index_from_cli and 'config-index' in cli_config and not provider_from_cli:
|
290
216
|
effective_config_index = cli_config['config-index']
|
291
217
|
|
292
218
|
# Check for mutual exclusivity between provider and config-index
|
293
219
|
if effective_config_index != 0 and effective_provider:
|
294
|
-
|
220
|
+
from_cli_config = not provider_from_cli and 'provider' in cli_config
|
221
|
+
provider_source = "CLI config file (ngpt-cli.conf)" if from_cli_config else "command-line arguments"
|
222
|
+
error_msg = f"--config-index and --provider cannot be used together. Provider from {provider_source}."
|
223
|
+
print(f"{COLORS['bold']}{COLORS['yellow']}error: {COLORS['reset']}{error_msg}\n")
|
224
|
+
sys.exit(2)
|
295
225
|
|
296
226
|
# Handle interactive configuration mode
|
297
227
|
if args.config is True: # --config was used without a value
|
@@ -301,7 +231,8 @@ def main():
|
|
301
231
|
if args.remove:
|
302
232
|
# Validate that config_index is explicitly provided
|
303
233
|
if '--config-index' not in sys.argv and not effective_provider:
|
304
|
-
|
234
|
+
print(f"{COLORS['bold']}{COLORS['yellow']}error: {COLORS['reset']}--remove requires explicitly specifying --config-index or --provider\n")
|
235
|
+
sys.exit(2)
|
305
236
|
|
306
237
|
# Show config details before asking for confirmation
|
307
238
|
configs = load_configs(str(config_path))
|
@@ -479,6 +410,8 @@ def main():
|
|
479
410
|
|
480
411
|
# For interactive mode, we'll allow continuing without a specific prompt
|
481
412
|
if not args.prompt and not (args.shell or args.code or args.text or args.interactive or args.show_config or args.list_models):
|
413
|
+
# Simply use the parser's help
|
414
|
+
parser = setup_argument_parser()
|
482
415
|
parser.print_help()
|
483
416
|
return
|
484
417
|
|
@@ -488,23 +421,10 @@ def main():
|
|
488
421
|
|
489
422
|
# Check if --prettify is used but no markdown renderer is available
|
490
423
|
# This will warn the user immediately if they request prettify but don't have the tools
|
491
|
-
has_renderer =
|
492
|
-
if
|
493
|
-
|
494
|
-
|
495
|
-
# Set a flag to disable prettify since we already warned the user
|
496
|
-
print(f"{COLORS['yellow']}Continuing without markdown rendering.{COLORS['reset']}")
|
497
|
-
show_available_renderers()
|
498
|
-
args.prettify = False
|
499
|
-
|
500
|
-
# Check if --prettify is used with --stream-prettify (conflict)
|
501
|
-
if args.prettify and args.stream_prettify:
|
502
|
-
parser.error("--prettify and --stream-prettify cannot be used together. Choose one option.")
|
503
|
-
|
504
|
-
# Check if --stream-prettify is used but Rich is not available
|
505
|
-
if args.stream_prettify and not has_markdown_renderer('rich'):
|
506
|
-
parser.error("--stream-prettify requires Rich to be installed. Install with: pip install \"ngpt[full]\" or pip install rich")
|
507
|
-
|
424
|
+
has_renderer, args = validate_markdown_renderer(args)
|
425
|
+
if not has_renderer:
|
426
|
+
show_available_renderers()
|
427
|
+
|
508
428
|
# Initialize client using the potentially overridden active_config
|
509
429
|
client = NGPTClient(**active_config)
|
510
430
|
|
ngpt/cli_config.py
CHANGED
@@ -7,7 +7,7 @@ from typing import Dict, Optional, Any, List, Union, Tuple
|
|
7
7
|
# CLI config options with their types and default values
|
8
8
|
CLI_CONFIG_OPTIONS = {
|
9
9
|
"language": {"type": "str", "default": "python", "context": ["code"]},
|
10
|
-
"provider": {"type": "str", "default": None, "context": ["all"]},
|
10
|
+
"provider": {"type": "str", "default": None, "context": ["all"], "exclusive": ["config-index"]},
|
11
11
|
"temperature": {"type": "float", "default": 0.7, "context": ["all"]},
|
12
12
|
"top_p": {"type": "float", "default": 1.0, "context": ["all"]},
|
13
13
|
"max_tokens": {"type": "int", "default": None, "context": ["all"]},
|
@@ -17,7 +17,7 @@ CLI_CONFIG_OPTIONS = {
|
|
17
17
|
"prettify": {"type": "bool", "default": False, "context": ["all"], "exclusive": ["no-stream", "stream-prettify"]},
|
18
18
|
"stream-prettify": {"type": "bool", "default": False, "context": ["all"], "exclusive": ["no-stream", "prettify"]},
|
19
19
|
"renderer": {"type": "str", "default": "auto", "context": ["all"]},
|
20
|
-
"config-index": {"type": "int", "default": 0, "context": ["all"]},
|
20
|
+
"config-index": {"type": "int", "default": 0, "context": ["all"], "exclusive": ["provider"]},
|
21
21
|
"web-search": {"type": "bool", "default": False, "context": ["all"]},
|
22
22
|
}
|
23
23
|
|
@@ -113,13 +113,19 @@ def set_cli_config_option(option: str, value: Any) -> Tuple[bool, str]:
|
|
113
113
|
else:
|
114
114
|
return False, f"Error: Unsupported option type '{option_type}'"
|
115
115
|
|
116
|
-
# Handle mutual exclusivity for
|
117
|
-
if
|
118
|
-
if
|
119
|
-
#
|
116
|
+
# Handle mutual exclusivity for options
|
117
|
+
if "exclusive" in CLI_CONFIG_OPTIONS[option]:
|
118
|
+
if option_type == "bool":
|
119
|
+
# For boolean options: only apply exclusivity when setting to True
|
120
|
+
if parsed_value:
|
121
|
+
for excl_option in CLI_CONFIG_OPTIONS[option]["exclusive"]:
|
122
|
+
config[excl_option] = False
|
123
|
+
# If setting to False, don't alter exclusive options
|
124
|
+
else:
|
125
|
+
# For non-boolean options: If setting this option to any value, remove exclusive options
|
120
126
|
for excl_option in CLI_CONFIG_OPTIONS[option]["exclusive"]:
|
121
|
-
|
122
|
-
|
127
|
+
if excl_option in config:
|
128
|
+
del config[excl_option]
|
123
129
|
|
124
130
|
# Set the value in the config
|
125
131
|
config[option] = parsed_value
|
@@ -203,6 +209,9 @@ def apply_cli_config(args: Any, mode: str) -> Any:
|
|
203
209
|
# Get command-line arguments provided by the user
|
204
210
|
explicit_args = set(arg for arg in sys.argv[1:] if arg.startswith('--'))
|
205
211
|
|
212
|
+
# Keep track of applied exclusive options
|
213
|
+
applied_exclusives = set()
|
214
|
+
|
206
215
|
# For each option in CLI config, check if it should be applied
|
207
216
|
for option, value in cli_config.items():
|
208
217
|
# Skip if not a valid option
|
@@ -221,6 +230,13 @@ def apply_cli_config(args: Any, mode: str) -> Any:
|
|
221
230
|
# Check common variants like --option
|
222
231
|
cli_option = f"--{option}"
|
223
232
|
if cli_option in explicit_args:
|
233
|
+
# Add to applied_exclusives if this option has exclusivity constraints
|
234
|
+
if "exclusive" in CLI_CONFIG_OPTIONS[option]:
|
235
|
+
applied_exclusives.update(CLI_CONFIG_OPTIONS[option]["exclusive"])
|
236
|
+
continue
|
237
|
+
|
238
|
+
# Skip if an exclusive option has already been applied
|
239
|
+
if option in applied_exclusives:
|
224
240
|
continue
|
225
241
|
|
226
242
|
# Check exclusivity constraints against *explicitly set* args
|
@@ -234,6 +250,9 @@ def apply_cli_config(args: Any, mode: str) -> Any:
|
|
234
250
|
break # Skip applying this CLI config value
|
235
251
|
if skip:
|
236
252
|
continue
|
253
|
+
|
254
|
+
# If we're applying this option, add its exclusives to the tracking set
|
255
|
+
applied_exclusives.update(CLI_CONFIG_OPTIONS[option]["exclusive"])
|
237
256
|
|
238
257
|
# Apply the value from CLI config
|
239
258
|
# Ensure the attribute exists on args before setting
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ngpt
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.10.0
|
4
4
|
Summary: A lightweight Python CLI and library for interacting with OpenAI-compatible APIs, supporting both official and self-hosted LLM endpoints.
|
5
5
|
Project-URL: Homepage, https://github.com/nazdridoy/ngpt
|
6
6
|
Project-URL: Repository, https://github.com/nazdridoy/ngpt
|
@@ -135,6 +135,7 @@ For more examples and detailed usage, visit the [CLI Usage Guide](https://nazdri
|
|
135
135
|
- 🎭 **System Prompts**: Customize model behavior with custom system prompts
|
136
136
|
- 📃 **Conversation Logging**: Save your conversations to text files for later reference
|
137
137
|
- 🧰 **CLI Components**: Reusable components for building custom AI-powered command-line tools
|
138
|
+
- 🔌 **Modular Architecture**: Well-structured codebase with clean separation of concerns
|
138
139
|
|
139
140
|
See the [Feature Overview](https://nazdridoy.github.io/ngpt/overview.html) for more details.
|
140
141
|
|
@@ -292,16 +293,14 @@ nGPT can also be used as a framework to build your own AI-powered command-line t
|
|
292
293
|
|
293
294
|
```python
|
294
295
|
from ngpt import NGPTClient, load_config
|
295
|
-
from ngpt.cli.
|
296
|
+
from ngpt.cli.interactive import interactive_chat_session
|
296
297
|
from ngpt.cli.renderers import prettify_markdown
|
297
|
-
from ngpt.cli.
|
298
|
-
import
|
298
|
+
from ngpt.cli.args import setup_argument_parser
|
299
|
+
import sys
|
299
300
|
|
300
301
|
# Create a custom CLI tool with colorized help
|
301
|
-
parser =
|
302
|
-
|
303
|
-
formatter_class=ColoredHelpFormatter
|
304
|
-
)
|
302
|
+
parser = setup_argument_parser()
|
303
|
+
parser.description = "Specialized Code Assistant"
|
305
304
|
parser.add_argument("prompt", nargs="?", help="Code description")
|
306
305
|
parser.add_argument("--language", "-l", default="python", help="Programming language")
|
307
306
|
parser.add_argument("--interactive", "-i", action="store_true", help="Start interactive mode")
|
@@ -318,6 +317,9 @@ elif args.prompt:
|
|
318
317
|
# Generate and prettify code
|
319
318
|
code = client.generate_code(args.prompt, language=args.language)
|
320
319
|
print(prettify_markdown(f"```{args.language}\n{code}\n```"))
|
320
|
+
else:
|
321
|
+
parser.print_help()
|
322
|
+
sys.exit(1)
|
321
323
|
```
|
322
324
|
|
323
325
|
This allows you to build specialized AI tools like:
|
@@ -1,13 +1,14 @@
|
|
1
1
|
ngpt/__init__.py,sha256=awvycdj3tgcOr0BO81L4XU6DOtnToxFqkPHe1Pyu0Bw,652
|
2
2
|
ngpt/cli.py,sha256=j3eFYPOtCCFBOGh7NK5IWEnADnTMMSEB9GLyIDoW724,66
|
3
|
-
ngpt/cli_config.py,sha256=
|
3
|
+
ngpt/cli_config.py,sha256=Om8dXqdBqPCP5V4THQMkzZgHTQvN2rMAV6QjoVDQcZ4,10000
|
4
4
|
ngpt/client.py,sha256=Rv-JO8RAmw1v3gdLkwaPe_PEw6p83cejO0YNT_DDjeg,15134
|
5
5
|
ngpt/config.py,sha256=WYOk_b1eiYjo6hpV3pfXr2RjqhOnmKqwZwKid1T41I4,10363
|
6
6
|
ngpt/cli/__init__.py,sha256=hebbDSMGiOd43YNnQP67uzr67Ue6rZPwm2czynr5iZY,43
|
7
|
+
ngpt/cli/args.py,sha256=Aybt1oyhSSeWZ4oC5CWUjHQ5P6dfRTMnxi2VIScVGoo,8817
|
7
8
|
ngpt/cli/config_manager.py,sha256=L091h99ntMBth_FM39npGCOtDCV5kVkukNSkCIj6dpI,3752
|
8
9
|
ngpt/cli/formatters.py,sha256=1ofNEWEZtFr0MJ3oWomoL_mFmZHlUdT3I5qGtbDQ4g0,9378
|
9
10
|
ngpt/cli/interactive.py,sha256=J6DFkJVBdJ6NjZllsDgJnY1J5RTiKW341p4Zn4wHpGc,11718
|
10
|
-
ngpt/cli/main.py,sha256=
|
11
|
+
ngpt/cli/main.py,sha256=xSHehXrTDH9HjZ6RBV2iBug-lXorpXDfMWP1oxEMBpU,24702
|
11
12
|
ngpt/cli/renderers.py,sha256=U3Vef3nY1NF2JKtLUtUjdFomyqIrijGWdxRPm46urr4,10546
|
12
13
|
ngpt/cli/ui.py,sha256=2JXkCRw5utaKpNZIy0u8F_Jh2zrWbm93dMz91wf9CkQ,5334
|
13
14
|
ngpt/cli/modes/__init__.py,sha256=11znFpqzHyRsEtaTrms5M3q2SrscT9VvUgr7C2B1o-E,179
|
@@ -16,8 +17,8 @@ ngpt/cli/modes/code.py,sha256=_Z3cKYdeifYZSXZ4dMnQWcnVpM2TvYQd-7S7Q3blfEw,3998
|
|
16
17
|
ngpt/cli/modes/shell.py,sha256=Fx83_JBc3P5vgCCPlXgXFSgzwTY0UMGfUwY4_CU10Ro,1654
|
17
18
|
ngpt/cli/modes/text.py,sha256=YpNpcujPweO_Biwg4aYwGw4_ShefzaNVtf8d_QrcR_Q,2719
|
18
19
|
ngpt/utils/__init__.py,sha256=NK8wlI9-YeaKPOaXBVfUj3mKOXohfD3GmNy5obOIXOM,20
|
19
|
-
ngpt-2.
|
20
|
-
ngpt-2.
|
21
|
-
ngpt-2.
|
22
|
-
ngpt-2.
|
23
|
-
ngpt-2.
|
20
|
+
ngpt-2.10.0.dist-info/METADATA,sha256=48epymmDevSCy9F2ItPVzelVF3NHGgXju87JDjEnrtw,20439
|
21
|
+
ngpt-2.10.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
22
|
+
ngpt-2.10.0.dist-info/entry_points.txt,sha256=1cnAMujyy34DlOahrJg19lePSnb08bLbkUs_kVerqdk,39
|
23
|
+
ngpt-2.10.0.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
|
24
|
+
ngpt-2.10.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|