ngpt 2.2.0__py3-none-any.whl → 2.3.1__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.py +321 -56
- {ngpt-2.2.0.dist-info → ngpt-2.3.1.dist-info}/METADATA +42 -1
- ngpt-2.3.1.dist-info/RECORD +9 -0
- ngpt-2.2.0.dist-info/RECORD +0 -9
- {ngpt-2.2.0.dist-info → ngpt-2.3.1.dist-info}/WHEEL +0 -0
- {ngpt-2.2.0.dist-info → ngpt-2.3.1.dist-info}/entry_points.txt +0 -0
- {ngpt-2.2.0.dist-info → ngpt-2.3.1.dist-info}/licenses/LICENSE +0 -0
ngpt/cli.py
CHANGED
@@ -5,6 +5,246 @@ from .client import NGPTClient
|
|
5
5
|
from .config import load_config, get_config_path, load_configs, add_config_entry, remove_config_entry
|
6
6
|
from . import __version__
|
7
7
|
|
8
|
+
# ANSI color codes for terminal output
|
9
|
+
COLORS = {
|
10
|
+
"reset": "\033[0m",
|
11
|
+
"bold": "\033[1m",
|
12
|
+
"cyan": "\033[36m",
|
13
|
+
"green": "\033[32m",
|
14
|
+
"yellow": "\033[33m",
|
15
|
+
"blue": "\033[34m",
|
16
|
+
"magenta": "\033[35m",
|
17
|
+
"gray": "\033[90m",
|
18
|
+
"bg_blue": "\033[44m",
|
19
|
+
"bg_cyan": "\033[46m"
|
20
|
+
}
|
21
|
+
|
22
|
+
# Check if ANSI colors are supported
|
23
|
+
def supports_ansi_colors():
|
24
|
+
"""Check if the current terminal supports ANSI colors."""
|
25
|
+
import os
|
26
|
+
import sys
|
27
|
+
|
28
|
+
# If not a TTY, probably redirected, so no color
|
29
|
+
if not sys.stdout.isatty():
|
30
|
+
return False
|
31
|
+
|
32
|
+
# Windows specific checks
|
33
|
+
if sys.platform == "win32":
|
34
|
+
try:
|
35
|
+
# Windows 10+ supports ANSI colors in cmd/PowerShell
|
36
|
+
import ctypes
|
37
|
+
kernel32 = ctypes.windll.kernel32
|
38
|
+
|
39
|
+
# Try to enable ANSI color support
|
40
|
+
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
|
41
|
+
|
42
|
+
# Check if TERM_PROGRAM is set (WSL/ConEmu/etc.)
|
43
|
+
if os.environ.get('TERM_PROGRAM') or os.environ.get('WT_SESSION'):
|
44
|
+
return True
|
45
|
+
|
46
|
+
# Check Windows version - 10+ supports ANSI natively
|
47
|
+
winver = sys.getwindowsversion()
|
48
|
+
if winver.major >= 10:
|
49
|
+
return True
|
50
|
+
|
51
|
+
return False
|
52
|
+
except Exception:
|
53
|
+
return False
|
54
|
+
|
55
|
+
# Most UNIX systems support ANSI colors
|
56
|
+
return True
|
57
|
+
|
58
|
+
# Initialize color support
|
59
|
+
HAS_COLOR = supports_ansi_colors()
|
60
|
+
|
61
|
+
# If we're on Windows, use brighter colors that work better in PowerShell
|
62
|
+
if sys.platform == "win32" and HAS_COLOR:
|
63
|
+
COLORS["magenta"] = "\033[95m" # Bright magenta for metavars
|
64
|
+
COLORS["cyan"] = "\033[96m" # Bright cyan for options
|
65
|
+
|
66
|
+
# If no color support, use empty color codes
|
67
|
+
if not HAS_COLOR:
|
68
|
+
for key in COLORS:
|
69
|
+
COLORS[key] = ""
|
70
|
+
|
71
|
+
# Custom help formatter with color support
|
72
|
+
class ColoredHelpFormatter(argparse.HelpFormatter):
|
73
|
+
"""Help formatter that properly handles ANSI color codes without breaking alignment."""
|
74
|
+
|
75
|
+
def __init__(self, prog):
|
76
|
+
# Import modules needed for terminal size detection
|
77
|
+
import re
|
78
|
+
import textwrap
|
79
|
+
import shutil
|
80
|
+
|
81
|
+
# Get terminal size for dynamic width adjustment
|
82
|
+
try:
|
83
|
+
self.term_width = shutil.get_terminal_size().columns
|
84
|
+
except:
|
85
|
+
self.term_width = 80 # Default if we can't detect terminal width
|
86
|
+
|
87
|
+
# Calculate dynamic layout values based on terminal width
|
88
|
+
self.formatter_width = self.term_width - 2 # Leave some margin
|
89
|
+
|
90
|
+
# For very wide terminals, limit the width to maintain readability
|
91
|
+
if self.formatter_width > 120:
|
92
|
+
self.formatter_width = 120
|
93
|
+
|
94
|
+
# Calculate help position based on terminal width (roughly 1/3 of width)
|
95
|
+
self.help_position = min(max(20, int(self.term_width * 0.33)), 36)
|
96
|
+
|
97
|
+
# Initialize the parent class with dynamic values
|
98
|
+
super().__init__(prog, max_help_position=self.help_position, width=self.formatter_width)
|
99
|
+
|
100
|
+
# Calculate wrap width based on remaining space after help position
|
101
|
+
self.wrap_width = self.formatter_width - self.help_position - 5
|
102
|
+
|
103
|
+
# Set up the text wrapper for help text
|
104
|
+
self.ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
105
|
+
self.wrapper = textwrap.TextWrapper(width=self.wrap_width)
|
106
|
+
|
107
|
+
def _strip_ansi(self, s):
|
108
|
+
"""Strip ANSI escape sequences for width calculations"""
|
109
|
+
return self.ansi_escape.sub('', s)
|
110
|
+
|
111
|
+
def _colorize(self, text, color, bold=False):
|
112
|
+
"""Helper to consistently apply color with optional bold"""
|
113
|
+
if bold:
|
114
|
+
return f"{COLORS['bold']}{COLORS[color]}{text}{COLORS['reset']}"
|
115
|
+
return f"{COLORS[color]}{text}{COLORS['reset']}"
|
116
|
+
|
117
|
+
def _format_action_invocation(self, action):
|
118
|
+
if not action.option_strings:
|
119
|
+
# For positional arguments
|
120
|
+
metavar = self._format_args(action, action.dest.upper())
|
121
|
+
return self._colorize(metavar, 'cyan', bold=True)
|
122
|
+
else:
|
123
|
+
# For optional arguments with different color for metavar
|
124
|
+
if action.nargs != argparse.SUPPRESS:
|
125
|
+
default = self._get_default_metavar_for_optional(action)
|
126
|
+
args_string = self._format_args(action, default)
|
127
|
+
|
128
|
+
# Color option name and metavar differently
|
129
|
+
option_part = ', '.join(action.option_strings)
|
130
|
+
colored_option = self._colorize(option_part, 'cyan', bold=True)
|
131
|
+
|
132
|
+
if args_string:
|
133
|
+
# Make metavars more visible with brackets and color
|
134
|
+
# If HAS_COLOR is False, brackets will help in PowerShell
|
135
|
+
if not HAS_COLOR:
|
136
|
+
# Add brackets to make metavars stand out even without color
|
137
|
+
formatted_args = f"<{args_string}>"
|
138
|
+
else:
|
139
|
+
# Use color for metavar
|
140
|
+
formatted_args = self._colorize(args_string, 'magenta')
|
141
|
+
|
142
|
+
return f"{colored_option} {formatted_args}"
|
143
|
+
else:
|
144
|
+
return colored_option
|
145
|
+
else:
|
146
|
+
return self._colorize(', '.join(action.option_strings), 'cyan', bold=True)
|
147
|
+
|
148
|
+
def _format_usage(self, usage, actions, groups, prefix):
|
149
|
+
usage_text = super()._format_usage(usage, actions, groups, prefix)
|
150
|
+
|
151
|
+
# Replace "usage:" with colored version
|
152
|
+
colored_usage = self._colorize("usage:", 'green', bold=True)
|
153
|
+
usage_text = usage_text.replace("usage:", colored_usage)
|
154
|
+
|
155
|
+
# We won't color metavars in usage text as it breaks the formatting
|
156
|
+
# Just return with the colored usage prefix
|
157
|
+
return usage_text
|
158
|
+
|
159
|
+
def _join_parts(self, part_strings):
|
160
|
+
"""Override to fix any potential formatting issues with section joins"""
|
161
|
+
return '\n'.join([part for part in part_strings if part])
|
162
|
+
|
163
|
+
def start_section(self, heading):
|
164
|
+
# Remove the colon as we'll add it with color
|
165
|
+
if heading.endswith(':'):
|
166
|
+
heading = heading[:-1]
|
167
|
+
heading_text = f"{self._colorize(heading, 'yellow', bold=True)}:"
|
168
|
+
super().start_section(heading_text)
|
169
|
+
|
170
|
+
def _get_help_string(self, action):
|
171
|
+
# Add color to help strings
|
172
|
+
help_text = action.help
|
173
|
+
if help_text:
|
174
|
+
return help_text.replace('(default:', f"{COLORS['gray']}(default:") + COLORS['reset']
|
175
|
+
return help_text
|
176
|
+
|
177
|
+
def _wrap_help_text(self, text, initial_indent="", subsequent_indent=" "):
|
178
|
+
"""Wrap long help text to prevent overflow"""
|
179
|
+
if not text:
|
180
|
+
return text
|
181
|
+
|
182
|
+
# Strip ANSI codes for width calculation
|
183
|
+
clean_text = self._strip_ansi(text)
|
184
|
+
|
185
|
+
# If the text is already short enough, return it as is
|
186
|
+
if len(clean_text) <= self.wrap_width:
|
187
|
+
return text
|
188
|
+
|
189
|
+
# Handle any existing ANSI codes
|
190
|
+
has_ansi = text != clean_text
|
191
|
+
wrap_text = clean_text
|
192
|
+
|
193
|
+
# Wrap the text
|
194
|
+
lines = self.wrapper.wrap(wrap_text)
|
195
|
+
|
196
|
+
# Add indentation to all but the first line
|
197
|
+
wrapped = lines[0]
|
198
|
+
for line in lines[1:]:
|
199
|
+
wrapped += f"\n{subsequent_indent}{line}"
|
200
|
+
|
201
|
+
# Re-add the ANSI codes if they were present
|
202
|
+
if has_ansi and text.endswith(COLORS['reset']):
|
203
|
+
wrapped += COLORS['reset']
|
204
|
+
|
205
|
+
return wrapped
|
206
|
+
|
207
|
+
def _format_action(self, action):
|
208
|
+
# For subparsers, just return the regular formatting
|
209
|
+
if isinstance(action, argparse._SubParsersAction):
|
210
|
+
return super()._format_action(action)
|
211
|
+
|
212
|
+
# Get the action header with colored parts (both option names and metavars)
|
213
|
+
# The coloring is now done in _format_action_invocation
|
214
|
+
action_header = self._format_action_invocation(action)
|
215
|
+
|
216
|
+
# Format help text
|
217
|
+
help_text = self._expand_help(action)
|
218
|
+
|
219
|
+
# Get the raw lengths without ANSI codes for formatting
|
220
|
+
raw_header_len = len(self._strip_ansi(action_header))
|
221
|
+
|
222
|
+
# Calculate the indent for the help text
|
223
|
+
help_position = min(self._action_max_length + 2, self._max_help_position)
|
224
|
+
help_indent = ' ' * help_position
|
225
|
+
|
226
|
+
# If the action header is too long, put help on the next line
|
227
|
+
if raw_header_len > help_position:
|
228
|
+
# An action header that's too long gets a line break
|
229
|
+
# Wrap the help text with proper indentation
|
230
|
+
wrapped_help = self._wrap_help_text(help_text, subsequent_indent=help_indent)
|
231
|
+
line = f"{action_header}\n{help_indent}{wrapped_help}"
|
232
|
+
else:
|
233
|
+
# Standard formatting with proper spacing
|
234
|
+
padding = ' ' * (help_position - raw_header_len)
|
235
|
+
# Wrap the help text with proper indentation
|
236
|
+
wrapped_help = self._wrap_help_text(help_text, subsequent_indent=help_indent)
|
237
|
+
line = f"{action_header}{padding}{wrapped_help}"
|
238
|
+
|
239
|
+
# Handle subactions
|
240
|
+
if action.help is argparse.SUPPRESS:
|
241
|
+
return line
|
242
|
+
|
243
|
+
if not action.help:
|
244
|
+
return line
|
245
|
+
|
246
|
+
return line
|
247
|
+
|
8
248
|
# Optional imports for enhanced UI
|
9
249
|
try:
|
10
250
|
from prompt_toolkit import prompt as pt_prompt
|
@@ -26,81 +266,67 @@ except ImportError:
|
|
26
266
|
|
27
267
|
def show_config_help():
|
28
268
|
"""Display help information about configuration."""
|
29
|
-
print("\
|
30
|
-
print(" 1. Create a config file at one of these locations:")
|
269
|
+
print(f"\n{COLORS['green']}{COLORS['bold']}Configuration Help:{COLORS['reset']}")
|
270
|
+
print(f" 1. {COLORS['cyan']}Create a config file at one of these locations:{COLORS['reset']}")
|
31
271
|
if sys.platform == "win32":
|
32
|
-
print(f" - %APPDATA%\\ngpt\\ngpt.conf")
|
272
|
+
print(f" - {COLORS['yellow']}%APPDATA%\\ngpt\\ngpt.conf{COLORS['reset']}")
|
33
273
|
elif sys.platform == "darwin":
|
34
|
-
print(f" - ~/Library/Application Support/ngpt/ngpt.conf")
|
274
|
+
print(f" - {COLORS['yellow']}~/Library/Application Support/ngpt/ngpt.conf{COLORS['reset']}")
|
35
275
|
else:
|
36
|
-
print(f" - ~/.config/ngpt/ngpt.conf")
|
276
|
+
print(f" - {COLORS['yellow']}~/.config/ngpt/ngpt.conf{COLORS['reset']}")
|
37
277
|
|
38
|
-
print(" 2. Format your config file as JSON:")
|
39
|
-
print(""" [
|
40
|
-
{
|
278
|
+
print(f" 2. {COLORS['cyan']}Format your config file as JSON:{COLORS['reset']}")
|
279
|
+
print(f"""{COLORS['yellow']} [
|
280
|
+
{{
|
41
281
|
"api_key": "your-api-key-here",
|
42
282
|
"base_url": "https://api.openai.com/v1/",
|
43
283
|
"provider": "OpenAI",
|
44
284
|
"model": "gpt-3.5-turbo"
|
45
|
-
},
|
46
|
-
{
|
285
|
+
}},
|
286
|
+
{{
|
47
287
|
"api_key": "your-second-api-key",
|
48
288
|
"base_url": "http://localhost:1337/v1/",
|
49
289
|
"provider": "Another Provider",
|
50
290
|
"model": "different-model"
|
51
|
-
}
|
52
|
-
]""")
|
53
|
-
|
54
|
-
print(" 3. Or set environment variables:")
|
55
|
-
print(" - OPENAI_API_KEY")
|
56
|
-
print(" - OPENAI_BASE_URL")
|
57
|
-
print(" - OPENAI_MODEL")
|
58
|
-
|
59
|
-
print(" 4. Or provide command line arguments:")
|
60
|
-
print(" ngpt --api-key your-key --base-url https://api.example.com --model your-model \"Your prompt\"")
|
61
|
-
|
62
|
-
print(" 5. Use --config-index to specify which configuration to use or edit:")
|
63
|
-
print(" ngpt --config-index 1 \"Your prompt\"")
|
64
|
-
|
65
|
-
print(" 6. Use --config without arguments to add a new configuration:")
|
66
|
-
print(" ngpt --config")
|
67
|
-
print(" Or specify an index to edit an existing configuration:")
|
68
|
-
print(" ngpt --config --config-index 1")
|
69
|
-
print(" 7. Remove a configuration at a specific index:")
|
70
|
-
print(" ngpt --config --remove --config-index 1")
|
71
|
-
print(" 8. List available models for the current configuration:")
|
72
|
-
print(" ngpt --list-models")
|
291
|
+
}}
|
292
|
+
]{COLORS['reset']}""")
|
293
|
+
|
294
|
+
print(f" 3. {COLORS['cyan']}Or set environment variables:{COLORS['reset']}")
|
295
|
+
print(f" - {COLORS['yellow']}OPENAI_API_KEY{COLORS['reset']}")
|
296
|
+
print(f" - {COLORS['yellow']}OPENAI_BASE_URL{COLORS['reset']}")
|
297
|
+
print(f" - {COLORS['yellow']}OPENAI_MODEL{COLORS['reset']}")
|
298
|
+
|
299
|
+
print(f" 4. {COLORS['cyan']}Or provide command line arguments:{COLORS['reset']}")
|
300
|
+
print(f" {COLORS['yellow']}ngpt --api-key your-key --base-url https://api.example.com --model your-model \"Your prompt\"{COLORS['reset']}")
|
301
|
+
|
302
|
+
print(f" 5. {COLORS['cyan']}Use --config-index to specify which configuration to use or edit:{COLORS['reset']}")
|
303
|
+
print(f" {COLORS['yellow']}ngpt --config-index 1 \"Your prompt\"{COLORS['reset']}")
|
304
|
+
|
305
|
+
print(f" 6. {COLORS['cyan']}Use --config without arguments to add a new configuration:{COLORS['reset']}")
|
306
|
+
print(f" {COLORS['yellow']}ngpt --config{COLORS['reset']}")
|
307
|
+
print(f" Or specify an index to edit an existing configuration:")
|
308
|
+
print(f" {COLORS['yellow']}ngpt --config --config-index 1{COLORS['reset']}")
|
309
|
+
print(f" 7. {COLORS['cyan']}Remove a configuration at a specific index:{COLORS['reset']}")
|
310
|
+
print(f" {COLORS['yellow']}ngpt --config --remove --config-index 1{COLORS['reset']}")
|
311
|
+
print(f" 8. {COLORS['cyan']}List available models for the current configuration:{COLORS['reset']}")
|
312
|
+
print(f" {COLORS['yellow']}ngpt --list-models{COLORS['reset']}")
|
73
313
|
|
74
314
|
def check_config(config):
|
75
315
|
"""Check config for common issues and provide guidance."""
|
76
316
|
if not config.get("api_key"):
|
77
|
-
print("Error: API key is not set.")
|
317
|
+
print(f"{COLORS['yellow']}{COLORS['bold']}Error: API key is not set.{COLORS['reset']}")
|
78
318
|
show_config_help()
|
79
319
|
return False
|
80
320
|
|
81
321
|
# Check for common URL mistakes
|
82
322
|
base_url = config.get("base_url", "")
|
83
323
|
if base_url and not (base_url.startswith("http://") or base_url.startswith("https://")):
|
84
|
-
print(f"Warning: Base URL '{base_url}' doesn't start with http:// or https://")
|
324
|
+
print(f"{COLORS['yellow']}Warning: Base URL '{base_url}' doesn't start with http:// or https://{COLORS['reset']}")
|
85
325
|
|
86
326
|
return True
|
87
327
|
|
88
|
-
def interactive_chat_session(client, web_search=False, no_stream=False, temperature=0.7, top_p=1.0, max_length=None, log_file=None):
|
328
|
+
def interactive_chat_session(client, web_search=False, no_stream=False, temperature=0.7, top_p=1.0, max_length=None, log_file=None, preprompt=None):
|
89
329
|
"""Run an interactive chat session with conversation history."""
|
90
|
-
# Define ANSI color codes for terminal output
|
91
|
-
COLORS = {
|
92
|
-
"reset": "\033[0m",
|
93
|
-
"bold": "\033[1m",
|
94
|
-
"cyan": "\033[36m",
|
95
|
-
"green": "\033[32m",
|
96
|
-
"yellow": "\033[33m",
|
97
|
-
"blue": "\033[34m",
|
98
|
-
"magenta": "\033[35m",
|
99
|
-
"gray": "\033[90m",
|
100
|
-
"bg_blue": "\033[44m",
|
101
|
-
"bg_cyan": "\033[46m"
|
102
|
-
}
|
103
|
-
|
104
330
|
# Get terminal width for better formatting
|
105
331
|
try:
|
106
332
|
term_width = shutil.get_terminal_size().columns
|
@@ -146,11 +372,16 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
|
|
146
372
|
print(f"\n{separator}\n")
|
147
373
|
|
148
374
|
# Initialize conversation history
|
149
|
-
system_prompt = "You are a helpful assistant."
|
375
|
+
system_prompt = preprompt if preprompt else "You are a helpful assistant."
|
150
376
|
conversation = []
|
151
377
|
system_message = {"role": "system", "content": system_prompt}
|
152
378
|
conversation.append(system_message)
|
153
379
|
|
380
|
+
# Log system prompt if logging is enabled
|
381
|
+
if log_handle and preprompt:
|
382
|
+
log_handle.write(f"System: {system_prompt}\n\n")
|
383
|
+
log_handle.flush()
|
384
|
+
|
154
385
|
# Initialize prompt_toolkit history
|
155
386
|
prompt_history = InMemoryHistory() if HAS_PROMPT_TOOLKIT else None
|
156
387
|
|
@@ -295,10 +526,25 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
|
|
295
526
|
log_handle.close()
|
296
527
|
|
297
528
|
def main():
|
298
|
-
|
529
|
+
# Colorize description - use a shorter description to avoid line wrapping issues
|
530
|
+
description = f"{COLORS['cyan']}{COLORS['bold']}nGPT{COLORS['reset']} - Interact with AI language models via OpenAI-compatible APIs"
|
531
|
+
parser = argparse.ArgumentParser(description=description, formatter_class=ColoredHelpFormatter)
|
532
|
+
|
533
|
+
# Add custom error method with color
|
534
|
+
original_error = parser.error
|
535
|
+
def error_with_color(message):
|
536
|
+
parser.print_usage(sys.stderr)
|
537
|
+
parser.exit(2, f"{COLORS['bold']}{COLORS['yellow']}error: {COLORS['reset']}{message}\n")
|
538
|
+
parser.error = error_with_color
|
539
|
+
|
540
|
+
# Custom version action with color
|
541
|
+
class ColoredVersionAction(argparse.Action):
|
542
|
+
def __call__(self, parser, namespace, values, option_string=None):
|
543
|
+
print(f"{COLORS['green']}{COLORS['bold']}nGPT{COLORS['reset']} version {COLORS['yellow']}{__version__}{COLORS['reset']}")
|
544
|
+
parser.exit()
|
299
545
|
|
300
546
|
# Version flag
|
301
|
-
parser.add_argument('-v', '--version', action=
|
547
|
+
parser.add_argument('-v', '--version', action=ColoredVersionAction, nargs=0, help='Show version information and exit')
|
302
548
|
|
303
549
|
# Config options
|
304
550
|
config_group = parser.add_argument_group('Configuration Options')
|
@@ -326,6 +572,8 @@ def main():
|
|
326
572
|
help='Set max response length in tokens')
|
327
573
|
global_group.add_argument('--log', metavar='FILE',
|
328
574
|
help='Set filepath to log conversation to (For interactive modes)')
|
575
|
+
global_group.add_argument('--preprompt',
|
576
|
+
help='Set preprompt')
|
329
577
|
|
330
578
|
# Mode flags (mutually exclusive)
|
331
579
|
mode_group = parser.add_argument_group('Modes (mutually exclusive)')
|
@@ -486,7 +734,7 @@ def main():
|
|
486
734
|
# Interactive chat mode
|
487
735
|
interactive_chat_session(client, web_search=args.web_search, no_stream=args.no_stream,
|
488
736
|
temperature=args.temperature, top_p=args.top_p,
|
489
|
-
max_length=args.max_length, log_file=args.log)
|
737
|
+
max_length=args.max_length, log_file=args.log, preprompt=args.preprompt)
|
490
738
|
elif args.shell:
|
491
739
|
if args.prompt is None:
|
492
740
|
try:
|
@@ -543,7 +791,6 @@ def main():
|
|
543
791
|
print(f"\nGenerated code:\n{generated_code}")
|
544
792
|
|
545
793
|
elif args.text:
|
546
|
-
# Multi-line text input mode
|
547
794
|
if args.prompt is not None:
|
548
795
|
prompt = args.prompt
|
549
796
|
else:
|
@@ -651,9 +898,18 @@ def main():
|
|
651
898
|
sys.exit(130)
|
652
899
|
|
653
900
|
print("\nSubmission successful. Waiting for response...")
|
901
|
+
|
902
|
+
# Create messages array with preprompt if available
|
903
|
+
messages = None
|
904
|
+
if args.preprompt:
|
905
|
+
messages = [
|
906
|
+
{"role": "system", "content": args.preprompt},
|
907
|
+
{"role": "user", "content": prompt}
|
908
|
+
]
|
909
|
+
|
654
910
|
response = client.chat(prompt, stream=not args.no_stream, web_search=args.web_search,
|
655
911
|
temperature=args.temperature, top_p=args.top_p,
|
656
|
-
max_tokens=args.max_length)
|
912
|
+
max_tokens=args.max_length, messages=messages)
|
657
913
|
if args.no_stream and response:
|
658
914
|
print(response)
|
659
915
|
|
@@ -668,9 +924,18 @@ def main():
|
|
668
924
|
sys.exit(130)
|
669
925
|
else:
|
670
926
|
prompt = args.prompt
|
927
|
+
|
928
|
+
# Create messages array with preprompt if available
|
929
|
+
messages = None
|
930
|
+
if args.preprompt:
|
931
|
+
messages = [
|
932
|
+
{"role": "system", "content": args.preprompt},
|
933
|
+
{"role": "user", "content": prompt}
|
934
|
+
]
|
935
|
+
|
671
936
|
response = client.chat(prompt, stream=not args.no_stream, web_search=args.web_search,
|
672
937
|
temperature=args.temperature, top_p=args.top_p,
|
673
|
-
max_tokens=args.max_length)
|
938
|
+
max_tokens=args.max_length, messages=messages)
|
674
939
|
if args.no_stream and response:
|
675
940
|
print(response)
|
676
941
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ngpt
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.3.1
|
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
|
@@ -81,8 +81,16 @@ ngpt --shell "list all files in the current directory"
|
|
81
81
|
|
82
82
|
# Use multiline editor for complex prompts
|
83
83
|
ngpt --text
|
84
|
+
|
85
|
+
# Use custom system prompt
|
86
|
+
ngpt --preprompt "You are a Linux expert" "How do I find large files?"
|
87
|
+
|
88
|
+
# Log your conversation to a file
|
89
|
+
ngpt --interactive --log conversation.log
|
84
90
|
```
|
85
91
|
|
92
|
+
For more examples and detailed usage, visit the [CLI Usage Guide](https://nazdridoy.github.io/ngpt/usage/cli_usage.html).
|
93
|
+
|
86
94
|
## Features
|
87
95
|
|
88
96
|
- ✅ **Dual Mode**: Use as a CLI tool or import as a Python library
|
@@ -95,6 +103,10 @@ ngpt --text
|
|
95
103
|
- 💻 **Shell Command Generation**: OS-aware command execution
|
96
104
|
- 🧩 **Clean Code Generation**: Output code without markdown or explanations
|
97
105
|
- 📝 **Rich Multiline Editor**: Interactive multiline text input with syntax highlighting and intuitive controls
|
106
|
+
- 🎭 **System Prompts**: Customize model behavior with custom system prompts
|
107
|
+
- 📃 **Conversation Logging**: Save your conversations to text files for later reference
|
108
|
+
|
109
|
+
See the [Feature Overview](https://nazdridoy.github.io/ngpt/overview.html) for more details.
|
98
110
|
|
99
111
|
## Documentation
|
100
112
|
|
@@ -102,6 +114,13 @@ Comprehensive documentation, including API reference, usage guides, and examples
|
|
102
114
|
|
103
115
|
**[https://nazdridoy.github.io/ngpt/](https://nazdridoy.github.io/ngpt/)**
|
104
116
|
|
117
|
+
Key documentation sections:
|
118
|
+
- [Installation Guide](https://nazdridoy.github.io/ngpt/installation.html)
|
119
|
+
- [CLI Usage Guide](https://nazdridoy.github.io/ngpt/usage/cli_usage.html)
|
120
|
+
- [Library Usage Guide](https://nazdridoy.github.io/ngpt/usage/library_usage.html)
|
121
|
+
- [Configuration Guide](https://nazdridoy.github.io/ngpt/configuration.html)
|
122
|
+
- [Examples & Tutorials](https://nazdridoy.github.io/ngpt/examples/basic.html)
|
123
|
+
|
105
124
|
## Installation
|
106
125
|
|
107
126
|
```bash
|
@@ -110,6 +129,8 @@ pip install ngpt
|
|
110
129
|
|
111
130
|
Requires Python 3.8 or newer.
|
112
131
|
|
132
|
+
For detailed installation instructions, see the [Installation Guide](https://nazdridoy.github.io/ngpt/installation.html).
|
133
|
+
|
113
134
|
## Usage
|
114
135
|
|
115
136
|
### As a CLI Tool
|
@@ -121,6 +142,12 @@ ngpt "Hello, how are you?"
|
|
121
142
|
# Interactive chat session with conversation history
|
122
143
|
ngpt -i
|
123
144
|
|
145
|
+
# Log conversation to a file
|
146
|
+
ngpt --interactive --log conversation.log
|
147
|
+
|
148
|
+
# Use custom system prompt to guide AI behavior
|
149
|
+
ngpt --preprompt "You are a Python programming tutor" "Explain decorators"
|
150
|
+
|
124
151
|
# Show version information
|
125
152
|
ngpt -v
|
126
153
|
|
@@ -157,6 +184,8 @@ ngpt -c "create a python function that calculates fibonacci numbers"
|
|
157
184
|
ngpt -t
|
158
185
|
```
|
159
186
|
|
187
|
+
For more CLI examples and detailed usage information, see the [CLI Usage Guide](https://nazdridoy.github.io/ngpt/usage/cli_usage.html).
|
188
|
+
|
160
189
|
### As a Library
|
161
190
|
|
162
191
|
```python
|
@@ -189,6 +218,8 @@ command = client.generate_shell_command("list all files")
|
|
189
218
|
code = client.generate_code("create a python function that calculates fibonacci numbers")
|
190
219
|
```
|
191
220
|
|
221
|
+
For more library examples and advanced usage, see the [Library Usage Guide](https://nazdridoy.github.io/ngpt/usage/library_usage.html).
|
222
|
+
|
192
223
|
#### Advanced Library Usage
|
193
224
|
|
194
225
|
```python
|
@@ -215,6 +246,8 @@ code = client.generate_code("function that converts Celsius to Fahrenheit")
|
|
215
246
|
print(code)
|
216
247
|
```
|
217
248
|
|
249
|
+
For advanced usage patterns and integrations, check out the [Advanced Examples](https://nazdridoy.github.io/ngpt/examples/advanced.html).
|
250
|
+
|
218
251
|
## Configuration
|
219
252
|
|
220
253
|
### Command Line Options
|
@@ -232,6 +265,8 @@ You can configure the client using the following options:
|
|
232
265
|
| `--temperature` | Set temperature (controls randomness, default: 0.7) |
|
233
266
|
| `--top_p` | Set top_p (controls diversity, default: 1.0) |
|
234
267
|
| `--max_length` | Set maximum response length in tokens |
|
268
|
+
| `--preprompt` | Set custom system prompt to control AI behavior |
|
269
|
+
| `--log` | Set filepath to log conversation to (for interactive modes) |
|
235
270
|
| `--config` | Path to a custom configuration file or, when used without a value, enters interactive configuration mode |
|
236
271
|
| `--config-index` | Index of the configuration to use (default: 0) |
|
237
272
|
| `--remove` | Remove the configuration at the specified index (requires --config and --config-index) |
|
@@ -243,6 +278,8 @@ You can configure the client using the following options:
|
|
243
278
|
| `-t, --text` | Open interactive multiline editor for complex prompts |
|
244
279
|
| `-v, --version` | Show version information |
|
245
280
|
|
281
|
+
For a complete reference of all available options, see the [CLI Usage Guide](https://nazdridoy.github.io/ngpt/usage/cli_usage.html).
|
282
|
+
|
246
283
|
### Interactive Configuration
|
247
284
|
|
248
285
|
The `--config` option without arguments enters interactive configuration mode, allowing you to add or edit configurations:
|
@@ -264,6 +301,8 @@ In interactive mode:
|
|
264
301
|
- For security, your API key is not displayed when editing configurations
|
265
302
|
- When removing a configuration, you'll be asked to confirm before deletion
|
266
303
|
|
304
|
+
For more details on configuring nGPT, see the [Configuration Guide](https://nazdridoy.github.io/ngpt/configuration.html).
|
305
|
+
|
267
306
|
### Configuration File
|
268
307
|
|
269
308
|
nGPT uses a configuration file stored in the standard user config directory for your operating system:
|
@@ -298,6 +337,8 @@ The configuration file uses a JSON list format, allowing you to store multiple c
|
|
298
337
|
]
|
299
338
|
```
|
300
339
|
|
340
|
+
For details on the configuration file format and structure, see the [Configuration Guide](https://nazdridoy.github.io/ngpt/configuration.html).
|
341
|
+
|
301
342
|
### Configuration Priority
|
302
343
|
|
303
344
|
nGPT determines configuration values in the following order (highest priority first):
|
@@ -0,0 +1,9 @@
|
|
1
|
+
ngpt/__init__.py,sha256=ehInP9w0MZlS1vZ1g6Cm4YE1ftmgF72CnEddQ3Le9n4,368
|
2
|
+
ngpt/cli.py,sha256=y3LzVYJnlAt570dFniUchPa3oUuD4MaDbGl6fGfH_ac,43255
|
3
|
+
ngpt/client.py,sha256=75xmzO7e9wQ7y_LzZCacg3mkZdheewcBxB6moPftqYw,13067
|
4
|
+
ngpt/config.py,sha256=BF0G3QeiPma8l7EQyc37bR7LWZog7FHJQNe7uj9cr4w,6896
|
5
|
+
ngpt-2.3.1.dist-info/METADATA,sha256=M7YPXzIzpSJRfHI8QKt4VvDmgoDaT2WY3P6tk_DRrtg,13535
|
6
|
+
ngpt-2.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
7
|
+
ngpt-2.3.1.dist-info/entry_points.txt,sha256=1cnAMujyy34DlOahrJg19lePSnb08bLbkUs_kVerqdk,39
|
8
|
+
ngpt-2.3.1.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
|
9
|
+
ngpt-2.3.1.dist-info/RECORD,,
|
ngpt-2.2.0.dist-info/RECORD
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
ngpt/__init__.py,sha256=ehInP9w0MZlS1vZ1g6Cm4YE1ftmgF72CnEddQ3Le9n4,368
|
2
|
-
ngpt/cli.py,sha256=H_EIwZr-mSpw5JdlSD-CT6zWGWLldCJ4G-BKyWGIoOE,31536
|
3
|
-
ngpt/client.py,sha256=75xmzO7e9wQ7y_LzZCacg3mkZdheewcBxB6moPftqYw,13067
|
4
|
-
ngpt/config.py,sha256=BF0G3QeiPma8l7EQyc37bR7LWZog7FHJQNe7uj9cr4w,6896
|
5
|
-
ngpt-2.2.0.dist-info/METADATA,sha256=seHeG-0SZyJcCN_SXpfpzI3jfwLBs9XrPkcdXt1mMhI,11278
|
6
|
-
ngpt-2.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
7
|
-
ngpt-2.2.0.dist-info/entry_points.txt,sha256=1cnAMujyy34DlOahrJg19lePSnb08bLbkUs_kVerqdk,39
|
8
|
-
ngpt-2.2.0.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
|
9
|
-
ngpt-2.2.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|