ngpt 3.6.0__py3-none-any.whl → 3.8.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 +7 -3
- ngpt/cli/main.py +1 -8
- ngpt/cli/modes/chat.py +13 -36
- ngpt/cli/modes/code.py +5 -1
- ngpt/cli/modes/gitcommsg.py +69 -4
- ngpt/cli/modes/rewrite.py +9 -6
- ngpt/cli/modes/shell.py +412 -71
- ngpt/utils/__init__.py +3 -1
- ngpt/utils/pipe.py +52 -0
- {ngpt-3.6.0.dist-info → ngpt-3.8.0.dist-info}/METADATA +27 -10
- {ngpt-3.6.0.dist-info → ngpt-3.8.0.dist-info}/RECORD +14 -13
- {ngpt-3.6.0.dist-info → ngpt-3.8.0.dist-info}/WHEEL +0 -0
- {ngpt-3.6.0.dist-info → ngpt-3.8.0.dist-info}/entry_points.txt +0 -0
- {ngpt-3.6.0.dist-info → ngpt-3.8.0.dist-info}/licenses/LICENSE +0 -0
ngpt/cli/args.py
CHANGED
@@ -69,6 +69,8 @@ def setup_argument_parser():
|
|
69
69
|
help='Model to use')
|
70
70
|
global_group.add_argument('--web-search', action='store_true',
|
71
71
|
help='Enable web search capability using DuckDuckGo to enhance prompts with relevant information')
|
72
|
+
global_group.add_argument('--pipe', action='store_true',
|
73
|
+
help='Read from stdin and use content with prompt. Use {} in prompt as placeholder for stdin content. Can be used with any mode option except --text and --interactive')
|
72
74
|
global_group.add_argument('--temperature', type=float, default=0.7,
|
73
75
|
help='Set temperature (controls randomness, default: 0.7)')
|
74
76
|
global_group.add_argument('--top_p', type=float, default=1.0,
|
@@ -119,8 +121,6 @@ def setup_argument_parser():
|
|
119
121
|
help='Generate code')
|
120
122
|
mode_exclusive_group.add_argument('-t', '--text', action='store_true',
|
121
123
|
help='Enter multi-line text input (submit with Ctrl+D)')
|
122
|
-
mode_exclusive_group.add_argument('-p', '--pipe', action='store_true',
|
123
|
-
help='Read from stdin and use content with prompt. Use {} in prompt as placeholder for stdin content')
|
124
124
|
mode_exclusive_group.add_argument('-r', '--rewrite', action='store_true',
|
125
125
|
help='Rewrite text from stdin to be more natural while preserving tone and meaning')
|
126
126
|
mode_exclusive_group.add_argument('-g', '--gitcommsg', action='store_true',
|
@@ -167,7 +167,11 @@ def validate_args(args):
|
|
167
167
|
if args.stream_prettify and not has_markdown_renderer('rich'):
|
168
168
|
raise ValueError("--stream-prettify requires Rich to be installed. Install with: pip install \"ngpt[full]\" or pip install rich")
|
169
169
|
|
170
|
-
#
|
170
|
+
# Check for incompatible --pipe flag with certain modes
|
171
|
+
if args.pipe and (args.text or args.interactive):
|
172
|
+
raise ValueError("--pipe flag cannot be used with --text or --interactive modes. These modes already handle input directly.")
|
173
|
+
|
174
|
+
# If pipe flag is used, check if input is available
|
171
175
|
if args.pipe and sys.stdin.isatty():
|
172
176
|
raise ValueError("--pipe was specified but no input is piped. Use echo 'content' | ngpt --pipe 'prompt with {}'")
|
173
177
|
|
ngpt/cli/main.py
CHANGED
@@ -550,13 +550,6 @@ def main():
|
|
550
550
|
# Text mode (multiline input)
|
551
551
|
text_mode(client, args, logger=logger)
|
552
552
|
|
553
|
-
elif args.pipe:
|
554
|
-
# Apply CLI config for pipe mode (similar to chat mode)
|
555
|
-
args = apply_cli_config(args, "all")
|
556
|
-
|
557
|
-
# Pipe mode (using the chat mode with stdin input)
|
558
|
-
chat_mode(client, args, logger=logger)
|
559
|
-
|
560
553
|
elif args.rewrite:
|
561
554
|
# Apply CLI config for rewrite mode
|
562
555
|
args = apply_cli_config(args, "all")
|
@@ -571,8 +564,8 @@ def main():
|
|
571
564
|
# Git commit message generation mode
|
572
565
|
gitcommsg_mode(client, args, logger=logger)
|
573
566
|
|
567
|
+
# Choose chat mode by default if no other specific mode is selected
|
574
568
|
else:
|
575
|
-
# Default to chat mode
|
576
569
|
# Apply CLI config for default chat mode
|
577
570
|
args = apply_cli_config(args, "all")
|
578
571
|
|
ngpt/cli/modes/chat.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from ..formatters import COLORS
|
2
2
|
from ..renderers import prettify_markdown, prettify_streaming_markdown
|
3
3
|
from ..ui import spinner
|
4
|
-
from ...utils import enhance_prompt_with_web_search
|
4
|
+
from ...utils import enhance_prompt_with_web_search, process_piped_input
|
5
5
|
import sys
|
6
6
|
import threading
|
7
7
|
|
@@ -13,43 +13,20 @@ def chat_mode(client, args, logger=None):
|
|
13
13
|
args: The parsed command-line arguments
|
14
14
|
logger: Optional logger instance
|
15
15
|
"""
|
16
|
+
# Get the prompt
|
17
|
+
if args.prompt is None:
|
18
|
+
try:
|
19
|
+
print("Enter your prompt: ", end='')
|
20
|
+
prompt = input()
|
21
|
+
except KeyboardInterrupt:
|
22
|
+
print("\nInput cancelled by user. Exiting gracefully.")
|
23
|
+
sys.exit(130)
|
24
|
+
else:
|
25
|
+
prompt = args.prompt
|
26
|
+
|
16
27
|
# Handle pipe mode
|
17
28
|
if args.pipe:
|
18
|
-
|
19
|
-
stdin_content = sys.stdin.read().strip()
|
20
|
-
|
21
|
-
# Get the prompt - either use provided one or ask user
|
22
|
-
if args.prompt is None:
|
23
|
-
try:
|
24
|
-
print("Enter your prompt (use {} as placeholder for stdin): ", end='')
|
25
|
-
prompt = input()
|
26
|
-
except KeyboardInterrupt:
|
27
|
-
print("\nInput cancelled by user. Exiting gracefully.")
|
28
|
-
sys.exit(130)
|
29
|
-
else:
|
30
|
-
prompt = args.prompt
|
31
|
-
|
32
|
-
# Replace the placeholder in the prompt with stdin content
|
33
|
-
placeholder = "{}"
|
34
|
-
|
35
|
-
# Check if the placeholder exists in the prompt
|
36
|
-
if placeholder not in prompt:
|
37
|
-
print(f"{COLORS['yellow']}Warning: Placeholder '{placeholder}' not found in prompt. Appending stdin content to the end.{COLORS['reset']}")
|
38
|
-
prompt = f"{prompt} {stdin_content}"
|
39
|
-
else:
|
40
|
-
prompt = prompt.replace(placeholder, stdin_content)
|
41
|
-
# Handle regular chat mode
|
42
|
-
else:
|
43
|
-
# Get the prompt
|
44
|
-
if args.prompt is None:
|
45
|
-
try:
|
46
|
-
print("Enter your prompt: ", end='')
|
47
|
-
prompt = input()
|
48
|
-
except KeyboardInterrupt:
|
49
|
-
print("\nInput cancelled by user. Exiting gracefully.")
|
50
|
-
sys.exit(130)
|
51
|
-
else:
|
52
|
-
prompt = args.prompt
|
29
|
+
prompt = process_piped_input(prompt, logger=logger)
|
53
30
|
|
54
31
|
# Log the user message if logging is enabled
|
55
32
|
if logger:
|
ngpt/cli/modes/code.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from ..formatters import COLORS
|
2
2
|
from ..renderers import prettify_markdown, prettify_streaming_markdown, has_markdown_renderer, show_available_renderers
|
3
3
|
from ..ui import spinner, copy_to_clipboard
|
4
|
-
from ...utils import enhance_prompt_with_web_search
|
4
|
+
from ...utils import enhance_prompt_with_web_search, process_piped_input
|
5
5
|
import sys
|
6
6
|
import threading
|
7
7
|
|
@@ -97,6 +97,10 @@ def code_mode(client, args, logger=None):
|
|
97
97
|
else:
|
98
98
|
prompt = args.prompt
|
99
99
|
|
100
|
+
# Apply piped input if --pipe is enabled
|
101
|
+
if args.pipe:
|
102
|
+
prompt = process_piped_input(prompt, logger=logger)
|
103
|
+
|
100
104
|
# Log the user prompt if logging is enabled
|
101
105
|
if logger:
|
102
106
|
logger.log("user", prompt)
|
ngpt/cli/modes/gitcommsg.py
CHANGED
@@ -11,6 +11,7 @@ from ..formatters import COLORS
|
|
11
11
|
from ..ui import spinner, copy_to_clipboard
|
12
12
|
from ...utils.log import create_gitcommsg_logger
|
13
13
|
from ...utils.cli_config import get_cli_config_option
|
14
|
+
from ...utils import process_piped_input
|
14
15
|
|
15
16
|
def get_diff_content(diff_file=None):
|
16
17
|
"""Get git diff content from file or git staged changes.
|
@@ -964,6 +965,39 @@ REMINDER:
|
|
964
965
|
|
965
966
|
DO NOT ask for the original diff or add explanations outside the commit message format."""
|
966
967
|
|
968
|
+
def is_git_diff(content):
|
969
|
+
"""Check if the content looks like a git diff.
|
970
|
+
|
971
|
+
Args:
|
972
|
+
content: The content to check
|
973
|
+
|
974
|
+
Returns:
|
975
|
+
bool: True if the content looks like a git diff, False otherwise
|
976
|
+
"""
|
977
|
+
# Check for common git diff patterns
|
978
|
+
diff_patterns = [
|
979
|
+
r'diff --git a/.*? b/.*?', # diff --git a/file b/file
|
980
|
+
r'index [a-f0-9]+\.\.[a-f0-9]+', # index hash..hash
|
981
|
+
r'--- a/.*?', # --- a/file
|
982
|
+
r'\+\+\+ b/.*?', # +++ b/file
|
983
|
+
r'@@ -\d+,\d+ \+\d+,\d+ @@' # @@ -line,count +line,count @@
|
984
|
+
]
|
985
|
+
|
986
|
+
# Check if the content contains at least one of these patterns
|
987
|
+
for pattern in diff_patterns:
|
988
|
+
if re.search(pattern, content):
|
989
|
+
return True
|
990
|
+
|
991
|
+
# Check if the content contains lines starting with + or - (changes)
|
992
|
+
lines = content.splitlines()
|
993
|
+
plus_minus_lines = [line for line in lines if line.startswith('+') or line.startswith('-')]
|
994
|
+
|
995
|
+
# If there are many +/- lines, it's likely a diff
|
996
|
+
if len(plus_minus_lines) > 5 and len(plus_minus_lines) / len(lines) > 0.1:
|
997
|
+
return True
|
998
|
+
|
999
|
+
return False
|
1000
|
+
|
967
1001
|
def gitcommsg_mode(client, args, logger=None):
|
968
1002
|
"""Handle the Git commit message generation mode.
|
969
1003
|
|
@@ -987,16 +1021,47 @@ def gitcommsg_mode(client, args, logger=None):
|
|
987
1021
|
active_logger.debug(f"Args: {args}")
|
988
1022
|
|
989
1023
|
try:
|
1024
|
+
# Process piped input as diff content when --pipe flag is set
|
1025
|
+
piped_diff_content = None
|
1026
|
+
if args.pipe:
|
1027
|
+
if active_logger:
|
1028
|
+
active_logger.info("Processing piped input as diff content")
|
1029
|
+
|
1030
|
+
if not sys.stdin.isatty():
|
1031
|
+
piped_diff_content = sys.stdin.read().strip()
|
1032
|
+
if not piped_diff_content:
|
1033
|
+
print(f"{COLORS['yellow']}Warning: No diff content received from stdin.{COLORS['reset']}")
|
1034
|
+
else:
|
1035
|
+
# Validate that the piped content looks like a git diff
|
1036
|
+
if not is_git_diff(piped_diff_content):
|
1037
|
+
print(f"{COLORS['red']}Error: The piped content doesn't appear to be a git diff. Exiting.{COLORS['reset']}")
|
1038
|
+
if active_logger:
|
1039
|
+
active_logger.error("Piped content doesn't appear to be a git diff. Aborting.")
|
1040
|
+
return
|
1041
|
+
elif active_logger:
|
1042
|
+
active_logger.info(f"Received {len(piped_diff_content.splitlines())} lines of diff content from stdin")
|
1043
|
+
else:
|
1044
|
+
print(f"{COLORS['yellow']}Error: --pipe was specified but no input is piped.{COLORS['reset']}")
|
1045
|
+
return
|
1046
|
+
|
990
1047
|
# Check if --diff was explicitly passed on the command line
|
991
1048
|
diff_option_provided = '--diff' in sys.argv
|
992
1049
|
diff_path_provided = diff_option_provided and args.diff is not None and args.diff is not True
|
993
1050
|
|
1051
|
+
# If piped diff content is available, use it directly
|
1052
|
+
if piped_diff_content:
|
1053
|
+
diff_content = piped_diff_content
|
1054
|
+
if active_logger:
|
1055
|
+
active_logger.info("Using diff content from stdin")
|
994
1056
|
# If --diff wasn't explicitly provided on the command line, don't use the config value
|
995
|
-
|
1057
|
+
elif not diff_option_provided:
|
996
1058
|
# Even if diff is in CLI config, don't use it unless --diff flag is provided
|
997
1059
|
diff_file = None
|
998
1060
|
if active_logger:
|
999
1061
|
active_logger.info("Not using diff file from CLI config because --diff flag was not provided")
|
1062
|
+
|
1063
|
+
# Get diff content from git staged changes
|
1064
|
+
diff_content = get_diff_content(diff_file)
|
1000
1065
|
else:
|
1001
1066
|
# --diff flag was provided on command line
|
1002
1067
|
if args.diff is True:
|
@@ -1013,9 +1078,9 @@ def gitcommsg_mode(client, args, logger=None):
|
|
1013
1078
|
diff_file = args.diff
|
1014
1079
|
if active_logger:
|
1015
1080
|
active_logger.info(f"Using explicitly provided diff file: {diff_file}")
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1081
|
+
|
1082
|
+
# Get diff content from file
|
1083
|
+
diff_content = get_diff_content(diff_file)
|
1019
1084
|
|
1020
1085
|
if not diff_content:
|
1021
1086
|
print(f"{COLORS['red']}No diff content available. Exiting.{COLORS['reset']}")
|
ngpt/cli/modes/rewrite.py
CHANGED
@@ -4,7 +4,7 @@ import time
|
|
4
4
|
from ..formatters import COLORS
|
5
5
|
from ..renderers import prettify_markdown, prettify_streaming_markdown
|
6
6
|
from ..ui import get_multiline_input, spinner, copy_to_clipboard
|
7
|
-
from ...utils import enhance_prompt_with_web_search
|
7
|
+
from ...utils import enhance_prompt_with_web_search, process_piped_input
|
8
8
|
|
9
9
|
# System prompt for rewriting text
|
10
10
|
REWRITE_SYSTEM_PROMPT = """You are an expert text editor and rewriter. Your task is to rewrite the user's text to improve readability and flow while carefully preserving the original meaning, tone, and style.
|
@@ -78,14 +78,17 @@ def rewrite_mode(client, args, logger=None):
|
|
78
78
|
args: The parsed command-line arguments
|
79
79
|
logger: Optional logger instance
|
80
80
|
"""
|
81
|
-
#
|
82
|
-
if
|
81
|
+
# Check if using --pipe flag with a specific placeholder
|
82
|
+
if args.pipe and args.prompt:
|
83
|
+
input_text = process_piped_input(args.prompt, logger=logger)
|
84
|
+
# Normal rewrite mode functionality (direct stdin piping without --pipe flag)
|
85
|
+
elif not sys.stdin.isatty():
|
83
86
|
# Read from stdin if data is piped
|
84
87
|
input_text = sys.stdin.read().strip()
|
85
88
|
|
86
|
-
# If
|
87
|
-
if
|
88
|
-
input_text = args.prompt
|
89
|
+
# If prompt is also provided, append it to the piped input
|
90
|
+
if args.prompt:
|
91
|
+
input_text = f"{input_text}\n\n{args.prompt}"
|
89
92
|
elif args.prompt:
|
90
93
|
# Use the command-line argument if provided
|
91
94
|
input_text = args.prompt
|
ngpt/cli/modes/shell.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from ..formatters import COLORS
|
2
2
|
from ..ui import spinner, copy_to_clipboard
|
3
|
-
from ..renderers import prettify_markdown, has_markdown_renderer
|
4
|
-
from ...utils import enhance_prompt_with_web_search
|
3
|
+
from ..renderers import prettify_markdown, has_markdown_renderer, prettify_streaming_markdown, show_available_renderers
|
4
|
+
from ...utils import enhance_prompt_with_web_search, process_piped_input
|
5
5
|
import subprocess
|
6
6
|
import sys
|
7
7
|
import threading
|
@@ -9,6 +9,7 @@ import platform
|
|
9
9
|
import os
|
10
10
|
import shutil
|
11
11
|
import re
|
12
|
+
import time
|
12
13
|
|
13
14
|
# System prompt for shell command generation
|
14
15
|
SHELL_SYSTEM_PROMPT = """Your role: Provide only plain text without Markdown formatting. Do not show any warnings or information regarding your capabilities. Do not provide any description. If you need to store any data, assume it will be stored in the chat. Provide only {shell_name} command for {operating_system} without any description. If there is a lack of details, provide most logical solution. Ensure the output is a valid shell command. If multiple steps required try to combine them together.
|
@@ -131,6 +132,251 @@ def detect_shell():
|
|
131
132
|
else:
|
132
133
|
return "bash", "bash", operating_system
|
133
134
|
|
135
|
+
def setup_streaming(args, logger=None):
|
136
|
+
"""Set up streaming configuration based on command-line arguments.
|
137
|
+
|
138
|
+
Args:
|
139
|
+
args: The parsed command-line arguments
|
140
|
+
logger: Optional logger instance for logging
|
141
|
+
|
142
|
+
Returns:
|
143
|
+
tuple: (should_stream, use_stream_prettify, use_regular_prettify,
|
144
|
+
stream_setup) - Configuration settings and streaming components
|
145
|
+
"""
|
146
|
+
# Default values - initialize all at once
|
147
|
+
stream_callback = live_display = stop_spinner_func = None
|
148
|
+
stop_spinner = spinner_thread = stop_spinner_event = None
|
149
|
+
should_stream = True # Default to streaming
|
150
|
+
use_stream_prettify = use_regular_prettify = False
|
151
|
+
first_content_received = False
|
152
|
+
|
153
|
+
# Determine final behavior based on flag priority
|
154
|
+
if args.stream_prettify:
|
155
|
+
# Highest priority: stream-prettify
|
156
|
+
if has_markdown_renderer('rich'):
|
157
|
+
should_stream = True
|
158
|
+
use_stream_prettify = True
|
159
|
+
live_display, stream_callback, setup_spinner = prettify_streaming_markdown(args.renderer)
|
160
|
+
if not live_display:
|
161
|
+
# Fallback if live display fails
|
162
|
+
use_stream_prettify = False
|
163
|
+
use_regular_prettify = True
|
164
|
+
should_stream = False
|
165
|
+
print(f"{COLORS['yellow']}Live display setup failed. Falling back to regular prettify mode.{COLORS['reset']}")
|
166
|
+
else:
|
167
|
+
# Rich not available for stream-prettify
|
168
|
+
print(f"{COLORS['yellow']}Warning: Rich is not available for --stream-prettify. Install with: pip install \"ngpt[full]\".{COLORS['reset']}")
|
169
|
+
print(f"{COLORS['yellow']}Falling back to default streaming without prettify.{COLORS['reset']}")
|
170
|
+
should_stream = True
|
171
|
+
use_stream_prettify = False
|
172
|
+
elif args.no_stream:
|
173
|
+
# Second priority: no-stream
|
174
|
+
should_stream = False
|
175
|
+
use_regular_prettify = False # No prettify if no streaming
|
176
|
+
elif args.prettify:
|
177
|
+
# Third priority: prettify (requires disabling stream)
|
178
|
+
if has_markdown_renderer(args.renderer):
|
179
|
+
should_stream = False
|
180
|
+
use_regular_prettify = True
|
181
|
+
print(f"{COLORS['yellow']}Note: Using standard markdown rendering (--prettify). For streaming markdown rendering, use --stream-prettify instead.{COLORS['reset']}")
|
182
|
+
else:
|
183
|
+
# Renderer not available for prettify
|
184
|
+
print(f"{COLORS['yellow']}Warning: Renderer '{args.renderer}' not available for --prettify.{COLORS['reset']}")
|
185
|
+
show_available_renderers()
|
186
|
+
print(f"{COLORS['yellow']}Falling back to default streaming without prettify.{COLORS['reset']}")
|
187
|
+
should_stream = True
|
188
|
+
use_regular_prettify = False
|
189
|
+
|
190
|
+
# Create a wrapper for the stream callback that will stop the spinner on first content
|
191
|
+
if stream_callback:
|
192
|
+
original_callback = stream_callback
|
193
|
+
|
194
|
+
def spinner_handling_callback(content, **kwargs):
|
195
|
+
nonlocal first_content_received
|
196
|
+
|
197
|
+
# On first content, stop the spinner
|
198
|
+
if not first_content_received and stop_spinner_func:
|
199
|
+
first_content_received = True
|
200
|
+
# Stop the spinner
|
201
|
+
stop_spinner_func()
|
202
|
+
# Ensure spinner message is cleared with an extra blank line
|
203
|
+
sys.stdout.write("\r" + " " * 100 + "\r")
|
204
|
+
sys.stdout.flush()
|
205
|
+
|
206
|
+
# Call the original callback to update the display
|
207
|
+
if original_callback:
|
208
|
+
original_callback(content, **kwargs)
|
209
|
+
|
210
|
+
# Use our wrapper callback
|
211
|
+
if use_stream_prettify and live_display:
|
212
|
+
stream_callback = spinner_handling_callback
|
213
|
+
|
214
|
+
# Set up the spinner if we have a live display
|
215
|
+
stop_spinner_event = threading.Event()
|
216
|
+
stop_spinner_func = setup_spinner(stop_spinner_event, color=COLORS['cyan'])
|
217
|
+
|
218
|
+
# Create spinner for non-stream-prettify modes EXCEPT no-stream
|
219
|
+
if not use_stream_prettify and not args.no_stream:
|
220
|
+
# Prepare spinner (but don't start it yet - will be started in generate_with_model)
|
221
|
+
stop_spinner = threading.Event()
|
222
|
+
spinner_thread = threading.Thread(
|
223
|
+
target=spinner,
|
224
|
+
args=("Generating...",),
|
225
|
+
kwargs={"stop_event": stop_spinner, "color": COLORS['cyan']}
|
226
|
+
)
|
227
|
+
spinner_thread.daemon = True
|
228
|
+
|
229
|
+
# Create a stream_setup dict to hold all the variables - use a dict comprehension
|
230
|
+
stream_setup = {
|
231
|
+
'stream_callback': stream_callback,
|
232
|
+
'live_display': live_display,
|
233
|
+
'stop_spinner_func': stop_spinner_func,
|
234
|
+
'stop_spinner': stop_spinner,
|
235
|
+
'spinner_thread': spinner_thread,
|
236
|
+
'stop_spinner_event': stop_spinner_event,
|
237
|
+
'first_content_received': first_content_received
|
238
|
+
}
|
239
|
+
|
240
|
+
return (should_stream, use_stream_prettify, use_regular_prettify, stream_setup)
|
241
|
+
|
242
|
+
def generate_with_model(client, prompt, messages, args, stream_setup,
|
243
|
+
use_stream_prettify, should_stream, spinner_message="Generating...",
|
244
|
+
temp_override=None, logger=None):
|
245
|
+
"""Generate content using the model with proper streaming and spinner handling.
|
246
|
+
|
247
|
+
Args:
|
248
|
+
client: The NGPTClient instance
|
249
|
+
prompt: The prompt to send to the model
|
250
|
+
messages: The formatted messages to send
|
251
|
+
args: The parsed command-line arguments
|
252
|
+
stream_setup: The streaming setup from setup_streaming
|
253
|
+
use_stream_prettify: Whether to use stream prettify
|
254
|
+
should_stream: Whether to stream the response
|
255
|
+
spinner_message: Message to show in the spinner
|
256
|
+
temp_override: Optional temperature override
|
257
|
+
logger: Optional logger instance
|
258
|
+
|
259
|
+
Returns:
|
260
|
+
str: The generated content
|
261
|
+
"""
|
262
|
+
# Extract variables from stream_setup - only unpack what we need
|
263
|
+
stream_callback = stream_setup['stream_callback']
|
264
|
+
stop_spinner = stream_setup['stop_spinner']
|
265
|
+
spinner_thread = stream_setup['spinner_thread']
|
266
|
+
stop_spinner_event = stream_setup['stop_spinner_event']
|
267
|
+
stop_spinner_func = stream_setup['stop_spinner_func']
|
268
|
+
|
269
|
+
# Show spinner for all modes except no-stream
|
270
|
+
if not args.no_stream:
|
271
|
+
# Two possible spinner types:
|
272
|
+
# 1. Rich spinner for stream_prettify
|
273
|
+
# 2. Regular spinner for all other modes (including --prettify)
|
274
|
+
|
275
|
+
if use_stream_prettify and stop_spinner_func:
|
276
|
+
# Rich spinner is handled by callbacks
|
277
|
+
pass
|
278
|
+
elif spinner_thread and stop_spinner:
|
279
|
+
# Start the regular spinner thread
|
280
|
+
spinner_thread._args = (spinner_message,)
|
281
|
+
if not spinner_thread.is_alive():
|
282
|
+
spinner_thread.start()
|
283
|
+
else:
|
284
|
+
# No-stream mode just gets a status message
|
285
|
+
print(spinner_message)
|
286
|
+
|
287
|
+
# Set temperature
|
288
|
+
temp = args.temperature if temp_override is None else temp_override
|
289
|
+
|
290
|
+
try:
|
291
|
+
# Make the API call
|
292
|
+
return client.chat(
|
293
|
+
prompt=prompt,
|
294
|
+
stream=should_stream,
|
295
|
+
messages=messages,
|
296
|
+
temperature=temp,
|
297
|
+
top_p=args.top_p,
|
298
|
+
max_tokens=args.max_tokens,
|
299
|
+
stream_callback=stream_callback
|
300
|
+
)
|
301
|
+
except KeyboardInterrupt:
|
302
|
+
print("\nRequest cancelled by user.")
|
303
|
+
return ""
|
304
|
+
except Exception as e:
|
305
|
+
print(f"Error generating content: {e}")
|
306
|
+
return ""
|
307
|
+
finally:
|
308
|
+
# Stop the spinner
|
309
|
+
if use_stream_prettify and stop_spinner_event:
|
310
|
+
# Stop rich spinner
|
311
|
+
if not stream_setup['first_content_received']:
|
312
|
+
stop_spinner_event.set()
|
313
|
+
elif stop_spinner:
|
314
|
+
# Stop regular spinner
|
315
|
+
stop_spinner.set()
|
316
|
+
if spinner_thread and spinner_thread.is_alive():
|
317
|
+
spinner_thread.join()
|
318
|
+
|
319
|
+
# Clear the spinner line completely
|
320
|
+
sys.stdout.write("\r" + " " * 100 + "\r")
|
321
|
+
sys.stdout.flush()
|
322
|
+
|
323
|
+
def display_content(content, content_type, highlight_lang, args, use_stream_prettify, use_regular_prettify):
|
324
|
+
"""Display generated content with appropriate formatting.
|
325
|
+
|
326
|
+
Args:
|
327
|
+
content: The content to display
|
328
|
+
content_type: Type of content ('command' or 'description')
|
329
|
+
highlight_lang: Language for syntax highlighting
|
330
|
+
args: The parsed command-line arguments
|
331
|
+
use_stream_prettify: Whether stream prettify is enabled
|
332
|
+
use_regular_prettify: Whether regular prettify is enabled
|
333
|
+
"""
|
334
|
+
if not content:
|
335
|
+
return
|
336
|
+
|
337
|
+
# Define title based on content type - use a lookup instead of if-else
|
338
|
+
titles = {
|
339
|
+
'command': "Generated Command",
|
340
|
+
'description': "Command Description"
|
341
|
+
}
|
342
|
+
title = titles.get(content_type, "Generated Content")
|
343
|
+
|
344
|
+
# Format content appropriately - create formatted content only when needed
|
345
|
+
if use_regular_prettify and has_markdown_renderer(args.renderer):
|
346
|
+
if content_type == 'command':
|
347
|
+
formatted_content = f"### {title}\n\n```{highlight_lang}\n{content}\n```"
|
348
|
+
else: # description
|
349
|
+
formatted_content = f"### {title}\n\n{content}"
|
350
|
+
|
351
|
+
# Only show formatted content if not already shown by stream-prettify
|
352
|
+
if not use_stream_prettify:
|
353
|
+
if use_regular_prettify and has_markdown_renderer(args.renderer):
|
354
|
+
# Use rich renderer for pretty output
|
355
|
+
prettify_markdown(formatted_content, args.renderer)
|
356
|
+
elif args.no_stream:
|
357
|
+
# Simple output for no-stream mode (no box)
|
358
|
+
if content_type == 'command':
|
359
|
+
print(f"\n{title}:\n{COLORS['green']}{content}{COLORS['reset']}\n")
|
360
|
+
else:
|
361
|
+
print(f"\n{title}:\n{content}\n")
|
362
|
+
else:
|
363
|
+
# Regular display or fallback
|
364
|
+
if content_type == 'command':
|
365
|
+
# Box formatting for commands in regular mode - calculate once
|
366
|
+
term_width = shutil.get_terminal_size().columns
|
367
|
+
box_width = min(term_width - 4, len(content) + 8)
|
368
|
+
horizontal_line = "─" * box_width
|
369
|
+
spacing = box_width - len(title) - 11
|
370
|
+
content_spacing = box_width - len(content) - 2
|
371
|
+
|
372
|
+
print(f"\n┌{horizontal_line}┐")
|
373
|
+
print(f"│ {COLORS['bold']}{title}:{COLORS['reset']} {' ' * spacing}│")
|
374
|
+
print(f"│ {COLORS['green']}{content}{COLORS['reset']}{' ' * content_spacing}│")
|
375
|
+
print(f"└{horizontal_line}┘\n")
|
376
|
+
else:
|
377
|
+
# Simple display for descriptions
|
378
|
+
print(f"\n{content}\n")
|
379
|
+
|
134
380
|
def shell_mode(client, args, logger=None):
|
135
381
|
"""Handle the shell command generation mode.
|
136
382
|
|
@@ -139,6 +385,7 @@ def shell_mode(client, args, logger=None):
|
|
139
385
|
args: The parsed command-line arguments
|
140
386
|
logger: Optional logger instance
|
141
387
|
"""
|
388
|
+
# Get the user prompt more efficiently
|
142
389
|
if args.prompt is None:
|
143
390
|
try:
|
144
391
|
print("Enter shell command description: ", end='')
|
@@ -149,15 +396,20 @@ def shell_mode(client, args, logger=None):
|
|
149
396
|
else:
|
150
397
|
prompt = args.prompt
|
151
398
|
|
399
|
+
# Process piped input if --pipe flag is set
|
400
|
+
if args.pipe:
|
401
|
+
prompt = process_piped_input(prompt, logger=logger)
|
402
|
+
|
152
403
|
# Log the user prompt if logging is enabled
|
153
404
|
if logger:
|
154
405
|
logger.log("user", prompt)
|
155
406
|
|
156
|
-
# Enhance prompt with web search if enabled
|
407
|
+
# Enhance prompt with web search if enabled - reuse variables
|
157
408
|
if args.web_search:
|
409
|
+
original_prompt = prompt
|
410
|
+
web_search_succeeded = False
|
411
|
+
|
158
412
|
try:
|
159
|
-
original_prompt = prompt
|
160
|
-
|
161
413
|
# Start spinner for web search
|
162
414
|
stop_spinner = threading.Event()
|
163
415
|
spinner_thread = threading.Thread(
|
@@ -170,23 +422,23 @@ def shell_mode(client, args, logger=None):
|
|
170
422
|
|
171
423
|
try:
|
172
424
|
prompt = enhance_prompt_with_web_search(prompt, logger=logger, disable_citations=True)
|
173
|
-
|
425
|
+
web_search_succeeded = True
|
426
|
+
finally:
|
427
|
+
# Always stop the spinner
|
174
428
|
stop_spinner.set()
|
175
429
|
spinner_thread.join()
|
430
|
+
|
176
431
|
# Clear the spinner line completely
|
177
432
|
sys.stdout.write("\r" + " " * 100 + "\r")
|
178
433
|
sys.stdout.flush()
|
179
|
-
print("Enhanced input with web search results.")
|
180
|
-
except Exception as e:
|
181
|
-
# Stop the spinner before re-raising
|
182
|
-
stop_spinner.set()
|
183
|
-
spinner_thread.join()
|
184
|
-
raise e
|
185
434
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
435
|
+
if web_search_succeeded:
|
436
|
+
print("Enhanced input with web search results.")
|
437
|
+
|
438
|
+
# Log the enhanced prompt if logging is enabled
|
439
|
+
if logger:
|
440
|
+
# Use "web_search" role instead of "system" for clearer logs
|
441
|
+
logger.log("web_search", prompt.replace(original_prompt, "").strip())
|
190
442
|
except Exception as e:
|
191
443
|
print(f"{COLORS['yellow']}Warning: Failed to enhance prompt with web search: {str(e)}{COLORS['reset']}")
|
192
444
|
# Continue with the original prompt if web search fails
|
@@ -224,36 +476,21 @@ def shell_mode(client, args, logger=None):
|
|
224
476
|
if logger:
|
225
477
|
logger.log("system", system_prompt)
|
226
478
|
|
227
|
-
#
|
228
|
-
|
229
|
-
spinner_thread = threading.Thread(
|
230
|
-
target=spinner,
|
231
|
-
args=("Generating command...",),
|
232
|
-
kwargs={"stop_event": stop_spinner, "color": COLORS['cyan']}
|
233
|
-
)
|
234
|
-
spinner_thread.daemon = True
|
235
|
-
spinner_thread.start()
|
479
|
+
# Set up streaming once and reuse for both command and description
|
480
|
+
should_stream, use_stream_prettify, use_regular_prettify, stream_setup = setup_streaming(args)
|
236
481
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
finally:
|
250
|
-
# Stop the spinner
|
251
|
-
stop_spinner.set()
|
252
|
-
spinner_thread.join()
|
253
|
-
|
254
|
-
# Clear the spinner line completely
|
255
|
-
sys.stdout.write("\r" + " " * 100 + "\r")
|
256
|
-
sys.stdout.flush()
|
482
|
+
# Generate the command
|
483
|
+
command = generate_with_model(
|
484
|
+
client=client,
|
485
|
+
prompt=prompt,
|
486
|
+
messages=messages,
|
487
|
+
args=args,
|
488
|
+
stream_setup=stream_setup,
|
489
|
+
use_stream_prettify=use_stream_prettify,
|
490
|
+
should_stream=should_stream,
|
491
|
+
spinner_message="Generating command...",
|
492
|
+
logger=logger
|
493
|
+
)
|
257
494
|
|
258
495
|
if not command:
|
259
496
|
return # Error already printed by client
|
@@ -265,30 +502,45 @@ def shell_mode(client, args, logger=None):
|
|
265
502
|
# Get the most up-to-date shell type at command generation time
|
266
503
|
_, highlight_lang, _ = detect_shell()
|
267
504
|
|
268
|
-
#
|
269
|
-
|
505
|
+
# Format with proper syntax highlighting for streaming prettify - only if needed
|
506
|
+
if use_stream_prettify and stream_setup['stream_callback'] and command:
|
507
|
+
# Create properly formatted markdown for streaming display
|
508
|
+
formatted_command = f"```{highlight_lang}\n{command}\n```"
|
509
|
+
# Update the live display with the formatted command
|
510
|
+
stream_setup['stream_callback'](formatted_command, complete=True)
|
270
511
|
|
271
|
-
#
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
512
|
+
# Display the command
|
513
|
+
display_content(
|
514
|
+
content=command,
|
515
|
+
content_type='command',
|
516
|
+
highlight_lang=highlight_lang,
|
517
|
+
args=args,
|
518
|
+
use_stream_prettify=use_stream_prettify,
|
519
|
+
use_regular_prettify=use_regular_prettify
|
520
|
+
)
|
521
|
+
|
522
|
+
# Display options with better formatting - prepare strings once
|
523
|
+
options_text = f"{COLORS['bold']}Options:{COLORS['reset']}"
|
524
|
+
options = [
|
525
|
+
f" {COLORS['cyan']}C{COLORS['reset']} - Copy - Copy the command to clipboard",
|
526
|
+
f" {COLORS['cyan']}E{COLORS['reset']} - Execute - Run the command in your shell",
|
527
|
+
f" {COLORS['cyan']}D{COLORS['reset']} - Describe - Explain what this command does",
|
528
|
+
f" {COLORS['cyan']}A{COLORS['reset']} - Abort - Cancel and return to prompt"
|
529
|
+
]
|
530
|
+
prompt_text = f"\nWhat would you like to do? [{COLORS['cyan']}C{COLORS['reset']}/{COLORS['cyan']}E{COLORS['reset']}/{COLORS['cyan']}D{COLORS['reset']}/{COLORS['cyan']}A{COLORS['reset']}] "
|
531
|
+
|
532
|
+
# Print options with proper flushing to ensure display
|
533
|
+
print(options_text, flush=True)
|
534
|
+
for option in options:
|
535
|
+
print(option, flush=True)
|
536
|
+
|
537
|
+
# Add a small delay to ensure terminal rendering is complete,
|
538
|
+
# especially important for stream-prettify mode
|
539
|
+
if use_stream_prettify:
|
540
|
+
time.sleep(0.2)
|
541
|
+
|
542
|
+
# Print prompt and flush to ensure it appears
|
543
|
+
print(prompt_text, end='', flush=True)
|
292
544
|
|
293
545
|
try:
|
294
546
|
response = input().lower()
|
@@ -296,7 +548,7 @@ def shell_mode(client, args, logger=None):
|
|
296
548
|
print("\nCommand execution cancelled by user.")
|
297
549
|
return
|
298
550
|
|
299
|
-
if response == 'e'
|
551
|
+
if response == 'e':
|
300
552
|
# Log the execution if logging is enabled
|
301
553
|
if logger:
|
302
554
|
logger.log("system", f"Executing command: {command}")
|
@@ -326,7 +578,7 @@ def shell_mode(client, args, logger=None):
|
|
326
578
|
logger.log("system", f"Command error: {error}")
|
327
579
|
|
328
580
|
print(f"\nError:\n{error}")
|
329
|
-
elif response == 'c'
|
581
|
+
elif response == 'c':
|
330
582
|
# Copy command to clipboard without confirmation prompt
|
331
583
|
copied = copy_to_clipboard(command, skip_confirmation=True)
|
332
584
|
if not copied:
|
@@ -334,4 +586,93 @@ def shell_mode(client, args, logger=None):
|
|
334
586
|
|
335
587
|
# Log the copy if logging is enabled
|
336
588
|
if logger:
|
337
|
-
logger.log("system", "Command copied to clipboard")
|
589
|
+
logger.log("system", "Command copied to clipboard")
|
590
|
+
elif response == 'd':
|
591
|
+
# Ask LLM to describe what the command does
|
592
|
+
describe_prompt = f"Please explain this command: {command}"
|
593
|
+
|
594
|
+
# Create system prompt for description that includes OS and shell info
|
595
|
+
describe_system_prompt = f"You are a helpful assistant explaining shell commands. The user is running {shell_name} on {operating_system}. Explain what the following shell command does in detail, considering this specific environment. Include any potential risks, side effects, or compatibility issues with this OS/shell combination."
|
596
|
+
|
597
|
+
# Prepare messages for the chat API
|
598
|
+
describe_messages = [
|
599
|
+
{"role": "system", "content": describe_system_prompt},
|
600
|
+
{"role": "user", "content": describe_prompt}
|
601
|
+
]
|
602
|
+
|
603
|
+
# Log the system prompt if logging is enabled
|
604
|
+
if logger:
|
605
|
+
logger.log("system", f"Command description requested for {operating_system}/{shell_name}")
|
606
|
+
|
607
|
+
# Set up fresh streaming for description - reuse existing setup when possible
|
608
|
+
# We only need to refresh the streaming setup if we're using stream_prettify
|
609
|
+
if use_stream_prettify:
|
610
|
+
_, use_stream_prettify_desc, use_regular_prettify_desc, stream_setup_desc = setup_streaming(args)
|
611
|
+
else:
|
612
|
+
# Reuse the existing setup for non-prettify streaming
|
613
|
+
use_stream_prettify_desc = use_stream_prettify
|
614
|
+
use_regular_prettify_desc = use_regular_prettify
|
615
|
+
|
616
|
+
# Always create a fresh spinner for description
|
617
|
+
stop_spinner = threading.Event()
|
618
|
+
spinner_thread = threading.Thread(
|
619
|
+
target=spinner,
|
620
|
+
args=("Generating command description...",),
|
621
|
+
kwargs={"stop_event": stop_spinner, "color": COLORS['cyan']}
|
622
|
+
)
|
623
|
+
spinner_thread.daemon = True
|
624
|
+
|
625
|
+
# Create a new stream setup with the fresh spinner
|
626
|
+
stream_setup_desc = {
|
627
|
+
'stream_callback': stream_setup.get('stream_callback'),
|
628
|
+
'live_display': stream_setup.get('live_display'),
|
629
|
+
'stop_spinner_func': stream_setup.get('stop_spinner_func'),
|
630
|
+
'stop_spinner': stop_spinner,
|
631
|
+
'spinner_thread': spinner_thread,
|
632
|
+
'stop_spinner_event': stream_setup.get('stop_spinner_event'),
|
633
|
+
'first_content_received': False
|
634
|
+
}
|
635
|
+
|
636
|
+
# Generate the description
|
637
|
+
description = generate_with_model(
|
638
|
+
client=client,
|
639
|
+
prompt=describe_prompt,
|
640
|
+
messages=describe_messages,
|
641
|
+
args=args,
|
642
|
+
stream_setup=stream_setup_desc,
|
643
|
+
use_stream_prettify=use_stream_prettify_desc,
|
644
|
+
should_stream=should_stream,
|
645
|
+
spinner_message="Generating command description...",
|
646
|
+
temp_override=0.3,
|
647
|
+
logger=logger
|
648
|
+
)
|
649
|
+
|
650
|
+
if not description:
|
651
|
+
return # Error already printed
|
652
|
+
|
653
|
+
# Log the generated description if logging is enabled
|
654
|
+
if logger:
|
655
|
+
logger.log("assistant", description)
|
656
|
+
|
657
|
+
# Format with proper markdown for streaming prettify - only if needed
|
658
|
+
if use_stream_prettify_desc and stream_setup_desc['stream_callback'] and description:
|
659
|
+
# Format description as markdown for prettier display
|
660
|
+
md_description = f"### Command Description\n\n{description}"
|
661
|
+
# Update the live display with the formatted description
|
662
|
+
stream_setup_desc['stream_callback'](md_description, complete=True)
|
663
|
+
|
664
|
+
# Display the description
|
665
|
+
display_content(
|
666
|
+
content=description,
|
667
|
+
content_type='description',
|
668
|
+
highlight_lang=highlight_lang,
|
669
|
+
args=args,
|
670
|
+
use_stream_prettify=use_stream_prettify_desc,
|
671
|
+
use_regular_prettify=use_regular_prettify_desc
|
672
|
+
)
|
673
|
+
elif response == 'a' or response == '':
|
674
|
+
print("\nCommand aborted.")
|
675
|
+
|
676
|
+
# Log the abort if logging is enabled
|
677
|
+
if logger:
|
678
|
+
logger.log("system", "Command aborted by user")
|
ngpt/utils/__init__.py
CHANGED
@@ -27,6 +27,7 @@ from .web_search import (
|
|
27
27
|
get_web_search_results,
|
28
28
|
format_web_search_results_for_prompt
|
29
29
|
)
|
30
|
+
from .pipe import process_piped_input
|
30
31
|
|
31
32
|
__all__ = [
|
32
33
|
"create_logger", "Logger",
|
@@ -35,5 +36,6 @@ __all__ = [
|
|
35
36
|
"load_cli_config", "set_cli_config_option", "get_cli_config_option",
|
36
37
|
"unset_cli_config_option", "apply_cli_config", "list_cli_config_options",
|
37
38
|
"CLI_CONFIG_OPTIONS", "get_cli_config_dir", "get_cli_config_path",
|
38
|
-
"enhance_prompt_with_web_search", "get_web_search_results", "format_web_search_results_for_prompt"
|
39
|
+
"enhance_prompt_with_web_search", "get_web_search_results", "format_web_search_results_for_prompt",
|
40
|
+
"process_piped_input"
|
39
41
|
]
|
ngpt/utils/pipe.py
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
import sys
|
2
|
+
|
3
|
+
def process_piped_input(prompt, logger=None):
|
4
|
+
"""Process piped input to be used with a prompt.
|
5
|
+
|
6
|
+
Args:
|
7
|
+
prompt: The prompt string which may contain a {} placeholder
|
8
|
+
logger: Optional logger instance
|
9
|
+
|
10
|
+
Returns:
|
11
|
+
str: The processed prompt with piped input inserted at placeholder, or appended
|
12
|
+
"""
|
13
|
+
# Import COLORS here to avoid circular imports
|
14
|
+
from ..cli.formatters import COLORS
|
15
|
+
|
16
|
+
# Only process if stdin is not a TTY (i.e., if we're receiving piped input)
|
17
|
+
if not sys.stdin.isatty():
|
18
|
+
# Read input from stdin
|
19
|
+
stdin_content = sys.stdin.read().strip()
|
20
|
+
|
21
|
+
# If we have stdin content and prompt is provided
|
22
|
+
if stdin_content and prompt:
|
23
|
+
placeholder = "{}"
|
24
|
+
|
25
|
+
# Check if the placeholder exists in the prompt
|
26
|
+
if placeholder not in prompt:
|
27
|
+
print(f"{COLORS['yellow']}Warning: Placeholder '{placeholder}' not found in prompt. Appending stdin content to the end.{COLORS['reset']}")
|
28
|
+
processed_prompt = f"{prompt}\n\n{stdin_content}"
|
29
|
+
else:
|
30
|
+
# Replace the placeholder in the prompt with stdin content
|
31
|
+
processed_prompt = prompt.replace(placeholder, stdin_content)
|
32
|
+
|
33
|
+
# Log if a logger is provided
|
34
|
+
if logger:
|
35
|
+
logger.log("info", f"Processed piped input: Combined prompt with stdin content")
|
36
|
+
|
37
|
+
return processed_prompt
|
38
|
+
|
39
|
+
# If we have stdin content but no prompt, just use the stdin content
|
40
|
+
elif stdin_content:
|
41
|
+
return stdin_content
|
42
|
+
elif prompt:
|
43
|
+
# We have prompt but stdin is empty
|
44
|
+
print(f"{COLORS['yellow']}Warning: No stdin content received. Using only the provided prompt.{COLORS['reset']}")
|
45
|
+
return prompt
|
46
|
+
else:
|
47
|
+
# No stdin content and no prompt
|
48
|
+
print(f"{COLORS['yellow']}Error: No stdin content and no prompt provided.{COLORS['reset']}")
|
49
|
+
return ""
|
50
|
+
|
51
|
+
# If no stdin or no content in stdin, just return the original prompt
|
52
|
+
return prompt
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ngpt
|
3
|
-
Version: 3.
|
3
|
+
Version: 3.8.0
|
4
4
|
Summary: Swiss army knife for LLMs: powerful CLI and interactive chatbot in one package. Seamlessly work with OpenAI, Ollama, Groq, Claude, Gemini, or any OpenAI-compatible API to generate code, craft git commits, rewrite text, and execute shell commands.
|
5
5
|
Project-URL: Homepage, https://github.com/nazdridoy/ngpt
|
6
6
|
Project-URL: Repository, https://github.com/nazdridoy/ngpt
|
@@ -150,7 +150,19 @@ ngpt --code --stream-prettify "function to calculate the Fibonacci sequence"
|
|
150
150
|
ngpt --shell "list all files in the current directory"
|
151
151
|
|
152
152
|
# Read from stdin and use the content in your prompt
|
153
|
-
echo "What is this text about?" | ngpt
|
153
|
+
echo "What is this text about?" | ngpt --pipe "Analyze the following text: {}"
|
154
|
+
|
155
|
+
# Using here-string (<<<) for quick single-line input
|
156
|
+
ngpt --pipe {} <<< "What is the best way to learn shell redirects?"
|
157
|
+
|
158
|
+
# Using standard input redirection to process file contents
|
159
|
+
ngpt --pipe "summarise {}" < README.md
|
160
|
+
|
161
|
+
# Using here-document (<<EOF) for multiline input
|
162
|
+
ngpt --pipe {} << EOF
|
163
|
+
What is the best way to learn Golang?
|
164
|
+
Provide simple hello world example.
|
165
|
+
EOF
|
154
166
|
|
155
167
|
# Rewrite text to improve quality while preserving tone and meaning
|
156
168
|
echo "your text" | ngpt -r
|
@@ -173,6 +185,9 @@ ngpt -g --rec-chunk
|
|
173
185
|
# Process a diff file instead of staged changes
|
174
186
|
ngpt -g --diff /path/to/changes.diff
|
175
187
|
|
188
|
+
# Use piped diff content for commit message generation
|
189
|
+
git diff HEAD~1 | ngpt -g --pipe
|
190
|
+
|
176
191
|
# Generate a commit message with logging for debugging
|
177
192
|
ngpt -g --log commit_log.txt
|
178
193
|
|
@@ -201,7 +216,7 @@ ngpt --interactive --log conversation.log
|
|
201
216
|
ngpt --log "Tell me about quantum computing"
|
202
217
|
|
203
218
|
# Process text from stdin using the {} placeholder
|
204
|
-
cat README.md | ngpt
|
219
|
+
cat README.md | ngpt --pipe "Summarize this document: {}"
|
205
220
|
|
206
221
|
# Use different model providers by specifying the provider name
|
207
222
|
ngpt --provider Groq "Explain quantum computing"
|
@@ -249,13 +264,14 @@ For more examples and detailed usage, visit the [CLI Usage Guide](https://nazdri
|
|
249
264
|
|
250
265
|
```console
|
251
266
|
❯ ngpt -h
|
267
|
+
|
252
268
|
usage: ngpt [-h] [-v] [--language LANGUAGE] [--config [CONFIG]] [--config-index CONFIG_INDEX] [--provider PROVIDER]
|
253
269
|
[--remove] [--show-config] [--all] [--list-models] [--list-renderers] [--cli-config [COMMAND ...]]
|
254
|
-
[--api-key API_KEY] [--base-url BASE_URL] [--model MODEL] [--web-search] [--
|
255
|
-
[--top_p TOP_P] [--max_tokens MAX_TOKENS] [--log [FILE]] [--preprompt PREPROMPT]
|
256
|
-
--stream-prettify] [--renderer {auto,rich,glow}] [--rec-chunk] [--diff [FILE]]
|
257
|
-
[--analyses-chunk-size ANALYSES_CHUNK_SIZE] [--max-msg-lines MAX_MSG_LINES]
|
258
|
-
[--max-recursion-depth MAX_RECURSION_DEPTH] [-i | -s | -c | -t | -
|
270
|
+
[--api-key API_KEY] [--base-url BASE_URL] [--model MODEL] [--web-search] [--pipe]
|
271
|
+
[--temperature TEMPERATURE] [--top_p TOP_P] [--max_tokens MAX_TOKENS] [--log [FILE]] [--preprompt PREPROMPT]
|
272
|
+
[--no-stream | --prettify | --stream-prettify] [--renderer {auto,rich,glow}] [--rec-chunk] [--diff [FILE]]
|
273
|
+
[--chunk-size CHUNK_SIZE] [--analyses-chunk-size ANALYSES_CHUNK_SIZE] [--max-msg-lines MAX_MSG_LINES]
|
274
|
+
[--max-recursion-depth MAX_RECURSION_DEPTH] [-i | -s | -c | -t | -r | -g]
|
259
275
|
[prompt]
|
260
276
|
|
261
277
|
nGPT - Interact with AI language models via OpenAI-compatible APIs
|
@@ -288,6 +304,7 @@ Global Options::
|
|
288
304
|
--base-url BASE_URL Base URL for the API
|
289
305
|
--model MODEL Model to use
|
290
306
|
--web-search Enable web search capability using DuckDuckGo to enhance prompts with relevant information
|
307
|
+
--pipe Read from stdin and use content with prompt. Use {} in prompt as placeholder for stdin content. Can be used with any mode option except --text and --interactive
|
291
308
|
--temperature TEMPERATURE Set temperature (controls randomness, default: 0.7)
|
292
309
|
--top_p TOP_P Set top_p (controls diversity, default: 1.0)
|
293
310
|
--max_tokens MAX_TOKENS Set max response length in tokens
|
@@ -308,7 +325,7 @@ Git Commit Message Options::
|
|
308
325
|
--chunk-size CHUNK_SIZE Number of lines per chunk when chunking is enabled (default: 200)
|
309
326
|
--analyses-chunk-size ANALYSES_CHUNK_SIZE Number of lines per chunk when recursively chunking analyses (default: 200)
|
310
327
|
--max-msg-lines MAX_MSG_LINES Maximum number of lines in commit message before condensing (default: 20)
|
311
|
-
--max-recursion-depth MAX_RECURSION_DEPTH
|
328
|
+
--max-recursion-depth MAX_RECURSION_DEPTH Maximum recursion depth for commit message condensing (default: 3)
|
312
329
|
|
313
330
|
Modes (mutually exclusive)::
|
314
331
|
|
@@ -316,9 +333,9 @@ Modes (mutually exclusive)::
|
|
316
333
|
-s, --shell Generate and execute shell commands
|
317
334
|
-c, --code Generate code
|
318
335
|
-t, --text Enter multi-line text input (submit with Ctrl+D)
|
319
|
-
-p, --pipe Read from stdin and use content with prompt. Use {} in prompt as placeholder for stdin content
|
320
336
|
-r, --rewrite Rewrite text from stdin to be more natural while preserving tone and meaning
|
321
337
|
-g, --gitcommsg Generate AI-powered git commit messages from staged changes or diff file
|
338
|
+
|
322
339
|
```
|
323
340
|
|
324
341
|
> **Note**: For better visualization of conventional commit messages on GitHub, you can use the [GitHub Commit Labels](https://greasyfork.org/en/scripts/526153-github-commit-labels) userscript, which adds colorful labels to your commits.
|
@@ -2,27 +2,28 @@ ngpt/__init__.py,sha256=kpKhViLakwMdHZkuLht2vWcjt0uD_5gR33gvMhfXr6w,664
|
|
2
2
|
ngpt/__main__.py,sha256=j3eFYPOtCCFBOGh7NK5IWEnADnTMMSEB9GLyIDoW724,66
|
3
3
|
ngpt/client.py,sha256=XjpA2UnvrRvzk6_DzVEddUTzoPlF8koQ-cZURpHoT7c,9041
|
4
4
|
ngpt/cli/__init__.py,sha256=hebbDSMGiOd43YNnQP67uzr67Ue6rZPwm2czynr5iZY,43
|
5
|
-
ngpt/cli/args.py,sha256=
|
5
|
+
ngpt/cli/args.py,sha256=HYCDHhqP-BI_tibL1qGQ9we4483h_kCa2ksh-QxOeiU,12694
|
6
6
|
ngpt/cli/config_manager.py,sha256=NQQcWnjUppAAd0s0p9YAf8EyKS1ex5-0EB4DvKdB4dk,3662
|
7
7
|
ngpt/cli/formatters.py,sha256=HBYGlx_7eoAKyzfy0Vq5L0yn8yVKjngqYBukMmXCcz0,9401
|
8
|
-
ngpt/cli/main.py,sha256=
|
8
|
+
ngpt/cli/main.py,sha256=PVulo8Pm53-oQ2Pgc4G90YhwyPImt8j7HKmY38SJ7CM,28696
|
9
9
|
ngpt/cli/renderers.py,sha256=m71BeUXKynpKKGXFzwRSW1XngvyKiZ_xEsdujUbU0MA,16597
|
10
10
|
ngpt/cli/ui.py,sha256=tVJGTP1DWjCRq7ONFdOOKPHcVQz0MqiLyJtodKFabTk,9612
|
11
11
|
ngpt/cli/modes/__init__.py,sha256=KP7VR6Xw9k1p5Jcu0F38RDxSFvFIzH3j1ThDLNwznUI,363
|
12
|
-
ngpt/cli/modes/chat.py,sha256=
|
13
|
-
ngpt/cli/modes/code.py,sha256=
|
14
|
-
ngpt/cli/modes/gitcommsg.py,sha256=
|
12
|
+
ngpt/cli/modes/chat.py,sha256=1mH3nTDwU52qQJRliNUAFSqJWyyiJ6j0T5uVso-VnxE,6828
|
13
|
+
ngpt/cli/modes/code.py,sha256=QBFgMRPKJhxkYCmumoSkNSF15XNRGUDum5yuK8aBnyM,12662
|
14
|
+
ngpt/cli/modes/gitcommsg.py,sha256=iTg3KlZwI0lGMcmUa62b0ashwLcxegdEEvT29PPtpBc,49595
|
15
15
|
ngpt/cli/modes/interactive.py,sha256=TtBrZUX45CVfKOPvkb1ya7dIQhXLILtn7ajmfM9ohso,17419
|
16
|
-
ngpt/cli/modes/rewrite.py,sha256=
|
17
|
-
ngpt/cli/modes/shell.py,sha256=
|
16
|
+
ngpt/cli/modes/rewrite.py,sha256=yPmJPPkMHNxrnV-eoM0j6lMNRhdSAMXmcw2s9xG6TIo,10918
|
17
|
+
ngpt/cli/modes/shell.py,sha256=Bi5MDteUCOLc-KTecJtyUZKaY9Qab94xpN7qYz1_dOA,29487
|
18
18
|
ngpt/cli/modes/text.py,sha256=7t5WWXMFxGkBM5HMP4irbN9aQwxE2YgywjiVPep710k,6417
|
19
|
-
ngpt/utils/__init__.py,sha256=
|
19
|
+
ngpt/utils/__init__.py,sha256=_92f8eGMMOtQQA3uwgSRVwUEl1EIRFjWPUjcfGgI-eI,1244
|
20
20
|
ngpt/utils/cli_config.py,sha256=Ug8cECBTIuzOwkBWidLTfs-OAdOsCMJ2bNa70pOADfw,11195
|
21
21
|
ngpt/utils/config.py,sha256=wsArA4osnh8fKqOvtsPqqBxAz3DpdjtaWUFaRtnUdyc,10452
|
22
22
|
ngpt/utils/log.py,sha256=f1jg2iFo35PAmsarH8FVL_62plq4VXH0Mu2QiP6RJGw,15934
|
23
|
+
ngpt/utils/pipe.py,sha256=qRHF-Ma7bbU0cOcb1Yhe4S-kBavivtnnvLA3EYS4FY4,2162
|
23
24
|
ngpt/utils/web_search.py,sha256=w5ke4KJMRxq7r5jtbUXvspja6XhjoPZloVkZ0IvBXIE,30731
|
24
|
-
ngpt-3.
|
25
|
-
ngpt-3.
|
26
|
-
ngpt-3.
|
27
|
-
ngpt-3.
|
28
|
-
ngpt-3.
|
25
|
+
ngpt-3.8.0.dist-info/METADATA,sha256=O_lTxBgnFYmKUftGTShWc7MFdHzjXRPURp2QJVhtbmo,24503
|
26
|
+
ngpt-3.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
27
|
+
ngpt-3.8.0.dist-info/entry_points.txt,sha256=SqAAvLhMrsEpkIr4YFRdUeyuXQ9o0IBCeYgE6AVojoI,44
|
28
|
+
ngpt-3.8.0.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
|
29
|
+
ngpt-3.8.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|