ngpt 3.1.0__tar.gz → 3.2.0__tar.gz
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-3.1.0 → ngpt-3.2.0}/PKG-INFO +1 -1
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/interactive.py +40 -16
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/main.py +3 -3
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/modes/chat.py +1 -1
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/modes/code.py +1 -1
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/modes/rewrite.py +1 -1
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/modes/text.py +1 -1
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/renderers.py +45 -10
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/ui.py +17 -5
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/utils/config.py +7 -6
- {ngpt-3.1.0 → ngpt-3.2.0}/pyproject.toml +1 -1
- {ngpt-3.1.0 → ngpt-3.2.0}/uv.lock +1 -1
- {ngpt-3.1.0 → ngpt-3.2.0}/.github/workflows/aur-publish.yml +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/.github/workflows/python-publish.yml +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/.gitignore +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/.python-version +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/COMMIT_GUIDELINES.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/CONTRIBUTING.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/LICENSE +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/PKGBUILD +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/README.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/CONTRIBUTING.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/LICENSE.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/README.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/_config.yml +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/api/README.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/api/cli.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/api/cli_config.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/api/client.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/api/config.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/api/logging.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/assets/css/style.scss +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/configuration.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/examples/README.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/examples/advanced.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/examples/basic.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/examples/cli_components.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/examples/integrations.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/installation.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/overview.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/usage/README.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/usage/cli_config.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/usage/cli_framework.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/usage/cli_usage.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/usage/gitcommsg.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/docs/usage/library_usage.md +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/__init__.py +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/__main__.py +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/__init__.py +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/args.py +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/config_manager.py +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/formatters.py +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/modes/__init__.py +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/modes/gitcommsg.py +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/cli/modes/shell.py +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/client.py +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/utils/__init__.py +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/utils/cli_config.py +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/ngpt/utils/log.py +0 -0
- {ngpt-3.1.0 → ngpt-3.2.0}/wiki.md +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ngpt
|
3
|
-
Version: 3.
|
3
|
+
Version: 3.2.0
|
4
4
|
Summary: Swiss army knife for LLMs: powerful CLI, interactive chatbot, and flexible Python library. Works with OpenAI, Ollama, Groq, Claude, Gemini, and any OpenAI-compatible API.
|
5
5
|
Project-URL: Homepage, https://github.com/nazdridoy/ngpt
|
6
6
|
Project-URL: Repository, https://github.com/nazdridoy/ngpt
|
@@ -64,9 +64,16 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
|
|
64
64
|
if logger:
|
65
65
|
print(f"{COLORS['green']}Logging conversation to: {logger.get_log_path()}{COLORS['reset']}")
|
66
66
|
|
67
|
+
# Display a note about markdown rendering only once at the beginning
|
68
|
+
if prettify and not no_stream and not stream_prettify:
|
69
|
+
print(f"{COLORS['yellow']}Note: Using standard markdown rendering (--prettify). For streaming markdown rendering, use --stream-prettify instead.{COLORS['reset']}")
|
70
|
+
|
67
71
|
# Custom separator - use the same length for consistency
|
68
72
|
def print_separator():
|
69
|
-
|
73
|
+
# Make sure there's exactly one newline before and after
|
74
|
+
# Use sys.stdout.write for direct control, avoiding any extra newlines
|
75
|
+
sys.stdout.write(f"\n{separator}\n")
|
76
|
+
sys.stdout.flush()
|
70
77
|
|
71
78
|
# Initialize conversation history
|
72
79
|
system_prompt = preprompt if preprompt else "You are a helpful assistant."
|
@@ -182,19 +189,26 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
|
|
182
189
|
if logger:
|
183
190
|
logger.log("user", user_input)
|
184
191
|
|
185
|
-
# Print assistant indicator with formatting
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
192
|
+
# Print assistant indicator with formatting - but only if we're not going to show a rich formatted box
|
193
|
+
# With Rich prettify, no header should be printed as the Rich panel already includes it
|
194
|
+
should_print_header = True
|
195
|
+
|
196
|
+
# Determine if we should print a header based on formatting options
|
197
|
+
if prettify and renderer != 'glow':
|
198
|
+
# Don't print header for Rich prettify
|
199
|
+
should_print_header = False
|
200
|
+
|
201
|
+
# Print the header if needed
|
202
|
+
if should_print_header:
|
203
|
+
if not no_stream and not stream_prettify:
|
204
|
+
print(f"\n{ngpt_header()}: {COLORS['reset']}", end="", flush=True)
|
205
|
+
elif not stream_prettify:
|
206
|
+
print(f"\n{ngpt_header()}: {COLORS['reset']}", flush=True)
|
190
207
|
|
191
|
-
#
|
208
|
+
# Determine streaming behavior
|
192
209
|
if prettify and not no_stream and not stream_prettify:
|
193
|
-
print(f"\n{COLORS['yellow']}Note: Streaming disabled to enable markdown rendering.{COLORS['reset']}")
|
194
|
-
print(f"\n{ngpt_header()}: {COLORS['reset']}", flush=True)
|
195
210
|
should_stream = False
|
196
211
|
else:
|
197
|
-
# Regular behavior with stream-prettify taking precedence
|
198
212
|
should_stream = not no_stream
|
199
213
|
|
200
214
|
# Setup for stream-prettify
|
@@ -205,9 +219,15 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
|
|
205
219
|
first_content_received = False
|
206
220
|
|
207
221
|
if stream_prettify and should_stream:
|
208
|
-
#
|
209
|
-
|
210
|
-
|
222
|
+
# Don't pass the header_text for rich renderer as it already creates its own header,
|
223
|
+
# but pass it for other renderers like glow
|
224
|
+
if renderer == 'rich' or renderer == 'auto':
|
225
|
+
live_display, stream_callback, setup_spinner = prettify_streaming_markdown(renderer, is_interactive=True)
|
226
|
+
else:
|
227
|
+
# Get the correct header for interactive mode for non-rich renderers
|
228
|
+
header = ngpt_header()
|
229
|
+
live_display, stream_callback, setup_spinner = prettify_streaming_markdown(renderer, is_interactive=True, header_text=header)
|
230
|
+
|
211
231
|
if not live_display:
|
212
232
|
# Fallback to normal prettify if live display setup failed
|
213
233
|
prettify = True
|
@@ -229,8 +249,9 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
|
|
229
249
|
if stop_spinner_func:
|
230
250
|
stop_spinner_func()
|
231
251
|
|
232
|
-
# Clear the spinner line completely
|
233
|
-
|
252
|
+
# Clear the spinner line completely without leaving extra newlines
|
253
|
+
# Use direct terminal control to ensure consistency
|
254
|
+
sys.stdout.write("\r" + " " * shutil.get_terminal_size().columns + "\r")
|
234
255
|
sys.stdout.flush()
|
235
256
|
|
236
257
|
# Now start the live display
|
@@ -264,7 +285,9 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
|
|
264
285
|
# Ensure spinner is stopped if no content was received
|
265
286
|
if stop_spinner_event and not first_content_received:
|
266
287
|
stop_spinner_event.set()
|
267
|
-
|
288
|
+
# Clear the spinner line completely without leaving extra newlines
|
289
|
+
# Use direct terminal control to ensure consistency
|
290
|
+
sys.stdout.write("\r" + " " * shutil.get_terminal_size().columns + "\r")
|
268
291
|
sys.stdout.flush()
|
269
292
|
|
270
293
|
# Stop live display if using stream-prettify
|
@@ -281,6 +304,7 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
|
|
281
304
|
# Print response if not streamed (either due to no_stream or prettify)
|
282
305
|
if no_stream or prettify:
|
283
306
|
if prettify:
|
307
|
+
# For pretty formatting with rich, don't print any header text as the rich renderer already includes it
|
284
308
|
prettify_markdown(response, renderer)
|
285
309
|
else:
|
286
310
|
print(response)
|
@@ -297,12 +297,12 @@ def main():
|
|
297
297
|
elif len(matching_configs) > 1:
|
298
298
|
print(f"Multiple configurations found for provider '{effective_provider}':")
|
299
299
|
for i, idx in enumerate(matching_configs):
|
300
|
-
print(f" [{i}]
|
300
|
+
print(f" Choice [{i+1}] → Config #{idx}: {configs[idx].get('model', 'Unknown model')}")
|
301
301
|
|
302
302
|
try:
|
303
303
|
choice = input("Choose a configuration to remove (or press Enter to cancel): ")
|
304
|
-
if choice and choice.isdigit() and
|
305
|
-
config_index = matching_configs[int(choice)]
|
304
|
+
if choice and choice.isdigit() and 1 <= int(choice) <= len(matching_configs):
|
305
|
+
config_index = matching_configs[int(choice)-1]
|
306
306
|
else:
|
307
307
|
print("Configuration removal cancelled.")
|
308
308
|
return
|
@@ -87,7 +87,7 @@ def chat_mode(client, args, logger=None):
|
|
87
87
|
|
88
88
|
# If regular prettify is enabled with streaming, inform the user
|
89
89
|
if args.prettify and not args.no_stream:
|
90
|
-
print(f"{COLORS['yellow']}Note:
|
90
|
+
print(f"{COLORS['yellow']}Note: Using standard markdown rendering (--prettify). For streaming markdown rendering, use --stream-prettify instead.{COLORS['reset']}")
|
91
91
|
|
92
92
|
# Show a static message if live_display is not available
|
93
93
|
if args.stream_prettify and not live_display:
|
@@ -62,7 +62,7 @@ def code_mode(client, args, logger=None):
|
|
62
62
|
if has_markdown_renderer(args.renderer):
|
63
63
|
should_stream = False
|
64
64
|
use_regular_prettify = True
|
65
|
-
print(f"{COLORS['yellow']}Note:
|
65
|
+
print(f"{COLORS['yellow']}Note: Using standard markdown rendering (--prettify). For streaming markdown rendering, use --stream-prettify instead.{COLORS['reset']}")
|
66
66
|
else:
|
67
67
|
# Renderer not available for prettify
|
68
68
|
print(f"{COLORS['yellow']}Warning: Renderer '{args.renderer}' not available for --prettify.{COLORS['reset']}")
|
@@ -156,7 +156,7 @@ def rewrite_mode(client, args, logger=None):
|
|
156
156
|
|
157
157
|
# If regular prettify is enabled with streaming, inform the user
|
158
158
|
if args.prettify and not args.no_stream:
|
159
|
-
print(f"{COLORS['yellow']}Note:
|
159
|
+
print(f"{COLORS['yellow']}Note: Using standard markdown rendering (--prettify). For streaming markdown rendering, use --stream-prettify instead.{COLORS['reset']}")
|
160
160
|
|
161
161
|
# Show a static message if live_display is not available
|
162
162
|
if args.stream_prettify and not live_display:
|
@@ -58,7 +58,7 @@ def text_mode(client, args, logger=None):
|
|
58
58
|
|
59
59
|
# If regular prettify is enabled with streaming, inform the user
|
60
60
|
if args.prettify and not args.no_stream:
|
61
|
-
print(f"{COLORS['yellow']}Note:
|
61
|
+
print(f"{COLORS['yellow']}Note: Using standard markdown rendering (--prettify). For streaming markdown rendering, use --stream-prettify instead.{COLORS['reset']}")
|
62
62
|
|
63
63
|
# Show a static message if live_display is not available
|
64
64
|
if args.stream_prettify and not live_display:
|
@@ -168,8 +168,31 @@ def prettify_markdown(text, renderer='auto'):
|
|
168
168
|
# Use rich
|
169
169
|
try:
|
170
170
|
console = Console()
|
171
|
+
|
172
|
+
# Create a panel around the markdown for consistency with stream_prettify
|
173
|
+
from rich.panel import Panel
|
174
|
+
import rich.box
|
175
|
+
from rich.text import Text
|
176
|
+
|
177
|
+
# Get terminal dimensions
|
178
|
+
term_width = shutil.get_terminal_size().columns
|
179
|
+
|
180
|
+
# Create panel with similar styling to stream_prettify
|
181
|
+
clean_header = "🤖 nGPT"
|
182
|
+
panel_title = Text(clean_header, style="cyan bold")
|
183
|
+
|
171
184
|
md = Markdown(text)
|
172
|
-
|
185
|
+
panel = Panel(
|
186
|
+
md,
|
187
|
+
title=panel_title,
|
188
|
+
title_align="left",
|
189
|
+
border_style="cyan",
|
190
|
+
padding=(1, 1),
|
191
|
+
width=console.width - 4, # Make panel slightly narrower than console
|
192
|
+
box=rich.box.ROUNDED
|
193
|
+
)
|
194
|
+
|
195
|
+
console.print(panel)
|
173
196
|
return True
|
174
197
|
except Exception as e:
|
175
198
|
print(f"{COLORS['yellow']}Error using rich for markdown: {str(e)}{COLORS['reset']}")
|
@@ -237,7 +260,20 @@ def prettify_streaming_markdown(renderer='rich', is_interactive=False, header_te
|
|
237
260
|
box=rich.box.ROUNDED
|
238
261
|
)
|
239
262
|
else:
|
240
|
-
|
263
|
+
# Always use a panel - even in non-interactive mode
|
264
|
+
clean_header = "🤖 nGPT"
|
265
|
+
panel_title = Text(clean_header, style="cyan bold")
|
266
|
+
|
267
|
+
padding = (1, 1) # Less horizontal padding (left, right)
|
268
|
+
md_obj = Panel(
|
269
|
+
Markdown(""),
|
270
|
+
title=panel_title,
|
271
|
+
title_align="left",
|
272
|
+
border_style="cyan",
|
273
|
+
padding=padding,
|
274
|
+
width=console.width - 4, # Make panel slightly narrower than console
|
275
|
+
box=rich.box.ROUNDED
|
276
|
+
)
|
241
277
|
|
242
278
|
# Get terminal dimensions for better display
|
243
279
|
term_width = shutil.get_terminal_size().columns
|
@@ -275,9 +311,8 @@ def prettify_streaming_markdown(renderer='rich', is_interactive=False, header_te
|
|
275
311
|
# Start live display on first content
|
276
312
|
if first_update:
|
277
313
|
first_update = False
|
278
|
-
#
|
279
|
-
|
280
|
-
sys.stdout.flush()
|
314
|
+
# Let the spinner's clean_exit handle the cleanup
|
315
|
+
# No additional cleanup needed here
|
281
316
|
live.start()
|
282
317
|
|
283
318
|
# Update content in live display
|
@@ -304,17 +339,17 @@ def prettify_streaming_markdown(renderer='rich', is_interactive=False, header_te
|
|
304
339
|
if not is_complete:
|
305
340
|
# Calculate approximate lines needed
|
306
341
|
content_lines = content.count('\n') + 1
|
307
|
-
available_height = display_height -
|
342
|
+
available_height = display_height - 4 # Account for panel borders and padding
|
308
343
|
|
309
344
|
if content_lines > available_height:
|
310
345
|
# If content is too big, show only the last part that fits
|
311
346
|
lines = content.split('\n')
|
312
347
|
truncated_content = '\n'.join(lines[-available_height:])
|
313
|
-
md_obj = Markdown(truncated_content)
|
348
|
+
md_obj.renderable = Markdown(truncated_content)
|
314
349
|
else:
|
315
|
-
md_obj = Markdown(content)
|
350
|
+
md_obj.renderable = Markdown(content)
|
316
351
|
else:
|
317
|
-
md_obj = Markdown(content)
|
352
|
+
md_obj.renderable = Markdown(content)
|
318
353
|
|
319
354
|
live.update(md_obj)
|
320
355
|
|
@@ -344,7 +379,7 @@ def prettify_streaming_markdown(renderer='rich', is_interactive=False, header_te
|
|
344
379
|
spinner_thread = threading.Thread(
|
345
380
|
target=spinner,
|
346
381
|
args=(message,),
|
347
|
-
kwargs={"stop_event": stop_event, "color": color}
|
382
|
+
kwargs={"stop_event": stop_event, "color": color, "clean_exit": True}
|
348
383
|
)
|
349
384
|
spinner_thread.daemon = True
|
350
385
|
spinner_thread.start()
|
@@ -155,7 +155,7 @@ def get_multiline_input():
|
|
155
155
|
print("\nInput cancelled by user. Exiting gracefully.")
|
156
156
|
return None
|
157
157
|
|
158
|
-
def spinner(message, duration=5, spinner_chars="⣾⣽⣻⢿⡿⣟⣯⣷", color=None, stop_event=None):
|
158
|
+
def spinner(message, duration=5, spinner_chars="⣾⣽⣻⢿⡿⣟⣯⣷", color=None, stop_event=None, clean_exit=False):
|
159
159
|
"""Display a spinner animation with a message.
|
160
160
|
|
161
161
|
Args:
|
@@ -165,6 +165,7 @@ def spinner(message, duration=5, spinner_chars="⣾⣽⣻⢿⡿⣟⣯⣷", color
|
|
165
165
|
color: Optional color from COLORS dict to use for the message
|
166
166
|
stop_event: Optional threading.Event to signal when to stop the spinner
|
167
167
|
If provided, duration is ignored and spinner runs until event is set
|
168
|
+
clean_exit: When True, cleans up more aggressively to prevent blank lines
|
168
169
|
"""
|
169
170
|
char_duration = 0.2
|
170
171
|
|
@@ -173,19 +174,30 @@ def spinner(message, duration=5, spinner_chars="⣾⣽⣻⢿⡿⣟⣯⣷", color
|
|
173
174
|
if color:
|
174
175
|
colored_message = f"{color}{message}{COLORS['reset']}"
|
175
176
|
|
177
|
+
# Save cursor position - will be needed for clean exit
|
178
|
+
if clean_exit:
|
179
|
+
# Start by printing a \r to ensure we begin at the start of the line
|
180
|
+
sys.stdout.write("\r")
|
181
|
+
sys.stdout.flush()
|
182
|
+
|
176
183
|
if stop_event:
|
177
184
|
i = 0
|
178
185
|
while not stop_event.is_set():
|
179
186
|
char = spinner_chars[i % len(spinner_chars)]
|
180
|
-
|
187
|
+
# Always use sys.stdout.write for consistent behavior
|
188
|
+
sys.stdout.write(f"\r{colored_message} {char}")
|
189
|
+
sys.stdout.flush()
|
181
190
|
i += 1
|
182
191
|
time.sleep(char_duration)
|
183
192
|
else:
|
184
193
|
total_chars = int(duration / char_duration)
|
185
194
|
for i in range(total_chars):
|
186
195
|
char = spinner_chars[i % len(spinner_chars)]
|
187
|
-
|
196
|
+
sys.stdout.write(f"\r{colored_message} {char}")
|
197
|
+
sys.stdout.flush()
|
188
198
|
time.sleep(char_duration)
|
189
199
|
|
190
|
-
# Clear the line when done
|
191
|
-
|
200
|
+
# Clear the line when done - use terminal width to clear the entire line
|
201
|
+
terminal_width = shutil.get_terminal_size().columns
|
202
|
+
sys.stdout.write("\r" + " " * terminal_width + "\r")
|
203
|
+
sys.stdout.flush()
|
@@ -172,18 +172,19 @@ def load_config(custom_path: Optional[str] = None, config_index: int = 0, provid
|
|
172
172
|
elif len(matching_configs) > 1:
|
173
173
|
print(f"Warning: Multiple configurations found for provider '{provider}'.")
|
174
174
|
for i, idx in enumerate(matching_configs):
|
175
|
-
print(f" [{i}]
|
175
|
+
print(f" Choice [{i+1}] → Config #{idx}: {configs[idx].get('model', 'Unknown model')}")
|
176
176
|
|
177
177
|
try:
|
178
|
-
choice = input("
|
179
|
-
if choice and choice.isdigit() and
|
180
|
-
config_index = matching_configs[int(choice)]
|
178
|
+
choice = input("Enter choice number (or press Enter for the first one): ")
|
179
|
+
if choice and choice.isdigit() and 1 <= int(choice) <= len(matching_configs):
|
180
|
+
config_index = matching_configs[int(choice)-1]
|
181
|
+
print(f"Selected configuration #{config_index}.")
|
181
182
|
else:
|
182
183
|
config_index = matching_configs[0]
|
183
|
-
print(f"Using first matching configuration (
|
184
|
+
print(f"Using first matching configuration (config #{config_index}).")
|
184
185
|
except (ValueError, IndexError, KeyboardInterrupt):
|
185
186
|
config_index = matching_configs[0]
|
186
|
-
print(f"Using first matching configuration (
|
187
|
+
print(f"Using first matching configuration (config #{config_index}).")
|
187
188
|
else:
|
188
189
|
config_index = matching_configs[0]
|
189
190
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "ngpt"
|
3
|
-
version = "3.
|
3
|
+
version = "3.2.0"
|
4
4
|
description = "Swiss army knife for LLMs: powerful CLI, interactive chatbot, and flexible Python library. Works with OpenAI, Ollama, Groq, Claude, Gemini, and any OpenAI-compatible API."
|
5
5
|
authors = [
|
6
6
|
{name = "nazDridoy", email = "nazdridoy399@gmail.com"},
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|