ngpt 3.1.1__py3-none-any.whl → 3.2.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/interactive.py CHANGED
@@ -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
- print(f"\n{separator}\n")
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
- if not no_stream and not stream_prettify:
187
- print(f"\n{ngpt_header()}: {COLORS['reset']}", end="", flush=True)
188
- elif not stream_prettify:
189
- print(f"\n{ngpt_header()}: {COLORS['reset']}", flush=True)
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
- # If prettify is enabled with regular streaming
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
- # Get the correct header for interactive mode
209
- header = ngpt_header()
210
- live_display, stream_callback, setup_spinner = prettify_streaming_markdown(renderer, is_interactive=True, header_text=header)
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
- sys.stdout.write("\r" + " " * 100 + "\r")
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
- sys.stdout.write("\r" + " " * 100 + "\r")
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)
ngpt/cli/modes/chat.py CHANGED
@@ -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: Streaming disabled to enable markdown rendering.{COLORS['reset']}")
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:
ngpt/cli/modes/code.py CHANGED
@@ -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: Streaming disabled to enable regular markdown rendering (--prettify).{COLORS['reset']}")
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']}")
ngpt/cli/modes/rewrite.py CHANGED
@@ -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: Streaming disabled to enable markdown rendering.{COLORS['reset']}")
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:
ngpt/cli/modes/text.py CHANGED
@@ -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: Streaming disabled to enable markdown rendering.{COLORS['reset']}")
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:
ngpt/cli/renderers.py CHANGED
@@ -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
- console.print(md)
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
- md_obj = Markdown("")
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
- # Clear the spinner line completely before starting the display
279
- sys.stdout.write("\r" + " " * 100 + "\r")
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 - 1 # Account for minimal overhead
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()
ngpt/cli/ui.py CHANGED
@@ -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
- print(f"\r{colored_message} {char}", end="", flush=True)
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
- print(f"\r{colored_message} {char}", end="", flush=True)
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
- print("\r" + " " * (len(message) + 10) + "\r", end="", flush=True)
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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ngpt
3
- Version: 3.1.1
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
@@ -5,23 +5,23 @@ ngpt/cli/__init__.py,sha256=hebbDSMGiOd43YNnQP67uzr67Ue6rZPwm2czynr5iZY,43
5
5
  ngpt/cli/args.py,sha256=XQvofZs_WkbgUto3Dbx7Yw-AmPAQHh8kdHUe3uWy8w4,11382
6
6
  ngpt/cli/config_manager.py,sha256=NQQcWnjUppAAd0s0p9YAf8EyKS1ex5-0EB4DvKdB4dk,3662
7
7
  ngpt/cli/formatters.py,sha256=HBYGlx_7eoAKyzfy0Vq5L0yn8yVKjngqYBukMmXCcz0,9401
8
- ngpt/cli/interactive.py,sha256=Zep4yiGRFPVrJV1zp7xB5lXg7hVhXhbRlaVA4DgRBbQ,13241
8
+ ngpt/cli/interactive.py,sha256=4--9vucwV17n0aH3BNIB4mPGGL6tJYbfL4-h9ZxNezk,14842
9
9
  ngpt/cli/main.py,sha256=9um40RplKHSW5UHcUUO2cwMNqkGUhfQwikI1CHHFbnk,28926
10
- ngpt/cli/renderers.py,sha256=ovV0IexZyrjCk8LK5ZGVvf5K7wCLco4jRhDv_tp6Kvw,15184
11
- ngpt/cli/ui.py,sha256=m8qtd4cCSHBGHPUlHVdBEfun1G1Se4vLKTSgnS7QOKE,6775
10
+ ngpt/cli/renderers.py,sha256=m71BeUXKynpKKGXFzwRSW1XngvyKiZ_xEsdujUbU0MA,16597
11
+ ngpt/cli/ui.py,sha256=HoHDFpLiwMBP5wtMb8YYo244FMiqiPFRoBNcNGp6N0A,7310
12
12
  ngpt/cli/modes/__init__.py,sha256=R3aO662RIzWEOvr3moTrEI8Tpg0zDDyMGGh1-OxiRgM,285
13
- ngpt/cli/modes/chat.py,sha256=oibPaN4LAqrzIbWpw--ql0TwU0u_jlLCeNuxxyO7wPg,5887
14
- ngpt/cli/modes/code.py,sha256=AEsQKdL8mh4ImcI5R7TXzwEq_JBTkmS-L0AA_-dpHm0,5934
13
+ ngpt/cli/modes/chat.py,sha256=byOULc1pTl_16IxwRW5FxBgTy_WUF9AJ5TIhdfawzj0,5951
14
+ ngpt/cli/modes/code.py,sha256=CVZrqlbMS3Weej5xllBhw95qtlOKkNdHGN24trQDesw,5977
15
15
  ngpt/cli/modes/gitcommsg.py,sha256=rsfMoeOupmNp-5p5fsMSPAf18BbzXWq-4PF2HjEz6SY,46991
16
- ngpt/cli/modes/rewrite.py,sha256=CrzUHxUoZc48D3ooVIvDbCjIIuERO1v-ddG3ajhETQs,10646
16
+ ngpt/cli/modes/rewrite.py,sha256=2h3liGXch6cZyduj1HrEXpHTjKVDDV_zRJwB7fKdoGg,10710
17
17
  ngpt/cli/modes/shell.py,sha256=QkprnOxMMTg2v5DIwcofDnnr3JPNfuk-YgSQaae5Xps,3311
18
- ngpt/cli/modes/text.py,sha256=vCwZnVxIbsTCLE8YVJIPwZsGgk7VRs8v-eeaVwCOZqI,4702
18
+ ngpt/cli/modes/text.py,sha256=knz4vhOzCW2wbwRmFN2AdSl9twe8IgatlGIx-t7b9ZU,4766
19
19
  ngpt/utils/__init__.py,sha256=E46suk2-QgYBI0Qrs6WXOajOUOebF3ETAFY7ah8DTWs,942
20
20
  ngpt/utils/cli_config.py,sha256=IlHnOEEGpLoGZInynM778wgpxLVcJ_STKWxg2Ypvir4,11196
21
21
  ngpt/utils/config.py,sha256=wsArA4osnh8fKqOvtsPqqBxAz3DpdjtaWUFaRtnUdyc,10452
22
22
  ngpt/utils/log.py,sha256=f1jg2iFo35PAmsarH8FVL_62plq4VXH0Mu2QiP6RJGw,15934
23
- ngpt-3.1.1.dist-info/METADATA,sha256=YPavkwb6Jg4oblYwYtBX3XrDmvcQWVhQZO2d9r5yByI,28992
24
- ngpt-3.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
25
- ngpt-3.1.1.dist-info/entry_points.txt,sha256=SqAAvLhMrsEpkIr4YFRdUeyuXQ9o0IBCeYgE6AVojoI,44
26
- ngpt-3.1.1.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
27
- ngpt-3.1.1.dist-info/RECORD,,
23
+ ngpt-3.2.0.dist-info/METADATA,sha256=Oyhg9XpBGgDZ4M4wA700C0Vr92lQSVpWd0-OqXe5cHg,28992
24
+ ngpt-3.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
25
+ ngpt-3.2.0.dist-info/entry_points.txt,sha256=SqAAvLhMrsEpkIr4YFRdUeyuXQ9o0IBCeYgE6AVojoI,44
26
+ ngpt-3.2.0.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
27
+ ngpt-3.2.0.dist-info/RECORD,,
File without changes